ellf-cli 6.0.1__tar.gz → 6.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 (184) hide show
  1. {ellf_cli-6.0.1/ellf_cli.egg-info → ellf_cli-6.0.3}/PKG-INFO +1 -1
  2. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/about.json +1 -1
  3. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/auth.py +20 -6
  4. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/commands/general.py +11 -8
  5. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf.json +1 -1
  6. {ellf_cli-6.0.1 → ellf_cli-6.0.3/ellf_cli.egg-info}/PKG-INFO +1 -1
  7. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/tests/test_login.py +83 -1
  8. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/LICENSE +0 -0
  9. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/MANIFEST.in +0 -0
  10. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/README.md +0 -0
  11. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/__init__.py +0 -0
  12. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/__main__.py +0 -0
  13. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/about.py +0 -0
  14. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/appdirs.py +0 -0
  15. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/cli.py +0 -0
  16. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/cloud/__init__.py +0 -0
  17. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/cloud/gcp.py +0 -0
  18. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/cluster_config.py +0 -0
  19. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/commands/__init__.py +0 -0
  20. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/commands/_cluster_select.py +0 -0
  21. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/commands/_org_select.py +0 -0
  22. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/commands/_recipe_file.py +0 -0
  23. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/commands/_recipe_subcommand.py +0 -0
  24. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/commands/_state.py +0 -0
  25. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/commands/actions.py +0 -0
  26. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/commands/agents.py +0 -0
  27. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/commands/assets.py +0 -0
  28. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/commands/auth.py +0 -0
  29. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/commands/clusters.py +0 -0
  30. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/commands/config.py +0 -0
  31. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/commands/datasets.py +0 -0
  32. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/commands/files/__init__.py +0 -0
  33. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/commands/files/cp.py +0 -0
  34. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/commands/files/ls.py +0 -0
  35. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/commands/files/rm.py +0 -0
  36. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/commands/files/rsync.py +0 -0
  37. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/commands/files/stats.py +0 -0
  38. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/commands/import_export.py +0 -0
  39. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/commands/infra/__init__.py +0 -0
  40. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/commands/infra/_helpers.py +0 -0
  41. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/commands/infra/deploy.py +0 -0
  42. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/commands/infra/init_values.py +0 -0
  43. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/commands/infra/provision.py +0 -0
  44. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/commands/infra/register.py +0 -0
  45. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/commands/infra/setup.py +0 -0
  46. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/commands/infra/start.py +0 -0
  47. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/commands/infra/terraform.py +0 -0
  48. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/commands/infra/tls.py +0 -0
  49. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/commands/jobs.py +0 -0
  50. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/commands/packages.py +0 -0
  51. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/commands/paths.py +0 -0
  52. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/commands/plans.py +0 -0
  53. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/commands/projects.py +0 -0
  54. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/commands/publish_code.py +0 -0
  55. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/commands/publish_data.py +0 -0
  56. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/commands/recipes.py +0 -0
  57. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/commands/secrets.py +0 -0
  58. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/commands/support.py +0 -0
  59. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/commands/tasks.py +0 -0
  60. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/commands/todos.py +0 -0
  61. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/config.py +0 -0
  62. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/.claude-plugin/plugin.json +0 -0
  63. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/.gitignore +0 -0
  64. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skill_variants.json +0 -0
  65. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-annotate.assistant/SKILL.md +0 -0
  66. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-annotate.assistant/references/annotation_audit.md +0 -0
  67. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-annotate.assistant/references/builtin_ellf_annotation_recipes.md +0 -0
  68. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-annotate.coding/SKILL.md +0 -0
  69. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-annotate.coding/references/annotation_audit.md +0 -0
  70. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-annotate.coding/references/builtin_ellf_annotation_recipes.md +0 -0
  71. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-annotate.coding/references/builtin_prodigy_recipes.md +0 -0
  72. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-ask/SKILL.md +0 -0
  73. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-handoff/SKILL.md +0 -0
  74. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-monitor.assistant/SKILL.md +0 -0
  75. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-monitor.assistant/references/annotation_metrics.md +0 -0
  76. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-monitor.assistant/references/training_monitoring.md +0 -0
  77. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-monitor.coding/SKILL.md +0 -0
  78. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-monitor.coding/references/annotation_metrics.md +0 -0
  79. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-monitor.coding/references/training_monitoring.md +0 -0
  80. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-monitor.coding/scripts/check_training.py +0 -0
  81. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-ops.assistant/SKILL.md +0 -0
  82. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-ops.coding/SKILL.md +0 -0
  83. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-ops.coding/references/data_infra_cli.md +0 -0
  84. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-ops.coding/scripts/run_job.py +0 -0
  85. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-patterns/SKILL.md +0 -0
  86. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-patterns/references/pattern_strategies.md +0 -0
  87. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-prodigy/SKILL.md +0 -0
  88. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_action_recipe.py +0 -0
  89. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_agent_recipe.py +0 -0
  90. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_blocks_ui.py +0 -0
  91. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_correct.py +0 -0
  92. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_custom_ui.py +0 -0
  93. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_manual.py +0 -0
  94. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_pages_ui.py +0 -0
  95. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_routing.py +0 -0
  96. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_task_recipe.py +0 -0
  97. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-prodigy/assets/templates/template_teach.py +0 -0
  98. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-prodigy/references/builtin_recipes.md +0 -0
  99. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-prodigy/references/ellf_recipe_sdk.md +0 -0
  100. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-prodigy/references/lint_recipe.py +0 -0
  101. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-prodigy/references/prodigy_recipe_api.md +0 -0
  102. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-prodigy/references/template_index.md +0 -0
  103. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-project.assistant/SKILL.md +0 -0
  104. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-project.assistant/references/consulting_patterns.md +0 -0
  105. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-project.assistant/references/explosion_strategy.md +0 -0
  106. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-project.assistant/references/prodigy_llm_bot.md +0 -0
  107. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-project.coding/SKILL.md +0 -0
  108. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-project.coding/references/consulting_patterns.md +0 -0
  109. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-project.coding/references/explosion_strategy.md +0 -0
  110. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-project.coding/references/prodigy_llm_bot.md +0 -0
  111. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-support.assistant/SKILL.md +0 -0
  112. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-support.coding/SKILL.md +0 -0
  113. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-todo/SKILL.md +0 -0
  114. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-train.assistant/SKILL.md +0 -0
  115. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-train.assistant/references/diagnostics.md +0 -0
  116. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-train.assistant/references/evaluation_guide.md +0 -0
  117. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-train.assistant/references/model_selection.md +0 -0
  118. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-train.assistant/references/training_paradigms.md +0 -0
  119. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-train.assistant/references/workflow.md +0 -0
  120. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-train.coding/SKILL.md +0 -0
  121. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-train.coding/references/config_advanced.md +0 -0
  122. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-train.coding/references/config_architectures.md +0 -0
  123. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-train.coding/references/config_training.md +0 -0
  124. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-train.coding/references/diagnostics.md +0 -0
  125. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-train.coding/references/evaluation_guide.md +0 -0
  126. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-train.coding/references/experiment_patterns.md +0 -0
  127. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-train.coding/references/model_selection.md +0 -0
  128. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-train.coding/references/training_paradigms.md +0 -0
  129. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-train.coding/references/training_troubleshooting.md +0 -0
  130. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-train.coding/references/workflow.md +0 -0
  131. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ellf_skills/skills/ellf-train.coding/scripts/ellf_logger.py +0 -0
  132. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/errors.py +0 -0
  133. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/helm.py +0 -0
  134. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/key_pair.py +0 -0
  135. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/main.py +0 -0
  136. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/messages.py +0 -0
  137. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/query.py +0 -0
  138. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/recipes_cookiecutter/cookiecutter.json +0 -0
  139. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/.gitignore +0 -0
  140. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/README.md.tmpl +0 -0
  141. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/requirements-dev.in +0 -0
  142. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/requirements.in +0 -0
  143. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/setup.py.tmpl +0 -0
  144. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/{{cookiecutter.package_name}}/__init__.py +0 -0
  145. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/{{cookiecutter.package_name}}/about.py +0 -0
  146. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/{{cookiecutter.package_name}}/recipes/__init__.py +0 -0
  147. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/recipes_cookiecutter/{{cookiecutter.package_dir}}/{{cookiecutter.package_name}}/recipes/example_task.py +0 -0
  148. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/testing/__init__.py +0 -0
  149. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ty.py +0 -0
  150. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/ui.py +0 -0
  151. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/url.py +0 -0
  152. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli/util.py +0 -0
  153. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli.egg-info/SOURCES.txt +0 -0
  154. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli.egg-info/dependency_links.txt +0 -0
  155. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli.egg-info/entry_points.txt +0 -0
  156. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli.egg-info/not-zip-safe +0 -0
  157. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli.egg-info/requires.txt +0 -0
  158. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/ellf_cli.egg-info/top_level.txt +0 -0
  159. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/pyproject.toml +0 -0
  160. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/setup.cfg +0 -0
  161. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/setup.py +0 -0
  162. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/tests/test_appdirs.py +0 -0
  163. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/tests/test_auth.py +0 -0
  164. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/tests/test_config.py +0 -0
  165. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/tests/test_errors.py +0 -0
  166. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/tests/test_files_cp.py +0 -0
  167. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/tests/test_files_cp_helpers.py +0 -0
  168. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/tests/test_info.py +0 -0
  169. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/tests/test_invalid_secrets.py +0 -0
  170. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/tests/test_key_pair.py +0 -0
  171. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/tests/test_logout.py +0 -0
  172. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/tests/test_main.py +0 -0
  173. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/tests/test_org_select.py +0 -0
  174. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/tests/test_plans.py +0 -0
  175. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/tests/test_projects.py +0 -0
  176. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/tests/test_query.py +0 -0
  177. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/tests/test_recipe_file.py +0 -0
  178. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/tests/test_recipes.py +0 -0
  179. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/tests/test_state.py +0 -0
  180. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/tests/test_support.py +0 -0
  181. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/tests/test_ty.py +0 -0
  182. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/tests/test_ui.py +0 -0
  183. {ellf_cli-6.0.1 → ellf_cli-6.0.3}/tests/test_ui_extras.py +0 -0
  184. {ellf_cli-6.0.1 → ellf_cli-6.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: 6.0.1
3
+ Version: 6.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": "6.0.1",
4
+ "version": "6.0.3",
5
5
  "summary": "Ellf Command Line Interface",
6
6
  "uri": "https://prodi.gy",
7
7
  "prog": "ellf",
@@ -210,10 +210,11 @@ class AuthStateImpl:
210
210
  # PAM lookup-by-address every time the user runs an ellf command.
211
211
  # `auth.cluster_id` falls back to the address lookup only if this is None.
212
212
  self._cluster_id = settings.cluster_id
213
- # Seed org_id from saved settings too: the user's api_token already
214
- # carries the org_id in its `oid` claim, but we don't decode the JWT
215
- # here. The saved setting reflects whatever was chosen at the last
216
- # `ellf login` and stays in sync via set_active_org.
213
+ # Seed org_id from saved-defaults as a fallback only. The ``org_id``
214
+ # property reads from the api_token's ``oid`` claim which is what
215
+ # PAM authorizes against and overwrites this value on first access.
216
+ # The seed is just here to satisfy callers that read ``_org_id``
217
+ # before any token is fetched.
217
218
  self._org_id = settings.org_id
218
219
  self._secrets_path = ctx.secrets_path
219
220
  self._saved_settings_path = ctx.saved_settings_path
@@ -269,9 +270,22 @@ class AuthStateImpl:
269
270
 
270
271
  @property
271
272
  def org_id(self) -> UUID:
273
+ # PAM authorizes against the api_token's ``oid`` claim, so the JWT
274
+ # is the source of truth. The saved-defaults pointer (seeded into
275
+ # ``_org_id`` in __init__) can drift — e.g. when secrets.json is
276
+ # restored from backup, when a switch updates the pointer without
277
+ # re-minting the token, or when an ELLF_API_TOKEN env var injects
278
+ # a token from a different org. Reading from the JWT keeps every
279
+ # caller (server-side request bodies, display strings) aligned
280
+ # with what PAM will actually authorize against.
281
+ oid = self.get_api_token().header.get("oid")
282
+ if oid is not None:
283
+ self._org_id = UUID(oid)
284
+ return self._org_id
272
285
  if self._org_id is None:
273
- # Exploits that the login should only have the user's
274
- # own org accessible
286
+ # Token missing the oid claim (older PAM tokens, or an
287
+ # externally-supplied ELLF_API_TOKEN). Fall back to listing —
288
+ # the login should only have the user's own org accessible.
275
289
  orgs = list(self.client.org.all())
276
290
  if not orgs:
277
291
  raise EllfError(message="Could not fetch orgs")
@@ -152,17 +152,20 @@ def _select_and_persist_org(
152
152
  ) -> UUID:
153
153
  """Pick an org via pam and write it into SavedSettings.
154
154
 
155
- If the chosen org differs from the current api_token's org, calls
156
- ``/v1/token/switch-org`` to re-issue the token for the target org.
157
- The first time a person logs in here we always have *some* api_token
158
- (from the device-flow login), but we don't know which org it landed
159
- in without a round-trip, so the switch is unconditional when an org
160
- is explicitly selected that's cheap and avoids a JWT decode here.
155
+ The api_token's JWT carries an ``oid`` claim that PAM authorizes
156
+ against server-side; the saved-defaults ``org_id`` is only a local
157
+ display pointer. The two can drift apart (e.g. saved-defaults reset
158
+ independently, or a prior switch that updated the pointer without
159
+ re-minting). We compare ``chosen`` to the JWT claim not the saved
160
+ pointer so a ``login --org X`` that finds the pointer already at X
161
+ but the token issued for Y still calls ``/v1/token/switch-org`` and
162
+ converges both sides on X.
161
163
  """
162
164
  chosen = select_org(auth.client, name_or_id)
163
165
  settings = get_saved_settings()
164
- previous_org_id = settings.org_id
165
- if previous_org_id != chosen.org_id:
166
+ token_oid = auth.get_api_token().header.get("oid")
167
+ token_org_id = UUID(token_oid) if token_oid else None
168
+ if token_org_id != chosen.org_id:
166
169
  auth.set_active_org(chosen.org_id)
167
170
  settings.update("org_id", chosen.org_id)
168
171
  settings.save(get_root_cfg().saved_settings_path)
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "prog": "ellf",
3
3
  "help": "Ellf Command Line Interface.",
4
- "version": "6.0.0",
4
+ "version": "6.0.2",
5
5
  "extra_key": "_extra",
6
6
  "commands": {
7
7
  "actions": {
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ellf-cli
3
- Version: 6.0.1
3
+ Version: 6.0.3
4
4
  Summary: Ellf Command Line Interface
5
5
  Home-page: https://prodi.gy
6
6
  Author: ExplosionAI GmbH
@@ -1,6 +1,8 @@
1
1
  import json
2
2
  from pathlib import Path
3
- from unittest.mock import patch
3
+ from types import SimpleNamespace
4
+ from unittest.mock import MagicMock, patch
5
+ from uuid import uuid4
4
6
 
5
7
 
6
8
  def _plugin_runtime_files(base_dir: Path) -> dict[Path, bytes]:
@@ -154,6 +156,86 @@ def test_install_claude_skills_with_packaged_plugin(tmp_path, monkeypatch):
154
156
  assert (dest / "ellf-patterns").is_dir()
155
157
 
156
158
 
159
+ def _fake_membership(org_id, org_name="acme"):
160
+ return SimpleNamespace(
161
+ org_id=org_id,
162
+ org_name=org_name,
163
+ user_id=uuid4(),
164
+ is_org_admin=True,
165
+ is_org_developer=True,
166
+ )
167
+
168
+
169
+ def _build_select_org_test(tmp_path, monkeypatch, *, jwt_oid, saved_org, chosen_org):
170
+ """Common scaffolding for _select_and_persist_org tests.
171
+
172
+ Patches the three module-level lookups the function depends on
173
+ (``select_org``, ``get_saved_settings``, ``get_root_cfg``) and returns
174
+ a mock ``auth`` whose ``get_api_token().header['oid']`` is ``jwt_oid``.
175
+ """
176
+ from ellf_cli.commands import general
177
+ from ellf_cli.config import SavedSettings
178
+
179
+ settings = SavedSettings.blank()
180
+ settings.org_id = saved_org
181
+ settings_path = tmp_path / "saved-defaults.json"
182
+ monkeypatch.setattr(general, "get_saved_settings", lambda: settings)
183
+ monkeypatch.setattr(
184
+ general,
185
+ "get_root_cfg",
186
+ lambda: SimpleNamespace(saved_settings_path=settings_path),
187
+ )
188
+ monkeypatch.setattr(
189
+ general,
190
+ "select_org",
191
+ MagicMock(return_value=_fake_membership(chosen_org)),
192
+ )
193
+
194
+ auth = MagicMock()
195
+ auth.get_api_token.return_value = SimpleNamespace(header={"oid": str(jwt_oid)})
196
+ return general, auth, settings, settings_path
197
+
198
+
199
+ def test_select_and_persist_org_reissues_when_jwt_disagrees(tmp_path, monkeypatch):
200
+ """Regression: even if saved-defaults already names the chosen org, a
201
+ mismatched JWT must trigger ``/v1/token/switch-org`` so the local
202
+ pointer and the server-side authorization converge.
203
+ """
204
+ chosen_org = uuid4()
205
+ jwt_org = uuid4() # token was issued for a different org
206
+ general, auth, settings, settings_path = _build_select_org_test(
207
+ tmp_path,
208
+ monkeypatch,
209
+ jwt_oid=jwt_org,
210
+ saved_org=chosen_org, # stale pointer already at chosen
211
+ chosen_org=chosen_org,
212
+ )
213
+
214
+ result = general._select_and_persist_org(auth, chosen_org)
215
+
216
+ assert result == chosen_org
217
+ auth.set_active_org.assert_called_once_with(chosen_org)
218
+ assert settings.org_id == chosen_org
219
+ assert json.loads(settings_path.read_text())["org_id"] == str(chosen_org)
220
+
221
+
222
+ def test_select_and_persist_org_skips_reissue_when_jwt_matches(tmp_path, monkeypatch):
223
+ """No token round-trip when the JWT already authorizes the chosen org."""
224
+ chosen_org = uuid4()
225
+ general, auth, _, _ = _build_select_org_test(
226
+ tmp_path,
227
+ monkeypatch,
228
+ jwt_oid=chosen_org,
229
+ saved_org=uuid4(), # saved-defaults out of sync the other way
230
+ chosen_org=chosen_org,
231
+ )
232
+
233
+ result = general._select_and_persist_org(auth, chosen_org)
234
+
235
+ assert result == chosen_org
236
+ auth.set_active_org.assert_not_called()
237
+
238
+
157
239
  def test_install_claude_skills_leaves_existing_settings_untouched(
158
240
  tmp_path, monkeypatch
159
241
  ):
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
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