ellf-cli 18.0.28__tar.gz → 18.0.30__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (347) hide show
  1. {ellf_cli-18.0.28/ellf_cli.egg-info → ellf_cli-18.0.30}/PKG-INFO +1 -1
  2. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/actions.py +4 -2
  3. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/agents.py +13 -3
  4. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/projects.py +19 -1
  5. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/services.py +7 -1
  6. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/tasks.py +4 -6
  7. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/users.py +42 -4
  8. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf.json +1 -1
  9. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_agent_recipe.py +2 -2
  10. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/messages.py +5 -0
  11. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ui.py +0 -15
  12. ellf_cli-18.0.30/ellf_cli/version/18.0.28_2026-09-21-09-44-01_patch_4430a721-0b3c-4f6a-b9bc-a53e667161bb +3 -0
  13. ellf_cli-18.0.30/ellf_cli/version/18.0.29_2026-09-21-10-58-07_patch_83b2b942-60a4-4bef-afd1-99690ba52b69 +3 -0
  14. {ellf_cli-18.0.28 → ellf_cli-18.0.30/ellf_cli.egg-info}/PKG-INFO +1 -1
  15. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli.egg-info/SOURCES.txt +6 -0
  16. ellf_cli-18.0.30/tests/test_agents_info_stats.py +69 -0
  17. ellf_cli-18.0.30/tests/test_jobs_info_hides_creator.py +59 -0
  18. ellf_cli-18.0.30/tests/test_projects_delete_confirm.py +101 -0
  19. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_ui_extras.py +0 -16
  20. ellf_cli-18.0.30/tests/test_users_info_stats.py +115 -0
  21. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/LICENSE +0 -0
  22. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/MANIFEST.in +0 -0
  23. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/README.md +0 -0
  24. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/__init__.py +0 -0
  25. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/__main__.py +0 -0
  26. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/about.json +0 -0
  27. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/about.py +0 -0
  28. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/appdirs.py +0 -0
  29. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/auth.py +0 -0
  30. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/cli.py +0 -0
  31. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/cloud/__init__.py +0 -0
  32. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/cloud/gcp.py +0 -0
  33. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/cluster_config.py +0 -0
  34. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/__init__.py +0 -0
  35. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/_cluster_select.py +0 -0
  36. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/_org_select.py +0 -0
  37. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/_project_scope.py +0 -0
  38. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/_recipe_file.py +0 -0
  39. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/_recipe_subcommand.py +0 -0
  40. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/_state.py +0 -0
  41. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/_usage.py +0 -0
  42. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/assets.py +0 -0
  43. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/auth.py +0 -0
  44. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/clusters.py +0 -0
  45. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/config.py +0 -0
  46. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/datasets.py +0 -0
  47. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/files/__init__.py +0 -0
  48. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/files/cp.py +0 -0
  49. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/files/ls.py +0 -0
  50. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/files/rm.py +0 -0
  51. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/files/rsync.py +0 -0
  52. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/files/stats.py +0 -0
  53. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/general.py +0 -0
  54. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/import_export.py +0 -0
  55. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/infra/__init__.py +0 -0
  56. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/infra/_config_io.py +0 -0
  57. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/infra/_helpers.py +0 -0
  58. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/infra/configure.py +0 -0
  59. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/infra/deploy.py +0 -0
  60. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/infra/init.py +0 -0
  61. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/infra/init_values.py +0 -0
  62. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/infra/provision.py +0 -0
  63. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/infra/register.py +0 -0
  64. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/infra/setup.py +0 -0
  65. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/infra/start.py +0 -0
  66. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/infra/terraform.py +0 -0
  67. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/infra/tls.py +0 -0
  68. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/jobs.py +0 -0
  69. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/packages.py +0 -0
  70. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/paths.py +0 -0
  71. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/plans.py +0 -0
  72. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/publish_code.py +0 -0
  73. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/publish_data.py +0 -0
  74. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/recipes.py +0 -0
  75. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/secrets.py +0 -0
  76. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/support.py +0 -0
  77. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/commands/todos.py +0 -0
  78. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/config.py +0 -0
  79. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/.claude-plugin/plugin.json +0 -0
  80. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/.gitignore +0 -0
  81. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skill_variants.json +0 -0
  82. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-annotate.assistant/SKILL.md +0 -0
  83. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-annotate.assistant/references/annotation_audit.md +0 -0
  84. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-annotate.assistant/references/builtin_ellf_annotation_recipes.md +0 -0
  85. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-annotate.coding/SKILL.md +0 -0
  86. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-annotate.coding/references/annotation_audit.md +0 -0
  87. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-annotate.coding/references/builtin_ellf_annotation_recipes.md +0 -0
  88. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-annotate.coding/references/builtin_prodigy_recipes.md +0 -0
  89. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-ask/SKILL.md +0 -0
  90. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-handoff/SKILL.md +0 -0
  91. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-monitor.assistant/SKILL.md +0 -0
  92. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-monitor.assistant/references/annotation_metrics.md +0 -0
  93. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-monitor.assistant/references/training_monitoring.md +0 -0
  94. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-monitor.coding/SKILL.md +0 -0
  95. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-monitor.coding/references/annotation_metrics.md +0 -0
  96. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-monitor.coding/references/training_monitoring.md +0 -0
  97. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-monitor.coding/scripts/check_training.py +0 -0
  98. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-ops.assistant/SKILL.md +0 -0
  99. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-ops.coding/SKILL.md +0 -0
  100. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-ops.coding/references/data_infra_cli.md +0 -0
  101. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-ops.coding/scripts/run_job.py +0 -0
  102. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-patterns/SKILL.md +0 -0
  103. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-patterns/references/pattern_strategies.md +0 -0
  104. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-prodigy/SKILL.md +0 -0
  105. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_action_recipe.py +0 -0
  106. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_blocks_ui.py +0 -0
  107. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_correct.py +0 -0
  108. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_custom_ui.py +0 -0
  109. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_manual.py +0 -0
  110. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_pages_ui.py +0 -0
  111. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_routing.py +0 -0
  112. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_task_recipe.py +0 -0
  113. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_teach.py +0 -0
  114. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-prodigy/references/builtin_recipes.md +0 -0
  115. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-prodigy/references/ellf_recipe_sdk.md +0 -0
  116. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-prodigy/references/lint_recipe.py +0 -0
  117. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-prodigy/references/prodigy_recipe_api.md +0 -0
  118. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-prodigy/references/template_index.md +0 -0
  119. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-project.assistant/SKILL.md +0 -0
  120. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-project.assistant/references/consulting_patterns.md +0 -0
  121. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-project.assistant/references/explosion_strategy.md +0 -0
  122. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-project.assistant/references/prodigy_llm_bot.md +0 -0
  123. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-project.coding/SKILL.md +0 -0
  124. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-project.coding/references/consulting_patterns.md +0 -0
  125. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-project.coding/references/explosion_strategy.md +0 -0
  126. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-project.coding/references/prodigy_llm_bot.md +0 -0
  127. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-support.assistant/SKILL.md +0 -0
  128. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-support.coding/SKILL.md +0 -0
  129. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-todo/SKILL.md +0 -0
  130. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-train.assistant/SKILL.md +0 -0
  131. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-train.assistant/references/diagnostics.md +0 -0
  132. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-train.assistant/references/evaluation_guide.md +0 -0
  133. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-train.assistant/references/model_selection.md +0 -0
  134. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-train.assistant/references/training_paradigms.md +0 -0
  135. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-train.assistant/references/workflow.md +0 -0
  136. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-train.coding/SKILL.md +0 -0
  137. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-train.coding/references/config_advanced.md +0 -0
  138. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-train.coding/references/config_architectures.md +0 -0
  139. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-train.coding/references/config_training.md +0 -0
  140. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-train.coding/references/diagnostics.md +0 -0
  141. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-train.coding/references/evaluation_guide.md +0 -0
  142. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-train.coding/references/experiment_patterns.md +0 -0
  143. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-train.coding/references/model_selection.md +0 -0
  144. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-train.coding/references/training_paradigms.md +0 -0
  145. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-train.coding/references/training_troubleshooting.md +0 -0
  146. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-train.coding/references/workflow.md +0 -0
  147. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ellf_skills/skills/ellf-train.coding/scripts/ellf_logger.py +0 -0
  148. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/errors.py +0 -0
  149. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/helm.py +0 -0
  150. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/interactivity.py +0 -0
  151. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/key_pair.py +0 -0
  152. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/main.py +0 -0
  153. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/pypi_auth.py +0 -0
  154. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/query.py +0 -0
  155. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/recipes_cookiecutter/cookiecutter.json +0 -0
  156. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/.gitignore +0 -0
  157. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/README.md.tmpl +0 -0
  158. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/requirements-dev.in +0 -0
  159. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/requirements.in +0 -0
  160. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/setup.py.tmpl +0 -0
  161. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/{{cookiecutter.package_name}}/__init__.py +0 -0
  162. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/{{cookiecutter.package_name}}/about.py +0 -0
  163. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/{{cookiecutter.package_name}}/recipes/__init__.py +0 -0
  164. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/{{cookiecutter.package_name}}/recipes/example_task.py +0 -0
  165. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/testing/__init__.py +0 -0
  166. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/ty.py +0 -0
  167. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/url.py +0 -0
  168. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/util.py +0 -0
  169. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.114_2026-08-19-08-36-57__1a4e37c1-471e-4a81-b4d5-1ffe7dec3a61 +0 -0
  170. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.114_2026-08-19-09-25-21_patch_33ff1e26-0e5e-46d8-8052-ab1d032e8b6c +0 -0
  171. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.115_2026-08-19-09-27-13_patch_4ac728c3-0342-4a36-adbc-e28d2d99b1ba +0 -0
  172. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.116_2026-08-19-09-30-47_patch_c706fef6-6813-4183-b268-12e7b00d7d0b +0 -0
  173. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.117_2026-08-19-09-39-14_patch_c6b0495f-2f88-4aa8-9ebf-0a235b4c407e +0 -0
  174. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.118_2026-08-19-09-52-00_patch_b395655a-8d78-4344-9266-6901c20f6517 +0 -0
  175. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.119_2026-08-19-10-22-38_patch_8f95c49d-aedc-41b8-816d-ccb95f048831 +0 -0
  176. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.120_2026-08-19-10-26-58_patch_7fbf35e6-cc01-4b75-8c9a-f5d054bcaf93 +0 -0
  177. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.121_2026-08-19-10-42-02_patch_31a81b02-a886-4dea-84e0-67cc6ca412d5 +0 -0
  178. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.122_2026-08-19-11-16-49_patch_1c4d7307-48d4-493e-936d-80af799d644b +0 -0
  179. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.123_2026-08-19-11-47-32_patch_d3d78d04-9989-45b1-91b1-93b571d8814a +0 -0
  180. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.124_2026-08-20-08-03-17_patch_57f4c389-2d19-41ce-aca4-13b61c62f387 +0 -0
  181. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.125_2026-08-20-08-25-05_patch_7f368148-0d1c-4525-bbdf-c6d257ad861a +0 -0
  182. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.126_2026-08-20-14-51-36_patch_a99abcc8-3371-4c63-a759-bb7ea5b32432 +0 -0
  183. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.127_2026-08-21-05-48-58_patch_34b4ccee-7596-4f57-93e9-774973cca58e +0 -0
  184. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.128_2026-08-21-10-47-20_patch_693112b9-927c-4a8f-8553-b06eef86079d +0 -0
  185. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.129_2026-08-21-11-16-54_patch_7a981666-01d9-4d1b-9ba3-2129744e3bd5 +0 -0
  186. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.130_2026-08-21-13-09-36_patch_b7ad1a76-ce70-4d70-9d1a-7c414eba5bb4 +0 -0
  187. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.131_2026-08-21-14-31-10_patch_9128ef66-193e-4caf-8710-afd86128e638 +0 -0
  188. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.132_2026-08-24-08-59-34_patch_c600fc14-86a5-460f-9c4e-f0177056f756 +0 -0
  189. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.133_2026-08-24-10-28-29_patch_b6ce3e32-9d7b-48ac-981a-270c22c180dd +0 -0
  190. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.134_2026-08-24-11-00-51_patch_a4fc0484-4a64-46a2-94f8-aa805254c7b0 +0 -0
  191. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.135_2026-08-24-12-12-41_patch_a870a56f-9a65-49eb-92da-f572b26667d8 +0 -0
  192. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.136_2026-08-24-12-42-46_patch_9b399356-6482-4291-8da7-e748a99c4b53 +0 -0
  193. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.137_2026-08-24-12-53-57_patch_f7979348-1910-40cd-a535-90299eca84ee +0 -0
  194. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.138_2026-08-24-14-36-30_patch_ed0f8750-f663-498b-85e7-0d3f2213fcfe +0 -0
  195. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.139_2026-08-25-08-37-26_patch_3ba87e19-902b-4f76-b64c-90e5c0ca1fc6 +0 -0
  196. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.140_2026-08-25-09-52-45_patch_abc830f2-df15-4385-b797-cdaf72c10752 +0 -0
  197. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.141_2026-08-25-11-37-20_patch_1d9ec01e-06f6-4c8e-b095-af17d5b62b00 +0 -0
  198. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.142_2026-08-26-07-38-49_patch_3dd23e36-e7df-494c-986f-a412861a8d40 +0 -0
  199. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.143_2026-08-26-09-32-32_patch_7b7ebd34-de27-4b73-98a7-3e1501e16cd5 +0 -0
  200. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.144_2026-08-26-10-56-43_patch_166064f2-2208-4c9b-9e25-25dc4cd32084 +0 -0
  201. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.145_2026-08-26-11-37-29_patch_b997f3ee-d7d3-4408-9f2f-c8b6d08bdb3b +0 -0
  202. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.146_2026-08-26-12-15-20_patch_afbbb893-25db-468c-81db-2663ca373068 +0 -0
  203. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.147_2026-08-26-14-10-50_patch_fd753b1f-2fa1-4ddb-a9e7-de690da85254 +0 -0
  204. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.148_2026-08-27-09-08-35_patch_23814c0b-7f77-489c-b4a8-ceadd18146e3 +0 -0
  205. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.149_2026-08-27-09-40-17_patch_8879d72e-a3d8-4abc-a16a-d9dcf75f0fa1 +0 -0
  206. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.150_2026-08-27-10-57-25_patch_afbe60ed-3e23-4d88-be3c-f800d7002a7d +0 -0
  207. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.151_2026-08-28-09-02-46_patch_1d4ca23b-aeae-4ea8-96b1-8cf57371f29d +0 -0
  208. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.152_2026-08-28-15-37-24_patch_c9d0bdc1-a7e1-432b-92b9-92dc76fe0c78 +0 -0
  209. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.153_2026-08-29-09-03-27_patch_7531573a-1b33-4092-aa9c-0e65f47b201f +0 -0
  210. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.154_2026-08-29-11-56-35_patch_919f5787-39c8-4dc1-ae6a-f27c28380f21 +0 -0
  211. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.155_2026-08-29-15-15-59_patch_822f9ed8-c366-4e5f-9791-dee8467cf3ce +0 -0
  212. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.156_2026-08-29-16-19-01_patch_3ac2bf36-20f7-4c9f-9fdb-295d4466d59e +0 -0
  213. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.157_2026-08-30-17-32-17_patch_4302354c-c9c5-4be7-acd3-542872a748d8 +0 -0
  214. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.158_2026-08-31-09-28-33_patch_3523774f-c25d-4102-8c1a-92fff046a48d +0 -0
  215. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.159_2026-08-31-10-31-01_patch_fe3e6290-2553-4154-a0a0-8b8a418280ed +0 -0
  216. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.160_2026-08-31-11-23-39_patch_e701a085-3d23-4212-b0af-73b1d419a4c1 +0 -0
  217. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.161_2026-08-31-12-41-01_patch_074d7bde-c493-44db-bc15-e81fd91e2921 +0 -0
  218. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.162_2026-08-31-13-58-06_patch_84db1d60-4280-433b-bd3a-22e5611cd315 +0 -0
  219. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.163_2026-08-31-15-25-54_patch_1931c5ba-73df-48cb-8dcb-555ace0af267 +0 -0
  220. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.163_2026-08-31-15-44-37_patch_3962e805-2a92-494a-a87b-b6e6d66c40a0 +0 -0
  221. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.165_2026-08-31-19-48-59_patch_86e5b923-2b99-4b86-b542-469b197614be +0 -0
  222. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.166_2026-09-01-07-39-57_patch_c344892f-d542-4e7b-8f27-02365a411e15 +0 -0
  223. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.167_2026-09-01-08-21-53_patch_e199d419-282a-4dbb-8756-cf98b7037e2b +0 -0
  224. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.168_2026-09-01-09-12-31_patch_235c9fe0-da15-431d-bb56-01e2b9b15f4b +0 -0
  225. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.169_2026-09-01-11-54-19_patch_98b617ab-d130-42b5-93f2-19cae56d4c4c +0 -0
  226. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.170_2026-09-02-09-27-28_patch_e448d2e6-b7ec-4235-96bf-0173b3623c87 +0 -0
  227. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.171_2026-09-02-12-52-44_patch_e6a241c2-1d62-482c-a9eb-9e3373395c50 +0 -0
  228. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.172_2026-09-04-08-47-04_patch_daae079f-c104-4fcd-bb57-7bff68e80ad9 +0 -0
  229. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.173_2026-09-04-12-34-42_patch_399c4cbd-0c28-4dfd-a4ec-e569420a36e4 +0 -0
  230. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.174_2026-09-04-13-08-48_patch_f5cfe3d5-808e-45e4-bafc-00d0554838ee +0 -0
  231. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.175_2026-09-04-17-20-46_patch_1ef542fa-5d8e-4d90-aa15-9fc4cea3dfec +0 -0
  232. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.176_2026-09-05-08-25-05_patch_ced5521f-f9da-4640-a921-95fcb5cd2e6d +0 -0
  233. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.177_2026-09-05-09-00-16_patch_803c1ad4-a9af-4953-92c5-d882dec03ea2 +0 -0
  234. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.178_2026-09-05-09-30-34_patch_012f6259-2e3f-4591-9806-1920fd06cb1f +0 -0
  235. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.179_2026-09-05-09-33-43_patch_f4bdf28c-d584-47a5-b699-8c883330162c +0 -0
  236. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.180_2026-09-05-12-02-21_patch_34a14b01-d22d-44a8-ac63-be0370a61422 +0 -0
  237. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.181_2026-09-05-12-47-46_patch_56b18867-99f6-4d5c-8215-7b9d60d34010 +0 -0
  238. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.182_2026-09-05-13-35-18_patch_61b7d4d1-73c8-4603-9b0c-593e7405587f +0 -0
  239. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.183_2026-09-06-07-27-09_patch_d76331b2-e3a3-4c0b-8412-ecd131b6ac02 +0 -0
  240. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.184_2026-09-06-10-21-47_patch_7680b7a1-49f0-4eef-b12e-4c854407448c +0 -0
  241. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.185_2026-09-06-10-30-40_patch_e56c2183-77f8-44ef-a92e-7377ea6ce8c1 +0 -0
  242. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.186_2026-09-07-10-00-51_patch_67de5683-d208-4bc0-8fb6-7677377908e7 +0 -0
  243. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.187_2026-09-07-10-18-13_patch_2086cee2-276b-48f7-80ce-6a17397b9541 +0 -0
  244. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.188_2026-09-07-12-51-28_patch_d6590822-068f-4a19-814d-34bbf204ba6e +0 -0
  245. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.189_2026-09-07-12-55-23_patch_7e0fb04b-4d40-4490-974f-c74ba2ae55a1 +0 -0
  246. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.190_2026-09-08-08-58-03_patch_15e3ac1e-4475-4272-bdd8-d00bb0db8f17 +0 -0
  247. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.191_2026-09-08-09-55-58_patch_3d350e50-ee53-4282-bc75-d2251142c46e +0 -0
  248. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.192_2026-09-08-11-12-21_patch_0733223b-1ae2-4b5b-897f-dfc57574ff83 +0 -0
  249. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.193_2026-09-08-21-48-17_patch_8d07d4e6-3555-4720-85c9-9452570fae2d +0 -0
  250. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.194_2026-09-09-07-53-43_patch_69559429-aa42-4b62-9bd7-cdfded1c00d0 +0 -0
  251. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.195_2026-09-09-09-55-40_patch_bbf67f1d-3b9a-43a2-b1bc-22df3fed8a34 +0 -0
  252. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.196_2026-09-09-09-59-47_patch_70d76812-7b3c-4192-ad49-fcaccc29b0da +0 -0
  253. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.197_2026-09-09-13-06-06_patch_d455be0b-5421-492a-b952-fab0fd8110f8 +0 -0
  254. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.198_2026-09-09-13-14-34_patch_267f54e5-311e-435e-882f-8e19191325ea +0 -0
  255. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.199_2026-09-09-22-24-18_patch_81e3e453-ca0e-42fa-9c21-8f94d43c05f2 +0 -0
  256. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.200_2026-09-10-07-19-50_patch_911714fa-1fa3-4165-bfe6-d10c650bf5bf +0 -0
  257. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.201_2026-09-10-10-14-19_patch_864015f2-b0b0-4563-a1c8-155629cb9126 +0 -0
  258. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.202_2026-09-10-11-00-05_patch_18270ecf-e770-4e69-8bcf-e5143c39929b +0 -0
  259. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.203_2026-09-10-11-10-41_patch_ff273ab1-44f3-4bc0-b184-3396e943df04 +0 -0
  260. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.204_2026-09-10-13-02-02_patch_d4ee41e2-d13c-4941-8605-3f1a42b56b22 +0 -0
  261. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.205_2026-09-10-13-57-50_patch_284df67e-5957-4b0c-bc64-5581db6ddecc +0 -0
  262. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.206_2026-09-11-10-18-50_patch_1ac7e410-0e62-4eef-bbe4-ebf93c9d0869 +0 -0
  263. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/17.0.207_2026-09-11-15-26-02_major_3f1b3ed7-eaac-41f5-b740-0af3331595d9 +0 -0
  264. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/18.0.0_2026-09-12-10-01-10_patch_8995783a-a9fa-4c4e-90b3-6386a4c481eb +0 -0
  265. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/18.0.10_2026-09-14-16-09-38_patch_9403d562-eed0-4125-a8b3-09cd92a13298 +0 -0
  266. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/18.0.11_2026-09-15-09-05-57_patch_8b22dba2-d931-4c4d-ae60-db3238d5e73d +0 -0
  267. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/18.0.12_2026-09-15-11-18-52_patch_81314afa-e70d-4ad8-b45b-3f8d62b983e7 +0 -0
  268. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/18.0.13_2026-09-16-08-00-55_patch_bfa30fc0-3281-4de6-bb56-d7290c98d1c3 +0 -0
  269. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/18.0.14_2026-09-16-14-38-26_patch_aae6e9b0-88f4-4bcf-b611-3308c7fcd6eb +0 -0
  270. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/18.0.15_2026-09-17-07-58-54_patch_43d40582-effd-4925-a8bd-e70fecf1faab +0 -0
  271. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/18.0.16_2026-09-17-08-12-37_patch_b4db2d23-88f1-45c1-82d9-78d38014fcb6 +0 -0
  272. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/18.0.17_2026-09-18-09-38-19_patch_25697fc3-e9a8-4980-962b-e6cb0a4760c5 +0 -0
  273. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/18.0.18_2026-09-18-09-54-33_patch_d37d9c71-c469-4d94-90d7-4898915ee991 +0 -0
  274. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/18.0.19_2026-09-18-10-47-33_patch_47607b3c-d1b6-4864-b9b0-00a2a8d2ac32 +0 -0
  275. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/18.0.1_2026-09-12-17-33-30_patch_f6ea2c99-a715-4c4d-876a-23ed8c62a83d +0 -0
  276. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/18.0.20_2026-09-18-11-07-21_patch_5a336f91-63a6-4877-97ed-da89395cc8f5 +0 -0
  277. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/18.0.21_2026-09-18-13-34-52_patch_c56462e1-63d9-498f-a912-c80c5f0171d1 +0 -0
  278. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/18.0.22_2026-09-20-08-10-45_patch_6afa2c20-bcef-4b7d-8685-2c9815d8683f +0 -0
  279. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/18.0.23_2026-09-20-08-38-35_patch_a7f169e5-37ab-4ec0-bf89-d14770181303 +0 -0
  280. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/18.0.24_2026-09-20-15-49-27_patch_c1222081-ff44-4f20-aea2-5840b6d76efd +0 -0
  281. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/18.0.25_2026-09-21-06-11-40_patch_6df46dd9-e7d3-4e71-bec4-e5c93b4ac863 +0 -0
  282. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/18.0.26_2026-09-21-07-54-11_patch_cc52dd36-5eba-4094-a1b0-d6f79f103441 +0 -0
  283. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/18.0.27_2026-09-21-08-36-49_patch_a25fa125-9bcd-4317-8da5-851c1c329631 +0 -0
  284. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/18.0.2_2026-09-13-06-36-33_patch_176bb944-1c49-4dd3-a001-8fce908825a0 +0 -0
  285. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/18.0.3_2026-09-13-12-32-46_patch_58e5ce3a-f2a7-4789-b2cd-5cc75c72b6ec +0 -0
  286. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/18.0.4_2026-09-13-12-45-14_patch_b6c79d55-8f85-4f47-a304-df52b122c38c +0 -0
  287. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/18.0.5_2026-09-13-14-11-18_patch_9b111116-5e62-472a-b72c-d1bbafa22c97 +0 -0
  288. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/18.0.5_2026-09-13-17-01-11_patch_d4bf61c0-83ac-4dd3-b512-a65805d298c8 +0 -0
  289. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/18.0.6_2026-09-14-11-31-04_patch_fd2ebda6-22cb-4e7d-9452-2f6f2640e828 +0 -0
  290. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/18.0.7_2026-09-13-17-06-08_patch_428af161-0b27-43c2-8751-e5f7874df120 +0 -0
  291. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/18.0.9_2026-09-14-12-37-42_patch_b2ca0529-4b67-4fa4-8fdc-5f5e9f5d5166 +0 -0
  292. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/__init__.py +0 -0
  293. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli/version/read_version.py +0 -0
  294. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli.egg-info/dependency_links.txt +0 -0
  295. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli.egg-info/entry_points.txt +0 -0
  296. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli.egg-info/not-zip-safe +0 -0
  297. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli.egg-info/requires.txt +0 -0
  298. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/ellf_cli.egg-info/top_level.txt +0 -0
  299. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/pyproject.toml +0 -0
  300. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/setup.cfg +0 -0
  301. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/setup.py +0 -0
  302. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_appdirs.py +0 -0
  303. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_auth.py +0 -0
  304. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_config.py +0 -0
  305. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_errors.py +0 -0
  306. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_files_cp.py +0 -0
  307. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_files_cp_helpers.py +0 -0
  308. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_helm.py +0 -0
  309. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_import_export.py +0 -0
  310. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_info.py +0 -0
  311. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_infra_config_io.py +0 -0
  312. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_infra_configure.py +0 -0
  313. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_infra_deploy_node_labels.py +0 -0
  314. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_infra_get_terraform.py +0 -0
  315. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_infra_init.py +0 -0
  316. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_infra_provision.py +0 -0
  317. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_infra_start_fixes.py +0 -0
  318. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_infra_start_validation.py +0 -0
  319. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_infra_state_bucket.py +0 -0
  320. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_infra_terraform_init.py +0 -0
  321. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_invalid_secrets.py +0 -0
  322. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_job_list_project_filter.py +0 -0
  323. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_jobs_list_unmatched.py +0 -0
  324. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_key_pair.py +0 -0
  325. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_login.py +0 -0
  326. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_logout.py +0 -0
  327. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_main.py +0 -0
  328. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_org_select.py +0 -0
  329. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_plans.py +0 -0
  330. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_projects.py +0 -0
  331. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_publish_build_poll.py +0 -0
  332. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_publish_data.py +0 -0
  333. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_publish_import_root.py +0 -0
  334. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_publish_requirements.py +0 -0
  335. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_pypi_auth.py +0 -0
  336. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_query.py +0 -0
  337. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_recipe_file.py +0 -0
  338. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_recipes.py +0 -0
  339. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_state.py +0 -0
  340. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_support.py +0 -0
  341. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_todos.py +0 -0
  342. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_ty.py +0 -0
  343. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_ui.py +0 -0
  344. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_usage_counts.py +0 -0
  345. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_users_assign.py +0 -0
  346. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_users_list.py +0 -0
  347. {ellf_cli-18.0.28 → ellf_cli-18.0.30}/tests/test_util.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ellf-cli
3
- Version: 18.0.28
3
+ Version: 18.0.30
4
4
  Summary: Ellf Command Line Interface
5
5
  Home-page: https://prodi.gy
6
6
  Author: ExplosionAI GmbH
@@ -220,8 +220,10 @@ def info(
220
220
  as_json=as_json,
221
221
  select=select,
222
222
  stats=_stats(res),
223
- # Superseded by the stats block; --json still carries both.
224
- table_exclude=["stats", "related_tasks"],
223
+ # Noise in a table; --json and an explicit --select still carry them.
224
+ table_exclude=(
225
+ None if select else ["stats", "related_tasks", "created_by_user"]
226
+ ),
225
227
  )
226
228
  return res
227
229
 
@@ -3,6 +3,7 @@ from collections.abc import Sequence
3
3
  from typing import Any, Optional, Union
4
4
  from uuid import UUID
5
5
 
6
+ from pydantic import ValidationError
6
7
  from radicli import Arg
7
8
  from wasabi import msg
8
9
 
@@ -13,6 +14,7 @@ from ..errors import (
13
14
  BrokerError,
14
15
  CLIError,
15
16
  EllfError,
17
+ EllfErrors,
16
18
  HTTPXErrors,
17
19
  JobStartRejectedError,
18
20
  )
@@ -296,17 +298,25 @@ def info(
296
298
  as_json=as_json,
297
299
  select=select,
298
300
  stats=_stats(res),
299
- # Superseded by the stats block; --json still carries the ids.
300
- table_exclude=["related_tasks"],
301
+ # Noise in a table; --json and an explicit --select still carry them.
302
+ table_exclude=None if select else ["related_tasks", "created_by_user"],
301
303
  )
302
304
  return res
303
305
 
304
306
 
305
307
  def _stats(agent: AgentDetail) -> dict[str, Any]:
306
- return {
308
+ stats = {
307
309
  "tasks": len(agent.related_tasks),
308
310
  "last_started": agent.last_execution_started,
309
311
  }
312
+ try:
313
+ counts = get_auth_state().client.agent.annotation_counts(agent.id)
314
+ except (EllfErrors.EllfError, ValidationError, *HTTPXErrors):
315
+ # Stats are decorative; a mismatched PAM schema shouldn't fail `info`.
316
+ return stats
317
+ stats["annotations"] = counts.annotations
318
+ stats["sessions"] = counts.sessions
319
+ return stats
310
320
 
311
321
 
312
322
  @cli.subcommand(
@@ -1,5 +1,6 @@
1
1
  import builtins
2
2
  import logging
3
+ import sys
3
4
  from collections.abc import Sequence
4
5
  from typing import Any, Optional, Union
5
6
  from uuid import UUID
@@ -19,6 +20,7 @@ from ellf_pam_sdk.models import (
19
20
  from ..auth import AuthState
20
21
  from ..cli import cli
21
22
  from ..errors import BrokerError, CLIError, EllfErrors, HTTPXErrors
23
+ from ..interactivity import isatty
22
24
  from ..messages import Messages
23
25
  from ..query import resolve_project, resolve_project_id
24
26
  from ..ui import print_info_table, print_mutation_result, print_table_with_select
@@ -186,7 +188,9 @@ def update(
186
188
  )
187
189
  def delete(name_or_id: Union[str, UUID], as_json: bool = False) -> UUID:
188
190
  """Delete a project"""
189
- project_id = resolve_project_id(name_or_id)
191
+ project = resolve_project(name_or_id)
192
+ project_id = project.id
193
+ _confirm_delete(project)
190
194
  auth = get_auth_state()
191
195
  _stop_project_jobs(auth, project_id)
192
196
  try:
@@ -205,6 +209,20 @@ def delete(name_or_id: Union[str, UUID], as_json: bool = False) -> UUID:
205
209
  return project_id
206
210
 
207
211
 
212
+ def _confirm_delete(project: ProjectDetail) -> None:
213
+ """Type the project name back to confirm.
214
+
215
+ Refused without a terminal, so a script or an agent fails instead of
216
+ hanging on a prompt nothing will answer.
217
+ """
218
+ if not isatty(sys.stdin) or not isatty(sys.stdout):
219
+ raise CLIError(Messages.E193.format(name=project.name))
220
+ msg.warn(Messages.T071.format(name=project.name))
221
+ typed = input(Messages.project_delete_confirm).strip()
222
+ if typed != project.name:
223
+ raise CLIError(Messages.E194.format(name=project.name))
224
+
225
+
208
226
  def _stop_project_jobs(auth: AuthState, project_id: UUID) -> None:
209
227
  """Stop all cluster jobs for a project's tasks, actions, and agents."""
210
228
  job_ids: builtins.list[tuple[UUID, broker_models.JobType]] = []
@@ -212,7 +212,13 @@ def info(
212
212
  except (BrokerError, *HTTPXErrors) as e:
213
213
  msg.warn(f"Could not fetch live status from the cluster: {e}")
214
214
  print_info_table(
215
- res, exclude=["plan"], as_json=as_json, select=select, stats=_stats(res)
215
+ res,
216
+ exclude=["plan"],
217
+ as_json=as_json,
218
+ select=select,
219
+ stats=_stats(res),
220
+ # Noise in a table; --json and an explicit --select still carry it.
221
+ table_exclude=None if select else ["created_by_user"],
216
222
  )
217
223
  return res
218
224
 
@@ -29,7 +29,6 @@ from ..query import (
29
29
  stop_job,
30
30
  )
31
31
  from ..ui import (
32
- format_duration,
33
32
  print_args_table,
34
33
  print_info_table,
35
34
  print_logs,
@@ -225,8 +224,8 @@ def info(
225
224
  as_json=as_json,
226
225
  select=select,
227
226
  stats=_stats(res, as_json=as_json),
228
- # Superseded by the stats block; --json still carries the ids.
229
- table_exclude=["related_actions"],
227
+ # Noise in a table; --json and an explicit --select still carry them.
228
+ table_exclude=None if select else ["related_actions", "created_by_user"],
230
229
  )
231
230
  return res
232
231
 
@@ -239,14 +238,13 @@ def _stats(task: TaskDetail, *, as_json: bool) -> dict[str, Any]:
239
238
  # Stats are decorative. A PAM release whose stats schema differs from
240
239
  # this CLI's (older, or newer) should not take down `info`.
241
240
  return {"related_actions": len(task.related_actions)}
242
- duration = stats.duration or 0
243
241
  return {
242
+ "annotations": stats.annotations,
244
243
  "annotators": stats.annotators,
245
244
  "agents": stats.agents or 0,
246
245
  "sessions": stats.sessions,
247
246
  "live_sessions": stats.live_sessions,
248
- # Whole seconds for --json, as PAM reports it; readable in the table.
249
- "duration": duration if as_json else format_duration(duration),
247
+ "duration": stats.duration,
250
248
  "related_actions": len(task.related_actions),
251
249
  }
252
250
 
@@ -1,9 +1,10 @@
1
1
  import builtins
2
2
  from collections.abc import Sequence
3
3
  from datetime import datetime, timezone
4
- from typing import Literal, Optional, Union
4
+ from typing import Any, Literal, 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
 
@@ -25,7 +26,11 @@ from ..cli import cli
25
26
  from ..errors import CLIError, EllfErrors, HTTPXErrors
26
27
  from ..messages import Messages
27
28
  from ..query import _try_uuid, resolve_task
28
- from ..ui import print_info_table, print_mutation_result, print_table_with_select
29
+ from ..ui import (
30
+ print_info_table,
31
+ print_mutation_result,
32
+ print_table_with_select,
33
+ )
29
34
  from ._state import get_auth_state
30
35
 
31
36
  # Mirrors the role model of the web app's members page: a user is an
@@ -177,7 +182,7 @@ def info(
177
182
  ) -> UserDetail:
178
183
  """Get detailed info for a user"""
179
184
  res = _resolve_user(email_name_or_id)
180
- print_info_table(res, as_json=as_json, select=select)
185
+ _print_user(res, select=select, as_json=as_json)
181
186
  return res
182
187
 
183
188
 
@@ -197,10 +202,43 @@ def whoami(
197
202
  """Show the currently authenticated user"""
198
203
  auth = get_auth_state()
199
204
  res = auth.client.user.read(id=auth.user_id)
200
- print_info_table(res, as_json=as_json, select=select)
205
+ _print_user(res, select=select, as_json=as_json)
201
206
  return res
202
207
 
203
208
 
209
+ def _print_user(
210
+ user: UserDetail, *, select: Optional[builtins.list[str]], as_json: bool
211
+ ) -> None:
212
+ """The shared body of `users info` and `users whoami`."""
213
+ print_info_table(
214
+ user,
215
+ as_json=as_json,
216
+ select=select,
217
+ stats=_stats(user, as_json=as_json),
218
+ # Noise in a table; --json and an explicit --select still carry them.
219
+ table_exclude=None if select else ["groups", "tags"],
220
+ )
221
+
222
+
223
+ def _stats(user: UserDetail, *, as_json: bool) -> dict[str, Any]:
224
+ """This member's org-wide annotation stats, for the info block."""
225
+ client = get_auth_state().client
226
+ try:
227
+ stats = client.user.stats(user.id)
228
+ except (EllfErrors.EllfError, ValidationError, *HTTPXErrors):
229
+ # Stats are decorative; a mismatched PAM schema shouldn't fail `info`.
230
+ return {}
231
+ return {
232
+ "projects": stats.projects,
233
+ "tasks": stats.tasks,
234
+ "sessions": stats.sessions,
235
+ "annotations": stats.annotations,
236
+ "duration": stats.duration,
237
+ "last_annotated": stats.last_annotated,
238
+ "last_active": stats.last_active,
239
+ }
240
+
241
+
204
242
  @cli.subcommand(
205
243
  "users",
206
244
  "update",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "prog": "ellf",
3
3
  "help": "Ellf Command Line Interface.",
4
- "version": "18.0.27",
4
+ "version": "18.0.29",
5
5
  "extra_key": "_extra",
6
6
  "commands": {
7
7
  "actions": {
@@ -68,8 +68,8 @@ def create_llm_client(api_key: str, model_name: str):
68
68
  #
69
69
  # The SDK reads poll_interval and idle_shutdown_seconds from the recipe
70
70
  # parameters automatically — you don't need to handle them yourself. The
71
- # same goes for icon: this is the avatar used in the app wherever the agent's
72
- # annotations are attributed to it. Pick the default that suits your agent — the
71
+ # same goes for icon: this is the avatar used in the app wherever the agent's
72
+ # annotations are attributed to it. Pick the default that suits your agent — the
73
73
  # model provider it calls, or what it does — from the set in props.icon.
74
74
  #
75
75
  # Agents are created and managed like tasks and actions:
@@ -142,6 +142,9 @@ ellf secret create my-credentials OPENAI_API_KEY="sk-..." CUSTOM_SECRET=-
142
142
  T068 = "Select your plan here:\n{url}"
143
143
  T069 = "Your free trial has ended"
144
144
  T070 = "Today is the last day of your free trial"
145
+ T071 = "This deletes {name} and everything in it: tasks, actions, agents, services, project plans and annotations. Any annotated datasets stay on your cluster."
146
+
147
+ project_delete_confirm = "Type the project name to confirm: "
145
148
 
146
149
  # CLI docstrings for placeholders (parent commands with no functions)
147
150
  doc_todos = "Manage todos in your Ellf projects"
@@ -302,6 +305,8 @@ ellf secret create my-credentials OPENAI_API_KEY="sk-..." CUSTOM_SECRET=-
302
305
  E190 = "No project set, so there is nothing to list {plural} for. Pass --project, or set a default with `ellf config project`"
303
306
  E191 = "Could not create {noun} {name}: input validation failed"
304
307
  E192 = "Annotators can't log in via the CLI. Ask an admin of your organization '{org}' to upgrade your role to developer, then run `{command}` again."
308
+ E193 = "Deleting a project has to be confirmed via a terminal. Run `ellf projects delete {name}` again in an interactive terminal session."
309
+ E194 = "Wrong project name, so {name} was not deleted"
305
310
 
306
311
 
307
312
  # fmt: on
@@ -150,21 +150,6 @@ def _json_default(value: Any) -> Any:
150
150
  return str(value)
151
151
 
152
152
 
153
- def format_duration(seconds: int) -> str:
154
- """A span of seconds as ``1h 30m``, ``45m`` or ``0m``.
155
-
156
- Annotation time is reported in whole seconds, but nobody reads a session
157
- to the second: minutes are the finest unit shown.
158
- """
159
- minutes, _ = divmod(max(0, int(seconds)), 60)
160
- hours, minutes = divmod(minutes, 60)
161
- if hours and minutes:
162
- return f"{hours}h {minutes}m"
163
- if hours:
164
- return f"{hours}h"
165
- return f"{minutes}m"
166
-
167
-
168
153
  def print_info_table(
169
154
  item: BaseModel,
170
155
  select: list[str] | None = None,
@@ -0,0 +1,3 @@
1
+ 18.0.28
2
+ patch
3
+ 4430a721-0b3c-4f6a-b9bc-a53e667161bb
@@ -0,0 +1,3 @@
1
+ 18.0.29
2
+ patch
3
+ 83b2b942-60a4-4bef-afd1-99690ba52b69
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ellf-cli
3
- Version: 18.0.28
3
+ Version: 18.0.30
4
4
  Summary: Ellf Command Line Interface
5
5
  Home-page: https://prodi.gy
6
6
  Author: ExplosionAI GmbH
@@ -281,6 +281,8 @@ ellf_cli/version/18.0.24_2026-09-20-15-49-27_patch_c1222081-ff44-4f20-aea2-5840b
281
281
  ellf_cli/version/18.0.25_2026-09-21-06-11-40_patch_6df46dd9-e7d3-4e71-bec4-e5c93b4ac863
282
282
  ellf_cli/version/18.0.26_2026-09-21-07-54-11_patch_cc52dd36-5eba-4094-a1b0-d6f79f103441
283
283
  ellf_cli/version/18.0.27_2026-09-21-08-36-49_patch_a25fa125-9bcd-4317-8da5-851c1c329631
284
+ ellf_cli/version/18.0.28_2026-09-21-09-44-01_patch_4430a721-0b3c-4f6a-b9bc-a53e667161bb
285
+ ellf_cli/version/18.0.29_2026-09-21-10-58-07_patch_83b2b942-60a4-4bef-afd1-99690ba52b69
284
286
  ellf_cli/version/18.0.2_2026-09-13-06-36-33_patch_176bb944-1c49-4dd3-a001-8fce908825a0
285
287
  ellf_cli/version/18.0.3_2026-09-13-12-32-46_patch_58e5ce3a-f2a7-4789-b2cd-5cc75c72b6ec
286
288
  ellf_cli/version/18.0.4_2026-09-13-12-45-14_patch_b6c79d55-8f85-4f47-a304-df52b122c38c
@@ -291,6 +293,7 @@ ellf_cli/version/18.0.7_2026-09-13-17-06-08_patch_428af161-0b27-43c2-8751-e5f787
291
293
  ellf_cli/version/18.0.9_2026-09-14-12-37-42_patch_b2ca0529-4b67-4fa4-8fdc-5f5e9f5d5166
292
294
  ellf_cli/version/__init__.py
293
295
  ellf_cli/version/read_version.py
296
+ tests/test_agents_info_stats.py
294
297
  tests/test_appdirs.py
295
298
  tests/test_auth.py
296
299
  tests/test_config.py
@@ -312,6 +315,7 @@ tests/test_infra_state_bucket.py
312
315
  tests/test_infra_terraform_init.py
313
316
  tests/test_invalid_secrets.py
314
317
  tests/test_job_list_project_filter.py
318
+ tests/test_jobs_info_hides_creator.py
315
319
  tests/test_jobs_list_unmatched.py
316
320
  tests/test_key_pair.py
317
321
  tests/test_login.py
@@ -320,6 +324,7 @@ tests/test_main.py
320
324
  tests/test_org_select.py
321
325
  tests/test_plans.py
322
326
  tests/test_projects.py
327
+ tests/test_projects_delete_confirm.py
323
328
  tests/test_publish_build_poll.py
324
329
  tests/test_publish_data.py
325
330
  tests/test_publish_import_root.py
@@ -336,5 +341,6 @@ tests/test_ui.py
336
341
  tests/test_ui_extras.py
337
342
  tests/test_usage_counts.py
338
343
  tests/test_users_assign.py
344
+ tests/test_users_info_stats.py
339
345
  tests/test_users_list.py
340
346
  tests/test_util.py
@@ -0,0 +1,69 @@
1
+ """`agents info` reports what the agent has annotated, as the app's table does."""
2
+
3
+ from datetime import datetime, timezone
4
+ from types import SimpleNamespace
5
+ from typing import Any, cast
6
+ from uuid import uuid4
7
+
8
+ import pytest
9
+
10
+ from ellf_cli.commands import agents as agents_cmd
11
+ from ellf_cli.errors import EllfErrors
12
+ from ellf_pam_sdk.models import AgentAnnotationStats, AgentDetail
13
+
14
+ STARTED = datetime(2026, 9, 20, 8, 46, tzinfo=timezone.utc)
15
+
16
+
17
+ @pytest.fixture
18
+ def stats_of(monkeypatch):
19
+ """Build an agent's stats block against a fixed annotation-counts reply."""
20
+
21
+ def _run(counts: Any, tasks: int = 2) -> dict[str, Any]:
22
+ agent_id = uuid4()
23
+ # _stats reads three attributes; a full AgentDetail would need the
24
+ # cluster and recipe plumbing none of them touch.
25
+ agent = cast(
26
+ AgentDetail,
27
+ SimpleNamespace(
28
+ id=agent_id,
29
+ related_tasks=[object()] * tasks,
30
+ last_execution_started=STARTED,
31
+ ),
32
+ )
33
+
34
+ def _counts(_id):
35
+ if isinstance(counts, Exception):
36
+ raise counts
37
+ return counts
38
+
39
+ auth = SimpleNamespace(
40
+ client=SimpleNamespace(agent=SimpleNamespace(annotation_counts=_counts))
41
+ )
42
+ monkeypatch.setattr(agents_cmd, "get_auth_state", lambda: auth)
43
+ return agents_cmd._stats(agent)
44
+
45
+ return _run
46
+
47
+
48
+ def test_annotations_and_sessions_join_the_block(stats_of):
49
+ counts = AgentAnnotationStats(agent_id=uuid4(), annotations=450, sessions=3)
50
+ assert stats_of(counts) == {
51
+ "tasks": 2,
52
+ "last_started": STARTED,
53
+ "annotations": 450,
54
+ "sessions": 3,
55
+ }
56
+
57
+
58
+ def test_unreported_annotations_pass_through_as_none(stats_of):
59
+ """None means no cluster has reported, which is not a zero."""
60
+ counts = AgentAnnotationStats(agent_id=uuid4(), annotations=None, sessions=0)
61
+ assert stats_of(counts)["annotations"] is None
62
+
63
+
64
+ def test_counts_failure_leaves_the_rest_of_the_block(stats_of):
65
+ """Stats are decorative: the counts are lost, not the command."""
66
+ assert stats_of(EllfErrors.EllfError("boom")) == {
67
+ "tasks": 2,
68
+ "last_started": STARTED,
69
+ }
@@ -0,0 +1,59 @@
1
+ """`<job> info` keeps `created_by_user` out of the default table."""
2
+
3
+ from types import SimpleNamespace
4
+ from typing import Any
5
+
6
+ import pytest
7
+
8
+ from ellf_cli.commands import actions as actions_cmd
9
+ from ellf_cli.commands import agents as agents_cmd
10
+ from ellf_cli.commands import services as services_cmd
11
+ from ellf_cli.commands import tasks as tasks_cmd
12
+
13
+ # module, the name it resolves the job with, and what it hides by default
14
+ JOBS = [
15
+ (tasks_cmd, "resolve_task", ["related_actions", "created_by_user"]),
16
+ (actions_cmd, "resolve_action", ["stats", "related_tasks", "created_by_user"]),
17
+ (agents_cmd, "resolve_agent", ["related_tasks", "created_by_user"]),
18
+ (services_cmd, "resolve_service", ["created_by_user"]),
19
+ ]
20
+
21
+
22
+ @pytest.fixture
23
+ def shown(monkeypatch):
24
+ """Run one job's `info` and capture what it asks to be printed."""
25
+
26
+ def _run(module: Any, resolver: str, **kwargs: Any) -> dict[str, Any]:
27
+ captured: dict[str, Any] = {}
28
+ job = SimpleNamespace(id="job-id")
29
+ status = SimpleNamespace(state=SimpleNamespace(value="ready"))
30
+ auth = SimpleNamespace(
31
+ broker_client=SimpleNamespace(
32
+ jobs=SimpleNamespace(get_status=lambda _id: status)
33
+ )
34
+ )
35
+ monkeypatch.setattr(module, "get_auth_state", lambda: auth)
36
+ monkeypatch.setattr(module, resolver, lambda *a, **k: job)
37
+ monkeypatch.setattr(module, "_stats", lambda *a, **k: {})
38
+ monkeypatch.setattr(
39
+ module,
40
+ "print_info_table",
41
+ lambda item, **kw: captured.update(item=item, **kw),
42
+ )
43
+ module.info("whatever", **kwargs)
44
+ return captured
45
+
46
+ return _run
47
+
48
+
49
+ @pytest.mark.parametrize("module,resolver,hidden", JOBS)
50
+ def test_creator_is_hidden_from_the_default_table(shown, module, resolver, hidden):
51
+ assert shown(module, resolver)["table_exclude"] == hidden
52
+
53
+
54
+ @pytest.mark.parametrize("module,resolver,hidden", JOBS)
55
+ def test_naming_your_own_columns_keeps_them(shown, module, resolver, hidden):
56
+ """table_exclude would otherwise beat the include and empty the table."""
57
+ res = shown(module, resolver, select=["created_by_user"])
58
+ assert res["table_exclude"] is None
59
+ assert res["select"] == ["created_by_user"]
@@ -0,0 +1,101 @@
1
+ """`projects delete` must be confirmed by hand, and must fail without a TTY."""
2
+
3
+ from datetime import datetime, timezone
4
+ from uuid import uuid4
5
+
6
+ import pytest
7
+
8
+ from ellf_cli.commands import projects as projects_cmd
9
+ from ellf_cli.errors import CLIError
10
+ from ellf_pam_sdk.models import ProjectDetail
11
+
12
+ NAME = "my-project"
13
+
14
+
15
+ def _project(name: str = NAME) -> ProjectDetail:
16
+ now = datetime.now(timezone.utc)
17
+ return ProjectDetail(
18
+ id=uuid4(),
19
+ created=now,
20
+ updated=now,
21
+ org_id=uuid4(),
22
+ name=name,
23
+ slug=name,
24
+ description="",
25
+ )
26
+
27
+
28
+ class _Delete:
29
+ """Calls `projects delete` under a chosen set of terminals, and carries
30
+ the record of how far it got -- which the tests that expect a refusal
31
+ have to read off the fixture, since the call raises before returning."""
32
+
33
+ def __init__(self, monkeypatch, reached: list[str]):
34
+ self._monkeypatch = monkeypatch
35
+ self.reached = reached
36
+
37
+ def __call__(self, *, stdin_tty: bool, stdout_tty: bool, typed: str = NAME):
38
+ self._monkeypatch.setattr(
39
+ projects_cmd,
40
+ "isatty",
41
+ lambda stream: (
42
+ stdin_tty if stream is projects_cmd.sys.stdin else stdout_tty
43
+ ),
44
+ )
45
+ self._monkeypatch.setattr("builtins.input", lambda _prompt="": typed)
46
+ return projects_cmd.delete(NAME), self.reached
47
+
48
+
49
+ @pytest.fixture
50
+ def run(monkeypatch):
51
+ """Call `projects delete`, recording whether it got past the prompt."""
52
+ project = _project()
53
+ reached: list[str] = []
54
+
55
+ monkeypatch.setattr(projects_cmd, "resolve_project", lambda _: project)
56
+ monkeypatch.setattr(
57
+ projects_cmd,
58
+ "_stop_project_jobs",
59
+ lambda *a, **k: reached.append("stopped"),
60
+ )
61
+ monkeypatch.setattr(projects_cmd, "get_auth_state", lambda: _FakeAuth(reached))
62
+ monkeypatch.setattr(projects_cmd, "print_mutation_result", lambda *a, **k: None)
63
+ return _Delete(monkeypatch, reached)
64
+
65
+
66
+ class _FakeAuth:
67
+ def __init__(self, reached):
68
+ self.client = self
69
+ self.project = self
70
+ self._reached = reached
71
+
72
+ def delete(self, id): # noqa: A002 - matches the SDK's signature
73
+ self._reached.append("deleted")
74
+
75
+
76
+ def test_refuses_without_a_terminal(run):
77
+ with pytest.raises(CLIError, match="has to be confirmed via a terminal"):
78
+ run(stdin_tty=False, stdout_tty=True)
79
+ assert run.reached == []
80
+
81
+
82
+ def test_refuses_when_stdout_is_piped(run):
83
+ with pytest.raises(CLIError, match="has to be confirmed via a terminal"):
84
+ run(stdin_tty=True, stdout_tty=False)
85
+ assert run.reached == []
86
+
87
+
88
+ def test_refuses_a_name_that_does_not_match(run):
89
+ with pytest.raises(CLIError, match="Wrong project name"):
90
+ run(stdin_tty=True, stdout_tty=True, typed="My-Project")
91
+ assert run.reached == []
92
+
93
+
94
+ def test_deletes_once_the_name_is_typed(run):
95
+ _, reached = run(stdin_tty=True, stdout_tty=True, typed=NAME)
96
+ assert reached == ["stopped", "deleted"]
97
+
98
+
99
+ def test_accepts_the_name_with_stray_whitespace(run):
100
+ _, reached = run(stdin_tty=True, stdout_tty=True, typed=f" {NAME}\n")
101
+ assert reached == ["stopped", "deleted"]
@@ -14,7 +14,6 @@ from pydantic import BaseModel
14
14
 
15
15
  from ellf_cli.ui import (
16
16
  dicts_to_table,
17
- format_duration,
18
17
  is_silent,
19
18
  isatty,
20
19
  print_args_table,
@@ -215,21 +214,6 @@ class TestPrintInfoTable:
215
214
  assert json.loads(capsys.readouterr().out)["age"] == 30
216
215
 
217
216
 
218
- class TestFormatDuration:
219
- def test_minutes_are_the_finest_unit(self):
220
- assert format_duration(0) == "0m"
221
- assert format_duration(59) == "0m"
222
- assert format_duration(180) == "3m"
223
- assert format_duration(5400) == "1h 30m"
224
-
225
- def test_whole_hours_drop_the_minutes(self):
226
- assert format_duration(7200) == "2h"
227
- assert format_duration(3600 * 1824 + 24 * 60) == "1824h 24m"
228
-
229
- def test_never_negative(self):
230
- assert format_duration(-30) == "0m"
231
-
232
-
233
217
  class TestPrintTableWithSelect:
234
218
  def test_basic(self, capsys):
235
219
  items = [_SampleModel(name="Alice", age=30)]