gflow-cli 0.14.0__tar.gz → 0.15.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 (397) hide show
  1. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/CHANGELOG.md +37 -1
  2. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/PKG-INFO +2 -2
  3. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/docs/PROJECT_STATUS.md +4 -2
  4. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/docs/USAGE.md +45 -0
  5. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/pyproject.toml +3 -3
  6. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/__init__.py +1 -1
  7. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/api/image.py +18 -2
  8. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/api/transports/ui_automation.py +126 -0
  9. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/api/transports/ui_automation_video.py +17 -6
  10. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/cli_image.py +155 -13
  11. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/data/recorder.py +6 -1
  12. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/api/test_image.py +58 -0
  13. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/api/transports/test_ui_automation.py +166 -0
  14. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/cli/test_cli_image.py +289 -0
  15. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/features/test_image_steps.py +1 -0
  16. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/uv.lock +25 -25
  17. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/.claude/README.md +0 -0
  18. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/.claude/commands/gflow/active.md +0 -0
  19. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/.claude/commands/gflow/branch-review.md +0 -0
  20. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/.claude/commands/gflow/changelog.md +0 -0
  21. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/.claude/commands/gflow/check.md +0 -0
  22. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/.claude/commands/gflow/doc-review.md +0 -0
  23. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/.claude/commands/gflow/known-issues.md +0 -0
  24. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/.claude/commands/gflow/next.md +0 -0
  25. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/.claude/commands/gflow/plan.md +0 -0
  26. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/.claude/commands/gflow/pr-council-review.md +0 -0
  27. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/.claude/commands/gflow/predict.md +0 -0
  28. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/.claude/commands/gflow/release.md +0 -0
  29. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/.claude/commands/gflow/scenario.md +0 -0
  30. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/.claude/commands/gflow/status.md +0 -0
  31. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/.continue-here.md +0 -0
  32. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/.env.template +0 -0
  33. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/.gitattributes +0 -0
  34. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/.github/CODEOWNERS +0 -0
  35. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  36. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/.github/copilot-instructions.md +0 -0
  37. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/.github/dependabot.yml +0 -0
  38. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/.github/workflows/ci.yml +0 -0
  39. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/.github/workflows/external-pr-triage.yml +0 -0
  40. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/.github/workflows/governance-advisory.yml +0 -0
  41. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/.github/workflows/governance-benchmark.yml +0 -0
  42. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/.github/workflows/release.yml +0 -0
  43. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/.gitignore +0 -0
  44. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/.gitleaks.toml +0 -0
  45. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/.pre-commit-config.yaml +0 -0
  46. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/.secrets.baseline +0 -0
  47. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/AGENTS.md +0 -0
  48. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/CLAUDE.md +0 -0
  49. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/CONFIGURATION.md +0 -0
  50. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/CONTRIBUTING.md +0 -0
  51. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/DISCLAIMER.md +0 -0
  52. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/GEMINI.md +0 -0
  53. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/KNOWN_ISSUES.md +0 -0
  54. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/LICENSE +0 -0
  55. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/PLAN.md +0 -0
  56. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/PR162_MOVIE_CHARACTER_REVIEW.md +0 -0
  57. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/README.md +0 -0
  58. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/RELEASE.md +0 -0
  59. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/ROADMAP.md +0 -0
  60. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/conftest.py +0 -0
  61. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/docker-compose.yml +0 -0
  62. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/docs/AGENT_GUIDE.md +0 -0
  63. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/docs/ARCHITECTURE.md +0 -0
  64. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/docs/AUTHENTICATION.md +0 -0
  65. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/docs/CHARACTER.md +0 -0
  66. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/docs/CHARACTER_RECON.md +0 -0
  67. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/docs/CONFIGURATION.md +0 -0
  68. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/docs/DATA_LAYER.md +0 -0
  69. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/docs/DEBUGGING.md +0 -0
  70. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/docs/DEMOS.md +0 -0
  71. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/docs/DEVELOPMENT.md +0 -0
  72. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/docs/E2E_TESTING.md +0 -0
  73. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/docs/EXTERNAL_STORAGE.md +0 -0
  74. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/docs/GITHUB.md +0 -0
  75. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/docs/GOVERNANCE_BENCHMARK.md +0 -0
  76. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/docs/INDEX.md +0 -0
  77. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/docs/LIVE_VERIFICATION_data_layer.md +0 -0
  78. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/docs/LIVE_VERIFICATION_image_batch.md +0 -0
  79. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/docs/LIVE_VERIFICATION_v0.10.0.md +0 -0
  80. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/docs/LIVE_VERIFICATION_v0.11.0.md +0 -0
  81. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/docs/LIVE_VERIFICATION_v0.12.0.md +0 -0
  82. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/docs/LIVE_VERIFICATION_v0.13.0.md +0 -0
  83. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/docs/LIVE_VERIFICATION_v0.7.0.md +0 -0
  84. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/docs/LIVE_VERIFICATION_v0.8.1.md +0 -0
  85. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/docs/LIVE_VERIFICATION_v0.9.0.md +0 -0
  86. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/docs/LIVE_VERIFICATION_v0.9.1.md +0 -0
  87. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/docs/LIVE_VERIFICATION_video_download.md +0 -0
  88. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/docs/MOVIE.md +0 -0
  89. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/docs/SECURITY.md +0 -0
  90. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/docs/USER_GUIDE.md +0 -0
  91. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/docs/assets/demo-split-pf.gif +0 -0
  92. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/docs/assets/example-run.gif +0 -0
  93. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/docs/schemas/movie-handoff.schema.json +0 -0
  94. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/docs/superpowers/2026-05-17-issue-15-handover.md +0 -0
  95. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/docs/superpowers/character-scenario.md +0 -0
  96. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/docs/superpowers/plans/2026-06-01-governance-enforcement-advisory/PLAN.md +0 -0
  97. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/docs/superpowers/plans/2026-06-04-character-create-recording.md +0 -0
  98. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/docs/superpowers/plans/2026-06-06-movie-p0-asyncio-hotfix.md +0 -0
  99. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/docs/superpowers/plans/2026-06-06-movie-p1-composition-manifest.md +0 -0
  100. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/docs/superpowers/plans/2026-06-06-movie-p2-native-identity-voice.md +0 -0
  101. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/docs/superpowers/specs/2026-06-06-movie-consistency-design.md +0 -0
  102. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/docs/superpowers/verifications/2026-05-11-phase-4-stage-g.md +0 -0
  103. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/examples/README.md +0 -0
  104. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/examples/batch_from_config.py +0 -0
  105. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/examples/multi_prompt_t2i.py +0 -0
  106. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/examples/sample_config.json +0 -0
  107. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/examples/sample_prompts.txt +0 -0
  108. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/examples/single_image_t2i.py +0 -0
  109. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/examples/workflow_chain.py +0 -0
  110. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/llms.txt +0 -0
  111. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/samples/README.md +0 -0
  112. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/samples/captured/01_upload_image.json +0 -0
  113. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/samples/captured/02_batchAsyncGenerateVideoText.json +0 -0
  114. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/samples/captured/03_batchCheckAsyncVideoGenerationStatus.json +0 -0
  115. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/samples/captured/04_archive_workflow.json +0 -0
  116. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/samples/captured/05_createProject.json +0 -0
  117. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/samples/captured/06_batchGenerateImages.json +0 -0
  118. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/samples/captured/07_batchGenerateImages_seeded.json +0 -0
  119. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/samples/captured/08_batchAsyncGenerateVideoStartAndEndImage.json +0 -0
  120. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/samples/captured/09_batchAsyncGenerateVideoReferenceImages.json +0 -0
  121. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/samples/captured/10_batchCheckAsyncVideoGenerationStatus_successful.json +0 -0
  122. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/samples/captured/11_batchCheckAsyncVideoGenerationStatus_failed.json +0 -0
  123. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/samples/captured/12_create_scene.json +0 -0
  124. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/samples/captured/13_sceneWorkflows_update.json +0 -0
  125. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/samples/captured/14_get_scene_workflows.json +0 -0
  126. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/samples/captured/15_commit_flowWorkflow.json +0 -0
  127. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/scripts/ci/check_doc_links.py +0 -0
  128. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/scripts/ci/check_materiality.py +0 -0
  129. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/scripts/ci/check_repo_hygiene.py +0 -0
  130. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/scripts/debug_editor.py +0 -0
  131. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/scripts/debug_gen_settings.py +0 -0
  132. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/scripts/debug_settings.py +0 -0
  133. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/scripts/dev/_recording_client.py +0 -0
  134. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/scripts/dev/_spike_common.py +0 -0
  135. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/scripts/dev/active_plan.py +0 -0
  136. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/scripts/dev/capture_i2v_frame_slots_dom.py +0 -0
  137. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/scripts/dev/capture_i2v_intercept_submit.py +0 -0
  138. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/scripts/dev/capture_i2v_model_select_repro.py +0 -0
  139. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/scripts/dev/capture_i2v_post_bind_state.py +0 -0
  140. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/scripts/dev/capture_image_add_media_dom.py +0 -0
  141. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/scripts/dev/capture_locale_invariants.py +0 -0
  142. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/scripts/dev/cdp_drive_and_probe.py +0 -0
  143. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/scripts/dev/character_create_spike.py +0 -0
  144. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/scripts/dev/character_create_spike_v2.py +0 -0
  145. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/scripts/dev/dump_character_selectors.js +0 -0
  146. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/scripts/dev/make_project.py +0 -0
  147. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/scripts/dev/materiality_backtest.py +0 -0
  148. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/scripts/dev/monitor_pr_38.py +0 -0
  149. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/scripts/dev/patch_character.py +0 -0
  150. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/scripts/dev/record_flow_capture.py +0 -0
  151. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/scripts/dev/skillopt/README.md +0 -0
  152. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/scripts/dev/skillopt/harness.py +0 -0
  153. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/scripts/dev/skillopt/tasks.json +0 -0
  154. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/scripts/dev/spike_char_editor_dom.py +0 -0
  155. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/scripts/dev/spike_char_gen_capture.py +0 -0
  156. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/scripts/dev/spike_movie_attach_payload.py +0 -0
  157. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/scripts/dev/spike_movie_entity_recon.py +0 -0
  158. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/scripts/dev/spike_movie_gen_capture.py +0 -0
  159. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/scripts/dev/spike_movie_picker_select.py +0 -0
  160. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/scripts/dev/spike_movie_voice_list.py +0 -0
  161. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/scripts/dev/spike_patch_entity.py +0 -0
  162. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/scripts/diag_capture_flow_traffic.py +0 -0
  163. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/scripts/diag_recaptcha_mint.py +0 -0
  164. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/scripts/record_demo.ps1 +0 -0
  165. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/scripts/smoke_image.py +0 -0
  166. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/scripts/smoke_real_chrome_image.py +0 -0
  167. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/scripts/smoke_video_editor.py +0 -0
  168. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/scripts/smoke_worker_style.py +0 -0
  169. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/scripts/verify_chrome_auth_viability.py +0 -0
  170. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/skills/README.md +0 -0
  171. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/skills/gflow-cli/SKILL.md +0 -0
  172. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/skills/plan/SKILL.md +0 -0
  173. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/skills/pr-council-review/SKILL.md +0 -0
  174. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/skills/predict/SKILL.md +0 -0
  175. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/skills/scenario/SKILL.md +0 -0
  176. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/skills/status/SKILL.md +0 -0
  177. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/sonar-project.properties +0 -0
  178. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/__main__.py +0 -0
  179. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/_cli_helpers.py +0 -0
  180. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/api/__init__.py +0 -0
  181. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/api/_retry.py +0 -0
  182. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/api/_sapisidhash.py +0 -0
  183. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/api/character.py +0 -0
  184. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/api/client.py +0 -0
  185. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/api/dto.py +0 -0
  186. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/api/recaptcha.py +0 -0
  187. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/api/routes.py +0 -0
  188. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/api/scene.py +0 -0
  189. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/api/transports/__init__.py +0 -0
  190. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/api/transports/_common.py +0 -0
  191. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/api/transports/_fingerprint.py +0 -0
  192. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/api/transports/base.py +0 -0
  193. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/api/transports/experimental/__init__.py +0 -0
  194. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/api/transports/experimental/bearer.py +0 -0
  195. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/api/transports/experimental/evaluate_fetch.py +0 -0
  196. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/api/transports/experimental/sapisidhash.py +0 -0
  197. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/api/video.py +0 -0
  198. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/auth/__init__.py +0 -0
  199. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/auth/base.py +0 -0
  200. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/auth/factory.py +0 -0
  201. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/auth/internal_chromium.py +0 -0
  202. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/auth/real_chrome.py +0 -0
  203. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/auth/strategies.py +0 -0
  204. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/auth/verification.py +0 -0
  205. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/browser_manager.py +0 -0
  206. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/chain.py +0 -0
  207. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/chain_manifest.py +0 -0
  208. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/cli.py +0 -0
  209. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/cli_character.py +0 -0
  210. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/cli_data.py +0 -0
  211. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/cli_models.py +0 -0
  212. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/cli_movie.py +0 -0
  213. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/cli_run.py +0 -0
  214. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/cli_scene.py +0 -0
  215. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/cli_video.py +0 -0
  216. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/composition.py +0 -0
  217. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/config.py +0 -0
  218. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/data/__init__.py +0 -0
  219. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/data/chain_repo.py +0 -0
  220. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/data/migrations/0001_initial.sql +0 -0
  221. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/data/migrations/0002_add_cloud_storage.sql +0 -0
  222. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/data/migrations/0003_add_scene_tables.sql +0 -0
  223. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/data/migrations/0004_add_scene_output_path.sql +0 -0
  224. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/data/migrations/0005_add_chain_links.sql +0 -0
  225. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/data/migrations/0006_add_operations_metadata.sql +0 -0
  226. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/data/migrations/__init__.py +0 -0
  227. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/data/models.py +0 -0
  228. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/data/queries.py +0 -0
  229. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/data/redaction.py +0 -0
  230. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/data/repository.py +0 -0
  231. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/data/store.py +0 -0
  232. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/errors.py +0 -0
  233. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/exceptions.py +0 -0
  234. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/image_batch.py +0 -0
  235. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/json_output.py +0 -0
  236. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/manifest.py +0 -0
  237. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/media.py +0 -0
  238. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/movie_manifest.py +0 -0
  239. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/observability.py +0 -0
  240. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/paths.py +0 -0
  241. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/profile_store.py +0 -0
  242. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/services/__init__.py +0 -0
  243. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/services/character_create.py +0 -0
  244. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/src/gflow_cli/storage.py +0 -0
  245. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tasks/lessons.md +0 -0
  246. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/test_assets/sample_batch.json +0 -0
  247. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/test_assets/sample_batch.tsv +0 -0
  248. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/test_assets/sample_batch_invalid.tsv +0 -0
  249. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/__init__.py +0 -0
  250. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/api/__init__.py +0 -0
  251. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/api/fixtures/character_gen_response.json +0 -0
  252. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/api/fixtures/patch_entity_response.json +0 -0
  253. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/api/test_aisandbox_auth_error.py +0 -0
  254. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/api/test_aisandbox_auth_headers.py +0 -0
  255. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/api/test_bearer_redaction.py +0 -0
  256. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/api/test_character.py +0 -0
  257. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/api/test_client.py +0 -0
  258. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/api/test_client_character.py +0 -0
  259. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/api/test_client_delete_characters.py +0 -0
  260. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/api/test_client_generate_character.py +0 -0
  261. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/api/test_client_image.py +0 -0
  262. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/api/test_client_launch_kwargs.py +0 -0
  263. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/api/test_client_patch_entity.py +0 -0
  264. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/api/test_client_scene.py +0 -0
  265. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/api/test_concurrency.py +0 -0
  266. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/api/test_dto.py +0 -0
  267. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/api/test_image_dto.py +0 -0
  268. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/api/test_post_json_aisandbox_auth.py +0 -0
  269. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/api/test_recaptcha.py +0 -0
  270. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/api/test_retry.py +0 -0
  271. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/api/test_routes.py +0 -0
  272. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/api/test_routes_character.py +0 -0
  273. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/api/test_routes_scene.py +0 -0
  274. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/api/test_sapisidhash_helper.py +0 -0
  275. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/api/test_scene_models.py +0 -0
  276. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/api/test_video.py +0 -0
  277. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/api/test_video_request.py +0 -0
  278. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/api/transports/__init__.py +0 -0
  279. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/api/transports/test_base.py +0 -0
  280. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/api/transports/test_bearer.py +0 -0
  281. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/api/transports/test_common.py +0 -0
  282. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/api/transports/test_evaluate_fetch.py +0 -0
  283. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/api/transports/test_factory.py +0 -0
  284. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/api/transports/test_fingerprint.py +0 -0
  285. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/api/transports/test_sapisidhash.py +0 -0
  286. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/api/transports/test_transport_timeout.py +0 -0
  287. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/api/transports/test_ui_automation_batch.py +0 -0
  288. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/api/transports/test_ui_automation_image_mode.py +0 -0
  289. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/api/transports/test_ui_automation_video.py +0 -0
  290. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/api/transports/test_ui_character_editor.py +0 -0
  291. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/auth/strategies/test_factory.py +0 -0
  292. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/auth/strategies/test_strategies.py +0 -0
  293. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/auth/test_verification.py +0 -0
  294. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/cli/__init__.py +0 -0
  295. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/cli/test_cli_auth_list.py +0 -0
  296. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/cli/test_cli_character.py +0 -0
  297. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/cli/test_cli_character_create.py +0 -0
  298. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/cli/test_cli_data.py +0 -0
  299. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/cli/test_cli_image_seed_removed.py +0 -0
  300. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/cli/test_cli_models.py +0 -0
  301. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/cli/test_cli_movie.py +0 -0
  302. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/cli/test_cli_run.py +0 -0
  303. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/cli/test_cli_scene.py +0 -0
  304. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/cli/test_cli_video.py +0 -0
  305. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/cli/test_cli_video_chain.py +0 -0
  306. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/cli/test_error_handling.py +0 -0
  307. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/cli/test_helpers.py +0 -0
  308. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/cli/test_movie_manifest.py +0 -0
  309. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/cli/test_t2i_multi_prompt.py +0 -0
  310. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/composition/test_character.py +0 -0
  311. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/composition/test_compose_prompt.py +0 -0
  312. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/composition/test_handoff.py +0 -0
  313. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/conftest.py +0 -0
  314. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/data/__init__.py +0 -0
  315. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/data/test_chain_repo.py +0 -0
  316. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/data/test_find_incomplete_character.py +0 -0
  317. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/data/test_models.py +0 -0
  318. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/data/test_packaging.py +0 -0
  319. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/data/test_recorder.py +0 -0
  320. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/data/test_recorder_character.py +0 -0
  321. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/data/test_redaction.py +0 -0
  322. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/data/test_repository.py +0 -0
  323. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/data/test_scene_persistence.py +0 -0
  324. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/data/test_settings_and_errors.py +0 -0
  325. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/data/test_store_migrations.py +0 -0
  326. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/dev/test_record_flow_capture.py +0 -0
  327. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/dev/test_recording_client.py +0 -0
  328. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/e2e/__init__.py +0 -0
  329. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/e2e/conftest.py +0 -0
  330. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/e2e/test_aisandbox_auth_live.py +0 -0
  331. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/e2e/test_auth_verification_e2e.py +0 -0
  332. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/e2e/test_chain_e2e.py +0 -0
  333. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/e2e/test_character_create_e2e.py +0 -0
  334. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/e2e/test_data_layer_e2e.py +0 -0
  335. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/e2e/test_i2v_flags_e2e.py +0 -0
  336. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/e2e/test_image_batch_e2e.py +0 -0
  337. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/e2e/test_image_i2i_ref_cap_e2e.py +0 -0
  338. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/e2e/test_json_output_e2e.py +0 -0
  339. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/e2e/test_locale_selectors_e2e.py +0 -0
  340. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/e2e/test_scene_compose_live.py +0 -0
  341. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/e2e/test_transports_e2e.py +0 -0
  342. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/e2e/test_video_r2v_ref_cap_e2e.py +0 -0
  343. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/e2e/test_video_t2v_e2e.py +0 -0
  344. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/features/__init__.py +0 -0
  345. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/features/auth.feature +0 -0
  346. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/features/auth_login.feature +0 -0
  347. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/features/character_create.feature +0 -0
  348. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/features/character_read.feature +0 -0
  349. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/features/conftest.py +0 -0
  350. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/features/image.feature +0 -0
  351. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/features/test_auth_login_steps.py +0 -0
  352. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/features/test_auth_steps.py +0 -0
  353. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/features/test_character_create_steps.py +0 -0
  354. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/features/test_character_read_steps.py +0 -0
  355. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/features/test_step_collision_guard.py +0 -0
  356. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/features/test_video_chain_steps.py +0 -0
  357. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/features/video_chain.feature +0 -0
  358. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/fixtures/__init__.py +0 -0
  359. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/fixtures/seeded_catalog.py +0 -0
  360. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/image_batch/__init__.py +0 -0
  361. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/image_batch/test_image_manifest.py +0 -0
  362. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/image_batch/test_observability_events.py +0 -0
  363. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/integration/__init__.py +0 -0
  364. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/integration/conftest.py +0 -0
  365. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/integration/constants.py +0 -0
  366. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/integration/test_storage_gcs.py +0 -0
  367. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/integration/test_storage_s3.py +0 -0
  368. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/scripts/test_capture_locale_invariants.py +0 -0
  369. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/scripts/test_check_materiality.py +0 -0
  370. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/scripts/test_check_repo_hygiene.py +0 -0
  371. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/scripts/test_materiality_backtest.py +0 -0
  372. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/services/__init__.py +0 -0
  373. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/services/test_character_create_redaction.py +0 -0
  374. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/services/test_character_create_saga.py +0 -0
  375. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/services/test_character_gen_no_direct_post.py +0 -0
  376. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/smoke/__init__.py +0 -0
  377. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/smoke/test_profile_account_smoke.py +0 -0
  378. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/smoke/test_real_flow.py +0 -0
  379. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/test_auth.py +0 -0
  380. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/test_browser_manager.py +0 -0
  381. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/test_chain.py +0 -0
  382. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/test_chain_manifest.py +0 -0
  383. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/test_cli_data.py +0 -0
  384. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/test_config.py +0 -0
  385. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/test_conftest_isolation.py +0 -0
  386. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/test_data_queries.py +0 -0
  387. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/test_documentation_gate.py +0 -0
  388. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/test_errors.py +0 -0
  389. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/test_errors_403.py +0 -0
  390. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/test_json_output.py +0 -0
  391. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/test_manifest.py +0 -0
  392. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/test_marker_registry.py +0 -0
  393. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/test_media.py +0 -0
  394. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/test_observability.py +0 -0
  395. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/test_paths.py +0 -0
  396. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/test_profile_store.py +0 -0
  397. {gflow_cli-0.14.0 → gflow_cli-0.15.0}/tests/test_smoke.py +0 -0
@@ -7,6 +7,41 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.15.0] — 2026-06-09
11
+
12
+ ### Added
13
+
14
+ - **`gflow image` can reference locked CHARACTER entities** for character-consistent
15
+ stills. New `--reference-entity <id>` (repeatable) + `--reference-entity-name` on
16
+ `image t2i` / `i2i`, plus `--project <id>` to generate in an EXISTING project (where
17
+ the entities live) instead of a throwaway scratch project. Entities attach through
18
+ the editor's **Personagens picker** and ride the submit as `referenceEntities`
19
+ (confirmed against the live API); `_build_batch_generate_images_body` serializes them
20
+ so non-UI/headless transports get parity too. Entities count toward the per-model
21
+ reference cap. `--project` / `--reference-entity` are single-prompt only; for a pure
22
+ character reference use `t2i` (`i2i` still needs a `--ref`). See `docs/USAGE.md` →
23
+ "Character-consistent images (entity references)".
24
+
25
+ ### Fixed
26
+
27
+ - `--project` no longer overwrites an existing project's stored title/source in the
28
+ local history DB (the recorder preserves the curated title for non-created projects).
29
+
30
+ ### Security
31
+
32
+ - `--project` / `--reference-entity` ids are validated at the CLI boundary
33
+ (`[A-Za-z0-9-]{1,128}`), closing an unvalidated `page.goto` navigation path
34
+ (`project_editor_url` lacked the allowlist its sibling routes enforce) and a
35
+ CSS-selector injection vector (`data-tile-id='fe_id_<id>'`). The request-body debug
36
+ logger elides large reference-field values so Flow-built image bytes can't leak into
37
+ logs.
38
+
39
+ ### Internal
40
+
41
+ - Bump dev/CI `ruff` to `0.15.16` (both the `dev` extra and the `dependency-groups`
42
+ pin; supersedes Dependabot PR #165, which updated only the soft `>=` extra and not
43
+ the hard `==` group pin CI actually uses). `ruff check` / `format --check` clean.
44
+
10
45
  ## [0.14.0] — 2026-06-07
11
46
 
12
47
  ### Added
@@ -1434,7 +1469,8 @@ shell-script template that branches on these codes.
1434
1469
 
1435
1470
  First skeleton. Not functional end-to-end yet.
1436
1471
 
1437
- [Unreleased]: https://github.com/ffroliva/gflow-cli/compare/v0.14.0...HEAD
1472
+ [Unreleased]: https://github.com/ffroliva/gflow-cli/compare/v0.15.0...HEAD
1473
+ [0.15.0]: https://github.com/ffroliva/gflow-cli/compare/v0.14.0...v0.15.0
1438
1474
  [0.14.0]: https://github.com/ffroliva/gflow-cli/compare/v0.13.0...v0.14.0
1439
1475
  [0.13.0]: https://github.com/ffroliva/gflow-cli/compare/v0.12.0...v0.13.0
1440
1476
  [0.12.0]: https://github.com/ffroliva/gflow-cli/compare/v0.11.0...v0.12.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gflow-cli
3
- Version: 0.14.0
3
+ Version: 0.15.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
@@ -53,7 +53,7 @@ Requires-Dist: numpy>=1.24; extra == 'dev'
53
53
  Requires-Dist: pyright>=1.1.0; extra == 'dev'
54
54
  Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
55
55
  Requires-Dist: pytest>=8.0.0; extra == 'dev'
56
- Requires-Dist: ruff>=0.15.15; extra == 'dev'
56
+ Requires-Dist: ruff>=0.15.16; extra == 'dev'
57
57
  Provides-Extra: gcs
58
58
  Requires-Dist: gcsfs>=2024.2.0; extra == 'gcs'
59
59
  Requires-Dist: universal-pathlib>=0.2.5; extra == 'gcs'
@@ -4,9 +4,9 @@
4
4
 
5
5
  ## Current release
6
6
 
7
- **v0.13.0 — alpha.** High-fidelity CLI hardening and term alignment. **`gflow video i2v`** now uses the canonical `--initial-frame` and `--end-frame` flags (matching Flow's UI labels), with robust Click greedy-fill protection and positional backward compatibility. **In-project governance** (ruff T20, materiality classification) is now active to harden the AI-driven development flow. This release also refactors the **`character_create` saga** for improved type safety and documentation. Live-verified end-to-end on 2026-06-04 with the new flags and interpolation paths.
7
+ **v0.15.0 — alpha.** **Character-consistent images via entity references.** `gflow image t2i/i2i` can now reference locked Flow CHARACTER entities (`--reference-entity <id>`, repeatable) and generate in an existing project (`--project <id>`) where those entities live entities attach through the editor's Personagens picker and ride the submit as `referenceEntities` (confirmed against the live API; the body builder serializes them for headless transports too). Live-verified 2026-06-08: a 3-frame Dragon-Hook smoke rendered drift-free with all three characters on-model. LLM-council-reviewed (security/correctness/dedup) before merge. Carries forward the v0.14.0 `gflow movie` line (multi-scene, character-consistent video).
8
8
 
9
- **Develop (unreleased, post-v0.13.0):** *(empty — develop is the staging branch for the next release).*
9
+ **Develop (unreleased, post-v0.15.0):** *(empty — develop is the staging branch for the next release).*
10
10
 
11
11
  ## Milestone history
12
12
 
@@ -52,6 +52,8 @@
52
52
  | `gflow character rm` — free character deletion (#150) | ✅ done (v0.13.0) |
53
53
  | Align I2V CLI flags with Flow UI Labels (`--initial-frame`) (#122) | ✅ done (v0.13.0) |
54
54
  | In-project governance (ruff T20, materiality Classifier) | ✅ done (v0.13.0) |
55
+ | `gflow movie` — multi-scene, character-consistent video from a TOML manifest (entity reuse, resumable, handoff manifest) | ✅ done (v0.14.0) |
56
+ | `gflow image t2i/i2i` — reference locked CHARACTER entities (`--reference-entity`) + `--project` for character-consistent stills | ✅ done (v0.15.0) |
55
57
  | `gflow character` — reusable Flow Character entities (`create`/`list`/`show`/`voices`), persist-before-spend saga (#145) | ✅ done (v0.12.0) |
56
58
  | `gflow scene` — Add Clip / Scenes compose + credit-free server-side extended video (`runVideoFxConcatenation`) | ✅ done (v0.12.0) |
57
59
  | `gflow video chain` — last-frame I2V chaining from a JSONL manifest (`--dry-run`/`--max-links`/`--resume-from`) | ✅ done (v0.12.0) |
@@ -131,6 +131,13 @@ Options:
131
131
  Aspect ratio. [default: 9:16]
132
132
  -n, --count INTEGER How many images to generate (1-4). [default: 1]
133
133
  --out PATH Output directory (see "Output paths" below).
134
+ --project ID Generate in this EXISTING Flow project instead of a
135
+ scratch project. Required to reference locked
136
+ entities/assets in that project. Single-prompt only.
137
+ --reference-entity ID Flow CHARACTER entity id to reference for character
138
+ consistency (repeatable; must live in --project).
139
+ Single-prompt only.
140
+ --reference-entity-name N Display name paired with --reference-entity.
134
141
  --profile NAME Profile name (overrides default).
135
142
  ```
136
143
 
@@ -212,6 +219,10 @@ Options:
212
219
  Aspect ratio. [default: 9:16]
213
220
  -n, --count INTEGER How many images to generate (1-4). [default: 1]
214
221
  --out PATH Output directory (same semantics as t2i).
222
+ --project ID Generate in this EXISTING Flow project (skip scratch).
223
+ --reference-entity ID Flow CHARACTER entity id to reference (repeatable;
224
+ must live in --project). Counts toward the ref cap.
225
+ --reference-entity-name N Display name paired with --reference-entity.
215
226
  --profile NAME Profile name (overrides default).
216
227
  ```
217
228
 
@@ -248,6 +259,40 @@ A 2-image run produces files numbered `_1.png`, `_2.png`:
248
259
  ./variants/<media_name_b>_2.png
249
260
  ```
250
261
 
262
+ ## Character-consistent images (entity references)
263
+
264
+ `--reference-entity` makes `image t2i`/`i2i` reference a locked Flow **CHARACTER
265
+ entity** (minted via `gflow character create`) so the generated subject stays
266
+ on-model across shots — no per-shot prompt wrangling or fragile image refs.
267
+
268
+ How it works: entities are **project-scoped**, so you must generate **in the
269
+ project that owns them** via `--project <id>` (this also means no throwaway
270
+ scratch project is created). The CLI attaches each entity through the Flow editor's
271
+ resource picker; the submit then carries `referenceEntities`, exactly like the
272
+ video R2V path. Entities count toward the same per-model reference cap as `--ref`
273
+ images. `--reference-entity` / `--project` are **single-prompt only**.
274
+
275
+ For a *pure* character reference (no starting image) use `t2i` — `i2i` still
276
+ requires at least one `--ref`.
277
+
278
+ ```bash
279
+ # One locked character, consistent across runs (t2i = no image ref needed)
280
+ gflow image t2i "Aria explores a neon market, full body" \
281
+ --project 7fa97443-… --reference-entity 8a77f8cb-… --reference-entity-name Aria \
282
+ --model nano2 --aspect 9:16 -n 3
283
+
284
+ # Several characters in one scene (each --reference-entity repeatable)
285
+ gflow image t2i "Aria and Drako meet at the gate" --project 7fa97443-… \
286
+ --reference-entity 8a77f8cb-… --reference-entity 4ed5cb7f-…
287
+
288
+ # i2i: a starting image PLUS an entity for the character
289
+ gflow image i2i "place this hero in a snowy pass" --ref hero.png \
290
+ --project 7fa97443-… --reference-entity 8a77f8cb-…
291
+ ```
292
+
293
+ > Ids are validated at the CLI boundary (letters/digits/hyphens, ≤128 chars).
294
+ > The entity must already exist in `--project` (see `gflow character create`).
295
+
251
296
  ## `gflow image batch`
252
297
 
253
298
  Generate multiple images from a single manifest file. The format is dispatched by file extension (`.json` or `.tsv`).
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "gflow-cli"
3
- version = "0.14.0"
3
+ version = "0.15.0"
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" }
@@ -33,7 +33,7 @@ dependencies = [
33
33
  dev = [
34
34
  "pytest>=8.0.0",
35
35
  "pytest-asyncio>=0.23.0",
36
- "ruff>=0.15.15",
36
+ "ruff>=0.15.16",
37
37
  "pyright>=1.1.0",
38
38
  # video-chain: the last-frame extractor (media.py) decodes with PyAV, and its
39
39
  # tests build synthetic mp4 fixtures with numpy. Declared here so the dev/CI
@@ -134,7 +134,7 @@ dev = [
134
134
  "pytest-asyncio>=1.3.0",
135
135
  "pytest-bdd>=8.1.0",
136
136
  "pytest-cov>=7.1.0",
137
- "ruff==0.15.15",
137
+ "ruff==0.15.16",
138
138
  ]
139
139
  # Container-based integration tests — requires Docker daemon.
140
140
  # Install: uv sync --group containers
@@ -1,3 +1,3 @@
1
1
  """gflow-cli — unofficial CLI for Google Flow."""
2
2
 
3
- __version__ = "0.14.0"
3
+ __version__ = "0.15.0"
@@ -229,6 +229,14 @@ class GenerateImageRequest:
229
229
  # dialog by the ui_automation transport (the REST uploadImage path 401s).
230
230
  # The common i2i case; `refs` (pre-uploaded UUIDs) would need library-select.
231
231
  ref_paths: tuple[Path, ...] = ()
232
+ # Flow CHARACTER entity ids to reference (project-scoped, attached via the
233
+ # Add-Media → Personagens picker by the ui_automation transport). Entities
234
+ # keep generated subjects consistent with a locked character. They count
235
+ # toward the same per-model reference cap as image refs.
236
+ reference_entities: tuple[str, ...] = ()
237
+ # Optional display names paired with reference_entities (used by the UI
238
+ # picker when an id-keyed tile can't be located by id alone).
239
+ reference_entity_names: tuple[str, ...] = ()
232
240
  recaptcha_token: str = "" # populated by caller right before send; "" means unminted
233
241
  # number of images to generate (1–4); UI transport uses this to set Flow's count tab
234
242
  count: int = 1
@@ -240,10 +248,10 @@ class GenerateImageRequest:
240
248
  if not 1 <= self.count <= 4:
241
249
  msg = f"GenerateImageRequest.count must be 1–4, got {self.count}"
242
250
  raise ValueError(msg)
243
- n_refs = len(self.refs) + len(self.ref_paths)
251
+ n_refs = len(self.refs) + len(self.ref_paths) + len(self.reference_entities)
244
252
  cap = reference_cap_for(self.model)
245
253
  if n_refs > cap:
246
- msg = f"{self.model.value} allows at most {cap} reference image(s); got {n_refs}"
254
+ msg = f"{self.model.value} allows at most {cap} reference(s); got {n_refs}"
247
255
  raise ValueError(
248
256
  msg,
249
257
  )
@@ -299,6 +307,14 @@ def _build_batch_generate_images_body(
299
307
  # Always present — empty list for T2I, populated for I2I.
300
308
  "imageInputs": [r.to_wire() for r in req.refs],
301
309
  }
310
+ # Character entity references. Shape confirmed by live capture (2026-06-08):
311
+ # the image submit carries `referenceEntities: [{"entityId": <id>}]`. Added
312
+ # only when present so plain t2i/i2i bodies stay byte-identical to the
313
+ # captured samples. The ui_automation transport attaches entities via the
314
+ # Personagens picker and lets Flow's JS build this; non-UI transports rely
315
+ # on this serialization.
316
+ if req.reference_entities:
317
+ request["referenceEntities"] = [{"entityId": e} for e in req.reference_entities]
302
318
  return {
303
319
  "clientContext": cc,
304
320
  "mediaGenerationContext": {"batchId": batch_id},
@@ -31,6 +31,7 @@ from gflow_cli.api.transports._common import extract_project_id
31
31
  from gflow_cli.api.transports.ui_automation_video import (
32
32
  MODE_SWITCH_TRIGGER_SELECTORS,
33
33
  VideoGenerationMixin,
34
+ zip_entity_refs,
34
35
  )
35
36
  from gflow_cli.errors import (
36
37
  AuthExpiredError,
@@ -518,6 +519,70 @@ def _images_from_responses(
518
519
  return images, first_error_status, first_error_route
519
520
 
520
521
 
522
+ _REF_VALUE_MAX_CHARS = 512
523
+
524
+
525
+ def _elide_large_value(value: Any) -> Any:
526
+ """Return *value* verbatim when small, else a length-only redaction marker.
527
+
528
+ Guards the request-body logger against dumping large/secret payloads: if Flow
529
+ names an i2i image field `reference*`/`*entity*`, its base64 bytes would match
530
+ the reference-field filter and be logged in full. Small fields like
531
+ `referenceEntities` (a short list of `{entityId}`) pass through; anything
532
+ serializing beyond the cap is elided to a `<elided N chars>` marker.
533
+ """
534
+ try:
535
+ serialized = json.dumps(value, default=str)
536
+ except (TypeError, ValueError):
537
+ return f"<unserializable {type(value).__name__}>"
538
+ if len(serialized) > _REF_VALUE_MAX_CHARS:
539
+ return f"<elided {len(serialized)} chars>"
540
+ return value
541
+
542
+
543
+ def _summarize_batch_request_body(post_data: str | None) -> dict[str, Any]:
544
+ """Compact, byte-safe summary of an outgoing ``batchGenerateImages`` body.
545
+
546
+ This is the make-or-break signal for the entity-reference spike: it reveals
547
+ whether Flow's image submit carries a ``referenceEntities`` field after we
548
+ attach a character via the picker — WITHOUT logging the full body (i2i
549
+ bodies embed base64 image bytes that would flood the log and leak content).
550
+
551
+ Returns a dict with ``present``, ``mentions_reference_entities`` (cheap
552
+ substring probe that survives non-JSON bodies), and — when the body parses —
553
+ the keys of ``requests[0]`` plus any reference/entity-related fields verbatim
554
+ (those are small id lists, safe to surface).
555
+ """
556
+ if not post_data:
557
+ return {"present": False}
558
+ summary: dict[str, Any] = {
559
+ "present": True,
560
+ "bytes": len(post_data),
561
+ "mentions_reference_entities": "referenceEntit" in post_data,
562
+ }
563
+ try:
564
+ parsed: object = json.loads(post_data)
565
+ except (ValueError, TypeError):
566
+ return summary
567
+ if not isinstance(parsed, dict):
568
+ return summary
569
+ parsed_dict = cast("dict[str, Any]", parsed)
570
+ reqs = parsed_dict.get("requests")
571
+ if isinstance(reqs, list) and reqs and isinstance(reqs[0], dict):
572
+ first = cast("dict[str, Any]", reqs[0])
573
+ summary["request0_keys"] = sorted(first.keys())
574
+ ref_bits = {
575
+ k: _elide_large_value(v)
576
+ for k, v in first.items()
577
+ if "reference" in k.lower() or "entity" in k.lower()
578
+ }
579
+ if ref_bits:
580
+ summary["reference_fields"] = ref_bits
581
+ else:
582
+ summary["top_keys"] = sorted(parsed_dict.keys())
583
+ return summary
584
+
585
+
521
586
  class UiAutomationTransport(VideoGenerationMixin):
522
587
  """D.2.4 — Playwright UI mimicry strategy.
523
588
 
@@ -1593,6 +1658,51 @@ class UiAutomationTransport(VideoGenerationMixin):
1593
1658
 
1594
1659
  return captured, detach
1595
1660
 
1661
+ @staticmethod
1662
+ def _attach_batch_request_logger(
1663
+ page: Page,
1664
+ *,
1665
+ project_id: str | None = None,
1666
+ ) -> Callable[[], None]:
1667
+ """Register a ``page.on('request', ...)`` that logs a compact summary of
1668
+ each outgoing ``batchGenerateImages`` body.
1669
+
1670
+ The summary (see :func:`_summarize_batch_request_body`) surfaces whether
1671
+ the submit carries ``referenceEntities`` — the entity-reference spike's
1672
+ make-or-break signal — without dumping i2i image bytes. Returns an
1673
+ idempotent detach fn, torn down alongside the response listener.
1674
+ """
1675
+
1676
+ def on_request(request_obj: Any) -> None:
1677
+ if "batchGenerateImages" not in request_obj.url:
1678
+ return
1679
+ try:
1680
+ post_data = request_obj.post_data
1681
+ except Exception:
1682
+ post_data = None
1683
+ log.info(
1684
+ "ui_automation.batch_request_body",
1685
+ url=request_obj.url,
1686
+ filter_project_id=project_id,
1687
+ summary=_summarize_batch_request_body(post_data),
1688
+ )
1689
+
1690
+ page.on("request", on_request)
1691
+
1692
+ _detached = False
1693
+
1694
+ def detach() -> None:
1695
+ nonlocal _detached
1696
+ if _detached:
1697
+ return
1698
+ _detached = True
1699
+ try:
1700
+ page.remove_listener("request", on_request)
1701
+ except Exception:
1702
+ pass
1703
+
1704
+ return detach
1705
+
1596
1706
  @staticmethod
1597
1707
  async def _await_captured(
1598
1708
  captured: list[dict[str, Any]],
@@ -1814,6 +1924,18 @@ class UiAutomationTransport(VideoGenerationMixin):
1814
1924
  if request.ref_paths:
1815
1925
  await self._attach_references(page, list(request.ref_paths), out_dir=out_dir)
1816
1926
 
1927
+ # Entity references: attach locked CHARACTER entities via the Personagens
1928
+ # picker (inherited from the video transport). The entity must live in the
1929
+ # project we generate in (pass --project / project_id). Flow's JS then
1930
+ # includes them on the submit; the request logger below records whether the
1931
+ # outgoing batchGenerateImages carries `referenceEntities` (spike signal).
1932
+ if request.reference_entities:
1933
+ await self._attach_character_entities(
1934
+ page,
1935
+ zip_entity_refs(request.reference_entities, request.reference_entity_names),
1936
+ out_dir=out_dir,
1937
+ )
1938
+
1817
1939
  # Attach the response listener SYNCHRONOUSLY before any prompt
1818
1940
  # action. asyncio.create_task is unsafe here: it defers the listener
1819
1941
  # registration until the new task gets event-loop scheduling, which
@@ -1821,6 +1943,9 @@ class UiAutomationTransport(VideoGenerationMixin):
1821
1943
  # attach/await eliminates that race. Project-ID filter prevents stale
1822
1944
  # responses from previously-visited projects accumulating in the list.
1823
1945
  captured, detach = self._attach_batch_response_listener(page, project_id=nav_project_id)
1946
+ # Also log the OUTGOING request body summary so the entity-reference spike
1947
+ # can read whether the submit carries `referenceEntities`.
1948
+ req_log_detach = self._attach_batch_request_logger(page, project_id=nav_project_id)
1824
1949
  # Record submit_time BEFORE the click so the post-submit-time filter
1825
1950
  # in _await_captured can distinguish this prompt's responses from any
1826
1951
  # stale entries that arrived between listener attach and the click.
@@ -1835,6 +1960,7 @@ class UiAutomationTransport(VideoGenerationMixin):
1835
1960
  )
1836
1961
  finally:
1837
1962
  detach()
1963
+ req_log_detach()
1838
1964
 
1839
1965
  # Collect images from ALL captured responses (Flow makes one API call
1840
1966
  # per image when count > 1).
@@ -318,6 +318,22 @@ VIDEO_SUBMODE_SELECTORS: dict[str, tuple[str, ...]] = {
318
318
  ),
319
319
  }
320
320
 
321
+
322
+ def zip_entity_refs(
323
+ entity_ids: tuple[str, ...],
324
+ entity_names: tuple[str, ...],
325
+ ) -> list[tuple[str, str]]:
326
+ """Pair character entity ids with display names for the Personagens picker.
327
+
328
+ Tiles are addressed by id (``data-tile-id="fe_id_<id>"``); the name is only a
329
+ human label for logs/error screenshots. When fewer names than ids are given,
330
+ the id stands in as its own name so the pairing never drops an entity. Shared
331
+ by the image (`ui_automation`) and video (R2V) entity-attach paths.
332
+ """
333
+ names = list(entity_names)
334
+ return [(eid, names[i] if i < len(names) else eid) for i, eid in enumerate(entity_ids)]
335
+
336
+
321
337
  # The editor SPA's ready anchor — the Slate prompt textbox. The /project/ URL
322
338
  # nav fires before the UI mounts; this is the readiness gate (used by
323
339
  # _wait_video_editor_ready and asserted in its test).
@@ -1585,14 +1601,9 @@ class VideoGenerationMixin:
1585
1601
  )
1586
1602
  elif request.mode is Mode.R2V:
1587
1603
  if request.reference_entities:
1588
- names = request.reference_entity_names or request.reference_entities
1589
- entities = [
1590
- (entity_id, names[i] if i < len(names) else entity_id)
1591
- for i, entity_id in enumerate(request.reference_entities)
1592
- ]
1593
1604
  await VideoGenerationMixin._attach_character_entities(
1594
1605
  page,
1595
- entities,
1606
+ zip_entity_refs(request.reference_entities, request.reference_entity_names),
1596
1607
  out_dir=out_dir,
1597
1608
  )
1598
1609
  if request.reference_images: