gflow-cli 0.6.0a3__tar.gz → 0.6.0a4__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 (177) hide show
  1. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/CHANGELOG.md +27 -0
  2. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/PKG-INFO +1 -1
  3. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/pyproject.toml +1 -1
  4. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/src/gflow_cli/__init__.py +1 -1
  5. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/src/gflow_cli/cli_image.py +4 -3
  6. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/src/gflow_cli/cli_run.py +17 -104
  7. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/src/gflow_cli/image_batch.py +104 -21
  8. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/src/gflow_cli/paths.py +15 -0
  9. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/cli/test_cli_run.py +15 -7
  10. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/cli/test_t2i_multi_prompt.py +5 -5
  11. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/.claude/README.md +0 -0
  12. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/.claude/commands/release.md +0 -0
  13. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/.env.template +0 -0
  14. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/.gitattributes +0 -0
  15. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/.github/workflows/ci.yml +0 -0
  16. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/.github/workflows/release.yml +0 -0
  17. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/.gitignore +0 -0
  18. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/.planning/todos/pending/2026-05-11-add-project-logo-and-docs-site-promotion-plan.md +0 -0
  19. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/CLAUDE.md +0 -0
  20. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/CONFIGURATION.md +0 -0
  21. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/CONTRIBUTING.md +0 -0
  22. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/DISCLAIMER.md +0 -0
  23. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/KNOWN_ISSUES.md +0 -0
  24. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/LICENSE +0 -0
  25. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/PLAN.md +0 -0
  26. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/README.md +0 -0
  27. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/RELEASE.md +0 -0
  28. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/docs/ARCHITECTURE.md +0 -0
  29. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/docs/AUTHENTICATION.md +0 -0
  30. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/docs/CONFIGURATION.md +0 -0
  31. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/docs/INDEX.md +0 -0
  32. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/docs/SECURITY.md +0 -0
  33. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/docs/USAGE.md +0 -0
  34. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/docs/USER_GUIDE.md +0 -0
  35. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/docs/assets/example-run.gif +0 -0
  36. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/docs/superpowers/plans/2026-05-09-image-mvp-orchestration.md +0 -0
  37. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/docs/superpowers/plans/2026-05-09-image-mvp.md +0 -0
  38. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/docs/superpowers/plans/2026-05-09-video-mvp-orchestration.md +0 -0
  39. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/docs/superpowers/plans/2026-05-09-video-mvp.md +0 -0
  40. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/docs/superpowers/plans/2026-05-10-phase-4-hardening-orchestration.md +0 -0
  41. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/docs/superpowers/plans/2026-05-10-phase-4-hardening.md +0 -0
  42. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/docs/superpowers/plans/2026-05-14-shell-multi-prompt/2026-05-14-shell-multi-prompt-orchestration.md +0 -0
  43. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/docs/superpowers/plans/2026-05-14-shell-multi-prompt/COUNCIL_FINAL_ARCH.md +0 -0
  44. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/docs/superpowers/plans/2026-05-14-shell-multi-prompt/COUNCIL_FINAL_SEC_UX.md +0 -0
  45. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/docs/superpowers/plans/2026-05-14-shell-multi-prompt/COUNCIL_REVIEW_CODE.md +0 -0
  46. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/docs/superpowers/plans/2026-05-14-shell-multi-prompt/COUNCIL_REVIEW_GEMINI.md +0 -0
  47. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/docs/superpowers/plans/2026-05-14-shell-multi-prompt/COUNCIL_REVIEW_SECURITY.md +0 -0
  48. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/docs/superpowers/plans/2026-05-14-shell-multi-prompt/IMPLEMENTATION_REVIEW_PYTHON.md +0 -0
  49. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/docs/superpowers/plans/2026-05-14-shell-multi-prompt/IMPLEMENTATION_REVIEW_SECURITY.md +0 -0
  50. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/docs/superpowers/plans/2026-05-14-shell-multi-prompt/PLAN.md +0 -0
  51. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/docs/superpowers/plans/2026-05-14-shell-multi-prompt/PLAN_REVIEW_CODE.md +0 -0
  52. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/docs/superpowers/plans/2026-05-14-shell-multi-prompt/PLAN_REVIEW_FOLLOWUP.md +0 -0
  53. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/docs/superpowers/plans/2026-05-14-shell-multi-prompt/PLAN_REVIEW_PLANNER.md +0 -0
  54. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/docs/superpowers/plans/2026-05-14-shell-multi-prompt/PLAN_REVIEW_SECURITY.md +0 -0
  55. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/docs/superpowers/plans/2026-05-14-shell-multi-prompt/PLAN_REVIEW_SECURITY_FOLLOWUP.md +0 -0
  56. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/docs/superpowers/plans/2026-05-15-auth-login-real-chrome/COUNCIL_FINAL_SEC_UX_VERIFIED.md +0 -0
  57. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/docs/superpowers/plans/2026-05-15-auth-login-real-chrome/COUNCIL_REVIEW_PLAN_SECURITY.md +0 -0
  58. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/docs/superpowers/plans/2026-05-15-auth-login-real-chrome/COUNCIL_REVIEW_SPEC_SECURITY.md +0 -0
  59. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/docs/superpowers/plans/2026-05-15-auth-login-real-chrome/PLAN.md +0 -0
  60. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/docs/superpowers/plans/2026-05-15-auth-login-real-chrome/orchestration.md +0 -0
  61. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/docs/superpowers/specs/2026-05-10-phase-4-hardening-design.md +0 -0
  62. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/docs/superpowers/specs/2026-05-14-shell-multi-prompt-design.md +0 -0
  63. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/docs/superpowers/specs/2026-05-15-auth-login-real-chrome-design.md +0 -0
  64. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/docs/superpowers/verifications/2026-05-11-phase-4-stage-g.md +0 -0
  65. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/examples/README.md +0 -0
  66. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/examples/batch_from_config.py +0 -0
  67. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/examples/multi_prompt_t2i.py +0 -0
  68. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/examples/sample_config.json +0 -0
  69. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/examples/sample_prompts.txt +0 -0
  70. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/examples/single_image_t2i.py +0 -0
  71. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/samples/README.md +0 -0
  72. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/samples/captured/01_upload_image.json +0 -0
  73. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/samples/captured/02_batchAsyncGenerateVideoText.json +0 -0
  74. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/samples/captured/03_batchCheckAsyncVideoGenerationStatus.json +0 -0
  75. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/samples/captured/04_archive_workflow.json +0 -0
  76. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/samples/captured/05_createProject.json +0 -0
  77. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/samples/captured/06_batchGenerateImages.json +0 -0
  78. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/samples/captured/07_batchGenerateImages_seeded.json +0 -0
  79. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/scripts/diag_capture_flow_traffic.py +0 -0
  80. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/scripts/diag_recaptcha_mint.py +0 -0
  81. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/scripts/record_demo.ps1 +0 -0
  82. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/scripts/smoke_e2e.py +0 -0
  83. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/scripts/smoke_image.py +0 -0
  84. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/scripts/smoke_real_chrome_image.py +0 -0
  85. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/scripts/smoke_worker_style.py +0 -0
  86. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/scripts/verify_chrome_auth_viability.py +0 -0
  87. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/skills/README.md +0 -0
  88. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/skills/gflow-cli/SKILL.md +0 -0
  89. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/src/gflow_cli/__main__.py +0 -0
  90. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/src/gflow_cli/_cli_helpers.py +0 -0
  91. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/src/gflow_cli/api/__init__.py +0 -0
  92. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/src/gflow_cli/api/_retry.py +0 -0
  93. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/src/gflow_cli/api/client.py +0 -0
  94. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/src/gflow_cli/api/dto.py +0 -0
  95. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/src/gflow_cli/api/image.py +0 -0
  96. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/src/gflow_cli/api/recaptcha.py +0 -0
  97. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/src/gflow_cli/api/routes.py +0 -0
  98. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/src/gflow_cli/api/transports/__init__.py +0 -0
  99. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/src/gflow_cli/api/transports/_common.py +0 -0
  100. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/src/gflow_cli/api/transports/_fingerprint.py +0 -0
  101. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/src/gflow_cli/api/transports/base.py +0 -0
  102. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/src/gflow_cli/api/transports/experimental/__init__.py +0 -0
  103. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/src/gflow_cli/api/transports/experimental/bearer.py +0 -0
  104. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/src/gflow_cli/api/transports/experimental/evaluate_fetch.py +0 -0
  105. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/src/gflow_cli/api/transports/experimental/sapisidhash.py +0 -0
  106. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/src/gflow_cli/api/transports/ui_automation.py +0 -0
  107. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/src/gflow_cli/api/video.py +0 -0
  108. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/src/gflow_cli/auth/__init__.py +0 -0
  109. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/src/gflow_cli/auth/base.py +0 -0
  110. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/src/gflow_cli/auth/factory.py +0 -0
  111. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/src/gflow_cli/auth/internal_chromium.py +0 -0
  112. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/src/gflow_cli/auth/real_chrome.py +0 -0
  113. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/src/gflow_cli/auth/strategies.py +0 -0
  114. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/src/gflow_cli/browser_manager.py +0 -0
  115. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/src/gflow_cli/cli.py +0 -0
  116. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/src/gflow_cli/cli_video.py +0 -0
  117. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/src/gflow_cli/config.py +0 -0
  118. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/src/gflow_cli/errors.py +0 -0
  119. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/src/gflow_cli/manifest.py +0 -0
  120. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/src/gflow_cli/observability.py +0 -0
  121. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/src/gflow_cli/profile_store.py +0 -0
  122. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tasks/lessons.md +0 -0
  123. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/__init__.py +0 -0
  124. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/api/__init__.py +0 -0
  125. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/api/test_client.py +0 -0
  126. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/api/test_client_generate_video.py +0 -0
  127. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/api/test_client_image.py +0 -0
  128. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/api/test_concurrency.py +0 -0
  129. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/api/test_dto.py +0 -0
  130. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/api/test_image.py +0 -0
  131. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/api/test_image_dto.py +0 -0
  132. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/api/test_recaptcha.py +0 -0
  133. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/api/test_retry.py +0 -0
  134. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/api/test_routes.py +0 -0
  135. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/api/test_video.py +0 -0
  136. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/api/transports/__init__.py +0 -0
  137. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/api/transports/test_base.py +0 -0
  138. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/api/transports/test_bearer.py +0 -0
  139. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/api/transports/test_common.py +0 -0
  140. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/api/transports/test_evaluate_fetch.py +0 -0
  141. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/api/transports/test_factory.py +0 -0
  142. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/api/transports/test_fingerprint.py +0 -0
  143. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/api/transports/test_sapisidhash.py +0 -0
  144. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/api/transports/test_ui_automation.py +0 -0
  145. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/auth/strategies/test_factory.py +0 -0
  146. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/auth/strategies/test_strategies.py +0 -0
  147. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/cli/__init__.py +0 -0
  148. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/cli/test_cli_image.py +0 -0
  149. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/cli/test_error_handling.py +0 -0
  150. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/cli/test_helpers.py +0 -0
  151. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/conftest.py +0 -0
  152. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/e2e/__init__.py +0 -0
  153. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/e2e/test_transports_e2e.py +0 -0
  154. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/features/__init__.py +0 -0
  155. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/features/auth.feature +0 -0
  156. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/features/auth_login.feature +0 -0
  157. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/features/conftest.py +0 -0
  158. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/features/image.feature +0 -0
  159. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/features/test_auth_login_steps.py +0 -0
  160. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/features/test_auth_steps.py +0 -0
  161. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/features/test_image_steps.py +0 -0
  162. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/features/test_step_collision_guard.py +0 -0
  163. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/features/test_video_steps.py +0 -0
  164. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/features/video.feature +0 -0
  165. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/smoke/__init__.py +0 -0
  166. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/smoke/test_real_flow.py +0 -0
  167. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/test_auth.py +0 -0
  168. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/test_browser_manager.py +0 -0
  169. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/test_cli_video.py +0 -0
  170. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/test_config.py +0 -0
  171. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/test_errors.py +0 -0
  172. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/test_manifest.py +0 -0
  173. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/test_observability.py +0 -0
  174. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/test_paths.py +0 -0
  175. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/test_profile_store.py +0 -0
  176. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/tests/test_smoke.py +0 -0
  177. {gflow_cli-0.6.0a3 → gflow_cli-0.6.0a4}/uv.lock +0 -0
@@ -7,6 +7,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.6.0a4] — 2026-05-17
11
+
12
+ > **Unified output resolution + batch orchestration refactor.** This release
13
+ > aligns the CLI output structure across all commands and refactors the batch
14
+ > runner to be more generic, preparing the codebase for Phase 6.
15
+
16
+ ### Added
17
+
18
+ - **`resolve_batch_output_dir` helper** in `paths.py` — centralizes the
19
+ date-partitioned output directory logic used by all generation commands.
20
+ - **`parse_batch_item_dict` helper** in `image_batch.py` — deduplicates JSON
21
+ prompt validation between `gflow run` and other batch sources.
22
+
23
+ ### Changed
24
+
25
+ - **`gflow run` output directory** — now defaults to date-partitioned
26
+ `$GFLOW_CLI_OUTPUT_DIR/images/<YYYY-MM-DD>/` instead of the legacy
27
+ `out/<UTC-timestamp>/`, matching the `gflow image` convention.
28
+ - **Refactored `run_image_batch`** into a generic `run_sequential_batch`
29
+ orchestrator — now accepts a swappable worker callback, allowing for uniform
30
+ video and image batch handling in the future.
31
+
32
+ ### Fixed
33
+
34
+ - Removed ~80 lines of duplicate validation logic from `cli_run.py`.
35
+ - Corrected test imports and expectations for unified output resolution.
36
+
10
37
  ## [0.6.0a3] — 2026-05-17
11
38
 
12
39
  > **Deterministic timeouts + agent-friendly exit codes.** This release hardens
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gflow-cli
3
- Version: 0.6.0a3
3
+ Version: 0.6.0a4
4
4
  Summary: Unofficial CLI for Google Flow — drive Veo image-to-video generations from the terminal.
5
5
  Project-URL: Homepage, https://github.com/ffroliva/gflow-cli
6
6
  Project-URL: Issues, https://github.com/ffroliva/gflow-cli/issues
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "gflow-cli"
3
- version = "0.6.0a3"
3
+ version = "0.6.0a4"
4
4
  description = "Unofficial CLI for Google Flow — drive Veo image-to-video generations from the terminal."
5
5
  readme = "README.md"
6
6
  license = { file = "LICENSE" }
@@ -1,3 +1,3 @@
1
1
  """gflow-cli — unofficial CLI for Google Flow."""
2
2
 
3
- __version__ = "0.6.0a3"
3
+ __version__ = "0.6.0a4"
@@ -60,13 +60,12 @@ from gflow_cli.image_batch import (
60
60
  prompt_items_from_texts,
61
61
  read_prompt_file,
62
62
  render_image_batch_summary,
63
- resolve_t2i_batch_output_dir,
64
63
  run_image_batch,
65
64
  )
66
65
  from gflow_cli.image_batch import (
67
66
  MIN_COUNT as _MIN_COUNT,
68
67
  )
69
- from gflow_cli.paths import image_output_path
68
+ from gflow_cli.paths import image_output_path, resolve_batch_output_dir
70
69
 
71
70
  # Case-insensitive 8-4-4-4-12 hex with hyphens — Flow's media UUIDs.
72
71
  # When a `--ref` value matches this regex it's treated as an already-uploaded
@@ -417,7 +416,9 @@ def t2i(
417
416
  profile_name = _resolve_profile(profile)
418
417
  provider_dir = _make_provider_dir(profile_name)
419
418
  settings = get_settings()
420
- output_dir = resolve_t2i_batch_output_dir(out=out, output_root=settings.output_dir)
419
+ output_dir = resolve_batch_output_dir(
420
+ cli_override=out, output_root=settings.output_dir, kind="images"
421
+ )
421
422
  console.print(
422
423
  f"\n[bold]gflow image t2i[/bold] · profile=[bold]{profile_name}[/bold] "
423
424
  f"· {len(batch_prompts)} prompt(s) · up to {len(batch_prompts) * count} image(s)"
@@ -15,7 +15,6 @@ from __future__ import annotations
15
15
  import asyncio
16
16
  import json
17
17
  import sys
18
- import time
19
18
  from dataclasses import dataclass
20
19
  from pathlib import Path
21
20
  from typing import Any, cast
@@ -29,45 +28,16 @@ from gflow_cli.api.transports import EXPERIMENTAL_TRANSPORTS
29
28
  from gflow_cli.config import get_settings
30
29
  from gflow_cli.errors import ConfigurationError
31
30
  from gflow_cli.image_batch import (
32
- ALLOWED_ASPECT_RATIOS as _ALLOWED_ASPECT_RATIOS,
33
- )
34
- from gflow_cli.image_batch import (
35
- ALLOWED_MODELS as _ALLOWED_MODELS,
36
- )
37
- from gflow_cli.image_batch import (
38
- DEFAULT_ASPECT_RATIO as _DEFAULT_ASPECT_RATIO,
39
- )
40
- from gflow_cli.image_batch import (
41
- DEFAULT_COUNT as _DEFAULT_COUNT,
42
- )
43
- from gflow_cli.image_batch import (
44
- DEFAULT_MODEL as _DEFAULT_MODEL,
45
- )
46
- from gflow_cli.image_batch import (
47
- MAX_COUNT as _MAX_COUNT,
48
- )
49
- from gflow_cli.image_batch import (
50
- MAX_PROMPTS as _MAX_PROMPTS,
51
- )
52
- from gflow_cli.image_batch import (
53
- MAX_TEXT_LEN as _MAX_TEXT_LEN,
54
- )
55
- from gflow_cli.image_batch import (
56
- MIN_COUNT as _MIN_COUNT,
57
- )
58
- from gflow_cli.image_batch import (
59
- MIN_PROMPTS as _MIN_PROMPTS,
60
- )
61
- from gflow_cli.image_batch import (
62
- MIN_TEXT_LEN as _MIN_TEXT_LEN,
63
- )
64
- from gflow_cli.image_batch import (
31
+ MAX_PROMPTS,
32
+ MIN_PROMPTS,
65
33
  BatchOutcome,
66
34
  BatchPromptItem,
35
+ parse_batch_item_dict,
67
36
  render_image_batch_summary,
68
37
  resolve_exit_code,
69
38
  run_image_batch,
70
39
  )
40
+ from gflow_cli.paths import resolve_batch_output_dir
71
41
 
72
42
  console = Console()
73
43
 
@@ -75,9 +45,6 @@ console = Console()
75
45
  _ALLOWED_TOP_LEVEL_KEYS: frozenset[str] = frozenset(
76
46
  {"profile", "transport", "output_dir", "prompts"}
77
47
  )
78
- _ALLOWED_PROMPT_KEYS: frozenset[str] = frozenset(
79
- {"text", "aspect_ratio", "model", "count", "output_filename"}
80
- )
81
48
  # ---------------------------------------------------------------------------
82
49
  # Dataclasses — validated config + per-prompt outcome.
83
50
  # ---------------------------------------------------------------------------
@@ -129,14 +96,17 @@ class BatchConfig:
129
96
  if not isinstance(prompts_raw_obj, list):
130
97
  raise ConfigurationError("'prompts' must be a JSON array.")
131
98
  prompts_raw = cast("list[Any]", prompts_raw_obj)
132
- if not (_MIN_PROMPTS <= len(prompts_raw) <= _MAX_PROMPTS):
99
+
100
+ if not (MIN_PROMPTS <= len(prompts_raw) <= MAX_PROMPTS):
133
101
  raise ConfigurationError(
134
- f"'prompts' must have between {_MIN_PROMPTS} and "
135
- f"{_MAX_PROMPTS} entries (got {len(prompts_raw)})."
102
+ f"'prompts' must have between {MIN_PROMPTS} and "
103
+ f"{MAX_PROMPTS} entries (got {len(prompts_raw)})."
136
104
  )
137
105
  prompts: list[BatchPromptItem] = []
138
106
  for idx, p in enumerate(prompts_raw):
139
- prompts.append(cls._parse_prompt(p, idx))
107
+ if not isinstance(p, dict):
108
+ raise ConfigurationError(f"prompts[{idx}] must be a JSON object.")
109
+ prompts.append(parse_batch_item_dict(cast("dict[str, Any]", p), idx))
140
110
 
141
111
  profile = data.get("profile")
142
112
  if profile is not None and (not isinstance(profile, str) or not profile):
@@ -155,73 +125,12 @@ class BatchConfig:
155
125
  output_dir=output_dir,
156
126
  )
157
127
 
158
- @staticmethod
159
- def _parse_prompt(p: object, idx: int) -> BatchPromptItem:
160
- if not isinstance(p, dict):
161
- raise ConfigurationError(f"prompts[{idx}] must be a JSON object.")
162
- item = cast("dict[str, Any]", p)
163
- unknown = set(item) - _ALLOWED_PROMPT_KEYS
164
- if unknown:
165
- raise ConfigurationError(
166
- f"prompts[{idx}] has unknown key(s) {sorted(unknown)!r}. "
167
- f"Valid: {sorted(_ALLOWED_PROMPT_KEYS)!r}."
168
- )
169
- text_raw = item.get("text")
170
- if not isinstance(text_raw, str):
171
- raise ConfigurationError(f"prompts[{idx}].text must be a string.")
172
- if not (_MIN_TEXT_LEN <= len(text_raw) <= _MAX_TEXT_LEN):
173
- raise ConfigurationError(
174
- f"prompts[{idx}].text length must be between {_MIN_TEXT_LEN} "
175
- f"and {_MAX_TEXT_LEN} (got {len(text_raw)})."
176
- )
177
- aspect_ratio = item.get("aspect_ratio", _DEFAULT_ASPECT_RATIO)
178
- if aspect_ratio not in _ALLOWED_ASPECT_RATIOS:
179
- raise ConfigurationError(
180
- f"prompts[{idx}].aspect_ratio {aspect_ratio!r} is invalid. "
181
- f"Valid: {list(_ALLOWED_ASPECT_RATIOS)!r}."
182
- )
183
- model = item.get("model", _DEFAULT_MODEL)
184
- if model not in _ALLOWED_MODELS:
185
- raise ConfigurationError(
186
- f"prompts[{idx}].model {model!r} is invalid. Valid: {list(_ALLOWED_MODELS)!r}."
187
- )
188
- count = item.get("count", _DEFAULT_COUNT)
189
- if not isinstance(count, int) or isinstance(count, bool):
190
- raise ConfigurationError(f"prompts[{idx}].count must be an integer.")
191
- if not (_MIN_COUNT <= count <= _MAX_COUNT):
192
- raise ConfigurationError(
193
- f"prompts[{idx}].count must be between {_MIN_COUNT} and {_MAX_COUNT} (got {count})."
194
- )
195
- output_filename = item.get("output_filename")
196
- if output_filename is not None and (
197
- not isinstance(output_filename, str) or not output_filename
198
- ):
199
- raise ConfigurationError(f"prompts[{idx}].output_filename must be a non-empty string.")
200
- return BatchPromptItem(
201
- text=text_raw,
202
- aspect_ratio=aspect_ratio,
203
- model=model,
204
- count=count,
205
- output_filename=output_filename,
206
- index=idx,
207
- )
208
-
209
128
 
210
129
  # ---------------------------------------------------------------------------
211
- # Helpers — output dir resolution + experimental-transport gating.
130
+ # Helpers — experimental-transport gating.
212
131
  # ---------------------------------------------------------------------------
213
132
 
214
133
 
215
- def _resolve_output_dir(*, cli_override: Path | None, config_value: str | None) -> Path:
216
- """CLI flag > config value > default (``out/<UTC-timestamp>/``)."""
217
- if cli_override is not None:
218
- return cli_override
219
- if config_value is not None:
220
- return Path(config_value)
221
- stamp = time.strftime("%Y%m%dT%H%M%SZ", time.gmtime())
222
- return Path("out") / stamp
223
-
224
-
225
134
  def _check_transport_gated(transport: str | None) -> None:
226
135
  """Per spec D.1 rule 5: experimental transports require env var.
227
136
 
@@ -325,7 +234,11 @@ def run(
325
234
  profile_name = _resolve_profile(profile or cfg.profile)
326
235
  provider_dir = _make_provider_dir(profile_name)
327
236
  settings = get_settings()
328
- output_dir = _resolve_output_dir(cli_override=output_dir_override, config_value=cfg.output_dir)
237
+ output_dir = resolve_batch_output_dir(
238
+ cli_override=output_dir_override,
239
+ config_value=cfg.output_dir,
240
+ output_root=settings.output_dir,
241
+ )
329
242
 
330
243
  console.print(
331
244
  f"\n[bold]gflow run[/bold] · profile=[bold]{profile_name}[/bold] "
@@ -3,7 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import re
6
- from collections.abc import Callable
6
+ from collections.abc import Callable, Coroutine
7
7
  from dataclasses import dataclass, field
8
8
  from pathlib import Path
9
9
  from typing import TYPE_CHECKING, Any
@@ -18,6 +18,7 @@ from gflow_cli._cli_helpers import (
18
18
  from gflow_cli.api.client import FlowApiClient
19
19
  from gflow_cli.api.image import Aspect, GenerateImageRequest, Model
20
20
  from gflow_cli.errors import EXIT_CODE_MAP, ConfigurationError, GFlowError
21
+ from gflow_cli.paths import resolve_batch_output_dir
21
22
 
22
23
  if TYPE_CHECKING:
23
24
  from gflow_cli.api.dto import GeneratedImage
@@ -157,6 +158,63 @@ def read_prompt_file(path: Path) -> tuple[ParsedPromptLine, ...]:
157
158
  return parse_prompt_lines(text, source_label=label)
158
159
 
159
160
 
161
+ _ALLOWED_PROMPT_KEYS: frozenset[str] = frozenset(
162
+ {"text", "aspect_ratio", "model", "count", "output_filename"}
163
+ )
164
+
165
+
166
+ def parse_batch_item_dict(p: dict[str, Any], idx: int) -> BatchPromptItem:
167
+ """Parse and validate a dictionary (e.g. from JSON) into a BatchPromptItem.
168
+
169
+ Used by `gflow run` to centralize validation logic.
170
+ """
171
+ unknown = set(p) - _ALLOWED_PROMPT_KEYS
172
+ if unknown:
173
+ raise ConfigurationError(
174
+ f"prompts[{idx}] has unknown key(s) {sorted(unknown)!r}. "
175
+ f"Valid: {sorted(_ALLOWED_PROMPT_KEYS)!r}."
176
+ )
177
+ text_raw = p.get("text")
178
+ if not isinstance(text_raw, str):
179
+ raise ConfigurationError(f"prompts[{idx}].text must be a string.")
180
+ if not (MIN_TEXT_LEN <= len(text_raw) <= MAX_TEXT_LEN):
181
+ raise ConfigurationError(
182
+ f"prompts[{idx}].text length must be between {MIN_TEXT_LEN} "
183
+ f"and {MAX_TEXT_LEN} (got {len(text_raw)})."
184
+ )
185
+ aspect_ratio = p.get("aspect_ratio", DEFAULT_ASPECT_RATIO)
186
+ if aspect_ratio not in ALLOWED_ASPECT_RATIOS:
187
+ raise ConfigurationError(
188
+ f"prompts[{idx}].aspect_ratio {aspect_ratio!r} is invalid. "
189
+ f"Valid: {list(ALLOWED_ASPECT_RATIOS)!r}."
190
+ )
191
+ model = p.get("model", DEFAULT_MODEL)
192
+ if model not in ALLOWED_MODELS:
193
+ raise ConfigurationError(
194
+ f"prompts[{idx}].model {model!r} is invalid. Valid: {list(ALLOWED_MODELS)!r}."
195
+ )
196
+ count = p.get("count", DEFAULT_COUNT)
197
+ if not isinstance(count, int) or isinstance(count, bool):
198
+ raise ConfigurationError(f"prompts[{idx}].count must be an integer.")
199
+ if not (MIN_COUNT <= count <= MAX_COUNT):
200
+ raise ConfigurationError(
201
+ f"prompts[{idx}].count must be between {MIN_COUNT} and {MAX_COUNT} (got {count})."
202
+ )
203
+ output_filename = p.get("output_filename")
204
+ if output_filename is not None and (
205
+ not isinstance(output_filename, str) or not output_filename
206
+ ):
207
+ raise ConfigurationError(f"prompts[{idx}].output_filename must be a non-empty string.")
208
+ return BatchPromptItem(
209
+ text=text_raw,
210
+ aspect_ratio=aspect_ratio,
211
+ model=model,
212
+ count=count,
213
+ output_filename=output_filename,
214
+ index=idx,
215
+ )
216
+
217
+
160
218
  def _validate_item_values(
161
219
  *,
162
220
  aspect_ratio: str,
@@ -238,15 +296,6 @@ def prompt_items_from_texts(
238
296
  return tuple(items)
239
297
 
240
298
 
241
- def resolve_t2i_batch_output_dir(*, out: Path | None, output_root: Path) -> Path:
242
- if out is not None:
243
- return out
244
- from datetime import date
245
-
246
- today = date.today().isoformat()
247
- return output_root / "images" / today
248
-
249
-
250
299
  async def run_one_image_prompt(
251
300
  *,
252
301
  client: Any,
@@ -298,26 +347,60 @@ async def run_image_batch(
298
347
  client_factory: Callable[..., Any] | None = None,
299
348
  ) -> list[BatchOutcome]:
300
349
  """Run prompts sequentially through one FlowApiClient session."""
301
- output_dir.mkdir(parents=True, exist_ok=True)
350
+
351
+ async def image_worker(
352
+ client: Any, project_id: str, idx: int, item: BatchPromptItem
353
+ ) -> BatchOutcome:
354
+ return await run_one_image_prompt(
355
+ client=client,
356
+ project_id=project_id,
357
+ idx=idx,
358
+ item=item,
359
+ output_dir=output_dir,
360
+ )
361
+
362
+ return await run_sequential_batch(
363
+ profile_dir=profile_dir,
364
+ headless=headless,
365
+ transport=transport,
366
+ items=prompts,
367
+ continue_on_error=continue_on_error,
368
+ project_title=project_title,
369
+ worker=image_worker,
370
+ client_factory=client_factory,
371
+ output_dir=output_dir,
372
+ )
373
+
374
+
375
+ async def run_sequential_batch(
376
+ *,
377
+ profile_dir: Path,
378
+ headless: bool,
379
+ transport: str | None,
380
+ items: tuple[Any, ...],
381
+ continue_on_error: bool,
382
+ project_title: str,
383
+ worker: Callable[[Any, str, int, Any], Coroutine[Any, Any, BatchOutcome]],
384
+ output_dir: Path | None = None,
385
+ client_factory: Callable[..., Any] | None = None,
386
+ ) -> list[BatchOutcome]:
387
+ """Generic sequential orchestrator for Flow API batches."""
388
+ if output_dir:
389
+ output_dir.mkdir(parents=True, exist_ok=True)
390
+
302
391
  outcomes: list[BatchOutcome] = []
303
392
  factory = client_factory or FlowApiClient
304
393
  async with factory(profile_dir=profile_dir, headless=headless, transport=transport) as client:
305
394
  project = await client.create_project(title=project_title)
306
- for idx, item in enumerate(prompts):
307
- outcome = await run_one_image_prompt(
308
- client=client,
309
- project_id=project.project_id,
310
- idx=idx,
311
- item=item,
312
- output_dir=output_dir,
313
- )
395
+ for idx, item in enumerate(items):
396
+ outcome = await worker(client, project.project_id, idx, item)
314
397
  outcomes.append(outcome)
315
398
  if outcome.status == "fail" and not continue_on_error:
316
- for skip_idx in range(idx + 1, len(prompts)):
399
+ for skip_idx in range(idx + 1, len(items)):
317
400
  outcomes.append(
318
401
  BatchOutcome(
319
402
  index=skip_idx,
320
- prompt=prompts[skip_idx],
403
+ prompt=items[skip_idx],
321
404
  status="skipped",
322
405
  )
323
406
  )
@@ -59,6 +59,21 @@ def _validate_job_id(job_id: str) -> str:
59
59
  return job_id
60
60
 
61
61
 
62
+ def resolve_batch_output_dir(
63
+ *,
64
+ cli_override: Path | None,
65
+ config_value: str | None = None,
66
+ output_root: Path,
67
+ kind: str = "images",
68
+ ) -> Path:
69
+ """CLI flag > config value > default (``<output_root>/<kind>/<YYYY-MM-DD>/``)."""
70
+ if cli_override is not None:
71
+ return cli_override
72
+ if config_value is not None:
73
+ return Path(config_value)
74
+ return output_root / kind / date.today().isoformat()
75
+
76
+
62
77
  def video_output_path(
63
78
  output_dir: Path,
64
79
  *,
@@ -21,8 +21,8 @@ from gflow_cli.cli import main as cli_main
21
21
  from gflow_cli.cli_run import (
22
22
  BatchConfig,
23
23
  _check_transport_gated,
24
- _resolve_output_dir,
25
24
  )
25
+ from gflow_cli.paths import resolve_batch_output_dir
26
26
  from gflow_cli.errors import ConfigurationError, WafRejectionError
27
27
 
28
28
  # ---------------------------------------------------------------------------
@@ -179,18 +179,26 @@ class TestBatchConfigValidation:
179
179
 
180
180
  class TestResolveOutputDir:
181
181
  def test_cli_override_wins(self) -> None:
182
- result = _resolve_output_dir(cli_override=Path("/cli"), config_value="config")
182
+ result = resolve_batch_output_dir(
183
+ cli_override=Path("/cli"), config_value="config", output_root=Path("/root")
184
+ )
183
185
  assert result == Path("/cli")
184
186
 
185
187
  def test_config_value_used_when_no_cli(self) -> None:
186
- result = _resolve_output_dir(cli_override=None, config_value="cfg")
188
+ result = resolve_batch_output_dir(
189
+ cli_override=None, config_value="cfg", output_root=Path("/root")
190
+ )
187
191
  assert result == Path("cfg")
188
192
 
189
193
  def test_default_when_neither_set(self) -> None:
190
- result = _resolve_output_dir(cli_override=None, config_value=None)
191
- # Format: out/<timestamp>
192
- assert result.parts[0] == "out"
193
- assert len(result.parts) == 2
194
+ from datetime import date
195
+
196
+ today = date.today().isoformat()
197
+ result = resolve_batch_output_dir(
198
+ cli_override=None, config_value=None, output_root=Path("/root")
199
+ )
200
+ # Unified format: <root>/images/<YYYY-MM-DD>
201
+ assert result == Path("/root/images") / today
194
202
 
195
203
 
196
204
  class TestCheckTransportGated:
@@ -184,7 +184,7 @@ def test_t2i_rejects_51_positional_prompts_before_profile_and_output_dir() -> No
184
184
 
185
185
  with (
186
186
  patch("gflow_cli.cli_image._resolve_profile") as resolve_profile,
187
- patch("gflow_cli.cli_image.resolve_t2i_batch_output_dir") as resolve_output,
187
+ patch("gflow_cli.cli_image.resolve_batch_output_dir") as resolve_output,
188
188
  ):
189
189
  result = CliRunner().invoke(
190
190
  main,
@@ -203,7 +203,7 @@ def test_t2i_rejects_long_positional_prompt_before_profile_and_output_dir() -> N
203
203
 
204
204
  with (
205
205
  patch("gflow_cli.cli_image._resolve_profile") as resolve_profile,
206
- patch("gflow_cli.cli_image.resolve_t2i_batch_output_dir") as resolve_output,
206
+ patch("gflow_cli.cli_image.resolve_batch_output_dir") as resolve_output,
207
207
  ):
208
208
  result = CliRunner().invoke(
209
209
  main,
@@ -235,7 +235,7 @@ def test_t2i_rejects_invalid_prompt_files_before_profile_and_output_dir(
235
235
  path.write_bytes(content)
236
236
  with (
237
237
  patch("gflow_cli.cli_image._resolve_profile") as resolve_profile,
238
- patch("gflow_cli.cli_image.resolve_t2i_batch_output_dir") as resolve_output,
238
+ patch("gflow_cli.cli_image.resolve_batch_output_dir") as resolve_output,
239
239
  ):
240
240
  result = CliRunner().invoke(
241
241
  main,
@@ -255,7 +255,7 @@ def test_t2i_rejects_missing_prompt_file_before_profile_and_output_dir(tmp_path:
255
255
  missing = tmp_path / "missing.txt"
256
256
  with (
257
257
  patch("gflow_cli.cli_image._resolve_profile") as resolve_profile,
258
- patch("gflow_cli.cli_image.resolve_t2i_batch_output_dir") as resolve_output,
258
+ patch("gflow_cli.cli_image.resolve_batch_output_dir") as resolve_output,
259
259
  ):
260
260
  result = CliRunner().invoke(
261
261
  main,
@@ -279,7 +279,7 @@ def test_t2i_rejects_prompt_file_directory_before_profile_and_output_dir(
279
279
  directory.mkdir()
280
280
  with (
281
281
  patch("gflow_cli.cli_image._resolve_profile") as resolve_profile,
282
- patch("gflow_cli.cli_image.resolve_t2i_batch_output_dir") as resolve_output,
282
+ patch("gflow_cli.cli_image.resolve_batch_output_dir") as resolve_output,
283
283
  ):
284
284
  result = CliRunner().invoke(
285
285
  main,
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