ellf-cli 10.0.1__tar.gz → 10.0.3__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 (185) hide show
  1. {ellf_cli-10.0.1/ellf_cli.egg-info → ellf_cli-10.0.3}/PKG-INFO +1 -1
  2. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/about.json +1 -1
  3. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/commands/actions.py +16 -2
  4. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/commands/agents.py +14 -2
  5. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/commands/services.py +16 -2
  6. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/commands/tasks.py +14 -2
  7. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/errors.py +10 -0
  8. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/messages.py +1 -0
  9. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/query.py +19 -5
  10. {ellf_cli-10.0.1 → ellf_cli-10.0.3/ellf_cli.egg-info}/PKG-INFO +1 -1
  11. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/tests/test_query.py +103 -0
  12. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/LICENSE +0 -0
  13. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/MANIFEST.in +0 -0
  14. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/README.md +0 -0
  15. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/__init__.py +0 -0
  16. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/__main__.py +0 -0
  17. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/about.py +0 -0
  18. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/appdirs.py +0 -0
  19. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/auth.py +0 -0
  20. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/cli.py +0 -0
  21. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/cloud/__init__.py +0 -0
  22. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/cloud/gcp.py +0 -0
  23. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/cluster_config.py +0 -0
  24. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/commands/__init__.py +0 -0
  25. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/commands/_cluster_select.py +0 -0
  26. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/commands/_org_select.py +0 -0
  27. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/commands/_recipe_file.py +0 -0
  28. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/commands/_recipe_subcommand.py +0 -0
  29. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/commands/_state.py +0 -0
  30. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/commands/assets.py +0 -0
  31. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/commands/auth.py +0 -0
  32. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/commands/clusters.py +0 -0
  33. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/commands/config.py +0 -0
  34. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/commands/datasets.py +0 -0
  35. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/commands/files/__init__.py +0 -0
  36. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/commands/files/cp.py +0 -0
  37. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/commands/files/ls.py +0 -0
  38. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/commands/files/rm.py +0 -0
  39. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/commands/files/rsync.py +0 -0
  40. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/commands/files/stats.py +0 -0
  41. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/commands/general.py +0 -0
  42. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/commands/import_export.py +0 -0
  43. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/commands/infra/__init__.py +0 -0
  44. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/commands/infra/_helpers.py +0 -0
  45. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/commands/infra/deploy.py +0 -0
  46. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/commands/infra/init_values.py +0 -0
  47. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/commands/infra/provision.py +0 -0
  48. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/commands/infra/register.py +0 -0
  49. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/commands/infra/setup.py +0 -0
  50. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/commands/infra/start.py +0 -0
  51. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/commands/infra/terraform.py +0 -0
  52. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/commands/infra/tls.py +0 -0
  53. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/commands/jobs.py +0 -0
  54. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/commands/packages.py +0 -0
  55. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/commands/paths.py +0 -0
  56. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/commands/plans.py +0 -0
  57. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/commands/projects.py +0 -0
  58. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/commands/publish_code.py +0 -0
  59. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/commands/publish_data.py +0 -0
  60. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/commands/recipes.py +0 -0
  61. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/commands/secrets.py +0 -0
  62. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/commands/support.py +0 -0
  63. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/commands/todos.py +0 -0
  64. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/config.py +0 -0
  65. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf.json +0 -0
  66. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/.claude-plugin/plugin.json +0 -0
  67. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/.gitignore +0 -0
  68. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skill_variants.json +0 -0
  69. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-annotate.assistant/SKILL.md +0 -0
  70. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-annotate.assistant/references/annotation_audit.md +0 -0
  71. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-annotate.assistant/references/builtin_ellf_annotation_recipes.md +0 -0
  72. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-annotate.coding/SKILL.md +0 -0
  73. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-annotate.coding/references/annotation_audit.md +0 -0
  74. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-annotate.coding/references/builtin_ellf_annotation_recipes.md +0 -0
  75. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-annotate.coding/references/builtin_prodigy_recipes.md +0 -0
  76. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-ask/SKILL.md +0 -0
  77. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-handoff/SKILL.md +0 -0
  78. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-monitor.assistant/SKILL.md +0 -0
  79. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-monitor.assistant/references/annotation_metrics.md +0 -0
  80. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-monitor.assistant/references/training_monitoring.md +0 -0
  81. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-monitor.coding/SKILL.md +0 -0
  82. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-monitor.coding/references/annotation_metrics.md +0 -0
  83. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-monitor.coding/references/training_monitoring.md +0 -0
  84. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-monitor.coding/scripts/check_training.py +0 -0
  85. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-ops.assistant/SKILL.md +0 -0
  86. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-ops.coding/SKILL.md +0 -0
  87. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-ops.coding/references/data_infra_cli.md +0 -0
  88. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-ops.coding/scripts/run_job.py +0 -0
  89. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-patterns/SKILL.md +0 -0
  90. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-patterns/references/pattern_strategies.md +0 -0
  91. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-prodigy/SKILL.md +0 -0
  92. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_action_recipe.py +0 -0
  93. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_agent_recipe.py +0 -0
  94. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_blocks_ui.py +0 -0
  95. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_correct.py +0 -0
  96. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_custom_ui.py +0 -0
  97. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_manual.py +0 -0
  98. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_pages_ui.py +0 -0
  99. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_routing.py +0 -0
  100. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_task_recipe.py +0 -0
  101. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_teach.py +0 -0
  102. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-prodigy/references/builtin_recipes.md +0 -0
  103. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-prodigy/references/ellf_recipe_sdk.md +0 -0
  104. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-prodigy/references/lint_recipe.py +0 -0
  105. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-prodigy/references/prodigy_recipe_api.md +0 -0
  106. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-prodigy/references/template_index.md +0 -0
  107. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-project.assistant/SKILL.md +0 -0
  108. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-project.assistant/references/consulting_patterns.md +0 -0
  109. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-project.assistant/references/explosion_strategy.md +0 -0
  110. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-project.assistant/references/prodigy_llm_bot.md +0 -0
  111. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-project.coding/SKILL.md +0 -0
  112. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-project.coding/references/consulting_patterns.md +0 -0
  113. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-project.coding/references/explosion_strategy.md +0 -0
  114. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-project.coding/references/prodigy_llm_bot.md +0 -0
  115. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-support.assistant/SKILL.md +0 -0
  116. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-support.coding/SKILL.md +0 -0
  117. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-todo/SKILL.md +0 -0
  118. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-train.assistant/SKILL.md +0 -0
  119. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-train.assistant/references/diagnostics.md +0 -0
  120. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-train.assistant/references/evaluation_guide.md +0 -0
  121. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-train.assistant/references/model_selection.md +0 -0
  122. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-train.assistant/references/training_paradigms.md +0 -0
  123. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-train.assistant/references/workflow.md +0 -0
  124. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-train.coding/SKILL.md +0 -0
  125. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-train.coding/references/config_advanced.md +0 -0
  126. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-train.coding/references/config_architectures.md +0 -0
  127. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-train.coding/references/config_training.md +0 -0
  128. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-train.coding/references/diagnostics.md +0 -0
  129. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-train.coding/references/evaluation_guide.md +0 -0
  130. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-train.coding/references/experiment_patterns.md +0 -0
  131. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-train.coding/references/model_selection.md +0 -0
  132. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-train.coding/references/training_paradigms.md +0 -0
  133. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-train.coding/references/training_troubleshooting.md +0 -0
  134. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-train.coding/references/workflow.md +0 -0
  135. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ellf_skills/skills/ellf-train.coding/scripts/ellf_logger.py +0 -0
  136. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/helm.py +0 -0
  137. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/key_pair.py +0 -0
  138. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/main.py +0 -0
  139. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/recipes_cookiecutter/cookiecutter.json +0 -0
  140. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/.gitignore +0 -0
  141. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/README.md.tmpl +0 -0
  142. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/requirements-dev.in +0 -0
  143. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/requirements.in +0 -0
  144. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/setup.py.tmpl +0 -0
  145. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/{{cookiecutter.package_name}}/__init__.py +0 -0
  146. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/{{cookiecutter.package_name}}/about.py +0 -0
  147. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/{{cookiecutter.package_name}}/recipes/__init__.py +0 -0
  148. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/{{cookiecutter.package_name}}/recipes/example_task.py +0 -0
  149. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/testing/__init__.py +0 -0
  150. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ty.py +0 -0
  151. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/ui.py +0 -0
  152. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/url.py +0 -0
  153. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli/util.py +0 -0
  154. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli.egg-info/SOURCES.txt +0 -0
  155. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli.egg-info/dependency_links.txt +0 -0
  156. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli.egg-info/entry_points.txt +0 -0
  157. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli.egg-info/not-zip-safe +0 -0
  158. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli.egg-info/requires.txt +0 -0
  159. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/ellf_cli.egg-info/top_level.txt +0 -0
  160. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/pyproject.toml +0 -0
  161. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/setup.cfg +0 -0
  162. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/setup.py +0 -0
  163. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/tests/test_appdirs.py +0 -0
  164. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/tests/test_auth.py +0 -0
  165. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/tests/test_config.py +0 -0
  166. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/tests/test_errors.py +0 -0
  167. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/tests/test_files_cp.py +0 -0
  168. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/tests/test_files_cp_helpers.py +0 -0
  169. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/tests/test_info.py +0 -0
  170. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/tests/test_invalid_secrets.py +0 -0
  171. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/tests/test_key_pair.py +0 -0
  172. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/tests/test_login.py +0 -0
  173. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/tests/test_logout.py +0 -0
  174. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/tests/test_main.py +0 -0
  175. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/tests/test_org_select.py +0 -0
  176. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/tests/test_plans.py +0 -0
  177. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/tests/test_projects.py +0 -0
  178. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/tests/test_recipe_file.py +0 -0
  179. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/tests/test_recipes.py +0 -0
  180. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/tests/test_state.py +0 -0
  181. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/tests/test_support.py +0 -0
  182. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/tests/test_ty.py +0 -0
  183. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/tests/test_ui.py +0 -0
  184. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/tests/test_ui_extras.py +0 -0
  185. {ellf_cli-10.0.1 → ellf_cli-10.0.3}/tests/test_util.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ellf-cli
3
- Version: 10.0.1
3
+ Version: 10.0.3
4
4
  Summary: Ellf Command Line Interface
5
5
  Home-page: https://prodi.gy
6
6
  Author: ExplosionAI GmbH
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "title": "Ellf CLI",
3
3
  "name": "ellf-cli",
4
- "version": "10.0.1",
4
+ "version": "10.0.3",
5
5
  "summary": "Ellf Command Line Interface",
6
6
  "uri": "https://prodi.gy",
7
7
  "prog": "ellf",
@@ -9,7 +9,13 @@ from wasabi import msg
9
9
  from ellf_pam_sdk.models import ActionDetail, ActionSummary
10
10
 
11
11
  from ..cli import cli
12
- from ..errors import BrokerError, CLIError, EllfError, HTTPXErrors
12
+ from ..errors import (
13
+ BrokerError,
14
+ CLIError,
15
+ EllfError,
16
+ HTTPXErrors,
17
+ JobStartRejectedError,
18
+ )
13
19
  from ..messages import Messages
14
20
  from ..query import delete_job, resolve_action, resolve_recipe, start_job, stop_job
15
21
  from ..ui import (
@@ -108,9 +114,17 @@ def create(
108
114
  if not no_start:
109
115
  try:
110
116
  start(action_id, no_wait=no_wait, as_json=as_json)
111
- except Exception:
117
+ except JobStartRejectedError:
118
+ # The broker rejected the start, so no workload exists and the
119
+ # PAM record can be safely rolled back.
112
120
  auth.client.action.delete(id=action_id)
113
121
  raise
122
+ except Exception:
123
+ # The job may have been submitted, so keep the record.
124
+ msg.warn(
125
+ Messages.E060.format(noun="action", name=action_id, plural="actions")
126
+ )
127
+ raise
114
128
  return action_id
115
129
 
116
130
 
@@ -9,7 +9,13 @@ from wasabi import msg
9
9
  from ellf_pam_sdk.models import AgentDetail, AgentSummary
10
10
 
11
11
  from ..cli import cli
12
- from ..errors import BrokerError, CLIError, EllfError, HTTPXErrors
12
+ from ..errors import (
13
+ BrokerError,
14
+ CLIError,
15
+ EllfError,
16
+ HTTPXErrors,
17
+ JobStartRejectedError,
18
+ )
13
19
  from ..messages import Messages
14
20
  from ..query import (
15
21
  delete_job,
@@ -115,9 +121,15 @@ def create(
115
121
  if not no_start:
116
122
  try:
117
123
  start(agent_id, no_wait=no_wait, as_json=as_json)
118
- except Exception:
124
+ except JobStartRejectedError:
125
+ # The broker rejected the start, so no workload exists and the
126
+ # PAM record can be safely rolled back.
119
127
  auth.client.agent.delete(id=agent_id)
120
128
  raise
129
+ except Exception:
130
+ # The job may have been submitted, so keep the record.
131
+ msg.warn(Messages.E060.format(noun="agent", name=agent_id, plural="agents"))
132
+ raise
121
133
  return agent_id
122
134
 
123
135
 
@@ -9,7 +9,13 @@ from wasabi import msg
9
9
  from ellf_pam_sdk.models import ServiceDetail, ServiceSummary
10
10
 
11
11
  from ..cli import cli
12
- from ..errors import BrokerError, CLIError, EllfError, HTTPXErrors
12
+ from ..errors import (
13
+ BrokerError,
14
+ CLIError,
15
+ EllfError,
16
+ HTTPXErrors,
17
+ JobStartRejectedError,
18
+ )
13
19
  from ..messages import Messages
14
20
  from ..query import (
15
21
  delete_job,
@@ -114,9 +120,17 @@ def create(
114
120
  if not no_start:
115
121
  try:
116
122
  start(service_id, no_wait=no_wait, as_json=as_json)
117
- except Exception:
123
+ except JobStartRejectedError:
124
+ # The broker rejected the start, so no workload exists and the
125
+ # PAM record can be safely rolled back.
118
126
  auth.client.service.delete(id=service_id)
119
127
  raise
128
+ except Exception:
129
+ # The job may have been submitted, so keep the record.
130
+ msg.warn(
131
+ Messages.E060.format(noun="service", name=service_id, plural="services")
132
+ )
133
+ raise
120
134
  return service_id
121
135
 
122
136
 
@@ -11,7 +11,13 @@ from wasabi import msg
11
11
  from ellf_pam_sdk.models import TaskDetail, TaskSummary
12
12
 
13
13
  from ..cli import cli
14
- from ..errors import BrokerError, CLIError, EllfError, HTTPXErrors
14
+ from ..errors import (
15
+ BrokerError,
16
+ CLIError,
17
+ EllfError,
18
+ HTTPXErrors,
19
+ JobStartRejectedError,
20
+ )
15
21
  from ..messages import Messages
16
22
  from ..query import delete_job, resolve_recipe, resolve_task, start_job, stop_job
17
23
  from ..ui import (
@@ -116,9 +122,15 @@ def create(
116
122
  if not no_start:
117
123
  try:
118
124
  start(task_id, no_wait=no_wait, as_json=as_json)
119
- except Exception:
125
+ except JobStartRejectedError:
126
+ # The broker rejected the start, so no workload exists and the
127
+ # PAM record can be safely rolled back.
120
128
  auth.client.task.delete(id=task_id)
121
129
  raise
130
+ except Exception:
131
+ # The job may have been submitted, so keep the record.
132
+ msg.warn(Messages.E060.format(noun="task", name=task_id, plural="tasks"))
133
+ raise
122
134
  return task_id
123
135
 
124
136
 
@@ -61,6 +61,16 @@ class CLIError(Exception):
61
61
  super().__init__(self.message)
62
62
 
63
63
 
64
+ class JobStartRejectedError(CLIError):
65
+ """The broker rejected a job start outright, so no workload was created.
66
+
67
+ Raised only when the broker's start response reports a cluster or
68
+ validation error. Other start failures (e.g. status polling after the
69
+ job was submitted) must not use this class: the workload may be running,
70
+ so it is not safe to roll back the PAM record.
71
+ """
72
+
73
+
64
74
  class EllfParseSecretsError(EllfError):
65
75
  def __init__(
66
76
  self, secrets_file: Path | None, error: Exception | None = None
@@ -63,6 +63,7 @@ ellf secret create my-credentials OPENAI_API_KEY="sk-..." CUSTOM_SECRET=-
63
63
  E057 = "Could not import recipes from {package}"
64
64
  E058 = "Could not get a Prodigy login token from the broker for task {name}"
65
65
  E059 = "Could not fetch questions from the task server for {name}"
66
+ E060 = "Could not confirm that {noun} {name} started. The {noun} was created and still exists: check it with `ellf {plural} info {name}` or start it with `ellf {plural} start {name}`"
66
67
 
67
68
  T001 = "Not creating {noun} {name}: {noun} already exists"
68
69
  T002 = "Successfully created {noun} {name}"
@@ -22,7 +22,7 @@ from ellf_pam_sdk.errors import ELLF_ERRORS, EllfError
22
22
 
23
23
  from .auth import AuthState
24
24
  from .commands._state import get_auth_state, get_root_cfg, get_saved_settings
25
- from .errors import CLIError, EllfErrors
25
+ from .errors import CLIError, EllfErrors, JobStartRejectedError
26
26
  from .messages import Messages
27
27
  from .ty import Page
28
28
  from .util import URL
@@ -603,10 +603,10 @@ class JobOperations:
603
603
  return
604
604
  elif change_response.cluster_error:
605
605
  err = Messages.E025.format(noun=self.job_type.value, name=job_spec.job_id)
606
- raise CLIError(err, change_response.cluster_error)
606
+ raise JobStartRejectedError(err, change_response.cluster_error)
607
607
  elif change_response.validation_error:
608
608
  err = Messages.E044.format(noun=self.job_type.value, name=job_spec.job_id)
609
- raise CLIError(err, change_response.validation_error)
609
+ raise JobStartRejectedError(err, change_response.validation_error)
610
610
  elif not quiet:
611
611
  msg.info(
612
612
  Messages.T006.format(noun=self.job_type.value, name=job_spec.job_id)
@@ -826,7 +826,11 @@ def delete_job(job: _JobT, auth: AuthState, quiet: bool = False) -> _JobT:
826
826
 
827
827
 
828
828
  def check_job_started(
829
- job: _JobT, auth: AuthState, *, wait_seconds: int = 60 * 2
829
+ job: _JobT,
830
+ auth: AuthState,
831
+ *,
832
+ wait_seconds: int = 60 * 2,
833
+ not_found_grace_seconds: int = 10,
830
834
  ) -> _JobT | None:
831
835
  job_type = _get_job_type(job)
832
836
  started_states = {
@@ -846,7 +850,7 @@ def check_job_started(
846
850
  from .ui import loading
847
851
 
848
852
  with loading(Messages.T034.format(noun=job_type)):
849
- for _ in range(wait_seconds):
853
+ for attempt in range(wait_seconds):
850
854
  try:
851
855
  status = auth.broker_client.jobs.get_status(job.id)
852
856
  except (httpx.HTTPError, BrokerError):
@@ -858,6 +862,16 @@ def check_job_started(
858
862
  and getattr(status, "health", None) not in failed_healths
859
863
  ):
860
864
  break
865
+ if (
866
+ status.state == broker_models.WorkloadState.not_found
867
+ and attempt < not_found_grace_seconds
868
+ and getattr(status, "health", None) not in failed_healths
869
+ ):
870
+ # A just-submitted job can report not_found before its
871
+ # resources appear in the cluster listing, so an early
872
+ # not_found is not a failure: keep polling.
873
+ time.sleep(1)
874
+ continue
861
875
  if status.state in failed_states or (
862
876
  getattr(status, "health", None) in failed_healths
863
877
  ):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ellf-cli
3
- Version: 10.0.1
3
+ Version: 10.0.3
4
4
  Summary: Ellf Command Line Interface
5
5
  Home-page: https://prodi.gy
6
6
  Author: ExplosionAI GmbH
@@ -8,7 +8,9 @@ from uuid import UUID, uuid4
8
8
 
9
9
  import pytest
10
10
 
11
+ from ellf_broker_sdk import models as broker_models
11
12
  from ellf_cli import query
13
+ from ellf_cli.errors import CLIError
12
14
  from ellf_cli.query import _normalise_scope_params, get_not_found_error
13
15
  from ellf_pam_sdk.errors import ClusterNotFound, ProjectNotFound
14
16
 
@@ -119,3 +121,104 @@ class TestNormaliseScopeParams:
119
121
  def test_does_not_touch_unrelated_keys(self, fake_scope_resolution):
120
122
  out = _normalise_scope_params({"name": "foo", "after": "cursor-1"})
121
123
  assert out == {"name": "foo", "after": "cursor-1"}
124
+
125
+
126
+ # ---------------------------------------------------------------------------
127
+ # check_job_started: a just-submitted job can report ``not_found`` before its
128
+ # resources appear in the cluster listing (the broker derives ``not_found``
129
+ # from an empty label-selector list). The first polls must therefore tolerate
130
+ # ``not_found`` instead of treating it as a terminal failure -- this race
131
+ # caused freshly created actions to be reported as failed-to-start (and, before
132
+ # the JobStartRejectedError split, rolled back and deleted) while the workload
133
+ # was in fact running.
134
+
135
+
136
+ class _FakeJobsEndpoint:
137
+ """Broker jobs stub returning a scripted sequence of statuses."""
138
+
139
+ def __init__(self, statuses: list[Any]) -> None:
140
+ self._statuses = statuses
141
+ self.calls = 0
142
+
143
+ def get_status(self, job_id: UUID) -> Any:
144
+ index = min(self.calls, len(self._statuses) - 1)
145
+ self.calls += 1
146
+ return self._statuses[index]
147
+
148
+
149
+ class TestCheckJobStarted:
150
+ def _status(
151
+ self,
152
+ state: broker_models.WorkloadState,
153
+ health: broker_models.WorkloadHealth,
154
+ *,
155
+ is_ready: bool = False,
156
+ ) -> broker_models.JobStatusResponse:
157
+ return broker_models.JobStatusResponse(
158
+ job_id=uuid4(), state=state, health=health, is_ready=is_ready
159
+ )
160
+
161
+ def _run(
162
+ self,
163
+ statuses: list[broker_models.JobStatusResponse],
164
+ monkeypatch: pytest.MonkeyPatch,
165
+ **kwargs: Any,
166
+ ) -> tuple[Any, _FakeJobsEndpoint]:
167
+ monkeypatch.setattr(query.time, "sleep", lambda seconds: None)
168
+ jobs = _FakeJobsEndpoint(statuses)
169
+ auth = SimpleNamespace(broker_client=SimpleNamespace(jobs=jobs))
170
+ job = SimpleNamespace(id=uuid4(), url_logs="https://example.com/logs")
171
+ result = query.check_job_started(job, auth, **kwargs) # type: ignore[arg-type]
172
+ return result, jobs
173
+
174
+ def test_not_found_within_grace_then_running(
175
+ self, monkeypatch: pytest.MonkeyPatch
176
+ ) -> None:
177
+ statuses = [
178
+ self._status(
179
+ broker_models.WorkloadState.not_found,
180
+ broker_models.WorkloadHealth.inactive,
181
+ )
182
+ ] * 3 + [
183
+ self._status(
184
+ broker_models.WorkloadState.running,
185
+ broker_models.WorkloadHealth.progressing,
186
+ )
187
+ ]
188
+ result, jobs = self._run(statuses, monkeypatch)
189
+ assert result is not None
190
+ assert jobs.calls == 4
191
+
192
+ def test_not_found_past_grace_raises(self, monkeypatch: pytest.MonkeyPatch) -> None:
193
+ statuses = [
194
+ self._status(
195
+ broker_models.WorkloadState.not_found,
196
+ broker_models.WorkloadHealth.inactive,
197
+ )
198
+ ]
199
+ with pytest.raises(CLIError):
200
+ self._run(statuses, monkeypatch, not_found_grace_seconds=5)
201
+ # Polls through the grace window, then fails on the next poll.
202
+
203
+ def test_failed_state_raises_without_grace(
204
+ self, monkeypatch: pytest.MonkeyPatch
205
+ ) -> None:
206
+ statuses = [
207
+ self._status(
208
+ broker_models.WorkloadState.failed,
209
+ broker_models.WorkloadHealth.terminal,
210
+ )
211
+ ]
212
+ with pytest.raises(CLIError):
213
+ self._run(statuses, monkeypatch)
214
+
215
+ def test_succeeded_returns_job(self, monkeypatch: pytest.MonkeyPatch) -> None:
216
+ statuses = [
217
+ self._status(
218
+ broker_models.WorkloadState.succeeded,
219
+ broker_models.WorkloadHealth.inactive,
220
+ )
221
+ ]
222
+ result, jobs = self._run(statuses, monkeypatch)
223
+ assert result is not None
224
+ assert jobs.calls == 1
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes