gflow-cli 0.15.1__tar.gz → 0.16.0__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 (417) hide show
  1. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/CHANGELOG.md +12 -1
  2. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/KNOWN_ISSUES.md +31 -1
  3. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/PKG-INFO +1 -1
  4. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/docs/CHARACTER.md +2 -1
  5. gflow_cli-0.16.0/docs/IMAGE_UPSCALE_RECON.md +115 -0
  6. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/docs/INDEX.md +1 -0
  7. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/docs/MOVIE.md +7 -4
  8. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/docs/USAGE.md +38 -0
  9. gflow_cli-0.16.0/docs/superpowers/plans/2026-06-11-image-upscale/PLAN.md +206 -0
  10. gflow_cli-0.16.0/docs/superpowers/plans/2026-06-11-image-upscale/SCENARIO.md +79 -0
  11. gflow_cli-0.16.0/docs/superpowers/plans/2026-06-11-issue-170-locale-selectors/PLAN.md +218 -0
  12. gflow_cli-0.16.0/docs/superpowers/plans/2026-06-11-issue-170-locale-selectors/SCENARIO.md +101 -0
  13. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/pyproject.toml +1 -1
  14. gflow_cli-0.16.0/scripts/dev/spike_image_upscale_capture.py +382 -0
  15. gflow_cli-0.16.0/scripts/dev/spike_image_upscale_drive.py +383 -0
  16. gflow_cli-0.16.0/scripts/dev/spike_image_upscale_recaptcha_action.py +188 -0
  17. gflow_cli-0.16.0/scripts/dev/spike_image_upscale_rest_probe.py +277 -0
  18. gflow_cli-0.16.0/scripts/dev/spike_issue170_picker_locale_recon.py +234 -0
  19. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/scripts/dev/spike_movie_attach_payload.py +13 -1
  20. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/__init__.py +1 -1
  21. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/api/client.py +137 -0
  22. gflow_cli-0.16.0/src/gflow_cli/api/image_upscale.py +159 -0
  23. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/api/routes.py +4 -0
  24. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/api/transports/ui_automation.py +91 -3
  25. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/api/transports/ui_automation_video.py +106 -18
  26. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/cli_image.py +176 -0
  27. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/errors.py +31 -0
  28. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/exceptions.py +1 -0
  29. gflow_cli-0.16.0/tests/api/test_client_upscale.py +169 -0
  30. gflow_cli-0.16.0/tests/api/test_image_upscale.py +153 -0
  31. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/api/transports/test_ui_automation.py +143 -0
  32. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/api/transports/test_ui_automation_video.py +159 -13
  33. gflow_cli-0.16.0/tests/cli/test_cli_image_upscale.py +140 -0
  34. gflow_cli-0.16.0/tests/features/image_upscale.feature +29 -0
  35. gflow_cli-0.16.0/tests/features/locale_picker_include.feature +25 -0
  36. gflow_cli-0.16.0/tests/features/test_image_upscale_steps.py +130 -0
  37. gflow_cli-0.16.0/tests/features/test_locale_picker_include_steps.py +204 -0
  38. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/test_errors.py +14 -0
  39. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/uv.lock +1 -1
  40. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/.claude/README.md +0 -0
  41. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/.claude/commands/gflow/active.md +0 -0
  42. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/.claude/commands/gflow/branch-review.md +0 -0
  43. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/.claude/commands/gflow/changelog.md +0 -0
  44. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/.claude/commands/gflow/check.md +0 -0
  45. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/.claude/commands/gflow/doc-review.md +0 -0
  46. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/.claude/commands/gflow/known-issues.md +0 -0
  47. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/.claude/commands/gflow/next.md +0 -0
  48. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/.claude/commands/gflow/plan.md +0 -0
  49. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/.claude/commands/gflow/pr-council-review.md +0 -0
  50. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/.claude/commands/gflow/predict.md +0 -0
  51. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/.claude/commands/gflow/release.md +0 -0
  52. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/.claude/commands/gflow/scenario.md +0 -0
  53. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/.claude/commands/gflow/status.md +0 -0
  54. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/.continue-here.md +0 -0
  55. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/.env.template +0 -0
  56. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/.gitattributes +0 -0
  57. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/.github/CODEOWNERS +0 -0
  58. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  59. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/.github/copilot-instructions.md +0 -0
  60. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/.github/dependabot.yml +0 -0
  61. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/.github/workflows/ci.yml +0 -0
  62. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/.github/workflows/external-pr-triage.yml +0 -0
  63. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/.github/workflows/governance-advisory.yml +0 -0
  64. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/.github/workflows/governance-benchmark.yml +0 -0
  65. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/.github/workflows/release.yml +0 -0
  66. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/.gitignore +0 -0
  67. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/.gitleaks.toml +0 -0
  68. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/.pre-commit-config.yaml +0 -0
  69. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/.secrets.baseline +0 -0
  70. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/AGENTS.md +0 -0
  71. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/CLAUDE.md +0 -0
  72. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/CONFIGURATION.md +0 -0
  73. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/CONTRIBUTING.md +0 -0
  74. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/DISCLAIMER.md +0 -0
  75. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/GEMINI.md +0 -0
  76. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/LICENSE +0 -0
  77. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/PLAN.md +0 -0
  78. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/PR162_MOVIE_CHARACTER_REVIEW.md +0 -0
  79. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/README.md +0 -0
  80. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/RELEASE.md +0 -0
  81. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/ROADMAP.md +0 -0
  82. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/conftest.py +0 -0
  83. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/docker-compose.yml +0 -0
  84. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/docs/AGENT_GUIDE.md +0 -0
  85. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/docs/ARCHITECTURE.md +0 -0
  86. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/docs/AUTHENTICATION.md +0 -0
  87. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/docs/CHARACTER_RECON.md +0 -0
  88. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/docs/CONFIGURATION.md +0 -0
  89. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/docs/DATA_LAYER.md +0 -0
  90. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/docs/DEBUGGING.md +0 -0
  91. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/docs/DEMOS.md +0 -0
  92. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/docs/DEVELOPMENT.md +0 -0
  93. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/docs/E2E_TESTING.md +0 -0
  94. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/docs/EXTERNAL_STORAGE.md +0 -0
  95. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/docs/GITHUB.md +0 -0
  96. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/docs/GOVERNANCE_BENCHMARK.md +0 -0
  97. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/docs/LIVE_VERIFICATION_data_layer.md +0 -0
  98. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/docs/LIVE_VERIFICATION_image_batch.md +0 -0
  99. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/docs/LIVE_VERIFICATION_v0.10.0.md +0 -0
  100. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/docs/LIVE_VERIFICATION_v0.11.0.md +0 -0
  101. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/docs/LIVE_VERIFICATION_v0.12.0.md +0 -0
  102. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/docs/LIVE_VERIFICATION_v0.13.0.md +0 -0
  103. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/docs/LIVE_VERIFICATION_v0.7.0.md +0 -0
  104. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/docs/LIVE_VERIFICATION_v0.8.1.md +0 -0
  105. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/docs/LIVE_VERIFICATION_v0.9.0.md +0 -0
  106. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/docs/LIVE_VERIFICATION_v0.9.1.md +0 -0
  107. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/docs/LIVE_VERIFICATION_video_download.md +0 -0
  108. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/docs/PROJECT_STATUS.md +0 -0
  109. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/docs/SECURITY.md +0 -0
  110. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/docs/USER_GUIDE.md +0 -0
  111. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/docs/assets/demo-split-pf.gif +0 -0
  112. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/docs/assets/example-run.gif +0 -0
  113. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/docs/schemas/movie-handoff.schema.json +0 -0
  114. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/docs/superpowers/2026-05-17-issue-15-handover.md +0 -0
  115. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/docs/superpowers/character-scenario.md +0 -0
  116. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/docs/superpowers/plans/2026-06-01-governance-enforcement-advisory/PLAN.md +0 -0
  117. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/docs/superpowers/plans/2026-06-04-character-create-recording.md +0 -0
  118. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/docs/superpowers/plans/2026-06-06-movie-p0-asyncio-hotfix.md +0 -0
  119. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/docs/superpowers/plans/2026-06-06-movie-p1-composition-manifest.md +0 -0
  120. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/docs/superpowers/plans/2026-06-06-movie-p2-native-identity-voice.md +0 -0
  121. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/docs/superpowers/specs/2026-06-06-movie-consistency-design.md +0 -0
  122. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/docs/superpowers/verifications/2026-05-11-phase-4-stage-g.md +0 -0
  123. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/examples/README.md +0 -0
  124. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/examples/batch_from_config.py +0 -0
  125. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/examples/multi_prompt_t2i.py +0 -0
  126. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/examples/sample_config.json +0 -0
  127. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/examples/sample_prompts.txt +0 -0
  128. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/examples/single_image_t2i.py +0 -0
  129. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/examples/workflow_chain.py +0 -0
  130. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/llms.txt +0 -0
  131. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/samples/README.md +0 -0
  132. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/samples/captured/01_upload_image.json +0 -0
  133. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/samples/captured/02_batchAsyncGenerateVideoText.json +0 -0
  134. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/samples/captured/03_batchCheckAsyncVideoGenerationStatus.json +0 -0
  135. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/samples/captured/04_archive_workflow.json +0 -0
  136. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/samples/captured/05_createProject.json +0 -0
  137. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/samples/captured/06_batchGenerateImages.json +0 -0
  138. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/samples/captured/07_batchGenerateImages_seeded.json +0 -0
  139. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/samples/captured/08_batchAsyncGenerateVideoStartAndEndImage.json +0 -0
  140. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/samples/captured/09_batchAsyncGenerateVideoReferenceImages.json +0 -0
  141. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/samples/captured/10_batchCheckAsyncVideoGenerationStatus_successful.json +0 -0
  142. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/samples/captured/11_batchCheckAsyncVideoGenerationStatus_failed.json +0 -0
  143. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/samples/captured/12_create_scene.json +0 -0
  144. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/samples/captured/13_sceneWorkflows_update.json +0 -0
  145. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/samples/captured/14_get_scene_workflows.json +0 -0
  146. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/samples/captured/15_commit_flowWorkflow.json +0 -0
  147. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/scripts/ci/check_doc_links.py +0 -0
  148. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/scripts/ci/check_materiality.py +0 -0
  149. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/scripts/ci/check_repo_hygiene.py +0 -0
  150. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/scripts/debug_editor.py +0 -0
  151. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/scripts/debug_gen_settings.py +0 -0
  152. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/scripts/debug_settings.py +0 -0
  153. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/scripts/dev/_recording_client.py +0 -0
  154. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/scripts/dev/_spike_common.py +0 -0
  155. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/scripts/dev/active_plan.py +0 -0
  156. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/scripts/dev/capture_i2v_frame_slots_dom.py +0 -0
  157. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/scripts/dev/capture_i2v_intercept_submit.py +0 -0
  158. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/scripts/dev/capture_i2v_model_select_repro.py +0 -0
  159. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/scripts/dev/capture_i2v_post_bind_state.py +0 -0
  160. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/scripts/dev/capture_image_add_media_dom.py +0 -0
  161. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/scripts/dev/capture_locale_invariants.py +0 -0
  162. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/scripts/dev/cdp_drive_and_probe.py +0 -0
  163. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/scripts/dev/character_create_spike.py +0 -0
  164. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/scripts/dev/character_create_spike_v2.py +0 -0
  165. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/scripts/dev/dump_character_selectors.js +0 -0
  166. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/scripts/dev/make_project.py +0 -0
  167. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/scripts/dev/materiality_backtest.py +0 -0
  168. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/scripts/dev/monitor_pr_38.py +0 -0
  169. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/scripts/dev/patch_character.py +0 -0
  170. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/scripts/dev/record_flow_capture.py +0 -0
  171. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/scripts/dev/skillopt/README.md +0 -0
  172. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/scripts/dev/skillopt/harness.py +0 -0
  173. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/scripts/dev/skillopt/tasks.json +0 -0
  174. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/scripts/dev/spike_char_editor_dom.py +0 -0
  175. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/scripts/dev/spike_char_gen_capture.py +0 -0
  176. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/scripts/dev/spike_movie_entity_recon.py +0 -0
  177. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/scripts/dev/spike_movie_gen_capture.py +0 -0
  178. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/scripts/dev/spike_movie_picker_select.py +0 -0
  179. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/scripts/dev/spike_movie_voice_list.py +0 -0
  180. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/scripts/dev/spike_patch_entity.py +0 -0
  181. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/scripts/diag/README.md +0 -0
  182. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/scripts/diag/capture_flow_traffic.py +0 -0
  183. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/scripts/diag/memory_profile.py +0 -0
  184. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/scripts/diag/recaptcha_mint.py +0 -0
  185. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/scripts/record_demo.ps1 +0 -0
  186. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/scripts/smoke_image.py +0 -0
  187. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/scripts/smoke_real_chrome_image.py +0 -0
  188. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/scripts/smoke_video_editor.py +0 -0
  189. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/scripts/smoke_worker_style.py +0 -0
  190. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/scripts/verify_chrome_auth_viability.py +0 -0
  191. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/skills/README.md +0 -0
  192. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/skills/gflow-cli/SKILL.md +0 -0
  193. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/skills/plan/SKILL.md +0 -0
  194. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/skills/pr-council-review/SKILL.md +0 -0
  195. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/skills/predict/SKILL.md +0 -0
  196. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/skills/scenario/SKILL.md +0 -0
  197. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/skills/status/SKILL.md +0 -0
  198. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/sonar-project.properties +0 -0
  199. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/__main__.py +0 -0
  200. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/_cli_helpers.py +0 -0
  201. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/api/__init__.py +0 -0
  202. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/api/_retry.py +0 -0
  203. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/api/_sapisidhash.py +0 -0
  204. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/api/character.py +0 -0
  205. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/api/dto.py +0 -0
  206. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/api/image.py +0 -0
  207. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/api/recaptcha.py +0 -0
  208. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/api/scene.py +0 -0
  209. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/api/transports/__init__.py +0 -0
  210. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/api/transports/_common.py +0 -0
  211. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/api/transports/_fingerprint.py +0 -0
  212. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/api/transports/base.py +0 -0
  213. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/api/transports/experimental/__init__.py +0 -0
  214. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/api/transports/experimental/bearer.py +0 -0
  215. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/api/transports/experimental/evaluate_fetch.py +0 -0
  216. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/api/transports/experimental/sapisidhash.py +0 -0
  217. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/api/video.py +0 -0
  218. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/auth/__init__.py +0 -0
  219. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/auth/base.py +0 -0
  220. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/auth/factory.py +0 -0
  221. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/auth/internal_chromium.py +0 -0
  222. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/auth/real_chrome.py +0 -0
  223. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/auth/strategies.py +0 -0
  224. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/auth/verification.py +0 -0
  225. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/browser_manager.py +0 -0
  226. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/chain.py +0 -0
  227. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/chain_manifest.py +0 -0
  228. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/cli.py +0 -0
  229. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/cli_character.py +0 -0
  230. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/cli_data.py +0 -0
  231. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/cli_models.py +0 -0
  232. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/cli_movie.py +0 -0
  233. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/cli_run.py +0 -0
  234. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/cli_scene.py +0 -0
  235. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/cli_video.py +0 -0
  236. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/composition.py +0 -0
  237. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/config.py +0 -0
  238. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/data/__init__.py +0 -0
  239. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/data/chain_repo.py +0 -0
  240. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/data/migrations/0001_initial.sql +0 -0
  241. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/data/migrations/0002_add_cloud_storage.sql +0 -0
  242. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/data/migrations/0003_add_scene_tables.sql +0 -0
  243. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/data/migrations/0004_add_scene_output_path.sql +0 -0
  244. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/data/migrations/0005_add_chain_links.sql +0 -0
  245. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/data/migrations/0006_add_operations_metadata.sql +0 -0
  246. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/data/migrations/__init__.py +0 -0
  247. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/data/models.py +0 -0
  248. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/data/queries.py +0 -0
  249. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/data/recorder.py +0 -0
  250. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/data/redaction.py +0 -0
  251. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/data/repository.py +0 -0
  252. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/data/store.py +0 -0
  253. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/image_batch.py +0 -0
  254. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/json_output.py +0 -0
  255. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/manifest.py +0 -0
  256. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/media.py +0 -0
  257. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/movie_manifest.py +0 -0
  258. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/observability.py +0 -0
  259. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/paths.py +0 -0
  260. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/profile_store.py +0 -0
  261. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/services/__init__.py +0 -0
  262. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/services/character_create.py +0 -0
  263. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/src/gflow_cli/storage.py +0 -0
  264. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tasks/lessons.md +0 -0
  265. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/test_assets/sample_batch.json +0 -0
  266. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/test_assets/sample_batch.tsv +0 -0
  267. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/test_assets/sample_batch_invalid.tsv +0 -0
  268. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/__init__.py +0 -0
  269. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/api/__init__.py +0 -0
  270. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/api/fixtures/character_gen_response.json +0 -0
  271. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/api/fixtures/patch_entity_response.json +0 -0
  272. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/api/test_aisandbox_auth_error.py +0 -0
  273. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/api/test_aisandbox_auth_headers.py +0 -0
  274. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/api/test_bearer_redaction.py +0 -0
  275. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/api/test_character.py +0 -0
  276. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/api/test_client.py +0 -0
  277. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/api/test_client_character.py +0 -0
  278. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/api/test_client_delete_characters.py +0 -0
  279. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/api/test_client_generate_character.py +0 -0
  280. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/api/test_client_image.py +0 -0
  281. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/api/test_client_launch_kwargs.py +0 -0
  282. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/api/test_client_patch_entity.py +0 -0
  283. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/api/test_client_scene.py +0 -0
  284. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/api/test_concurrency.py +0 -0
  285. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/api/test_dto.py +0 -0
  286. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/api/test_image.py +0 -0
  287. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/api/test_image_dto.py +0 -0
  288. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/api/test_post_json_aisandbox_auth.py +0 -0
  289. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/api/test_recaptcha.py +0 -0
  290. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/api/test_retry.py +0 -0
  291. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/api/test_routes.py +0 -0
  292. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/api/test_routes_character.py +0 -0
  293. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/api/test_routes_scene.py +0 -0
  294. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/api/test_sapisidhash_helper.py +0 -0
  295. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/api/test_scene_models.py +0 -0
  296. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/api/test_video.py +0 -0
  297. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/api/test_video_request.py +0 -0
  298. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/api/transports/__init__.py +0 -0
  299. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/api/transports/test_base.py +0 -0
  300. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/api/transports/test_bearer.py +0 -0
  301. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/api/transports/test_common.py +0 -0
  302. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/api/transports/test_evaluate_fetch.py +0 -0
  303. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/api/transports/test_factory.py +0 -0
  304. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/api/transports/test_fingerprint.py +0 -0
  305. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/api/transports/test_sapisidhash.py +0 -0
  306. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/api/transports/test_transport_timeout.py +0 -0
  307. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/api/transports/test_ui_automation_batch.py +0 -0
  308. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/api/transports/test_ui_automation_image_mode.py +0 -0
  309. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/api/transports/test_ui_character_editor.py +0 -0
  310. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/auth/strategies/test_factory.py +0 -0
  311. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/auth/strategies/test_strategies.py +0 -0
  312. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/auth/test_verification.py +0 -0
  313. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/cli/__init__.py +0 -0
  314. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/cli/test_cli_auth_list.py +0 -0
  315. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/cli/test_cli_character.py +0 -0
  316. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/cli/test_cli_character_create.py +0 -0
  317. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/cli/test_cli_data.py +0 -0
  318. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/cli/test_cli_image.py +0 -0
  319. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/cli/test_cli_image_seed_removed.py +0 -0
  320. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/cli/test_cli_models.py +0 -0
  321. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/cli/test_cli_movie.py +0 -0
  322. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/cli/test_cli_run.py +0 -0
  323. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/cli/test_cli_scene.py +0 -0
  324. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/cli/test_cli_video.py +0 -0
  325. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/cli/test_cli_video_chain.py +0 -0
  326. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/cli/test_error_handling.py +0 -0
  327. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/cli/test_helpers.py +0 -0
  328. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/cli/test_movie_manifest.py +0 -0
  329. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/cli/test_t2i_multi_prompt.py +0 -0
  330. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/composition/test_character.py +0 -0
  331. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/composition/test_compose_prompt.py +0 -0
  332. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/composition/test_handoff.py +0 -0
  333. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/conftest.py +0 -0
  334. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/data/__init__.py +0 -0
  335. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/data/test_chain_repo.py +0 -0
  336. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/data/test_find_incomplete_character.py +0 -0
  337. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/data/test_models.py +0 -0
  338. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/data/test_packaging.py +0 -0
  339. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/data/test_recorder.py +0 -0
  340. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/data/test_recorder_character.py +0 -0
  341. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/data/test_redaction.py +0 -0
  342. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/data/test_repository.py +0 -0
  343. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/data/test_scene_persistence.py +0 -0
  344. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/data/test_settings_and_errors.py +0 -0
  345. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/data/test_store_migrations.py +0 -0
  346. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/dev/test_record_flow_capture.py +0 -0
  347. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/dev/test_recording_client.py +0 -0
  348. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/e2e/__init__.py +0 -0
  349. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/e2e/conftest.py +0 -0
  350. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/e2e/test_aisandbox_auth_live.py +0 -0
  351. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/e2e/test_auth_verification_e2e.py +0 -0
  352. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/e2e/test_chain_e2e.py +0 -0
  353. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/e2e/test_character_create_e2e.py +0 -0
  354. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/e2e/test_data_layer_e2e.py +0 -0
  355. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/e2e/test_i2v_flags_e2e.py +0 -0
  356. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/e2e/test_image_batch_e2e.py +0 -0
  357. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/e2e/test_image_i2i_ref_cap_e2e.py +0 -0
  358. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/e2e/test_json_output_e2e.py +0 -0
  359. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/e2e/test_locale_selectors_e2e.py +0 -0
  360. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/e2e/test_scene_compose_live.py +0 -0
  361. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/e2e/test_transports_e2e.py +0 -0
  362. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/e2e/test_video_r2v_ref_cap_e2e.py +0 -0
  363. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/e2e/test_video_t2v_e2e.py +0 -0
  364. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/features/__init__.py +0 -0
  365. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/features/auth.feature +0 -0
  366. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/features/auth_login.feature +0 -0
  367. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/features/character_create.feature +0 -0
  368. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/features/character_read.feature +0 -0
  369. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/features/conftest.py +0 -0
  370. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/features/image.feature +0 -0
  371. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/features/test_auth_login_steps.py +0 -0
  372. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/features/test_auth_steps.py +0 -0
  373. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/features/test_character_create_steps.py +0 -0
  374. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/features/test_character_read_steps.py +0 -0
  375. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/features/test_image_steps.py +0 -0
  376. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/features/test_step_collision_guard.py +0 -0
  377. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/features/test_video_chain_steps.py +0 -0
  378. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/features/video_chain.feature +0 -0
  379. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/fixtures/__init__.py +0 -0
  380. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/fixtures/seeded_catalog.py +0 -0
  381. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/image_batch/__init__.py +0 -0
  382. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/image_batch/test_image_manifest.py +0 -0
  383. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/image_batch/test_observability_events.py +0 -0
  384. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/integration/__init__.py +0 -0
  385. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/integration/conftest.py +0 -0
  386. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/integration/constants.py +0 -0
  387. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/integration/test_storage_gcs.py +0 -0
  388. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/integration/test_storage_s3.py +0 -0
  389. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/scripts/test_capture_locale_invariants.py +0 -0
  390. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/scripts/test_check_materiality.py +0 -0
  391. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/scripts/test_check_repo_hygiene.py +0 -0
  392. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/scripts/test_materiality_backtest.py +0 -0
  393. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/services/__init__.py +0 -0
  394. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/services/test_character_create_redaction.py +0 -0
  395. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/services/test_character_create_saga.py +0 -0
  396. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/services/test_character_gen_no_direct_post.py +0 -0
  397. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/smoke/__init__.py +0 -0
  398. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/smoke/test_profile_account_smoke.py +0 -0
  399. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/smoke/test_real_flow.py +0 -0
  400. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/test_auth.py +0 -0
  401. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/test_browser_manager.py +0 -0
  402. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/test_chain.py +0 -0
  403. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/test_chain_manifest.py +0 -0
  404. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/test_cli_data.py +0 -0
  405. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/test_config.py +0 -0
  406. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/test_conftest_isolation.py +0 -0
  407. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/test_data_queries.py +0 -0
  408. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/test_documentation_gate.py +0 -0
  409. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/test_errors_403.py +0 -0
  410. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/test_json_output.py +0 -0
  411. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/test_manifest.py +0 -0
  412. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/test_marker_registry.py +0 -0
  413. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/test_media.py +0 -0
  414. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/test_observability.py +0 -0
  415. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/test_paths.py +0 -0
  416. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/test_profile_store.py +0 -0
  417. {gflow_cli-0.15.1 → gflow_cli-0.16.0}/tests/test_smoke.py +0 -0
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.16.0] — 2026-06-12
11
+
12
+ ### Fixed
13
+
14
+ - **`--reference-entity` no longer fails on non-Portuguese Flow accounts** ([#170](https://github.com/ffroliva/gflow-cli/issues/170)) — the resource-picker include selectors hardcoded the pt-BR caption "Incluir no comando"; they are now locale-free tier cascades (context-menu `add`-ligature anchor + pt/ru/en text fallback), fixing `gflow image t2i --reference-entity`, movie R2V entity attach, and Vozes voice attach for every account language. Failures are now typed (`TransportTimeoutError`, exit 9) with a locale-neutral remediation hint, the matched selector tier is logged (`include_selector_tier`) for drift telemetry, and a new image-side submit backstop raises `WireFormatError` (exit 7) if a staged character never reaches the wire instead of silently returning a text-only generation. Thanks @papushin7987 for the live-verified report
15
+
16
+ ### Added
17
+
18
+ - **`gflow image upscale <mediaId> --scale 2k|4k`** — upscale a platform-generated image to 2K or 4K (Flow's download-menu 1K/2K/4K options) and save it locally; credit-free. The owning project is resolved from the local catalog (or pass `--project` for images gflow didn't record). 4K requires a Flow Ultra subscription — a non-Ultra 4K request fails fast with exit code 22 (`UpscaleUnavailableError`) rather than a generic 403. Reverse-engineered wire documented in [docs/IMAGE_UPSCALE_RECON.md](docs/IMAGE_UPSCALE_RECON.md); live-verified end-to-end (issue #171)
19
+
10
20
  ## [0.15.1] — 2026-06-10
11
21
 
12
22
  ### Changed
@@ -1484,7 +1494,8 @@ shell-script template that branches on these codes.
1484
1494
 
1485
1495
  First skeleton. Not functional end-to-end yet.
1486
1496
 
1487
- [Unreleased]: https://github.com/ffroliva/gflow-cli/compare/v0.15.1...HEAD
1497
+ [Unreleased]: https://github.com/ffroliva/gflow-cli/compare/v0.16.0...HEAD
1498
+ [0.16.0]: https://github.com/ffroliva/gflow-cli/compare/v0.15.1...v0.16.0
1488
1499
  [0.15.1]: https://github.com/ffroliva/gflow-cli/compare/v0.15.0...v0.15.1
1489
1500
  [0.15.0]: https://github.com/ffroliva/gflow-cli/compare/v0.14.0...v0.15.0
1490
1501
  [0.14.0]: https://github.com/ffroliva/gflow-cli/compare/v0.13.0...v0.14.0
@@ -14,6 +14,22 @@ Living list of behaviour that's broken, surprising, or limited by design — alo
14
14
 
15
15
  ## Open
16
16
 
17
+ ### 4K image upscale requires a Flow Ultra subscription
18
+
19
+ - **Status:** **Open** (by design — a Flow platform limit, not a gflow bug)
20
+ - **Severity:** Low · **Affects:** `gflow image upscale --scale 4k` on non-Ultra accounts
21
+
22
+ `gflow image upscale <mediaId> --scale 4k` returns **exit code 22**
23
+ (`UpscaleUnavailableError`) on accounts below the Ultra tier — Flow gates 4K
24
+ upscaling behind Ultra (the web UI shows an "Upgrade" button instead of a 4K
25
+ option). The account tier is reported on the wire as `userPaygateTier` but is
26
+ enforced server-side, so gflow cannot grant 4K locally.
27
+
28
+ **Workaround:** use `--scale 2k` (available on all tiers), or upgrade the Flow
29
+ account to Ultra. If you just upgraded, re-run `gflow auth login --profile <name>`
30
+ to refresh the session before retrying 4K. Wire detail:
31
+ [docs/IMAGE_UPSCALE_RECON.md](docs/IMAGE_UPSCALE_RECON.md) (#171).
32
+
17
33
  ### Image generation returns HTTP 401 — `aisandbox-pa` generation endpoint
18
34
 
19
35
  - **Status:** **RESOLVED in v0.7.0** — moved to [Resolved](#resolved) section
@@ -332,7 +348,7 @@ issue and not blocked by any code change in this repo.
332
348
 
333
349
  ### `UiAutomationTransport` selectors locale-agnostic — issue #24 Phase 5 complete
334
350
 
335
- - **Status:** Resolved (pending owner live e2e on non-EN profile) · **Severity:** Low · **Tracking:** [issue #24](https://github.com/ffroliva/gflow-cli/issues/24), [issue #94](https://github.com/ffroliva/gflow-cli/issues/94)
351
+ - **Status:** Resolved (pending owner live e2e on non-EN profile) · **Severity:** Low · **Tracking:** [issue #24](https://github.com/ffroliva/gflow-cli/issues/24), [issue #94](https://github.com/ffroliva/gflow-cli/issues/94), [issue #170](https://github.com/ffroliva/gflow-cli/issues/170)
336
352
 
337
353
  `--lang=en-US` removed in PR #127 (2026-05-30). All selector groups now use
338
354
  locale-stable anchors: `IMAGE_MODEL_OPTION_SELECTORS` and
@@ -343,6 +359,20 @@ issue and not blocked by any code change in this repo.
343
359
  Playwright kwarg (persists across all in-session navigations). Full resolution
344
360
  gate: live e2e with `gflow image t2i` (each model) on a non-EN Chrome profile.
345
361
 
362
+ **2026-06-12 correction (issue #170):** the "all selector groups" claim above
363
+ had two stragglers — `PICKER_INCLUDE_BUTTON` and `PICKER_CONTEXT_INCLUDE`
364
+ hardcoded the pt-BR caption "Incluir no comando", breaking
365
+ `--reference-entity` (image t2i), movie R2V entity attach, and Vozes voice
366
+ attach on every non-Portuguese account (Flow renders in the ACCOUNT language;
367
+ `?hl=en` cannot override it). Fixed by converting both constants to sequential
368
+ tier cascades — context-menu Tier 1 is the locale-free `add`-ligature menuitem
369
+ scoped to the open menu; the Vozes button has no ligature, so pt/ru/en text
370
+ leads with a lone-iconless-dialog-button structural fallback. The matched tier
371
+ is emitted as `ui_automation_video.include_selector_tier` (drift telemetry),
372
+ exhaustion raises typed `TransportTimeoutError` (exit 9) with a locale-neutral
373
+ remediation hint, and an image-side submit backstop now raises
374
+ `WireFormatError` when a requested entity never rode the wire.
375
+
346
376
  **Phase 2 progress (2026-05-25, develop / post-v0.8.1, unreleased):**
347
377
 
348
378
  - **`ONBOARDING_SELECTORS` restructured** — replaced the original 9 English/PT-BR
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gflow-cli
3
- Version: 0.15.1
3
+ Version: 0.16.0
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
@@ -375,7 +375,8 @@ convenience later.
375
375
  generate) so Flow's JS sets `entityContext` and passes the WAF; gflow captures `media[]`/`workflows[]`
376
376
  from the response. Reuse gflow's prompt-submit + response-capture machinery (`ui_automation.py`), but in
377
377
  the character-editor context (NOT the new-project flow). Video `--character` reuse = resource picker
378
- (`Pesquisar recursos` → Personagens → *Incluir no comando*) generate; mirrors #123 `_attach_likeness`.
378
+ (search resources → Personagens → *include-in-prompt* context-menu action; captions are localized,
379
+ selectors locale-free since issue #170) → generate; mirrors #123 `_attach_likeness`.
379
380
  All selectors **ligature/structural only** (language-agnostic).
380
381
  - **Cost reality:** the feature is UI-automation-heavy (new character-editor + picker selectors), not the
381
382
  "mostly REST" earlier framing. Structural metadata is REST; the value-generating steps are UI.
@@ -0,0 +1,115 @@
1
+ # Image Upscale — Reverse-Engineering Recon
2
+
3
+ > Status: **recon complete, pre-implementation**. Wire empirically captured 2026-06-11
4
+ > (denon82 Pro account, project `ffb768fb…`). Spikes: `scripts/dev/spike_image_upscale_drive.py`
5
+ > (UI drive + capture) and `scripts/dev/spike_image_upscale_rest_probe.py` (schema + REST probe).
6
+
7
+ ## Feature
8
+
9
+ Flow's image editor download menu offers **1K Original / 2K Upscaled / 4K Upscaled**. gflow
10
+ today saves only the 1K original (the pre-signed `fifeUrl` from the generation response). This
11
+ feature adds `gflow image upscale <mediaId> --scale 2k|4k` to fetch the upscaled variant.
12
+
13
+ Scope: **platform-generated images only** (referenced by `mediaId`). Uploaded images are not
14
+ supported by this endpoint. **4K is Ultra-tier-gated**; Pro accounts see "Upgrade" in the UI.
15
+
16
+ ## Wire protocol
17
+
18
+ ### Endpoint
19
+ ```
20
+ POST https://aisandbox-pa.googleapis.com/v1/flow/upsampleImage
21
+ ```
22
+ Same host + auth class as image generation (`batchGenerateImages`).
23
+
24
+ ### Request body
25
+ ```json
26
+ {
27
+ "mediaId": "<source-image-uuid>",
28
+ "targetResolution": "UPSAMPLE_IMAGE_RESOLUTION_2K",
29
+ "clientContext": {
30
+ "recaptchaContext": {
31
+ "token": "<reCAPTCHA-Enterprise-token>",
32
+ "applicationType": "RECAPTCHA_APPLICATION_TYPE_WEB"
33
+ },
34
+ "projectId": "<project-that-owns-the-media>",
35
+ "sessionId": ";<epoch_ms>",
36
+ "tool": "PINHOLE",
37
+ "userPaygateTier": "PAYGATE_TIER_ONE"
38
+ }
39
+ }
40
+ ```
41
+ - `targetResolution` enum: `UPSAMPLE_IMAGE_RESOLUTION_2K` | `UPSAMPLE_IMAGE_RESOLUTION_4K`.
42
+ 1K = original (no API call — gflow already has it).
43
+ - **The FULL `clientContext` is required.** A minimal `{recaptchaContext}` body returns
44
+ **403 even with a valid token** (confirmed by live smoke). All five fields are needed;
45
+ `projectId` (the project owning the media) is load-bearing.
46
+ - `sessionId` format is `;<epoch_ms>` (e.g. `;1781190457842`), same as image generation.
47
+ - `userPaygateTier` is **where the account tier surfaces on the wire** (`PAYGATE_TIER_ONE`
48
+ on a Pro account). It is client-reported telemetry — the server enforces the real tier
49
+ independently (a non-Ultra 4K request 403s regardless), so it is not a security control.
50
+
51
+ ### reCAPTCHA action — `IMAGE_GENERATION`
52
+ The token MUST be minted with action **`IMAGE_GENERATION`** (uppercase). reCAPTCHA Enterprise
53
+ scores by action; the guessed `"upsampleImage"` scored low and 403'd. Captured live by hooking
54
+ `grecaptcha.enterprise.execute` (`scripts/dev/spike_image_upscale_recaptcha_action.py`). The
55
+ site key is page-discovered, so it auto-matches; minting on the bootstrap page is fine — the
56
+ action was the only gap. **Live-verified 2026-06-11**: a 2K upscale wrote a 3.8 MB JPEG.
57
+
58
+ ### Response
59
+ ```json
60
+ { "encodedImage": "<base64>" }
61
+ ```
62
+ - Single field. ~3.8–5.1 MB base64 observed (varies by source image). Decode → write image bytes.
63
+ - Synchronous: one call returns the result. **No async poll/status loop** (unlike video gen).
64
+
65
+ ## Transport decision — browser required, REST is dead
66
+
67
+ | Approach | Viable? | Evidence |
68
+ |---|---|---|
69
+ | Pure REST / Bearer-only | ❌ No | Bearer `ya29` + **no** reCAPTCHA token → **403** (probed with and without `clientContext`) |
70
+ | Browser reCAPTCHA-mint + POST | ✅ Yes | UI drive returns 200; reuses the existing `batchGenerateImages` transport |
71
+ | Hybrid (browser submit + Bearer poll) | ⚠️ N/A | No poll/download phase exists — response is synchronous inline base64 |
72
+
73
+ **Conclusion:** reCAPTCHA token is **mandatory**. Reuse the existing image-generation reCAPTCHA
74
+ minting path; there is no Bearer-only slice to peel off. See [REST-path capability matrix] — a
75
+ generative-class op stays reCAPTCHA-gated.
76
+
77
+ ## Tier gating (4K = Ultra)
78
+
79
+ - 4K is Ultra-only. On Pro, the UI renders "Fazer upgrade" (Upgrade) instead of a clickable 4K.
80
+ - Account tier is **NOT** in `GET /fx/api/auth/session` (only `user` / `expires` / `access_token`).
81
+ It surfaces via the upsell recommendations (`fetchUserRecommendations` onramp list) and is
82
+ enforced server-side. A 4K request on a non-Ultra account is expected to 403 (permission), which
83
+ must be distinguished from a WAF/reCAPTCHA 403.
84
+ - Session staleness is real: an idle session returns `error: ACCESS_TOKEN_REFRESH_NEEDED` and
85
+ re-auths before the call succeeds — so a freshly-upgraded account may need a session refresh
86
+ before 4K unlocks.
87
+
88
+ ## UI navigation facts (for the spike/transport)
89
+
90
+ - **Cold-loading `/project/<pid>/edit/<mediaId>` 500s** ("Algo deu errado"). Must land on the
91
+ gallery `/project/<pid>` then click the `a[href*=<mediaId>]` tile (in-app SPA routing).
92
+ - A "What's new" changelog modal overlays the gallery but does **not** block the href tile click.
93
+ - Working selectors (locale leaks into the UI — match language-neutral tokens):
94
+ - Download button: `button:has(i.google-symbols:text-is('download'))`
95
+ - Scale menu item: `[role='menuitem']:has-text('2K')` (and `'4K'`)
96
+ - pt labels are 1K="Tamanho original", 2K/4K="Aumentada" — **never** match on "Aumentada".
97
+
98
+ ## Implementation sketch (from `/gflow:predict` GO, 7.5/10)
99
+
100
+ - `api/routes.py`: `UPSAMPLE_IMAGE = f"{FLOW_API_BASE}/flow/upsampleImage"`.
101
+ - `api/image.py`: frozen `UpsampleImageRequest(media_id, target_resolution, recaptcha_token="")`;
102
+ `TargetResolution` enum with `from_cli("2k"|"4k")`.
103
+ - `api/client.py`: `upsample_image()` — mint reCAPTCHA (existing path), POST, validate + decode
104
+ `encodedImage` with `MAX_UPSAMPLE_B64_LEN ≈ 50 MB` cap + `del` + PNG/JPEG magic-byte check
105
+ (mirror `concatenate_scene`).
106
+ - `cli_image.py`: `gflow image upscale <mediaId> --scale 2k|4k [--out]`.
107
+ - `errors.py`: `UpscaleUnavailableError` (exit 22), distinct from WAF/reCAPTCHA 403; **no auto-retry**
108
+ on a 4K 403.
109
+ - Never log `encodedImage`. Record the output via `OperationRecorder` honoring the redaction gate.
110
+ - Discover mediaIds via the existing `gflow data list images`.
111
+
112
+ ## Credit cost
113
+
114
+ Image operation → expected **credit-free** (only video generation spends credits). Not yet
115
+ stress-confirmed; treat as free but do not batch aggressively (per-profile WAF heat).
@@ -34,6 +34,7 @@ Welcome to the `gflow-cli` documentation. This index is the routing layer: it te
34
34
  | **[docs/DATA_LAYER.md](DATA_LAYER.md)** | Local SQLite catalog: goals, schema, recording flow, redaction, `gflow data` CLI, migrations, extension guide | Anything touching `gflow_cli.data`, debugging missing rows, building I2V/repair tooling, auditing what is stored |
35
35
  | **[docs/CHARACTER.md](CHARACTER.md)** | Characters feature spec & system design: domain model, endpoint/cost matrix, sequence diagrams, JSON payloads (I/O), CLI surface, reuse via `referenceEntities` (#145) | Working on `gflow character`, reusing a character in generations, or understanding Flow's character wire protocol |
36
36
  | **[docs/MOVIE.md](MOVIE.md)** | `gflow movie` — multi-scene character-consistent films: manifest format, run lifecycle (browser stays open through generate→poll→download), entity-attach mechanism (Personagens right-click), resume/handoff, credits | Working on `gflow movie`, debugging scene generation, or understanding the character-entity attach + consistency model |
37
+ | **[docs/IMAGE_UPSCALE_RECON.md](IMAGE_UPSCALE_RECON.md)** | `gflow image upscale` wire protocol: `upsampleImage` endpoint, full clientContext (projectId/sessionId/tool/userPaygateTier), reCAPTCHA action `IMAGE_GENERATION`, inline base64 response, 4K Ultra-gating, REST-is-dead proof | Working on `gflow image upscale`, debugging the 403/reCAPTCHA path, or understanding Flow's image-upscale wire protocol (#171) |
37
38
  | **[tasks/lessons.md](../tasks/lessons.md)** | Running notebook of patterns + reviewer findings, dated and traced to commits | Starting a new phase; debugging "why did the council flag this?" |
38
39
  | **[skills/README.md](../skills/README.md)** | Installable agent skill docs (gflow-cli, predict, pr-council-review, scenario) — cross-tool portable Markdown consumed by Claude Code, Cursor, Codex, Gemini CLI, Aider, etc. | Any agent wanting to use gflow-cli correctly |
39
40
  | **[scripts/dev/skillopt/README.md](../scripts/dev/skillopt/README.md)** | SkillOpt mock harness — rollout→score loop for measuring and improving skill doc accuracy across multiple LLM providers | Measuring a skill edit's impact; comparing Claude vs GPT-4o vs Gemini on gflow tasks |
@@ -100,10 +100,13 @@ attached by driving Flow's resource picker:
100
100
  1. Click **Add Media** in the composer (references sub-mode).
101
101
  2. Switch to the **Personagens** (Characters) tab.
102
102
  3. **Right-click** the entity tile — addressed by entity id as
103
- `data-tile-id="fe_id_<entityId>"` — and choose **"Incluir no comando"** from
104
- the context menu. *(A plain left-click navigates into the character editor;
105
- the inline "Incluir" button on the **Tudo** tab attaches the character's
106
- thumbnail as a plain image, not the entity.)*
103
+ `data-tile-id="fe_id_<entityId>"` — and choose the **include-in-prompt**
104
+ action from the context menu (the `add`-ligature menu item; its caption is
105
+ localized per account language, e.g. "Incluir no comando" on pt-BR or
106
+ "Добавить в запрос" on ru selectors are locale-free since issue #170).
107
+ *(A plain left-click navigates into the character editor; the inline include
108
+ button on the **Tudo** tab attaches the character's thumbnail as a plain
109
+ image, not the entity.)*
107
110
 
108
111
  This puts `referenceEntities:[{entityId}]` on the
109
112
  `video:batchAsyncGenerateVideoReferenceImages` **request**. Flow's **response**
@@ -105,6 +105,44 @@ UUID=$(gflow image upload hero.png | awk '/Asset UUID:/ {print $3}')
105
105
  gflow image i2i "make it cinematic" --ref "$UUID"
106
106
  ```
107
107
 
108
+ ## `gflow image upscale`
109
+
110
+ Upscale a **platform-generated** image to 2K or 4K (the same 1K/2K/4K options Flow's
111
+ download menu offers) and save it locally. Uploaded images are not supported.
112
+
113
+ ```text
114
+ gflow image upscale MEDIA_ID --scale 2k|4k [OPTIONS]
115
+
116
+ Arguments:
117
+ MEDIA_ID UUID of a Flow-generated image (find one with
118
+ `gflow data list images`).
119
+
120
+ Options:
121
+ --scale [2k|4k] Target resolution. 4k requires a Flow Ultra
122
+ subscription; 1k is the original (no upscale).
123
+ --project ID Project that owns the image. Resolved from the local
124
+ catalog when omitted; pass it explicitly for images
125
+ gflow didn't record (e.g. generated in the web UI).
126
+ --out PATH Output directory (see "Output paths" below).
127
+ --profile NAME Profile name (overrides default).
128
+ ```
129
+
130
+ ```bash
131
+ # Upscale a previously generated image to 2K (project auto-resolved from the catalog)
132
+ gflow image upscale 3a56bb5e-92a2-44f4-9992-3c6a9bf0cd14 --scale 2k
133
+
134
+ # Upscale an image generated in the Flow web UI (not in the local catalog)
135
+ gflow image upscale <mediaId> --scale 2k --project <projectId>
136
+ ```
137
+
138
+ Notes:
139
+
140
+ - **Credit-free** — upscaling is an image operation and spends no credits.
141
+ - **4K is Ultra-only.** On a non-Ultra account a 4K request fails with exit code 22
142
+ (`UpscaleUnavailableError`) and a hint to use `--scale 2k` or upgrade.
143
+ - The result is saved as `<output_dir>/images/<YYYY-MM-DD>/<mediaId>_<scale>.<ext>`
144
+ (extension matches the returned format — usually `.jpg`).
145
+
108
146
  ## `gflow image t2i`
109
147
 
110
148
  Generate 1–4 images from one text prompt, or run a shell-friendly batch of 1–50
@@ -0,0 +1,206 @@
1
+ # Image Upscale Implementation Plan
2
+
3
+ > **For agentic workers:** Run `/gflow:status --feature image-upscale` to find the next
4
+ > unchecked task. Implement one task at a time. Run `/gflow:check` before every commit.
5
+
6
+ **Goal:** Add `gflow image upscale <mediaId> --scale 2k|4k` so a user can fetch Flow's
7
+ server-side 2K/4K upscaled variant of a platform-generated image and save it locally.
8
+
9
+ **Architecture:** Reuses the existing image-generation reCAPTCHA browser transport — same host
10
+ (`aisandbox-pa.googleapis.com`) and `recaptchaContext` shape as `batchGenerateImages`. New work is
11
+ glue: a route constant, a frozen request value object, a synchronous `FlowApiClient.upsample_image()`
12
+ that mints a reCAPTCHA token, POSTs, and decodes the inline `{encodedImage: base64}` (mirroring
13
+ `concatenate_scene`'s cap + `del` + magic-byte pattern), a new exit-coded error, and a Click
14
+ subcommand. No transport-Protocol change (it's a client-level op like `concatenate_scene`). No new
15
+ auth, no new persistent state, no async poll loop.
16
+
17
+ **Tracking issue:** [#171](https://github.com/ffroliva/gflow-cli/issues/171)
18
+
19
+ **Predict verdict:** GO — confidence 7.5/10. Recon: `docs/IMAGE_UPSCALE_RECON.md`. Scenario matrix
20
+ complete (20 scenarios; 12 Critical/High are must-cover).
21
+
22
+ **Risk register:**
23
+ | Severity | Risk | Mitigation |
24
+ |---|---|---|
25
+ | Critical | 403 ambiguity: tier-gate (4K/Pro) vs WAF heat vs expired token all return 403 | Disambiguate by response context; `UpscaleUnavailableError` (exit 22) for tier; **never auto-retry** a 4K 403 |
26
+ | Critical | ~5 MB `encodedImage` base64 leaks into logs / error context | Never log the field; decode then `del`; structlog carries size only |
27
+ | High | Pathological oversized base64 (4K PNG) → OOM | `MAX_UPSAMPLE_B64_LEN` (~50 MB) cap **before** decode; typed error |
28
+ | High | Response missing `encodedImage` / decodes to non-image | `WireFormatError` (redacted discovery payload); PNG/JPEG magic-byte check |
29
+ | High | Per-profile reCAPTCHA/WAF heat (a 4th gated op) | One upscale per invocation; no implicit batch; document the heat |
30
+ | High | mediaId injection / malformed input | UUID allowlist; reject before any request fires |
31
+ | Medium | Selector drift (download button / scale menu) on Flow UI update | Structural-first, locale-neutral selectors (`download`, `2K`/`4K`); re-capture via spike |
32
+
33
+ ---
34
+
35
+ ## File structure
36
+
37
+ ### New files
38
+ ```
39
+ src/gflow_cli/api/image_upscale.py (or extend api/image.py)
40
+ UpsampleImageRequest frozen dataclass + TargetResolution enum (from_cli "2k"/"4k")
41
+ tests/api/test_image_upscale.py
42
+ Unit tests: request build, response parse (cap/magic-byte/missing-field), 403 disambiguation
43
+ tests/features/image_upscale.feature
44
+ BDD scenarios from /gflow:scenario (happy 2K, 4K-on-Pro, oversized, malformed mediaId)
45
+ tests/features/test_image_upscale_steps.py
46
+ Step defs mirroring runtime signatures
47
+ ```
48
+
49
+ ### Modified files
50
+ ```
51
+ src/gflow_cli/api/routes.py
52
+ + UPSAMPLE_IMAGE = f"{FLOW_API_BASE}/flow/upsampleImage"
53
+ src/gflow_cli/api/client.py
54
+ + MAX_UPSAMPLE_B64_LEN; + async def upsample_image(media_id, target_resolution, out_path)
55
+ (mint reCAPTCHA via _mint_recaptcha_token; POST; cap+decode+magic-byte; write via image_output_path)
56
+ src/gflow_cli/errors.py
57
+ + UpscaleUnavailableError (exit 22) registered in EXIT_CODE_MAP; distinct from WAF/reCAPTCHA 403
58
+ src/gflow_cli/cli_image.py
59
+ + `upscale` Click command: <mediaId> arg, --scale Choice(['2k','4k']), --out, --profile
60
+ src/gflow_cli/paths.py
61
+ (reuse image_output_path; add <mediaId>_<scale> naming if needed)
62
+ docs/USAGE.md, README.md, docs/INDEX.md, CHANGELOG.md
63
+ document the command + the platform-only / Ultra-only constraints
64
+ ```
65
+
66
+ ---
67
+
68
+ ## Task 1 — Error type + exit code (test-first)
69
+
70
+ **What:** Add `UpscaleUnavailableError` (exit 22) for tier-gated/permission failures, distinct from WAF/reCAPTCHA 403.
71
+
72
+ **Files:**
73
+ - `tests/test_errors.py` — assert exit code 22, EXIT_CODE_MAP registration, ordering invariant
74
+ - `src/gflow_cli/errors.py` — new class + map entry
75
+
76
+ **Steps:**
77
+ - [x] Red test: `UpscaleUnavailableError` maps to 22; not equal to WAF 403 code
78
+ - [x] Add the class (RFC 9457 fields: type/title/status/detail/remediation_hint) + register in `EXIT_CODE_MAP`
79
+ - [x] Verify `test_exit_code_map_ordering_invariant` still passes (inheritance trap — see memory)
80
+
81
+ **Tests created (red):**
82
+ - [x] `test_upscale_unavailable_exit_code` — exit 22, distinct from WAF/reCAPTCHA
83
+
84
+ ---
85
+
86
+ ## Task 2 — Value objects (test-first)
87
+
88
+ **What:** `TargetResolution` enum (`2K`/`4K` ↔ wire `UPSAMPLE_IMAGE_RESOLUTION_*`, `from_cli`) and frozen `UpsampleImageRequest`.
89
+
90
+ **Files:**
91
+ - `tests/api/test_image_upscale.py` — enum mapping, `from_cli` rejects bad values, request immutability
92
+ - `src/gflow_cli/api/image.py` (or new `image_upscale.py`) — enum + dataclass + body builder
93
+
94
+ **Steps:**
95
+ - [x] Red tests: `TargetResolution.from_cli("2k")` → enum; `"1k"`/`"8k"` rejected; request frozen
96
+ - [x] Add enum + `UpsampleImageRequest(media_id, target_resolution, recaptcha_token="")`
97
+ - [x] Body builder → `{mediaId, targetResolution, clientContext:{recaptchaContext:{token}}}`
98
+ - [x] mediaId UUID-allowlist validation (reject malformed before request)
99
+
100
+ **Tests created (red):**
101
+ - [x] `test_target_resolution_from_cli` / `test_target_resolution_rejects_1k_and_unknown`
102
+ - [x] `test_upsample_request_body_shape`
103
+ - [x] `test_media_id_uuid_validation`
104
+
105
+ ---
106
+
107
+ ## Task 3 — Client `upsample_image()` (test-first)
108
+
109
+ **What:** Synchronous client method: mint reCAPTCHA, POST `upsampleImage`, parse `{encodedImage}` with cap + magic-byte, write file. Route constant added.
110
+
111
+ **Files:**
112
+ - `tests/api/test_image_upscale.py` — mocked transport/HTTP: happy path, oversized cap, missing field, non-image bytes, 403→typed error, no-retry on 4K 403, `encodedImage` never logged
113
+ - `src/gflow_cli/api/routes.py` — `UPSAMPLE_IMAGE`
114
+ - `src/gflow_cli/api/client.py` — `MAX_UPSAMPLE_B64_LEN = 50 * 1024 * 1024`; `upsample_image(...)`
115
+
116
+ **Steps:**
117
+ - [x] Red tests for: 200 happy (decode → PNG/JPEG bytes written); response > cap → typed error before decode; missing `encodedImage` → `WireFormatError` (redacted); decoded bytes fail magic-byte → typed error
118
+ - [x] Red tests for 403 disambiguation: tier 403 → `UpscaleUnavailableError` (no retry); WAF 403 → existing `WafRejectionError`; expired-token 401 → existing refresh+single-retry
119
+ - [x] Red test: structlog capture asserts `encodedImage` value never present in any event
120
+ - [x] Add route + method; reuse `_mint_recaptcha_token("upscaleImage" or correct action)`; single Page checkout (no nested checkout — use `context.request` if aux call needed)
121
+ - [x] Decode mirrors `concatenate_scene` (cap → `b64decode` → magic-byte → `del encoded`)
122
+ - [x] Write via `paths.image_output_path` as `<mediaId>_<scale>.<ext>` (ext from magic byte)
123
+
124
+ **Tests created (red):**
125
+ - [x] `test_upsample_happy_2k`, `test_upsample_oversized_rejected`, `test_upsample_missing_field`,
126
+ `test_upsample_non_image_bytes`, `test_upsample_4k_pro_403_no_retry`,
127
+ `test_upsample_waf_403_distinct`, `test_upsample_encoded_image_never_logged`
128
+
129
+ ---
130
+
131
+ ## Task 4 — CLI surface (test-first)
132
+
133
+ **What:** `gflow image upscale <mediaId> --scale 2k|4k [--out] [--profile]` with self-contained `--help`.
134
+
135
+ **Files:**
136
+ - `tests/test_cli_image.py` — Click invocation: arg/flag parsing, `--scale` Choice, exit codes, 4K/Pro hint, malformed mediaId usage error (no request), structlog `upscale_started/completed/unavailable`
137
+ - `src/gflow_cli/cli_image.py` — `upscale` command
138
+
139
+ **Steps:**
140
+ - [x] Red tests: happy invocation calls `upsample_image` with mapped enum; `--scale 8k` rejected by Click; `--scale 1k` rejected with "1k is the original" hint; malformed mediaId → usage error, no HTTP
141
+ - [x] Red test: 4K-on-Pro path surfaces exit 22 + Ultra-only remediation; no auto-retry
142
+ - [x] Implement command; emit `upscale_started/completed/unavailable` (stable keys); `--help` states platform-only + Ultra-only constraints + "find mediaId via `gflow data list images`"
143
+ - [x] Record the output asset via `OperationRecorder` honoring the redaction gate; wrap callback in try/except `DataStoreError`
144
+
145
+ **Tests created (red):**
146
+ - [x] `test_upscale_cli_happy`, `test_upscale_cli_scale_choice`, `test_upscale_cli_1k_rejected`,
147
+ `test_upscale_cli_bad_media_id`, `test_upscale_cli_4k_pro_exit22`
148
+
149
+ ---
150
+
151
+ ## Task 5 — BDD scaffold + green
152
+
153
+ **What:** Gherkin feature covering all Critical+High scenarios; step defs mirror runtime signatures.
154
+
155
+ **Files:**
156
+ - `tests/features/image_upscale.feature` — happy 2K, 4K-on-Pro, oversized, malformed mediaId
157
+ - `tests/features/test_image_upscale_steps.py` — typed step defs (mirror signatures — see memory)
158
+
159
+ **Steps:**
160
+ - [x] Add the four `/gflow:scenario` BDD scenarios as a feature file
161
+ - [x] Implement step defs against the real command; assert exit codes + redaction
162
+ - [x] All BDD green
163
+
164
+ **Tests created:**
165
+ - [x] BDD: 2K happy, 4K/Pro exit 22, oversized rejected, malformed mediaId usage error
166
+
167
+ ---
168
+
169
+ ## Task 6 — Docs + recon finalize
170
+
171
+ **What:** User-facing docs + cross-references; recon already written.
172
+
173
+ **Files:**
174
+ - `docs/USAGE.md` — `gflow image upscale` section (workflow: `data list images` → `upscale`)
175
+ - `README.md` command table; `docs/INDEX.md` route to `IMAGE_UPSCALE_RECON.md`
176
+ - `docs/KNOWN_ISSUES.md` — note 4K Ultra-gating + per-profile WAF heat from the extra gated op
177
+ - `CHANGELOG.md` `[Unreleased]` — `feat(image): upscale to 2K/4K (#171)`
178
+
179
+ **Steps:**
180
+ - [x] Write usage + constraints (platform-only, Ultra-only, credit-free expectation)
181
+ - [x] Index the recon doc; cross-link spikes
182
+ - [x] CHANGELOG entry referencing #171
183
+
184
+ ---
185
+
186
+ ## Task 7 — Full gates + PR
187
+
188
+ **What:** Green `/gflow:check`; open PR to `develop`.
189
+
190
+ **Steps:**
191
+ - [x] `/gflow:check` green (ruff + format + `pyright src` + pytest ≥ 80% on changed dirs)
192
+ - [x] Live smoke (opt-in, supervised): one real 2K upscale on a known mediaId (credit-free) confirms wire still matches
193
+ - [x] PR `feature/image-upscale` → `develop`, `Closes #171`
194
+
195
+ ---
196
+
197
+ ## Definition of done
198
+
199
+ - [x] All task steps checked off
200
+ - [x] `/gflow:check` green (ruff / format / pyright / pytest ≥ 80% coverage)
201
+ - [x] `CHANGELOG.md` `[Unreleased]` updated (`#171`)
202
+ - [x] Docs updated (`USAGE.md`, `README.md`, `INDEX.md`, `KNOWN_ISSUES.md`)
203
+ - [x] BDD feature covers all Critical + High scenarios from `/gflow:scenario`
204
+ - [x] `encodedImage` proven absent from logs (test) — Critical mitigation verified
205
+ - [x] 4K/Pro 403 → exit 22, no auto-retry — Critical mitigation verified
206
+ - [x] No `# TODO` in diff without a tracked issue link
@@ -0,0 +1,79 @@
1
+ # Scenario: `gflow image upscale <mediaId> --scale 2k|4k`
2
+
3
+ > `/gflow:scenario` output for issue #171. Produced pre-implementation, then
4
+ > revised against the **live-verified** wire (see `docs/IMAGE_UPSCALE_RECON.md`).
5
+ > Feeds the test matrix in `tests/api/test_client_upscale.py`,
6
+ > `tests/cli/test_cli_image_upscale.py`, and `tests/features/image_upscale.feature`.
7
+
8
+ ## Coverage map
9
+
10
+ **Active dimensions:**
11
+ - **D1 Auth & session lifecycle** — Bearer/session staleness observed live
12
+ (`ACCESS_TOKEN_REFRESH_NEEDED`); the existing refresh-on-401 path applies.
13
+ - **D2 WAF / reCAPTCHA** — the gating mechanism. Action `IMAGE_GENERATION` is
14
+ mandatory; per-profile heat is the dominant operational risk.
15
+ - **D3 Selector drift** — download button + 1K/2K/4K menu (locale-neutral).
16
+ - **D6 Data layer** — the catalog resolves the owning `projectId` from `mediaId`.
17
+ - **D7 Error propagation & exit codes** — 403 disambiguation, base64 redaction.
18
+ - **D8 Cross-platform paths** — `<mediaId>_<scale>.<ext>` output, Windows.
19
+ - **D9 Transport edge cases** — `encodedImage` parse/cap/magic-byte.
20
+ - **D11 Input validation** — mediaId/projectId UUID, `--scale`, 4K/Pro, uploads.
21
+ - **D12 Observability** — `image.upscale_{started,completed}` events, RFC 9457.
22
+
23
+ **Skipped:**
24
+ - **D4 Batch manifest** — v1 is single-image; no batch runner.
25
+ - **D5 Concurrency / Page pool** — one synchronous op, single Page checkout, no
26
+ fan-out (no `asyncio.gather`, no nested checkout).
27
+ - **D10 Headless/CI** — the browser + reCAPTCHA requirement is identical to the
28
+ existing image-generation path; inherited, not new.
29
+
30
+ ## Scenario table
31
+
32
+ | # | Dimension | Scenario | Severity | Expected behaviour | Test |
33
+ |---|---|---|---|---|---|
34
+ | 1 | D2 | reCAPTCHA action mismatch (e.g. wrong/guessed action) | **Critical** | Token scores low → 403. Action MUST be `IMAGE_GENERATION`. *(Root-caused live; locked by the action constant.)* | Live + recon spike |
35
+ | 2 | D9 | Incomplete `clientContext` (missing projectId/sessionId/tool/tier) | **Critical** | Server 403s even with a valid token. Full 5-field clientContext is sent. *(Root-caused live.)* | Unit (`test_body_shape`) |
36
+ | 3 | D2/D7 | 4K requested on a non-Ultra account → server 403 | **Critical** | `UpscaleUnavailableError` (exit 22), "Ultra-only" hint; **no auto-retry** | Unit + CLI + BDD |
37
+ | 4 | D7 | 403 disambiguation: 4K-tier vs WAF vs expired token | **Critical** | 4K-403 → exit 22; 2K-403 → `WafRejectionError` (10); 401 → refresh+retry | Unit (`test_upsample_4k_403…`, `…2k_403_stays_waf`) |
38
+ | 5 | D7/D12 | The ~4 MB `encodedImage` base64 leaks into logs | **Critical** | Never logged; event carries `bytes` int only | Unit (`…never_logged`, `capture_logs`) |
39
+ | 6 | D9 | Oversized response (pathological 4K base64 > cap) | **High** | Reject before decode at `MAX_UPSAMPLE_B64_LEN` (50 MB); no write | Unit (`…oversized…`) |
40
+ | 7 | D9 | Response missing `encodedImage` / unexpected shape | **High** | `WireFormatError` + discovery payload, not a crash | Unit (`…missing…`) |
41
+ | 8 | D9 | `encodedImage` decodes but isn't PNG/JPEG | **High** | Magic-byte check → `WireFormatError`; no garbage written | Unit (`…non_image…`, `…undecodable…`) |
42
+ | 9 | D11/D6 | `projectId` unresolvable (not in catalog, no `--project`) | **High** | Fail fast exit 2 + "pass --project" hint; no browser/mint | CLI + BDD (`…unresolvable…`) |
43
+ | 10 | D11 | Malformed `mediaId` / `--project` (non-UUID) | **High** | Rejected before any work (exit 2) | Unit + CLI + BDD (`…bad_media_id…`) |
44
+ | 11 | D6 | mediaId owned by a different account/profile | **High** | Catalog lookup is profile-scoped (ownership proof); server is final arbiter | CLI (catalog resolve path) |
45
+ | 12 | D1 | Bearer/session stale at call time (`ACCESS_TOKEN_REFRESH_NEEDED`) | **High** | Existing refresh-on-401 mints fresh token + single retry | Live-observed |
46
+ | 13 | D11 | `--scale 1k` (the original, no upscale) | **Medium** | Rejected with "1k is the original" hint (exit 2) | Unit + CLI |
47
+ | 14 | D11 | `--scale` outside {2k,4k} | **Medium** | Rejected before I/O (exit 2) | Unit + CLI |
48
+ | 15 | D3 | Flow renames the download ligature / restructures the menu | **Medium** | Structural, locale-neutral selectors (`download`, `2K`/`4K`); re-capture via spike | Spike (manual) |
49
+ | 16 | D8 | Output filename on Windows; large binary write; PYTHONUTF8 unset | **Medium** | `write_bytes` (encoding-agnostic); platformdirs; `<mediaId>_<scale>.<ext>` | Unit (extension) |
50
+ | 17 | D12 | New structlog events stable + documented | **Medium** | `image.upscale_started/completed`; RFC 9457 on error | Covered by event asserts |
51
+ | 18 | D6 | `--project` disagrees with catalog's project for the mediaId | **Low** | Warn (non-fatal); use `--project` as given | CLI (resolve path) |
52
+
53
+ ## Must-cover before merge (Critical + High) — all covered
54
+
55
+ 1. **reCAPTCHA action = `IMAGE_GENERATION` + full clientContext** (#1, #2) — root-caused
56
+ by live smoke; locked by the action constant and `build_upsample_image_body`.
57
+ 2. **403 disambiguation, no auto-retry on 4K** (#3, #4) — `UpscaleUnavailableError` (22).
58
+ 3. **base64 never logged + capped + magic-byte validated** (#5, #6, #7, #8).
59
+ 4. **projectId resolution + fail-fast + UUID validation** (#9, #10, #11).
60
+ 5. **Session refresh** (#12) — reuses the existing 401 path.
61
+
62
+ ## Deferred (Medium + Low — follow-ups, not blockers)
63
+
64
+ - Record the upscaled output in the data catalog (D6) — future follow-up.
65
+ - `gflow image t2i --upscale` convenience flow (one reCAPTCHA mint) — future follow-up.
66
+ - Selector-drift re-capture is manual via `scripts/dev/spike_image_upscale_drive.py`.
67
+
68
+ ## BDD scenarios (`tests/features/image_upscale.feature`)
69
+
70
+ Implemented: 2K happy (explicit project), catalog-resolved project, 4K→exit 22,
71
+ unresolvable project→exit 2, malformed mediaId→exit 2.
72
+
73
+ ## Known-issues cross-reference
74
+
75
+ - **reCAPTCHA Enterprise 403 / per-profile WAF heat** (KNOWN_ISSUES) — #1, #12 map
76
+ here. Upscale reuses the gated transport and adds a 4th gated op; mitigated by
77
+ one-at-a-time + no-auto-retry. Not resolved (inherent to the platform).
78
+ - **4K requires Ultra** — now a documented Open limitation in KNOWN_ISSUES (#171).
79
+ - **Locale leak / icon ligatures** — #15; selectors stay language-neutral.