ellf-cli 17.0.207__tar.gz → 18.0.2__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-17.0.207/ellf_cli.egg-info → ellf_cli-18.0.2}/PKG-INFO +1 -1
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/README.md +2 -2
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/_recipe_subcommand.py +1 -1
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/agents.py +14 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/import_export.py +110 -14
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/projects.py +4 -1
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/publish_code.py +104 -24
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/tasks.py +7 -3
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf.json +8 -6
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/messages.py +7 -3
- ellf_cli-18.0.2/ellf_cli/version/17.0.207_2026-09-11-15-26-02_major_3f1b3ed7-eaac-41f5-b740-0af3331595d9 +3 -0
- ellf_cli-18.0.2/ellf_cli/version/18.0.0_2026-09-12-10-01-10_patch_8995783a-a9fa-4c4e-90b3-6386a4c481eb +3 -0
- ellf_cli-18.0.2/ellf_cli/version/18.0.1_2026-09-12-17-33-30_patch_f6ea2c99-a715-4c4d-876a-23ed8c62a83d +3 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2/ellf_cli.egg-info}/PKG-INFO +1 -1
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli.egg-info/SOURCES.txt +5 -0
- ellf_cli-18.0.2/tests/test_import_export.py +260 -0
- ellf_cli-18.0.2/tests/test_publish_import_root.py +120 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/LICENSE +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/MANIFEST.in +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/__init__.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/__main__.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/about.json +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/about.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/appdirs.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/auth.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/cli.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/cloud/__init__.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/cloud/gcp.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/cluster_config.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/__init__.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/_cluster_select.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/_org_select.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/_project_scope.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/_recipe_file.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/_state.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/_usage.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/actions.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/assets.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/auth.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/clusters.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/config.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/datasets.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/files/__init__.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/files/cp.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/files/ls.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/files/rm.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/files/rsync.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/files/stats.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/general.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/infra/__init__.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/infra/_config_io.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/infra/_helpers.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/infra/configure.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/infra/deploy.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/infra/init.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/infra/init_values.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/infra/provision.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/infra/register.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/infra/setup.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/infra/start.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/infra/terraform.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/infra/tls.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/jobs.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/packages.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/paths.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/plans.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/publish_data.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/recipes.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/secrets.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/services.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/support.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/todos.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/commands/users.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/config.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/.claude-plugin/plugin.json +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/.gitignore +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skill_variants.json +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-annotate.assistant/SKILL.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-annotate.assistant/references/annotation_audit.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-annotate.assistant/references/builtin_ellf_annotation_recipes.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-annotate.coding/SKILL.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-annotate.coding/references/annotation_audit.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-annotate.coding/references/builtin_ellf_annotation_recipes.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-annotate.coding/references/builtin_prodigy_recipes.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-ask/SKILL.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-handoff/SKILL.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-monitor.assistant/SKILL.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-monitor.assistant/references/annotation_metrics.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-monitor.assistant/references/training_monitoring.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-monitor.coding/SKILL.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-monitor.coding/references/annotation_metrics.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-monitor.coding/references/training_monitoring.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-monitor.coding/scripts/check_training.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-ops.assistant/SKILL.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-ops.coding/SKILL.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-ops.coding/references/data_infra_cli.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-ops.coding/scripts/run_job.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-patterns/SKILL.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-patterns/references/pattern_strategies.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-prodigy/SKILL.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_action_recipe.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_agent_recipe.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_blocks_ui.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_correct.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_custom_ui.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_manual.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_pages_ui.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_routing.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_task_recipe.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_teach.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-prodigy/references/builtin_recipes.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-prodigy/references/ellf_recipe_sdk.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-prodigy/references/lint_recipe.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-prodigy/references/prodigy_recipe_api.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-prodigy/references/template_index.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-project.assistant/SKILL.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-project.assistant/references/consulting_patterns.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-project.assistant/references/explosion_strategy.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-project.assistant/references/prodigy_llm_bot.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-project.coding/SKILL.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-project.coding/references/consulting_patterns.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-project.coding/references/explosion_strategy.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-project.coding/references/prodigy_llm_bot.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-support.assistant/SKILL.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-support.coding/SKILL.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-todo/SKILL.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-train.assistant/SKILL.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-train.assistant/references/diagnostics.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-train.assistant/references/evaluation_guide.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-train.assistant/references/model_selection.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-train.assistant/references/training_paradigms.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-train.assistant/references/workflow.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-train.coding/SKILL.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-train.coding/references/config_advanced.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-train.coding/references/config_architectures.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-train.coding/references/config_training.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-train.coding/references/diagnostics.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-train.coding/references/evaluation_guide.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-train.coding/references/experiment_patterns.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-train.coding/references/model_selection.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-train.coding/references/training_paradigms.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-train.coding/references/training_troubleshooting.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-train.coding/references/workflow.md +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ellf_skills/skills/ellf-train.coding/scripts/ellf_logger.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/errors.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/helm.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/interactivity.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/key_pair.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/main.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/pypi_auth.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/query.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/recipes_cookiecutter/cookiecutter.json +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/.gitignore +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/README.md.tmpl +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/requirements-dev.in +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/requirements.in +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/setup.py.tmpl +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/{{cookiecutter.package_name}}/__init__.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/{{cookiecutter.package_name}}/about.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/{{cookiecutter.package_name}}/recipes/__init__.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/{{cookiecutter.package_name}}/recipes/example_task.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/testing/__init__.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ty.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/ui.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/url.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/util.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.114_2026-08-19-08-36-57__1a4e37c1-471e-4a81-b4d5-1ffe7dec3a61 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.114_2026-08-19-09-25-21_patch_33ff1e26-0e5e-46d8-8052-ab1d032e8b6c +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.115_2026-08-19-09-27-13_patch_4ac728c3-0342-4a36-adbc-e28d2d99b1ba +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.116_2026-08-19-09-30-47_patch_c706fef6-6813-4183-b268-12e7b00d7d0b +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.117_2026-08-19-09-39-14_patch_c6b0495f-2f88-4aa8-9ebf-0a235b4c407e +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.118_2026-08-19-09-52-00_patch_b395655a-8d78-4344-9266-6901c20f6517 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.119_2026-08-19-10-22-38_patch_8f95c49d-aedc-41b8-816d-ccb95f048831 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.120_2026-08-19-10-26-58_patch_7fbf35e6-cc01-4b75-8c9a-f5d054bcaf93 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.121_2026-08-19-10-42-02_patch_31a81b02-a886-4dea-84e0-67cc6ca412d5 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.122_2026-08-19-11-16-49_patch_1c4d7307-48d4-493e-936d-80af799d644b +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.123_2026-08-19-11-47-32_patch_d3d78d04-9989-45b1-91b1-93b571d8814a +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.124_2026-08-20-08-03-17_patch_57f4c389-2d19-41ce-aca4-13b61c62f387 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.125_2026-08-20-08-25-05_patch_7f368148-0d1c-4525-bbdf-c6d257ad861a +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.126_2026-08-20-14-51-36_patch_a99abcc8-3371-4c63-a759-bb7ea5b32432 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.127_2026-08-21-05-48-58_patch_34b4ccee-7596-4f57-93e9-774973cca58e +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.128_2026-08-21-10-47-20_patch_693112b9-927c-4a8f-8553-b06eef86079d +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.129_2026-08-21-11-16-54_patch_7a981666-01d9-4d1b-9ba3-2129744e3bd5 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.130_2026-08-21-13-09-36_patch_b7ad1a76-ce70-4d70-9d1a-7c414eba5bb4 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.131_2026-08-21-14-31-10_patch_9128ef66-193e-4caf-8710-afd86128e638 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.132_2026-08-24-08-59-34_patch_c600fc14-86a5-460f-9c4e-f0177056f756 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.133_2026-08-24-10-28-29_patch_b6ce3e32-9d7b-48ac-981a-270c22c180dd +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.134_2026-08-24-11-00-51_patch_a4fc0484-4a64-46a2-94f8-aa805254c7b0 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.135_2026-08-24-12-12-41_patch_a870a56f-9a65-49eb-92da-f572b26667d8 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.136_2026-08-24-12-42-46_patch_9b399356-6482-4291-8da7-e748a99c4b53 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.137_2026-08-24-12-53-57_patch_f7979348-1910-40cd-a535-90299eca84ee +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.138_2026-08-24-14-36-30_patch_ed0f8750-f663-498b-85e7-0d3f2213fcfe +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.139_2026-08-25-08-37-26_patch_3ba87e19-902b-4f76-b64c-90e5c0ca1fc6 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.140_2026-08-25-09-52-45_patch_abc830f2-df15-4385-b797-cdaf72c10752 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.141_2026-08-25-11-37-20_patch_1d9ec01e-06f6-4c8e-b095-af17d5b62b00 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.142_2026-08-26-07-38-49_patch_3dd23e36-e7df-494c-986f-a412861a8d40 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.143_2026-08-26-09-32-32_patch_7b7ebd34-de27-4b73-98a7-3e1501e16cd5 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.144_2026-08-26-10-56-43_patch_166064f2-2208-4c9b-9e25-25dc4cd32084 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.145_2026-08-26-11-37-29_patch_b997f3ee-d7d3-4408-9f2f-c8b6d08bdb3b +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.146_2026-08-26-12-15-20_patch_afbbb893-25db-468c-81db-2663ca373068 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.147_2026-08-26-14-10-50_patch_fd753b1f-2fa1-4ddb-a9e7-de690da85254 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.148_2026-08-27-09-08-35_patch_23814c0b-7f77-489c-b4a8-ceadd18146e3 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.149_2026-08-27-09-40-17_patch_8879d72e-a3d8-4abc-a16a-d9dcf75f0fa1 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.150_2026-08-27-10-57-25_patch_afbe60ed-3e23-4d88-be3c-f800d7002a7d +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.151_2026-08-28-09-02-46_patch_1d4ca23b-aeae-4ea8-96b1-8cf57371f29d +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.152_2026-08-28-15-37-24_patch_c9d0bdc1-a7e1-432b-92b9-92dc76fe0c78 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.153_2026-08-29-09-03-27_patch_7531573a-1b33-4092-aa9c-0e65f47b201f +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.154_2026-08-29-11-56-35_patch_919f5787-39c8-4dc1-ae6a-f27c28380f21 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.155_2026-08-29-15-15-59_patch_822f9ed8-c366-4e5f-9791-dee8467cf3ce +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.156_2026-08-29-16-19-01_patch_3ac2bf36-20f7-4c9f-9fdb-295d4466d59e +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.157_2026-08-30-17-32-17_patch_4302354c-c9c5-4be7-acd3-542872a748d8 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.158_2026-08-31-09-28-33_patch_3523774f-c25d-4102-8c1a-92fff046a48d +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.159_2026-08-31-10-31-01_patch_fe3e6290-2553-4154-a0a0-8b8a418280ed +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.160_2026-08-31-11-23-39_patch_e701a085-3d23-4212-b0af-73b1d419a4c1 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.161_2026-08-31-12-41-01_patch_074d7bde-c493-44db-bc15-e81fd91e2921 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.162_2026-08-31-13-58-06_patch_84db1d60-4280-433b-bd3a-22e5611cd315 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.163_2026-08-31-15-25-54_patch_1931c5ba-73df-48cb-8dcb-555ace0af267 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.163_2026-08-31-15-44-37_patch_3962e805-2a92-494a-a87b-b6e6d66c40a0 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.165_2026-08-31-19-48-59_patch_86e5b923-2b99-4b86-b542-469b197614be +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.166_2026-09-01-07-39-57_patch_c344892f-d542-4e7b-8f27-02365a411e15 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.167_2026-09-01-08-21-53_patch_e199d419-282a-4dbb-8756-cf98b7037e2b +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.168_2026-09-01-09-12-31_patch_235c9fe0-da15-431d-bb56-01e2b9b15f4b +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.169_2026-09-01-11-54-19_patch_98b617ab-d130-42b5-93f2-19cae56d4c4c +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.170_2026-09-02-09-27-28_patch_e448d2e6-b7ec-4235-96bf-0173b3623c87 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.171_2026-09-02-12-52-44_patch_e6a241c2-1d62-482c-a9eb-9e3373395c50 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.172_2026-09-04-08-47-04_patch_daae079f-c104-4fcd-bb57-7bff68e80ad9 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.173_2026-09-04-12-34-42_patch_399c4cbd-0c28-4dfd-a4ec-e569420a36e4 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.174_2026-09-04-13-08-48_patch_f5cfe3d5-808e-45e4-bafc-00d0554838ee +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.175_2026-09-04-17-20-46_patch_1ef542fa-5d8e-4d90-aa15-9fc4cea3dfec +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.176_2026-09-05-08-25-05_patch_ced5521f-f9da-4640-a921-95fcb5cd2e6d +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.177_2026-09-05-09-00-16_patch_803c1ad4-a9af-4953-92c5-d882dec03ea2 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.178_2026-09-05-09-30-34_patch_012f6259-2e3f-4591-9806-1920fd06cb1f +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.179_2026-09-05-09-33-43_patch_f4bdf28c-d584-47a5-b699-8c883330162c +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.180_2026-09-05-12-02-21_patch_34a14b01-d22d-44a8-ac63-be0370a61422 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.181_2026-09-05-12-47-46_patch_56b18867-99f6-4d5c-8215-7b9d60d34010 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.182_2026-09-05-13-35-18_patch_61b7d4d1-73c8-4603-9b0c-593e7405587f +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.183_2026-09-06-07-27-09_patch_d76331b2-e3a3-4c0b-8412-ecd131b6ac02 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.184_2026-09-06-10-21-47_patch_7680b7a1-49f0-4eef-b12e-4c854407448c +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.185_2026-09-06-10-30-40_patch_e56c2183-77f8-44ef-a92e-7377ea6ce8c1 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.186_2026-09-07-10-00-51_patch_67de5683-d208-4bc0-8fb6-7677377908e7 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.187_2026-09-07-10-18-13_patch_2086cee2-276b-48f7-80ce-6a17397b9541 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.188_2026-09-07-12-51-28_patch_d6590822-068f-4a19-814d-34bbf204ba6e +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.189_2026-09-07-12-55-23_patch_7e0fb04b-4d40-4490-974f-c74ba2ae55a1 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.190_2026-09-08-08-58-03_patch_15e3ac1e-4475-4272-bdd8-d00bb0db8f17 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.191_2026-09-08-09-55-58_patch_3d350e50-ee53-4282-bc75-d2251142c46e +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.192_2026-09-08-11-12-21_patch_0733223b-1ae2-4b5b-897f-dfc57574ff83 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.193_2026-09-08-21-48-17_patch_8d07d4e6-3555-4720-85c9-9452570fae2d +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.194_2026-09-09-07-53-43_patch_69559429-aa42-4b62-9bd7-cdfded1c00d0 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.195_2026-09-09-09-55-40_patch_bbf67f1d-3b9a-43a2-b1bc-22df3fed8a34 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.196_2026-09-09-09-59-47_patch_70d76812-7b3c-4192-ad49-fcaccc29b0da +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.197_2026-09-09-13-06-06_patch_d455be0b-5421-492a-b952-fab0fd8110f8 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.198_2026-09-09-13-14-34_patch_267f54e5-311e-435e-882f-8e19191325ea +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.199_2026-09-09-22-24-18_patch_81e3e453-ca0e-42fa-9c21-8f94d43c05f2 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.200_2026-09-10-07-19-50_patch_911714fa-1fa3-4165-bfe6-d10c650bf5bf +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.201_2026-09-10-10-14-19_patch_864015f2-b0b0-4563-a1c8-155629cb9126 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.202_2026-09-10-11-00-05_patch_18270ecf-e770-4e69-8bcf-e5143c39929b +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.203_2026-09-10-11-10-41_patch_ff273ab1-44f3-4bc0-b184-3396e943df04 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.204_2026-09-10-13-02-02_patch_d4ee41e2-d13c-4941-8605-3f1a42b56b22 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.205_2026-09-10-13-57-50_patch_284df67e-5957-4b0c-bc64-5581db6ddecc +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/17.0.206_2026-09-11-10-18-50_patch_1ac7e410-0e62-4eef-bbe4-ebf93c9d0869 +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/__init__.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli/version/read_version.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli.egg-info/dependency_links.txt +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli.egg-info/entry_points.txt +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli.egg-info/not-zip-safe +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli.egg-info/requires.txt +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/ellf_cli.egg-info/top_level.txt +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/pyproject.toml +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/setup.cfg +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/setup.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/tests/test_appdirs.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/tests/test_auth.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/tests/test_config.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/tests/test_errors.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/tests/test_files_cp.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/tests/test_files_cp_helpers.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/tests/test_helm.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/tests/test_info.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/tests/test_infra_config_io.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/tests/test_infra_configure.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/tests/test_infra_deploy_node_labels.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/tests/test_infra_get_terraform.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/tests/test_infra_init.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/tests/test_infra_provision.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/tests/test_infra_start_fixes.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/tests/test_infra_start_validation.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/tests/test_infra_state_bucket.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/tests/test_infra_terraform_init.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/tests/test_invalid_secrets.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/tests/test_job_list_project_filter.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/tests/test_jobs_list_unmatched.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/tests/test_key_pair.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/tests/test_login.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/tests/test_logout.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/tests/test_main.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/tests/test_org_select.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/tests/test_plans.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/tests/test_projects.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/tests/test_publish_build_poll.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/tests/test_publish_data.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/tests/test_publish_requirements.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/tests/test_pypi_auth.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/tests/test_query.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/tests/test_recipe_file.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/tests/test_recipes.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/tests/test_state.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/tests/test_support.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/tests/test_todos.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/tests/test_ty.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/tests/test_ui.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/tests/test_ui_extras.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/tests/test_usage_counts.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/tests/test_users_assign.py +0 -0
- {ellf_cli-17.0.207 → ellf_cli-18.0.2}/tests/test_util.py +0 -0
|
@@ -1631,11 +1631,11 @@ Save the state of the current app JSON file. If an assets directory is provided,
|
|
|
1631
1631
|
| --- | --- | --- | --- |
|
|
1632
1632
|
| `output` | `Path` | JSON output path for data | |
|
|
1633
1633
|
| `assets_dir` | `Path` | Local directory to download assets to | `None` |
|
|
1634
|
-
| `--include` | `str` | Comma-separated items to include | `['tasks', 'actions', 'assets', 'datasets', 'paths']` |
|
|
1634
|
+
| `--include` | `str` | Comma-separated items to include: tasks, actions, agents, services, assets, datasets, paths | `['tasks', 'actions', 'agents', 'services', 'assets', 'datasets', 'paths']` |
|
|
1635
1635
|
|
|
1636
1636
|
### `ellf import`
|
|
1637
1637
|
|
|
1638
|
-
Populate Ellf with data for projects, tasks, actions, assets and paths.
|
|
1638
|
+
Populate Ellf with data for projects, tasks, actions, agents, services, assets, datasets and paths.
|
|
1639
1639
|
|
|
1640
1640
|
| Argument | Type | Description | Default |
|
|
1641
1641
|
| --- | --- | --- | --- |
|
|
@@ -133,7 +133,7 @@ def create_from_recipe(
|
|
|
133
133
|
if object_validation.errors:
|
|
134
134
|
errors = ["\n".join(err) for err in object_validation.errors.values()]
|
|
135
135
|
noun = _get_noun(recipe)
|
|
136
|
-
err = Messages.
|
|
136
|
+
err = Messages.E191.format(noun=noun, name=task_or_action_name)
|
|
137
137
|
raise CLIError(err, "\n".join(errors))
|
|
138
138
|
_validate_plan(recipe, plan, object_validation)
|
|
139
139
|
jt = getattr(recipe, "job_type", None)
|
|
@@ -154,6 +154,20 @@ def assign(
|
|
|
154
154
|
auth = get_auth_state()
|
|
155
155
|
agent = resolve_agent(name_or_id, cluster_id=cluster_id, project_id=project_id)
|
|
156
156
|
task_obj = resolve_task(task, cluster_id=cluster_id, project_id=project_id)
|
|
157
|
+
# The API appends the agent to the task's annotator group without checking
|
|
158
|
+
# membership, so a repeat assignment fails on the database rather than
|
|
159
|
+
# being a no-op. Check first, so re-running a setup script is safe.
|
|
160
|
+
if task_obj.id in agent.related_tasks:
|
|
161
|
+
print_mutation_result(
|
|
162
|
+
{
|
|
163
|
+
"agent_id": str(agent.id),
|
|
164
|
+
"task_id": str(task_obj.id),
|
|
165
|
+
"status": "already_assigned",
|
|
166
|
+
},
|
|
167
|
+
Messages.T066.format(name=agent.name, task=task_obj.name),
|
|
168
|
+
as_json=as_json,
|
|
169
|
+
)
|
|
170
|
+
return
|
|
157
171
|
auth.client.task.add_agent(task_id=task_obj.id, agent_id=agent.id)
|
|
158
172
|
print_mutation_result(
|
|
159
173
|
{"agent_id": str(agent.id), "task_id": str(task_obj.id), "status": "assigned"},
|
|
@@ -26,8 +26,15 @@ def uuid7() -> uuid.UUID:
|
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
class ProjectData(models.ProjectSummary):
|
|
29
|
+
# Each job is the argument list of its `create` command, starting with the
|
|
30
|
+
# recipe name, e.g. ["ner", "--name", "my-task", "--dataset", "my-ds"].
|
|
29
31
|
tasks: Sequence[list[str]] = tuple()
|
|
30
32
|
actions: Sequence[list[str]] = tuple()
|
|
33
|
+
agents: Sequence[list[str]] = tuple()
|
|
34
|
+
services: Sequence[list[str]] = tuple()
|
|
35
|
+
# [agent name, task name] pairs. Agents only count as annotators on tasks
|
|
36
|
+
# they are assigned to, so this is part of the project's shape.
|
|
37
|
+
assignments: Sequence[list[str]] = tuple()
|
|
31
38
|
# Setting some defaults for creation
|
|
32
39
|
created: datetime = datetime.now()
|
|
33
40
|
updated: datetime = datetime.now()
|
|
@@ -69,9 +76,23 @@ class RecipeData(BaseModel):
|
|
|
69
76
|
class Data(BaseModel):
|
|
70
77
|
timestamp: datetime = datetime.now()
|
|
71
78
|
project: dict[str, ProjectData]
|
|
72
|
-
asset: dict[str, AssetData]
|
|
73
|
-
dataset: dict[str, DatasetData]
|
|
74
|
-
path: dict[str, PathData]
|
|
79
|
+
asset: dict[str, AssetData] = {}
|
|
80
|
+
dataset: dict[str, DatasetData] = {}
|
|
81
|
+
path: dict[str, PathData] = {}
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
IncludeItem = Literal[
|
|
85
|
+
"tasks", "actions", "agents", "services", "assets", "datasets", "paths"
|
|
86
|
+
]
|
|
87
|
+
ALL_INCLUDES: list[IncludeItem] = [
|
|
88
|
+
"tasks",
|
|
89
|
+
"actions",
|
|
90
|
+
"agents",
|
|
91
|
+
"services",
|
|
92
|
+
"assets",
|
|
93
|
+
"datasets",
|
|
94
|
+
"paths",
|
|
95
|
+
]
|
|
75
96
|
|
|
76
97
|
|
|
77
98
|
@cli.command(
|
|
@@ -84,13 +105,7 @@ def export_data(
|
|
|
84
105
|
# fmt: off
|
|
85
106
|
output: Path,
|
|
86
107
|
assets_dir: Optional[Path] = None,
|
|
87
|
-
include: list[
|
|
88
|
-
"tasks",
|
|
89
|
-
"actions",
|
|
90
|
-
"assets",
|
|
91
|
-
"datasets",
|
|
92
|
-
"paths",
|
|
93
|
-
],
|
|
108
|
+
include: list[IncludeItem] = ALL_INCLUDES,
|
|
94
109
|
# fmt: on
|
|
95
110
|
) -> None:
|
|
96
111
|
"""
|
|
@@ -108,26 +123,50 @@ def export_data(
|
|
|
108
123
|
datasets = ptc.datasets.list()
|
|
109
124
|
tasks = ptc.tasks.list()
|
|
110
125
|
actions = ptc.actions.list()
|
|
126
|
+
agents = ptc.agents.list()
|
|
127
|
+
services = ptc.services.list()
|
|
111
128
|
paths = ptc.paths.list()
|
|
112
129
|
if "paths" in include:
|
|
113
130
|
for path in paths:
|
|
114
131
|
result.path[path.name] = PathData(**path.model_dump())
|
|
115
132
|
tasks_by_project = defaultdict(list)
|
|
116
133
|
actions_by_project = defaultdict(list)
|
|
134
|
+
agents_by_project = defaultdict(list)
|
|
135
|
+
services_by_project = defaultdict(list)
|
|
117
136
|
if "tasks" in include:
|
|
118
137
|
for task in tasks:
|
|
119
138
|
tasks_by_project[task.project_id].append(task)
|
|
120
139
|
if "actions" in include:
|
|
121
140
|
for action in actions:
|
|
122
141
|
actions_by_project[action.project_id].append(action)
|
|
142
|
+
if "agents" in include:
|
|
143
|
+
for agent in agents:
|
|
144
|
+
agents_by_project[agent.project_id].append(agent)
|
|
145
|
+
if "services" in include:
|
|
146
|
+
for service in services:
|
|
147
|
+
services_by_project[service.project_id].append(service)
|
|
148
|
+
task_names = {task.id: task.name for task in tasks}
|
|
123
149
|
for project in projects:
|
|
124
150
|
info = ptc.projects.info(project.id)
|
|
125
151
|
project_tasks = tasks_by_project.get(project.id, [])
|
|
126
152
|
project_actions = actions_by_project.get(project.id, [])
|
|
153
|
+
project_agents = agents_by_project.get(project.id, [])
|
|
154
|
+
project_services = services_by_project.get(project.id, [])
|
|
155
|
+
assignments: list[list[str]] = []
|
|
156
|
+
# Assignments are only meaningful when both ends are exported.
|
|
157
|
+
if "tasks" in include:
|
|
158
|
+
for agent in project_agents:
|
|
159
|
+
detail = ptc.agents.info(agent.id)
|
|
160
|
+
for task_id in detail.related_tasks:
|
|
161
|
+
if task_id in task_names:
|
|
162
|
+
assignments.append([agent.name, task_names[task_id]])
|
|
127
163
|
result.project[info.name] = ProjectData(
|
|
128
164
|
**info.model_dump(),
|
|
129
165
|
tasks=[_get_cmd(t.cli_command) for t in project_tasks],
|
|
130
166
|
actions=[_get_cmd(a.cli_command) for a in project_actions],
|
|
167
|
+
agents=[_get_cmd(a.cli_command) for a in project_agents],
|
|
168
|
+
services=[_get_cmd(s.cli_command) for s in project_services],
|
|
169
|
+
assignments=assignments,
|
|
131
170
|
)
|
|
132
171
|
if "assets" in include:
|
|
133
172
|
assets_map = {}
|
|
@@ -154,6 +193,8 @@ def export_data(
|
|
|
154
193
|
n_projects=len(result.project),
|
|
155
194
|
n_tasks=sum(len(t) for t in tasks_by_project.values()),
|
|
156
195
|
n_actions=sum(len(a) for a in actions_by_project.values()),
|
|
196
|
+
n_agents=sum(len(a) for a in agents_by_project.values()),
|
|
197
|
+
n_services=sum(len(s) for s in services_by_project.values()),
|
|
157
198
|
n_assets=len(result.asset),
|
|
158
199
|
n_datasets=len(result.dataset),
|
|
159
200
|
n_paths=len(result.path),
|
|
@@ -171,7 +212,8 @@ def export_data(
|
|
|
171
212
|
)
|
|
172
213
|
def import_data(data: Union[Path, Data], strict: bool = False) -> None:
|
|
173
214
|
"""
|
|
174
|
-
Populate Ellf with data for projects, tasks, actions,
|
|
215
|
+
Populate Ellf with data for projects, tasks, actions, agents, services,
|
|
216
|
+
assets, datasets and paths.
|
|
175
217
|
"""
|
|
176
218
|
_import_data(data, strict=strict)
|
|
177
219
|
|
|
@@ -208,7 +250,7 @@ def _import_data(
|
|
|
208
250
|
msg.divider(Messages.T042)
|
|
209
251
|
for asset in data.asset.values():
|
|
210
252
|
ptc.files.cp(
|
|
211
|
-
asset.path,
|
|
253
|
+
_asset_source(asset.path),
|
|
212
254
|
asset.dest,
|
|
213
255
|
recurse=True,
|
|
214
256
|
make_dirs=True,
|
|
@@ -223,11 +265,17 @@ def _import_data(
|
|
|
223
265
|
meta=json.dumps(asset.meta),
|
|
224
266
|
exists_ok=not strict,
|
|
225
267
|
)
|
|
268
|
+
# Datasets come before jobs: a task may read a dataset that no other job
|
|
269
|
+
# creates, and creating a job that names a missing input dataset fails.
|
|
270
|
+
if data.dataset:
|
|
271
|
+
msg.divider(Messages.T064)
|
|
272
|
+
for dataset in data.dataset.values():
|
|
273
|
+
ptc.datasets.create(dataset.name, dataset.kind, exists_ok=not strict)
|
|
226
274
|
|
|
227
275
|
if data.project:
|
|
228
276
|
msg.divider(Messages.T044)
|
|
229
277
|
for project in data.project.values():
|
|
230
|
-
if project.tasks or project.actions:
|
|
278
|
+
if project.tasks or project.actions or project.agents or project.services:
|
|
231
279
|
ptc.config.project(project.name)
|
|
232
280
|
for task in project.tasks:
|
|
233
281
|
ptc.tasks.create(_extra=task, exists_ok=not strict, no_start=True)
|
|
@@ -235,7 +283,55 @@ def _import_data(
|
|
|
235
283
|
ptc.actions.create(
|
|
236
284
|
_extra=action, exists_ok=not strict, no_start=True
|
|
237
285
|
)
|
|
286
|
+
for agent in project.agents:
|
|
287
|
+
ptc.agents.create(_extra=agent, exists_ok=not strict, no_start=True)
|
|
288
|
+
for service in project.services:
|
|
289
|
+
ptc.services.create(
|
|
290
|
+
_extra=service, exists_ok=not strict, no_start=True
|
|
291
|
+
)
|
|
292
|
+
if any(project.assignments for project in data.project.values()):
|
|
293
|
+
msg.divider(Messages.T065)
|
|
294
|
+
for project in data.project.values():
|
|
295
|
+
if project.assignments:
|
|
296
|
+
ptc.config.project(project.name)
|
|
297
|
+
for agent_name, task_name in project.assignments:
|
|
298
|
+
ptc.agents.assign(agent_name, task=task_name)
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def _asset_source(path: str) -> str:
|
|
302
|
+
"""Make a local directory source copy its contents, not itself.
|
|
303
|
+
|
|
304
|
+
``files cp`` follows ``cp`` semantics: ``cp src dest`` nests ``src`` under
|
|
305
|
+
``dest`` once ``dest`` exists, so the second run of an import would leave
|
|
306
|
+
``dest/src/...`` beside the files from the first run. A trailing slash
|
|
307
|
+
means "the contents of src", which lands in ``dest`` on every run.
|
|
308
|
+
"""
|
|
309
|
+
from ellf_cli.util import is_local
|
|
310
|
+
|
|
311
|
+
if is_local(path) and Path(path).is_dir() and not path.endswith("/"):
|
|
312
|
+
return path + "/"
|
|
313
|
+
return path
|
|
238
314
|
|
|
239
315
|
|
|
240
316
|
def _get_cmd(command: str) -> list[str]:
|
|
241
|
-
|
|
317
|
+
"""Turn a job's ``ellf <noun> create ...`` string into import arguments.
|
|
318
|
+
|
|
319
|
+
Drops the ``ellf <noun> create`` prefix and the ``--project-id`` flag: the
|
|
320
|
+
import sets the default project before creating each project's jobs, and
|
|
321
|
+
a slug from the exporting deployment need not exist on the importing one.
|
|
322
|
+
Everything else, including ``--worker-class``, is kept.
|
|
323
|
+
"""
|
|
324
|
+
tokens = shlex.split(command)[3:]
|
|
325
|
+
args: list[str] = []
|
|
326
|
+
skip_next = False
|
|
327
|
+
for token in tokens:
|
|
328
|
+
if skip_next:
|
|
329
|
+
skip_next = False
|
|
330
|
+
continue
|
|
331
|
+
if token == "--project-id":
|
|
332
|
+
skip_next = True
|
|
333
|
+
continue
|
|
334
|
+
if token.startswith("--project-id="):
|
|
335
|
+
continue
|
|
336
|
+
args.append(token)
|
|
337
|
+
return args
|
|
@@ -4,6 +4,7 @@ from collections.abc import Sequence
|
|
|
4
4
|
from typing import Any, Optional, Union
|
|
5
5
|
from uuid import UUID
|
|
6
6
|
|
|
7
|
+
from pydantic import ValidationError
|
|
7
8
|
from radicli import Arg
|
|
8
9
|
from wasabi import msg
|
|
9
10
|
|
|
@@ -75,7 +76,9 @@ def _stats(project_id: UUID) -> dict[str, Any] | None:
|
|
|
75
76
|
client = get_auth_state().client
|
|
76
77
|
try:
|
|
77
78
|
stats = client.project.stats(project_id)
|
|
78
|
-
except (EllfErrors.EllfError, *HTTPXErrors):
|
|
79
|
+
except (EllfErrors.EllfError, ValidationError, *HTTPXErrors):
|
|
80
|
+
# Stats are decorative. A PAM release whose stats schema differs from
|
|
81
|
+
# this CLI's (older, or newer) should not take down `info`.
|
|
79
82
|
return None
|
|
80
83
|
return {
|
|
81
84
|
"tasks": stats.tasks_count,
|
|
@@ -6,12 +6,14 @@ import json
|
|
|
6
6
|
import os
|
|
7
7
|
import subprocess
|
|
8
8
|
import tarfile
|
|
9
|
+
import tempfile
|
|
9
10
|
import time
|
|
10
11
|
import zipfile
|
|
12
|
+
from contextlib import contextmanager
|
|
11
13
|
from dataclasses import dataclass
|
|
12
14
|
from importlib.metadata import PackageNotFoundError, distribution
|
|
13
15
|
from pathlib import Path
|
|
14
|
-
from typing import Optional, Union
|
|
16
|
+
from typing import Iterator, Optional, Union
|
|
15
17
|
from uuid import UUID
|
|
16
18
|
|
|
17
19
|
import httpx
|
|
@@ -44,7 +46,7 @@ class DistPackage:
|
|
|
44
46
|
|
|
45
47
|
def __init__(self, path: Path) -> None:
|
|
46
48
|
if path.suffix == ".whl":
|
|
47
|
-
name, version, _,
|
|
49
|
+
name, version, _, _ = parse_wheel_filename(path.name)
|
|
48
50
|
else:
|
|
49
51
|
name, version = parse_sdist_filename(path.name)
|
|
50
52
|
|
|
@@ -93,7 +95,7 @@ class ModulePackage:
|
|
|
93
95
|
def __init__(self, name: str, version_arg: str | None = None) -> None:
|
|
94
96
|
self.name = name
|
|
95
97
|
if version_arg is None:
|
|
96
|
-
utc_now = datetime.datetime.
|
|
98
|
+
utc_now = datetime.datetime.now(datetime.timezone.utc)
|
|
97
99
|
self.version = f"{utc_now.year}.{utc_now.month}.{utc_now.day}-{utc_now.second + 60 * utc_now.minute + 60 * 60 * utc_now.hour}"
|
|
98
100
|
else:
|
|
99
101
|
self.version = version_arg
|
|
@@ -127,7 +129,7 @@ class FilesPackage:
|
|
|
127
129
|
self.path = path
|
|
128
130
|
|
|
129
131
|
if version_arg is None:
|
|
130
|
-
utc_now = datetime.datetime.
|
|
132
|
+
utc_now = datetime.datetime.now(datetime.timezone.utc)
|
|
131
133
|
self.version = f"{utc_now.year}.{utc_now.month}.{utc_now.day}-{utc_now.second + 60 * utc_now.minute + 60 * 60 * utc_now.hour}"
|
|
132
134
|
else:
|
|
133
135
|
self.version = version_arg
|
|
@@ -169,14 +171,62 @@ class FilesPackage:
|
|
|
169
171
|
return output
|
|
170
172
|
|
|
171
173
|
|
|
174
|
+
def _unpack_stripped(tar_data: bytes, root: Path) -> None:
|
|
175
|
+
"""Unpack a tar into *root* with its top-level directory removed.
|
|
176
|
+
|
|
177
|
+
This is the same re-rooting the broker applies when it lays the
|
|
178
|
+
package into the image, so what create-meta imports locally is what
|
|
179
|
+
the cluster will import at runtime.
|
|
180
|
+
"""
|
|
181
|
+
with tarfile.open(fileobj=io.BytesIO(tar_data), mode="r:*") as tar:
|
|
182
|
+
for member in tar.getmembers():
|
|
183
|
+
parts = member.name.split("/", 1)
|
|
184
|
+
inner = parts[1] if len(parts) > 1 else parts[0]
|
|
185
|
+
if not inner or inner.startswith("/") or ".." in Path(inner).parts:
|
|
186
|
+
continue
|
|
187
|
+
dest = root / inner
|
|
188
|
+
if member.isdir():
|
|
189
|
+
dest.mkdir(parents=True, exist_ok=True)
|
|
190
|
+
elif member.isfile():
|
|
191
|
+
dest.parent.mkdir(parents=True, exist_ok=True)
|
|
192
|
+
fileobj = tar.extractfile(member)
|
|
193
|
+
assert fileobj is not None
|
|
194
|
+
dest.write_bytes(fileobj.read())
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
@contextmanager
|
|
198
|
+
def _import_root(
|
|
199
|
+
package: Union[DistPackage, ModulePackage, FilesPackage],
|
|
200
|
+
source_dir: Path | None,
|
|
201
|
+
contents: bytes,
|
|
202
|
+
) -> Iterator[Path | None]:
|
|
203
|
+
"""Directory to prepend to PYTHONPATH so create-meta can import the package.
|
|
204
|
+
|
|
205
|
+
The broker never installs the package: it strips the sdist's top-level
|
|
206
|
+
directory and puts the unpacked root on PYTHONPATH. Mirror that here so
|
|
207
|
+
publishing doesn't require the package to be installed locally.
|
|
208
|
+
"""
|
|
209
|
+
if isinstance(package, ModulePackage):
|
|
210
|
+
yield None
|
|
211
|
+
elif isinstance(package, FilesPackage):
|
|
212
|
+
yield package.path.resolve().parent
|
|
213
|
+
elif source_dir is not None:
|
|
214
|
+
yield source_dir.resolve()
|
|
215
|
+
else:
|
|
216
|
+
with tempfile.TemporaryDirectory(prefix="ellf-publish-") as tmp:
|
|
217
|
+
root = Path(tmp)
|
|
218
|
+
_unpack_stripped(contents, root)
|
|
219
|
+
yield root
|
|
220
|
+
|
|
221
|
+
|
|
172
222
|
def _run_create_meta(
|
|
173
223
|
package_name: str, *, extra_python_path: Path | None = None
|
|
174
224
|
) -> str:
|
|
175
225
|
"""Run create-meta locally to produce recipe metadata JSON.
|
|
176
226
|
|
|
177
227
|
If *extra_python_path* is given it is prepended to ``PYTHONPATH`` so
|
|
178
|
-
that ``create-meta`` can import
|
|
179
|
-
installed
|
|
228
|
+
that ``create-meta`` can import the package without it being
|
|
229
|
+
installed.
|
|
180
230
|
"""
|
|
181
231
|
env = None
|
|
182
232
|
if extra_python_path is not None:
|
|
@@ -200,9 +250,16 @@ def _run_create_meta(
|
|
|
200
250
|
env=env,
|
|
201
251
|
)
|
|
202
252
|
if result.returncode != 0:
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
253
|
+
message = f"create-meta failed (exit {result.returncode}):\n{result.stderr}"
|
|
254
|
+
if "RecipePackageNotFound" in result.stderr:
|
|
255
|
+
where = f" from {extra_python_path}" if extra_python_path else ""
|
|
256
|
+
message += (
|
|
257
|
+
f"\nCould not import '{package_name}'{where}. The package is "
|
|
258
|
+
"imported by name rather than installed, so the source must "
|
|
259
|
+
f"contain a '{package_name}' package (a directory with "
|
|
260
|
+
f"__init__.py) or a '{package_name}.py' module at its top level."
|
|
261
|
+
)
|
|
262
|
+
raise CLIError(message)
|
|
206
263
|
return result.stdout.strip()
|
|
207
264
|
|
|
208
265
|
|
|
@@ -425,10 +482,12 @@ def publish_code(
|
|
|
425
482
|
if auth.cluster_id is None:
|
|
426
483
|
raise CLIError(Messages.E036)
|
|
427
484
|
|
|
485
|
+
source_dir: Path | None = None
|
|
428
486
|
if is_path:
|
|
429
487
|
path = Path(name_or_path)
|
|
430
|
-
if path.is_dir() and (path
|
|
488
|
+
if path.is_dir() and _is_package_dir(path):
|
|
431
489
|
msg.info(f"Building sdist from {name_or_path}")
|
|
490
|
+
source_dir = path
|
|
432
491
|
package = DistPackage(_make_sdist_if_dir(path))
|
|
433
492
|
elif not path.is_file():
|
|
434
493
|
msg.info(f"Interpreting {name_or_path} as directory of code")
|
|
@@ -447,15 +506,13 @@ def publish_code(
|
|
|
447
506
|
if package_version is None:
|
|
448
507
|
msg.info(f"No version specified. Defaulting to {package.version}")
|
|
449
508
|
|
|
450
|
-
# Generate recipe metadata locally
|
|
451
|
-
#
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
with loading("Generating recipe metadata"):
|
|
458
|
-
meta_json = _run_create_meta(package.name, extra_python_path=extra_python_path)
|
|
509
|
+
# Generate recipe metadata locally, importing the package from its
|
|
510
|
+
# source rather than requiring it to be installed.
|
|
511
|
+
with _import_root(package, source_dir, contents) as extra_python_path:
|
|
512
|
+
with loading("Generating recipe metadata"):
|
|
513
|
+
meta_json = _run_create_meta(
|
|
514
|
+
package.name, extra_python_path=extra_python_path
|
|
515
|
+
)
|
|
459
516
|
|
|
460
517
|
meta_list = json.loads(meta_json)
|
|
461
518
|
if len(meta_list) == 0:
|
|
@@ -524,16 +581,39 @@ def _is_path(name_or_path: str) -> bool:
|
|
|
524
581
|
return path.exists()
|
|
525
582
|
|
|
526
583
|
|
|
584
|
+
def _is_package_dir(path: Path) -> bool:
|
|
585
|
+
return (path / "setup.py").exists() or (path / "pyproject.toml").exists()
|
|
586
|
+
|
|
587
|
+
|
|
527
588
|
def _make_sdist_if_dir(path: Path) -> Path:
|
|
528
589
|
if not path.is_dir():
|
|
529
590
|
return path
|
|
530
591
|
env = dict(os.environ)
|
|
531
592
|
env["SKIP_CYTHON"] = "1"
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
593
|
+
if (path / "setup.py").exists():
|
|
594
|
+
cmd = ["python", "setup.py", "sdist"]
|
|
595
|
+
else:
|
|
596
|
+
# pyproject.toml-only projects have no setup.py to run; `build`
|
|
597
|
+
# is the standard front-end but isn't a CLI dependency.
|
|
598
|
+
cmd = ["python", "-m", "build", "--sdist"]
|
|
599
|
+
probe = subprocess.run(
|
|
600
|
+
["python", "-c", "import build"], capture_output=True, text=True
|
|
601
|
+
)
|
|
602
|
+
if probe.returncode != 0:
|
|
603
|
+
raise CLIError(
|
|
604
|
+
f"{path} has a pyproject.toml but no setup.py, so building "
|
|
605
|
+
"its sdist needs the 'build' package. Install it with "
|
|
606
|
+
"`pip install build` and re-run the publish command."
|
|
607
|
+
)
|
|
608
|
+
r = subprocess.run(cmd, cwd=path, text=True, env=env)
|
|
609
|
+
if r.returncode != 0:
|
|
610
|
+
raise CLIError(f"Building the sdist in {path} failed (exit {r.returncode})")
|
|
611
|
+
dist_dir = path / "dist"
|
|
612
|
+
if not dist_dir.is_dir():
|
|
613
|
+
raise CLIError(f"sdist not created after building in {path}")
|
|
614
|
+
dists = [p for p in dist_dir.iterdir() if p.is_file()]
|
|
535
615
|
if len(dists) == 0:
|
|
536
|
-
raise
|
|
616
|
+
raise CLIError(f"sdist not created after building in {dist_dir}")
|
|
537
617
|
# Return last modified file
|
|
538
618
|
return max(dists, key=lambda p: p.stat().st_mtime)
|
|
539
619
|
|
|
@@ -646,6 +726,6 @@ def _read_record(path: Path) -> list[str]:
|
|
|
646
726
|
output = []
|
|
647
727
|
with path.open("r", encoding="utf8") as file_:
|
|
648
728
|
for line in file_.read().strip().split("\n"):
|
|
649
|
-
subpath,
|
|
729
|
+
subpath, _, _ = line.split(",")
|
|
650
730
|
output.append(subpath)
|
|
651
731
|
return output
|
|
@@ -5,6 +5,7 @@ from typing import Any, Optional, Union
|
|
|
5
5
|
from uuid import UUID
|
|
6
6
|
|
|
7
7
|
import httpx
|
|
8
|
+
from pydantic import ValidationError
|
|
8
9
|
from radicli import Arg
|
|
9
10
|
from wasabi import msg
|
|
10
11
|
|
|
@@ -234,15 +235,18 @@ def _stats(task: TaskDetail, *, as_json: bool) -> dict[str, Any]:
|
|
|
234
235
|
client = get_auth_state().client
|
|
235
236
|
try:
|
|
236
237
|
stats = client.task.stats(task.id)
|
|
237
|
-
except (EllfErrors.EllfError, *HTTPXErrors):
|
|
238
|
+
except (EllfErrors.EllfError, ValidationError, *HTTPXErrors):
|
|
239
|
+
# Stats are decorative. A PAM release whose stats schema differs from
|
|
240
|
+
# this CLI's (older, or newer) should not take down `info`.
|
|
238
241
|
return {"related_actions": len(task.related_actions)}
|
|
242
|
+
duration = stats.duration or 0
|
|
239
243
|
return {
|
|
240
244
|
"annotators": stats.annotators,
|
|
241
|
-
"agents": stats.agents,
|
|
245
|
+
"agents": stats.agents or 0,
|
|
242
246
|
"sessions": stats.sessions,
|
|
243
247
|
"live_sessions": stats.live_sessions,
|
|
244
248
|
# Whole seconds for --json, as PAM reports it; readable in the table.
|
|
245
|
-
"duration":
|
|
249
|
+
"duration": duration if as_json else format_duration(duration),
|
|
246
250
|
"related_actions": len(task.related_actions),
|
|
247
251
|
}
|
|
248
252
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"prog": "ellf",
|
|
3
3
|
"help": "Ellf Command Line Interface.",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "18.0.1",
|
|
5
5
|
"extra_key": "_extra",
|
|
6
6
|
"commands": {
|
|
7
7
|
"actions": {
|
|
@@ -432,20 +432,22 @@
|
|
|
432
432
|
"id": "include",
|
|
433
433
|
"option": "--include",
|
|
434
434
|
"short": null,
|
|
435
|
-
"orig_help": "Comma-separated items to include",
|
|
436
|
-
"default": "['tasks', 'actions', 'assets', 'datasets', 'paths']",
|
|
437
|
-
"help": "Comma-separated items to include (str)",
|
|
435
|
+
"orig_help": "Comma-separated items to include: tasks, actions, agents, services, assets, datasets, paths",
|
|
436
|
+
"default": "['tasks', 'actions', 'agents', 'services', 'assets', 'datasets', 'paths']",
|
|
437
|
+
"help": "Comma-separated items to include: tasks, actions, agents, services, assets, datasets, paths (str)",
|
|
438
438
|
"action": "append",
|
|
439
439
|
"choices": [
|
|
440
440
|
"tasks",
|
|
441
441
|
"actions",
|
|
442
|
+
"agents",
|
|
443
|
+
"services",
|
|
442
444
|
"assets",
|
|
443
445
|
"datasets",
|
|
444
446
|
"paths"
|
|
445
447
|
],
|
|
446
448
|
"has_converter": false,
|
|
447
449
|
"type": "str",
|
|
448
|
-
"orig_type": "list[Literal[tasks, actions, assets, datasets, paths]]"
|
|
450
|
+
"orig_type": "list[Literal[tasks, actions, agents, services, assets, datasets, paths]]"
|
|
449
451
|
}
|
|
450
452
|
],
|
|
451
453
|
"description": "\nSave the state of the current app JSON file. If an assets directory is\nprovided, assets will be downloaded and referenced in the JSON accordingly.\n",
|
|
@@ -483,7 +485,7 @@
|
|
|
483
485
|
"orig_type": "bool"
|
|
484
486
|
}
|
|
485
487
|
],
|
|
486
|
-
"description": "\nPopulate Ellf with data for projects, tasks, actions,
|
|
488
|
+
"description": "\nPopulate Ellf with data for projects, tasks, actions, agents, services,\nassets, datasets and paths.\n",
|
|
487
489
|
"allow_extra": false,
|
|
488
490
|
"parent": null,
|
|
489
491
|
"is_placeholder": false
|
|
@@ -109,13 +109,13 @@ ellf secret create my-credentials OPENAI_API_KEY="sk-..." CUSTOM_SECRET=-
|
|
|
109
109
|
T035 = "Downloading {count} assets"
|
|
110
110
|
T036 = "Downloaded assets"
|
|
111
111
|
T037 = "Not downloading any assets. Make sure to specify the source in the JSON manually if you're planning on using it to import later."
|
|
112
|
-
T038 = "Generated {n_projects} projects, {n_tasks} tasks, {n_actions} actions, {n_assets} assets, {n_datasets} datasets, {n_paths} paths"
|
|
112
|
+
T038 = "Generated {n_projects} projects, {n_tasks} tasks, {n_actions} actions, {n_agents} agents, {n_services} services, {n_assets} assets, {n_datasets} datasets, {n_paths} paths"
|
|
113
113
|
T039 = "Successfully saved data"
|
|
114
114
|
T040 = "Create projects"
|
|
115
115
|
T041 = "Configure cluster paths"
|
|
116
116
|
T042 = "Transfer asset files"
|
|
117
117
|
T043 = "Register assets"
|
|
118
|
-
T044 = "Create tasks and
|
|
118
|
+
T044 = "Create tasks, actions, agents and services"
|
|
119
119
|
T045 = "Upload recipes"
|
|
120
120
|
T046 = "Package {name} has no previous published version"
|
|
121
121
|
T047 = "Found previous published versions for package {name}: {versions}"
|
|
@@ -135,6 +135,9 @@ ellf secret create my-credentials OPENAI_API_KEY="sk-..." CUSTOM_SECRET=-
|
|
|
135
135
|
T061 = "Deactivated {noun} {name}. They no longer have access or take up a seat; their work stays in the organization and an admin can reactivate them with `ellf users reactivate`."
|
|
136
136
|
T062 = "Successfully reactivated {noun} {name}"
|
|
137
137
|
T063 = "Not reactivating {noun} {name}: not deactivated"
|
|
138
|
+
T064 = "Create datasets"
|
|
139
|
+
T065 = "Assign agents to tasks"
|
|
140
|
+
T066 = "Not assigning agent {name} to task {task}: already assigned"
|
|
138
141
|
|
|
139
142
|
# CLI docstrings for placeholders (parent commands with no functions)
|
|
140
143
|
doc_todos = "Manage todos for handoff between web assistant and local Claude Code"
|
|
@@ -220,7 +223,7 @@ ellf secret create my-credentials OPENAI_API_KEY="sk-..." CUSTOM_SECRET=-
|
|
|
220
223
|
import_data = "JSON file to import"
|
|
221
224
|
export_output = "JSON output path for data"
|
|
222
225
|
assets_dir = "Local directory to download assets to"
|
|
223
|
-
include = "Comma-separated items to include"
|
|
226
|
+
include = "Comma-separated items to include: tasks, actions, agents, services, assets, datasets, paths"
|
|
224
227
|
import_strict = "Error if items already exist and don't skip or overwrite anything"
|
|
225
228
|
files_expand_path_alias = "Expand path aliases when displaying remote paths"
|
|
226
229
|
secrets_path = "/ separated key prefix to save secrets to"
|
|
@@ -293,6 +296,7 @@ ellf secret create my-credentials OPENAI_API_KEY="sk-..." CUSTOM_SECRET=-
|
|
|
293
296
|
)
|
|
294
297
|
E189 = "Agent {name} is not assigned to task {task}. Check which tasks it is assigned to with `ellf agents info {name}`"
|
|
295
298
|
E190 = "No project set, so there is nothing to list {plural} for. Pass --project, or set a default with `ellf config project`"
|
|
299
|
+
E191 = "Could not create {noun} {name}: input validation failed"
|
|
296
300
|
|
|
297
301
|
|
|
298
302
|
# fmt: on
|