gflow-cli 0.18.0__tar.gz → 0.20.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.
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/.env.template +9 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/CHANGELOG.md +34 -1
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/PKG-INFO +3 -1
- gflow_cli-0.20.0/docs/AGENT_UI_E2E.md +108 -0
- gflow_cli-0.20.0/docs/AGENT_UI_RECON.md +192 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/docs/CONFIGURATION.md +10 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/docs/INDEX.md +2 -1
- gflow_cli-0.20.0/docs/LIVE_VERIFICATION_v0.19.0.md +60 -0
- gflow_cli-0.20.0/docs/LIVE_VERIFICATION_v0.20.0.md +76 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/docs/SECURITY.md +20 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/docs/USAGE.md +1 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/pyproject.toml +10 -1
- gflow_cli-0.20.0/scripts/dev/analyze_agent_ui_capture.py +146 -0
- gflow_cli-0.20.0/scripts/dev/spike_patchright.py +357 -0
- gflow_cli-0.20.0/scripts/e2e/agentic_image_e2e.ps1 +74 -0
- gflow_cli-0.20.0/scripts/e2e/capture_agent_toggle.py +101 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/__init__.py +1 -1
- gflow_cli-0.20.0/src/gflow_cli/api/_engine.py +113 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/api/_retry.py +5 -3
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/api/client.py +22 -3
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/api/recaptcha.py +11 -3
- gflow_cli-0.20.0/src/gflow_cli/api/transports/drivers/__init__.py +32 -0
- gflow_cli-0.20.0/src/gflow_cli/api/transports/drivers/agentic.py +465 -0
- gflow_cli-0.20.0/src/gflow_cli/api/transports/drivers/base.py +102 -0
- gflow_cli-0.20.0/src/gflow_cli/api/transports/drivers/classic.py +224 -0
- gflow_cli-0.20.0/src/gflow_cli/api/transports/drivers/factory.py +135 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/api/transports/ui_automation.py +156 -21
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/api/transports/ui_automation_video.py +49 -22
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/cli.py +21 -1
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/config.py +25 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/errors.py +46 -0
- gflow_cli-0.20.0/tests/api/test_engine.py +95 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/api/test_retry.py +27 -0
- gflow_cli-0.20.0/tests/api/transports/conftest.py +22 -0
- gflow_cli-0.20.0/tests/api/transports/drivers/test_agentic.py +500 -0
- gflow_cli-0.20.0/tests/api/transports/drivers/test_factory.py +143 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/api/transports/test_ui_automation.py +25 -5
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/api/transports/test_ui_automation_batch.py +29 -20
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/api/transports/test_ui_automation_image_mode.py +55 -16
- gflow_cli-0.20.0/tests/cli/test_settings_validation.py +35 -0
- gflow_cli-0.20.0/tests/features/test_video_agent_ui_steps.py +447 -0
- gflow_cli-0.20.0/tests/features/video_agent_ui.feature +34 -0
- gflow_cli-0.20.0/tests/scripts/test_analyze_agent_ui_capture.py +113 -0
- gflow_cli-0.20.0/tests/smoke/__init__.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/test_config.py +27 -1
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/test_errors.py +46 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/uv.lock +25 -2
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/.claude/README.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/.claude/commands/gflow/active.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/.claude/commands/gflow/branch-review.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/.claude/commands/gflow/changelog.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/.claude/commands/gflow/check.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/.claude/commands/gflow/doc-review.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/.claude/commands/gflow/known-issues.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/.claude/commands/gflow/next.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/.claude/commands/gflow/plan.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/.claude/commands/gflow/pr-council-review.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/.claude/commands/gflow/predict.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/.claude/commands/gflow/release.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/.claude/commands/gflow/scenario.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/.claude/commands/gflow/status.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/.gitattributes +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/.github/CODEOWNERS +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/.github/copilot-instructions.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/.github/dependabot.yml +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/.github/workflows/ci.yml +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/.github/workflows/external-pr-triage.yml +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/.github/workflows/governance-advisory.yml +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/.github/workflows/governance-benchmark.yml +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/.github/workflows/release.yml +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/.gitignore +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/.gitleaks.toml +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/.pre-commit-config.yaml +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/.secrets.baseline +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/AGENTS.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/CLAUDE.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/CONFIGURATION.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/CONTRIBUTING.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/DISCLAIMER.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/GEMINI.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/KNOWN_ISSUES.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/LICENSE +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/PLAN.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/README.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/RELEASE.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/ROADMAP.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/conftest.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/docker-compose.yml +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/docs/AGENT_GUIDE.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/docs/ARCHITECTURE.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/docs/AUTHENTICATION.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/docs/CHARACTER.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/docs/CHARACTER_RECON.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/docs/DATA_LAYER.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/docs/DEBUGGING.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/docs/DEMOS.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/docs/DEVELOPMENT.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/docs/E2E_TESTING.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/docs/EXTERNAL_STORAGE.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/docs/GITHUB.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/docs/GOVERNANCE_BENCHMARK.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/docs/IMAGE_UPSCALE_RECON.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/docs/LIVE_VERIFICATION_data_layer.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/docs/LIVE_VERIFICATION_image_batch.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/docs/LIVE_VERIFICATION_v0.10.0.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/docs/LIVE_VERIFICATION_v0.11.0.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/docs/LIVE_VERIFICATION_v0.12.0.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/docs/LIVE_VERIFICATION_v0.13.0.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/docs/LIVE_VERIFICATION_v0.16.0.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/docs/LIVE_VERIFICATION_v0.17.0.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/docs/LIVE_VERIFICATION_v0.18.0.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/docs/LIVE_VERIFICATION_v0.7.0.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/docs/LIVE_VERIFICATION_v0.8.1.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/docs/LIVE_VERIFICATION_v0.9.0.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/docs/LIVE_VERIFICATION_v0.9.1.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/docs/LIVE_VERIFICATION_video_download.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/docs/MOVIE.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/docs/PROJECT_STATUS.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/docs/USER_GUIDE.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/docs/assets/demo-split-pf.gif +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/docs/assets/example-run.gif +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/docs/schemas/movie-handoff.schema.json +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/docs/superpowers/plans/2026-06-12-issue-174-library-ui-attach/PLAN.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/examples/README.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/examples/batch_from_config.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/examples/multi_prompt_t2i.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/examples/sample_config.json +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/examples/sample_prompts.txt +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/examples/single_image_t2i.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/examples/workflow_chain.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/llms.txt +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/samples/README.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/samples/captured/01_upload_image.json +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/samples/captured/02_batchAsyncGenerateVideoText.json +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/samples/captured/03_batchCheckAsyncVideoGenerationStatus.json +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/samples/captured/04_archive_workflow.json +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/samples/captured/05_createProject.json +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/samples/captured/06_batchGenerateImages.json +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/samples/captured/07_batchGenerateImages_seeded.json +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/samples/captured/08_batchAsyncGenerateVideoStartAndEndImage.json +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/samples/captured/09_batchAsyncGenerateVideoReferenceImages.json +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/samples/captured/10_batchCheckAsyncVideoGenerationStatus_successful.json +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/samples/captured/11_batchCheckAsyncVideoGenerationStatus_failed.json +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/samples/captured/12_create_scene.json +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/samples/captured/13_sceneWorkflows_update.json +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/samples/captured/14_get_scene_workflows.json +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/samples/captured/15_commit_flowWorkflow.json +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/ci/check_doc_links.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/ci/check_materiality.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/ci/check_repo_hygiene.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/debug_editor.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/debug_gen_settings.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/debug_settings.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/dev/_recording_client.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/dev/_spike_common.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/dev/active_plan.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/dev/capture_i2v_frame_slots_dom.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/dev/capture_i2v_intercept_submit.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/dev/capture_i2v_model_select_repro.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/dev/capture_i2v_post_bind_state.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/dev/capture_image_add_media_dom.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/dev/capture_locale_invariants.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/dev/cdp_drive_and_probe.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/dev/character_create_spike.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/dev/character_create_spike_v2.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/dev/dump_character_selectors.js +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/dev/make_project.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/dev/materiality_backtest.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/dev/monitor_pr_38.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/dev/patch_character.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/dev/record_flow_capture.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/dev/skillopt/README.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/dev/skillopt/harness.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/dev/skillopt/tasks.json +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/dev/spike_char_editor_dom.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/dev/spike_char_gen_capture.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/dev/spike_image_upscale_capture.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/dev/spike_image_upscale_drive.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/dev/spike_image_upscale_recaptcha_action.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/dev/spike_image_upscale_rest_probe.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/dev/spike_issue170_picker_locale_recon.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/dev/spike_issue174_library_ui_recon.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/dev/spike_movie_attach_payload.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/dev/spike_movie_entity_recon.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/dev/spike_movie_gen_capture.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/dev/spike_movie_picker_select.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/dev/spike_movie_voice_list.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/dev/spike_patch_entity.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/diag/README.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/diag/capture_flow_traffic.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/diag/memory_profile.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/diag/recaptcha_mint.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/record_demo.ps1 +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/smoke_image.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/smoke_real_chrome_image.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/smoke_video_editor.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/smoke_worker_style.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/scripts/verify_chrome_auth_viability.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/skills/README.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/skills/gflow-cli/SKILL.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/skills/plan/SKILL.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/skills/pr-council-review/SKILL.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/skills/predict/SKILL.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/skills/scenario/SKILL.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/skills/status/SKILL.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/sonar-project.properties +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/__main__.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/_cli_helpers.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/api/__init__.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/api/_sapisidhash.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/api/character.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/api/dto.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/api/image.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/api/image_upscale.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/api/routes.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/api/scene.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/api/transports/__init__.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/api/transports/_common.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/api/transports/_fingerprint.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/api/transports/base.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/api/transports/experimental/__init__.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/api/transports/experimental/bearer.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/api/transports/experimental/evaluate_fetch.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/api/transports/experimental/sapisidhash.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/api/video.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/auth/__init__.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/auth/base.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/auth/cookies.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/auth/factory.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/auth/internal_chromium.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/auth/real_chrome.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/auth/strategies.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/auth/verification.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/browser_manager.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/chain.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/chain_manifest.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/cli_character.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/cli_data.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/cli_image.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/cli_models.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/cli_movie.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/cli_run.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/cli_scene.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/cli_video.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/composition.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/data/__init__.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/data/chain_repo.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/data/migrations/0001_initial.sql +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/data/migrations/0002_add_cloud_storage.sql +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/data/migrations/0003_add_scene_tables.sql +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/data/migrations/0004_add_scene_output_path.sql +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/data/migrations/0005_add_chain_links.sql +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/data/migrations/0006_add_operations_metadata.sql +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/data/migrations/__init__.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/data/models.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/data/queries.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/data/recorder.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/data/redaction.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/data/repository.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/data/store.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/exceptions.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/image_batch.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/json_output.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/manifest.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/media.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/movie_manifest.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/observability.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/paths.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/profile_store.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/services/__init__.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/services/character_create.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/src/gflow_cli/storage.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tasks/lessons.md +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/test_assets/sample_batch.json +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/test_assets/sample_batch.tsv +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/test_assets/sample_batch_invalid.tsv +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/__init__.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/api/__init__.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/api/fixtures/character_gen_response.json +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/api/fixtures/patch_entity_response.json +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/api/test_aisandbox_auth_error.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/api/test_aisandbox_auth_headers.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/api/test_bearer_redaction.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/api/test_character.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/api/test_client.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/api/test_client_character.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/api/test_client_delete_characters.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/api/test_client_generate_character.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/api/test_client_image.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/api/test_client_launch_kwargs.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/api/test_client_patch_entity.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/api/test_client_scene.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/api/test_client_upscale.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/api/test_concurrency.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/api/test_dto.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/api/test_image.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/api/test_image_dto.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/api/test_image_upscale.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/api/test_post_json_aisandbox_auth.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/api/test_recaptcha.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/api/test_routes.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/api/test_routes_character.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/api/test_routes_scene.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/api/test_sapisidhash_helper.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/api/test_scene_models.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/api/test_video.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/api/test_video_request.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/api/transports/__init__.py +0 -0
- {gflow_cli-0.18.0/tests/cli → gflow_cli-0.20.0/tests/api/transports/drivers}/__init__.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/api/transports/test_base.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/api/transports/test_bearer.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/api/transports/test_common.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/api/transports/test_evaluate_fetch.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/api/transports/test_factory.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/api/transports/test_fingerprint.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/api/transports/test_sapisidhash.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/api/transports/test_transport_timeout.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/api/transports/test_ui_automation_video.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/api/transports/test_ui_character_editor.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/auth/strategies/test_factory.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/auth/strategies/test_strategies.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/auth/test_verification.py +0 -0
- {gflow_cli-0.18.0/tests/data → gflow_cli-0.20.0/tests/cli}/__init__.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/cli/test_cli_auth_list.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/cli/test_cli_character.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/cli/test_cli_character_create.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/cli/test_cli_data.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/cli/test_cli_image.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/cli/test_cli_image_seed_removed.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/cli/test_cli_image_selector_drift.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/cli/test_cli_image_upscale.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/cli/test_cli_models.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/cli/test_cli_movie.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/cli/test_cli_run.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/cli/test_cli_scene.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/cli/test_cli_video.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/cli/test_cli_video_chain.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/cli/test_error_handling.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/cli/test_helpers.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/cli/test_movie_manifest.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/cli/test_t2i_multi_prompt.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/composition/test_character.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/composition/test_compose_prompt.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/composition/test_handoff.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/conftest.py +0 -0
- {gflow_cli-0.18.0/tests/e2e → gflow_cli-0.20.0/tests/data}/__init__.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/data/test_chain_repo.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/data/test_find_incomplete_character.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/data/test_models.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/data/test_packaging.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/data/test_recorder.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/data/test_recorder_character.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/data/test_redaction.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/data/test_repository.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/data/test_scene_persistence.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/data/test_settings_and_errors.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/data/test_store_migrations.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/dev/test_record_flow_capture.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/dev/test_recording_client.py +0 -0
- {gflow_cli-0.18.0/tests/features → gflow_cli-0.20.0/tests/e2e}/__init__.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/e2e/conftest.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/e2e/test_aisandbox_auth_live.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/e2e/test_auth_verification_e2e.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/e2e/test_chain_e2e.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/e2e/test_character_create_e2e.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/e2e/test_data_layer_e2e.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/e2e/test_i2v_flags_e2e.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/e2e/test_image_batch_e2e.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/e2e/test_image_i2i_ref_cap_e2e.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/e2e/test_json_output_e2e.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/e2e/test_locale_selectors_e2e.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/e2e/test_scene_compose_live.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/e2e/test_transports_e2e.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/e2e/test_video_r2v_ref_cap_e2e.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/e2e/test_video_t2v_e2e.py +0 -0
- {gflow_cli-0.18.0/tests/fixtures → gflow_cli-0.20.0/tests/features}/__init__.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/features/auth.feature +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/features/auth_login.feature +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/features/character_create.feature +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/features/character_read.feature +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/features/conftest.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/features/image.feature +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/features/image_upscale.feature +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/features/locale_picker_include.feature +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/features/test_auth_login_steps.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/features/test_auth_steps.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/features/test_character_create_steps.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/features/test_character_read_steps.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/features/test_image_steps.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/features/test_image_upscale_steps.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/features/test_locale_picker_include_steps.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/features/test_step_collision_guard.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/features/test_video_chain_steps.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/features/video_chain.feature +0 -0
- {gflow_cli-0.18.0/tests/image_batch → gflow_cli-0.20.0/tests/fixtures}/__init__.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/fixtures/seeded_catalog.py +0 -0
- {gflow_cli-0.18.0/tests/integration → gflow_cli-0.20.0/tests/image_batch}/__init__.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/image_batch/test_image_manifest.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/image_batch/test_observability_events.py +0 -0
- {gflow_cli-0.18.0/tests/services → gflow_cli-0.20.0/tests/integration}/__init__.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/integration/conftest.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/integration/constants.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/integration/test_storage_gcs.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/integration/test_storage_s3.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/scripts/test_capture_locale_invariants.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/scripts/test_check_materiality.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/scripts/test_check_repo_hygiene.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/scripts/test_materiality_backtest.py +0 -0
- {gflow_cli-0.18.0/tests/smoke → gflow_cli-0.20.0/tests/services}/__init__.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/services/test_character_create_redaction.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/services/test_character_create_saga.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/services/test_character_gen_no_direct_post.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/smoke/test_profile_account_smoke.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/smoke/test_real_flow.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/test_auth.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/test_browser_manager.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/test_chain.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/test_chain_manifest.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/test_cli_data.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/test_conftest_isolation.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/test_data_queries.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/test_documentation_gate.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/test_errors_403.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/test_json_output.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/test_manifest.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/test_marker_registry.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/test_media.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/test_observability.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/test_paths.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/test_profile_store.py +0 -0
- {gflow_cli-0.18.0 → gflow_cli-0.20.0}/tests/test_smoke.py +0 -0
|
@@ -106,6 +106,15 @@
|
|
|
106
106
|
# visible window). The session is still reused from the persistent profile.
|
|
107
107
|
# GFLOW_CLI_HEADLESS=true
|
|
108
108
|
|
|
109
|
+
# Browser automation engine — playwright (default) | patchright
|
|
110
|
+
# patchright is an OPT-IN, drop-in patched Playwright (Chromium) that avoids the
|
|
111
|
+
# Runtime.enable CDP leak for stronger reCAPTCHA-Enterprise evasion on the HEADED
|
|
112
|
+
# path. It is NOT a headless unlock. Requires a separate install:
|
|
113
|
+
# pip install patchright (or: pip install 'gflow-cli[patchright]')
|
|
114
|
+
# When using system Chrome (channel=chrome, the gflow default) you do NOT need
|
|
115
|
+
# `patchright install chromium`. Unset to revert to the default playwright engine.
|
|
116
|
+
# GFLOW_CLI_BROWSER_ENGINE=playwright
|
|
117
|
+
|
|
109
118
|
# BCP-47 locale tag passed to Playwright's launch `locale=` parameter
|
|
110
119
|
# (controls Accept-Language only — Chrome's UI language is forced to
|
|
111
120
|
# en-US via the `--lang=en-US` launch arg regardless of this setting, so
|
|
@@ -7,6 +7,37 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.20.0] — 2026-06-15
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **Forced Agentic UI detection** (exit code 25): dynamically detects if the account's Flow editor page has been placed in Google Flow's new Agentic UI A/B cohort. Raises `FlowAgentUiError` and captures a diagnostic viewport screenshot (`debug_forced_agent_ui.png`) to exit cleanly and prevent CLI hangs.
|
|
15
|
+
- **Pluggable Flow UI driver strategy** (`FlowUiDriver`): the UI-automation transport now probes the editor DOM **per generation** (the cohort flaps per page load) and binds a `ClassicFlowUiDriver` or `AgenticFlowUiDriver`, so the two cohorts' selectors never share code. Classic generation is unchanged.
|
|
16
|
+
- **Agentic-cohort image generation** (validated live): when the editor is served the Agentic UI, `gflow image` drives it by encoding settings (count / aspect) into the conversational prompt directive and scraping generated assets directly from the DOM — **deduplicated by media UUID** — because the Agentic UI routes generation through a background Web Worker that defeats page-level network capture. Content-policy blocks (detected in alert/dialog regions) fail fast with `ContentPolicyError` (exit 5). Agentic **video** still raises `FlowAgentUiError` (exit 25) pending a validated scraping path.
|
|
17
|
+
- **`GFLOW_CLI_FORCE_AGENT_UI`** (testing/diagnostic opt-in): forces the agentic composer by clicking the in-input "Agent" toggle after entering the editor, so the agentic path can be exercised deterministically regardless of the server-assigned A/B cohort (which has no client-readable flag). Unset by default — the cohort is auto-detected per generation. See `docs/AGENT_UI_E2E.md`.
|
|
18
|
+
|
|
19
|
+
## [0.19.0] — 2026-06-12
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- **Opt-in Patchright browser engine** via `GFLOW_CLI_BROWSER_ENGINE=patchright`
|
|
24
|
+
(default `playwright`, unchanged). Patchright is a drop-in patched Playwright
|
|
25
|
+
(Chromium) that runs page evaluations in an isolated execution context to
|
|
26
|
+
avoid the `Runtime.enable` CDP leak, for stronger reCAPTCHA-Enterprise evasion
|
|
27
|
+
on the **headed** path. It is **not** a headless unlock and must be installed
|
|
28
|
+
separately: `pip install 'gflow-cli[patchright]'`. The default engine is
|
|
29
|
+
byte-identical to before. `gflow auth status` now reports the active engine.
|
|
30
|
+
- `BrowserEngineUnavailableError` (exit code 24): selecting `patchright` without
|
|
31
|
+
the package installed now fails with a clear `pip install patchright`
|
|
32
|
+
remediation hint instead of a raw `ImportError` hashed to a generic exit 1.
|
|
33
|
+
|
|
34
|
+
### Fixed
|
|
35
|
+
|
|
36
|
+
- An invalid `GFLOW_CLI_*` enum value (e.g. a typo'd `GFLOW_CLI_BROWSER_ENGINE`,
|
|
37
|
+
`GFLOW_CLI_PROVIDER`, or `GFLOW_CLI_LOG_LEVEL`) now fails with a clean
|
|
38
|
+
configuration error and **exit code 11** naming the offending variable, instead
|
|
39
|
+
of leaking a raw pydantic `ValidationError` traceback and exiting 1.
|
|
40
|
+
|
|
10
41
|
## [0.18.0] — 2026-06-12
|
|
11
42
|
|
|
12
43
|
### Added
|
|
@@ -1527,7 +1558,9 @@ shell-script template that branches on these codes.
|
|
|
1527
1558
|
|
|
1528
1559
|
First skeleton. Not functional end-to-end yet.
|
|
1529
1560
|
|
|
1530
|
-
[Unreleased]: https://github.com/ffroliva/gflow-cli/compare/v0.
|
|
1561
|
+
[Unreleased]: https://github.com/ffroliva/gflow-cli/compare/v0.20.0...HEAD
|
|
1562
|
+
[0.20.0]: https://github.com/ffroliva/gflow-cli/compare/v0.19.0...v0.20.0
|
|
1563
|
+
[0.19.0]: https://github.com/ffroliva/gflow-cli/compare/v0.18.0...v0.19.0
|
|
1531
1564
|
[0.18.0]: https://github.com/ffroliva/gflow-cli/compare/v0.17.0...v0.18.0
|
|
1532
1565
|
[0.17.0]: https://github.com/ffroliva/gflow-cli/compare/v0.16.0...v0.17.0
|
|
1533
1566
|
[0.16.0]: https://github.com/ffroliva/gflow-cli/compare/v0.15.1...v0.16.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gflow-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.20.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
|
|
@@ -58,6 +58,8 @@ Requires-Dist: ruff>=0.15.16; extra == 'dev'
|
|
|
58
58
|
Provides-Extra: gcs
|
|
59
59
|
Requires-Dist: gcsfs>=2024.2.0; extra == 'gcs'
|
|
60
60
|
Requires-Dist: universal-pathlib>=0.2.5; extra == 'gcs'
|
|
61
|
+
Provides-Extra: patchright
|
|
62
|
+
Requires-Dist: patchright==1.60.1; extra == 'patchright'
|
|
61
63
|
Provides-Extra: s3
|
|
62
64
|
Requires-Dist: s3fs>=2024.2.0; extra == 's3'
|
|
63
65
|
Requires-Dist: universal-pathlib>=0.2.5; extra == 's3'
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Agentic UI — End-to-End Testing Runbook
|
|
2
|
+
|
|
3
|
+
> How to **deterministically** drive and validate `gflow`'s **Agentic UI** image
|
|
4
|
+
> path against live Google Flow. Companion to
|
|
5
|
+
> [AGENT_UI_RECON.md](AGENT_UI_RECON.md) (the reverse-engineered wire/DOM spec)
|
|
6
|
+
> and [LIVE_VERIFICATION_v0.20.0.md](LIVE_VERIFICATION_v0.20.0.md) (the shipped
|
|
7
|
+
> evidence).
|
|
8
|
+
|
|
9
|
+
## The problem, and the way in
|
|
10
|
+
|
|
11
|
+
Google Flow serves the composer via a **server-assigned A/B cohort** that flaps
|
|
12
|
+
per page load and has **no client-readable flag** — so the agentic UI cannot be
|
|
13
|
+
*requested*. A naive "retry until the dice land agentic" e2e is unreliable (we
|
|
14
|
+
observed 7 classic loads in a row).
|
|
15
|
+
|
|
16
|
+
**The deterministic trigger:** the *classic* composer exposes an in-input
|
|
17
|
+
**"Agent" toggle**. Clicking it switches the composer into the **same** agentic
|
|
18
|
+
layout — confirmed live (`scripts/e2e/capture_agent_toggle.py`, 2026-06-14):
|
|
19
|
+
|
|
20
|
+
| Signal | Before toggle | After toggle |
|
|
21
|
+
|---|---|---|
|
|
22
|
+
| `crop_*` media trigger | present | **gone** |
|
|
23
|
+
| `tune` settings gear | absent | **present** |
|
|
24
|
+
| Slate composer | present | present |
|
|
25
|
+
|
|
26
|
+
So `tune` appears and `crop_*` disappears → `detect_ui_mode` then binds the
|
|
27
|
+
agentic driver. An **`expand_content`** button opens the full agent side panel.
|
|
28
|
+
|
|
29
|
+
## Repeatable procedure (deterministic)
|
|
30
|
+
|
|
31
|
+
Set **`GFLOW_CLI_FORCE_AGENT_UI=1`** and the transport clicks the Agent toggle
|
|
32
|
+
after entering the editor, forcing the agentic path on any load. The harness
|
|
33
|
+
runs that and verifies the whole chain:
|
|
34
|
+
|
|
35
|
+
```powershell
|
|
36
|
+
# From repo root. Image generation is free — safe to run repeatedly.
|
|
37
|
+
.\scripts\e2e\agentic_image_e2e.ps1 -Profile denon82 -Count 1
|
|
38
|
+
.\scripts\e2e\agentic_image_e2e.ps1 -Profile denon82 -Count 3 # also exercises dedup
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
It PASSes only when all four hold (else it prints which stage failed):
|
|
42
|
+
|
|
43
|
+
1. `agent_mode_forced activated=true` — the toggle flipped the composer to agentic;
|
|
44
|
+
2. `ui_driver.bound mode=agentic` — the agentic driver was bound;
|
|
45
|
+
3. exit 0;
|
|
46
|
+
4. exactly `-Count` `.jpg` files written (scrape → dedup-by-UUID → download).
|
|
47
|
+
|
|
48
|
+
Run it directly without the harness, too:
|
|
49
|
+
|
|
50
|
+
```powershell
|
|
51
|
+
$env:GFLOW_CLI_FORCE_AGENT_UI="1"
|
|
52
|
+
uv run gflow image t2i "a ripe banana on a white plate" --profile denon82 --json
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
> `GFLOW_CLI_FORCE_AGENT_UI` is a **testing/diagnostic opt-in**, not a user
|
|
56
|
+
> feature: it forces the agentic path even when the server served classic.
|
|
57
|
+
> Leave it unset for normal use (the cohort is then auto-detected per
|
|
58
|
+
> generation).
|
|
59
|
+
|
|
60
|
+
## Validation result (2026-06-14, profile denon82)
|
|
61
|
+
|
|
62
|
+
Forced-agentic runs validated the three assumptions that mocked tests cannot:
|
|
63
|
+
|
|
64
|
+
- **`--count 1`** → exit 0, one **420 KB JPG**. Scrape found the asset, built the
|
|
65
|
+
full-res redirect URL, the `labs.google` download resolved with session
|
|
66
|
+
cookies, and the synthetic `GeneratedImage` fields (`workflow_id=""`, `seed=0`,
|
|
67
|
+
`dimensions=(0,0)`) flowed through the save path without error.
|
|
68
|
+
- **`--count 3`** → exit 0, **3 distinct JPGs** for 3 distinct media UUIDs —
|
|
69
|
+
confirming dedup: the agentic UI's multiple `<img>` nodes per asset collapse to
|
|
70
|
+
the correct count.
|
|
71
|
+
|
|
72
|
+
Classic remains unaffected: live `gflow image t2i` and `gflow video t2v` both
|
|
73
|
+
succeed when the cohort/loads serve classic.
|
|
74
|
+
|
|
75
|
+
## Knowledge extracted (this cost real e2e runs — keep it)
|
|
76
|
+
|
|
77
|
+
Three render-timing / detection bugs were found **only** by live e2e (mocked
|
|
78
|
+
tests passed throughout); each is now fixed with a regression test:
|
|
79
|
+
|
|
80
|
+
1. **Cohort detection raced the render.** `get_ui_driver` probed instantly after
|
|
81
|
+
navigation and missed the agentic `tune` indicator (it appeared ~1.25 s
|
|
82
|
+
later), defaulting to classic → spurious `FlowAgentUiError` (exit 25). Fix:
|
|
83
|
+
`detect_ui_mode` polls (8 s window). Test: `test_detect_agentic_after_delayed_render`.
|
|
84
|
+
2. **The force-toggle raced the render too.** `_force_agent_mode` probed for the
|
|
85
|
+
Agent toggle ~0.8 s after entering the editor, before the composer rendered →
|
|
86
|
+
`agent_toggle_not_found`, silently staying classic. Fix: `wait_for` the toggle
|
|
87
|
+
(8 s) before clicking.
|
|
88
|
+
3. **Content-policy detection false-positived on static chrome.** Scanning the
|
|
89
|
+
whole `document.body` for "content policy" matched a benign page's footer/menu
|
|
90
|
+
link → every agentic generation died with a spurious `ContentPolicyError`
|
|
91
|
+
(exit 5) in ~6 s. Fix: scope the scan to `[role=alert]`/`[role=dialog]`/
|
|
92
|
+
`[aria-live=assertive]` regions only (a real block surfaces there; a
|
|
93
|
+
chat-message-only refusal is missed, which is the safe trade — a miss →
|
|
94
|
+
timeout beats a false positive that breaks every generation). Tests:
|
|
95
|
+
`test_await_images_raises_content_policy_on_explicit_text`,
|
|
96
|
+
`test_await_images_ignores_body_chrome_policy_text`.
|
|
97
|
+
|
|
98
|
+
General lesson: **any agentic-composer probe must wait for the render** — instant
|
|
99
|
+
DOM checks race Flow's deferred composer mount.
|
|
100
|
+
|
|
101
|
+
## Still owed
|
|
102
|
+
|
|
103
|
+
- A **captured positive content-policy refusal** in the agentic cohort, to widen
|
|
104
|
+
detection safely beyond alert/dialog regions (chat-message refusals are
|
|
105
|
+
currently missed → timeout). Trigger a disallowed prompt under
|
|
106
|
+
`GFLOW_CLI_FORCE_AGENT_UI=1` and capture how the refusal surfaces.
|
|
107
|
+
- **Agentic video** — only the image path is validated; video stays on the
|
|
108
|
+
classic→`FlowAgentUiError` path pending a video scraping capture.
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# Agentic Flow UI — Recon Findings
|
|
2
|
+
|
|
3
|
+
> Reverse-engineered 2026-06-14 via `gflow-agent-browser-spike` (CDP-attached
|
|
4
|
+
> real Chrome, `agent-browser@0.27.0`) against the live Flow editor on two
|
|
5
|
+
> Chrome profiles. Account-specific values (emails, project UUIDs) are redacted.
|
|
6
|
+
> Relates to issues #183 (image `t2i` exit 23) and #174 (library-UI entity drop).
|
|
7
|
+
|
|
8
|
+
## TL;DR
|
|
9
|
+
|
|
10
|
+
- Flow runs a **server-side A/B cohort** that swaps the project composer between
|
|
11
|
+
the **classic** media UI and a new **agentic** UI (chat-style "Agent").
|
|
12
|
+
- It is **volatile**: the *same* Chrome profile rendered the classic composer at
|
|
13
|
+
one point and the agentic composer later on the *same* project — confirming the
|
|
14
|
+
#174 flapping (denon82 reverting within 12h).
|
|
15
|
+
- **gflow's own dedicated automation profiles DO land in the agentic cohort**
|
|
16
|
+
(not only the user's primary profile), so #183/#174 are reproducible through
|
|
17
|
+
gflow — the feature must handle it.
|
|
18
|
+
- The cohort is **NOT discoverable from client-side state**: localStorage keys and
|
|
19
|
+
JS-visible cookies are byte-identical across both UIs, and the prime-suspect key
|
|
20
|
+
`FLOW_MAIN_PROMPT_BOX_STATE` holds only generation settings, not a mode flag.
|
|
21
|
+
→ **Detection must be runtime DOM-based**; there is no pre-navigation cookie/flag
|
|
22
|
+
to read or flip.
|
|
23
|
+
- `navigator.webdriver` is **`false`** under CDP-attached real Chrome.
|
|
24
|
+
|
|
25
|
+
## How it was captured
|
|
26
|
+
|
|
27
|
+
`gflow-agent-browser-spike` launches real Chrome with `--remote-debugging-port`
|
|
28
|
+
on a chosen profile and drives `agent-browser` over CDP. JS is passed as
|
|
29
|
+
**base64-wrapped `eval`** (`eval(atob('…'))`) — multi-line eval args get mangled
|
|
30
|
+
through `npx`, so base64 is the reliable path (this also applies to
|
|
31
|
+
`capture-agent-ui.ps1`, which must minify/encode its eval expressions). Capturing
|
|
32
|
+
the user's **primary** profile required relaunching it with the debug flag (only
|
|
33
|
+
applied on a fresh Chrome start, so all windows must be closed first), which
|
|
34
|
+
triggered a one-time Google 2-Step Verification.
|
|
35
|
+
|
|
36
|
+
## The two UIs — DOM signature
|
|
37
|
+
|
|
38
|
+
| Signal | Classic | Agentic |
|
|
39
|
+
|---|---|---|
|
|
40
|
+
| `crop_*` inline aspect/mode trigger | **present** (`crop_9_16` etc.) | **absent** |
|
|
41
|
+
| Agent pill (`/\bAgent\b/` + `div[role='textbox']`) | no | **yes** |
|
|
42
|
+
| Chat ligatures `edit_square`, `thumb_up`, `thumb_down`, `flag` | no | **yes** |
|
|
43
|
+
| Agentic ligatures `article_spark`, `apps_spark_2`, `tune` | no | **yes** |
|
|
44
|
+
| "What do you want to create?" placeholder | no | **yes** |
|
|
45
|
+
| Aspect / model / upscale controls | inline `crop_*` dropdown | moved into **"Agent settings"** (`tune`) |
|
|
46
|
+
|
|
47
|
+
Agentic ligature inventory (captured): `accessibility_new, add, add_2,
|
|
48
|
+
apps_spark_2, arrow_back, arrow_forward, arrow_forward_ios, article_spark, close,
|
|
49
|
+
content_copy, dashboard, delete, edit_square, filter_list, flag, help, image,
|
|
50
|
+
left_panel_close, menu, more_vert, movie, search, settings_2, thumb_down,
|
|
51
|
+
thumb_up, tune, warning` — and **no `crop_*`**.
|
|
52
|
+
|
|
53
|
+
## Gating mechanism (the cohort key)
|
|
54
|
+
|
|
55
|
+
Captured on agentic profiles (both the user's primary and a gflow dedicated
|
|
56
|
+
profile), the client-visible state is **identical**:
|
|
57
|
+
|
|
58
|
+
- **localStorage keys**: `FLOW_MAIN_PROMPT_BOX_STATE`, `FLOW_QUICK_SEARCH_MODE`,
|
|
59
|
+
`_grecaptcha`, `glue.CookieNotificationBar`, `nextauth.message`.
|
|
60
|
+
- **JS-visible cookies**: `EMAIL`, `_ga`, `_ga_X2GNH8R5NS` (any cohort cookie
|
|
61
|
+
would be `httpOnly`, i.e. invisible to JS — recoverable only from HAR request
|
|
62
|
+
headers, not yet captured).
|
|
63
|
+
- **`FLOW_MAIN_PROMPT_BOX_STATE`** value (agentic):
|
|
64
|
+
`{"aspectRatio":"PORTRAIT","selectedImageModelFamily":"narwhal_display","selectedVideoModelFamily":"abra","imageOrVideoMode":"IMAGE","outputsPerPrompt":1,"selectedVideoDuration":4}`
|
|
65
|
+
— generation settings only, **no cohort/mode flag**.
|
|
66
|
+
|
|
67
|
+
Because (a) the key set is identical across classic and agentic, (b) the one
|
|
68
|
+
plausible key holds only settings, and (c) the same profile flaps between states,
|
|
69
|
+
the cohort is concluded to be **server-assigned per page load**, not a readable
|
|
70
|
+
client signal. Pre-navigation detection (read a cookie before driving) is **not
|
|
71
|
+
viable**; runtime DOM classification is the only reliable detector.
|
|
72
|
+
|
|
73
|
+
## Why the existing handling fails (#183)
|
|
74
|
+
|
|
75
|
+
`_exit_agent_mode` (`ui_automation_video.py`) assumes the classic composer is
|
|
76
|
+
recoverable and keys on `_media_panel_present` (the `crop_*` trigger). In the
|
|
77
|
+
agentic cohort there is **no `crop_*`** at all (aspect moved into Agent settings),
|
|
78
|
+
so the probe never recovers, the selector cascade exhausts, and the caller raises
|
|
79
|
+
`UiSelectorDriftError` (exit 23). The recovery has nothing to recover *to*.
|
|
80
|
+
|
|
81
|
+
## Recommendation for the feature plan
|
|
82
|
+
|
|
83
|
+
1. **Detection = runtime DOM classification.** Agentic if *no `crop_*` mode
|
|
84
|
+
trigger* **and** (*Agent pill present* **or** *chat-panel ligatures present*).
|
|
85
|
+
This is what `classify_composer` / a refreshed `_exit_agent_mode` should key
|
|
86
|
+
on. Run it every generation — the cohort flaps, so no caching.
|
|
87
|
+
2. **No pre-flight cookie/flag gate** — there is no client-readable cohort signal.
|
|
88
|
+
3. **Response (decide in the feature plan, evidence now supports either):**
|
|
89
|
+
- **Detect + fail cleanly** with a typed `FlowAgentUiError` (own exit code) +
|
|
90
|
+
screenshot — low risk, immediately diagnosable. *Recommended first step.*
|
|
91
|
+
- **Drive the agentic surface** — type into the chat composer, set aspect/model
|
|
92
|
+
via the Agent-settings (`tune`) panel, handle the confirm-before-generating
|
|
93
|
+
gate. Larger; intermittently needed because the cohort flaps.
|
|
94
|
+
|
|
95
|
+
## DOM scraping validation (2026-06-14, live capture)
|
|
96
|
+
|
|
97
|
+
> Captured via `scripts/capture-media-scrape.ps1` (CDP-attached real Chrome,
|
|
98
|
+
> profile `default`, agentic cohort confirmed: `crop=false, agentPill=true,
|
|
99
|
+
> chatPanel=true`). One image generation, before/after media-DOM snapshot.
|
|
100
|
+
> Artifact: `media-scrape-agentic-20260614-202739.json`.
|
|
101
|
+
|
|
102
|
+
This resolves the open question blocking `AgenticFlowUiDriver.await_images`: **how do
|
|
103
|
+
generated assets surface in the agentic DOM, and is count-delta scraping viable?**
|
|
104
|
+
|
|
105
|
+
- **Assets render as plain remote `<img>` nodes.** After the generation: `blob=0`,
|
|
106
|
+
`data=0`, `canvas=0`, `background-image=0`. The full-res asset is a normal
|
|
107
|
+
`<img src="https://…">`. Count-delta DOM scraping **is viable** — network
|
|
108
|
+
interception is not (see HAR note below).
|
|
109
|
+
- **The src is a same-origin tRPC redirect carrying a stable media id:**
|
|
110
|
+
```
|
|
111
|
+
https://labs.google/fx/api/trpc/media.getMediaUrlRedirect?name=<uuid>[&mediaUrlType=MEDIA_URL_TYPE_THUMBNAIL]
|
|
112
|
+
```
|
|
113
|
+
It 302-redirects to the CDN blob; same-origin (`labs.google`) means the session
|
|
114
|
+
cookies authorize the download. The `name=<uuid>` is the **stable backend media id**
|
|
115
|
+
— scraped assets correlate to media identity (usable for the batch ledger / dedup).
|
|
116
|
+
- **Dedupe by `name=<uuid>`, NOT by `<img>` node count.** One generated asset surfaces
|
|
117
|
+
as **multiple** `<img>` nodes (canvas full-res + filmstrip thumbnail + chat preview,
|
|
118
|
+
each in bare and `&mediaUrlType=MEDIA_URL_TYPE_THUMBNAIL` variants). The single
|
|
119
|
+
capture produced **9 new `<img>` nodes for only 3 distinct assets** (~3× inflation).
|
|
120
|
+
`await_images` must extract the `name` query param and **count distinct UUIDs** — a
|
|
121
|
+
raw `initial_count + expected_count` node check would massively over-count.
|
|
122
|
+
- **Page-level HAR captured 0 entries.** Confirms the Web-Worker delegation: the
|
|
123
|
+
`streamChat` + media requests bypass page-level network capture entirely. **DOM
|
|
124
|
+
scraping is the only viable capture path** in the agentic cohort.
|
|
125
|
+
- **`flag` is NOT a content-policy signal.** The warn-symbol probe matched the `flag`
|
|
126
|
+
ligature 11× on a *successful* generation — `flag`/`thumb_up`/`thumb_down` are the
|
|
127
|
+
normal per-message chat affordances. Fail-fast detection must key on `warning` /
|
|
128
|
+
`error` / `block` or specific dialog/stream text, **never `flag`**. *(A deliberate
|
|
129
|
+
content-policy-refusal capture is still outstanding — no positive block sample yet.)*
|
|
130
|
+
|
|
131
|
+
## Settings via prompt, not the `tune` popover (agentic acts MCP-like)
|
|
132
|
+
|
|
133
|
+
The agentic composer behaves like a conversational agent: generation parameters that the
|
|
134
|
+
classic UI exposes as discrete controls — **output count** (1 / 4 images), **video
|
|
135
|
+
duration** (4/6/8/10 s), and likely **aspect ratio** and **model** — can be expressed in
|
|
136
|
+
**natural language inside the prompt** (e.g. `Generate 4 images of <prompt> in 16:9`), and
|
|
137
|
+
the agent resolves the selection itself.
|
|
138
|
+
|
|
139
|
+
Implications for `AgenticFlowUiDriver`:
|
|
140
|
+
- **`configure_settings` becomes optional.** Encoding settings as a directive prompt
|
|
141
|
+
avoids driving the fragile `tune` → Radix-popover dropdowns — the most drift-prone
|
|
142
|
+
surface on a volatile A/B UI. Prefer prompt-encoding; treat popover automation as a
|
|
143
|
+
fallback only if prompt-steering proves unreliable.
|
|
144
|
+
- **The agent *interprets* the request → verify, don't trust.** It may produce a
|
|
145
|
+
different count or ignore a duration. This is precisely why scrape-and-dedup-by-UUID
|
|
146
|
+
is load-bearing: request N → poll until N distinct new media UUIDs → if the produced
|
|
147
|
+
count differs, raise a typed mismatch rather than silently returning the wrong set.
|
|
148
|
+
- **Compose carefully** so settings directives aren't read as subject text (e.g. a
|
|
149
|
+
`Generate {n} image(s){aspect}: {prompt}` template).
|
|
150
|
+
|
|
151
|
+
## Open follow-ups
|
|
152
|
+
|
|
153
|
+
- **Content-policy block sample (outstanding):** capture a deliberate-refusal generation
|
|
154
|
+
in the agentic cohort to learn how a block surfaces (chat message vs. dialog vs.
|
|
155
|
+
symbol) — needed to design `await_images` fail-fast. The `flag` symbol is ruled out.
|
|
156
|
+
|
|
157
|
+
## The Wire & API Endpoint (Resolved via Issue #183 Reporter)
|
|
158
|
+
|
|
159
|
+
The network request logs from the agentic UI have been resolved. The new UX bypasses the direct media generation endpoints (such as `batchGenerateImages`) and routes all generation requests through Google's conversational flow creation agent.
|
|
160
|
+
|
|
161
|
+
- **Endpoint**: `POST https://aisandbox-pa.googleapis.com/v1/flowCreationAgent:streamChat?alt=sse`
|
|
162
|
+
- **Transport**: Server-Sent Events (SSE) stream, preceded by a CORS OPTIONS preflight.
|
|
163
|
+
- **Request Payload Structure**:
|
|
164
|
+
```json
|
|
165
|
+
{
|
|
166
|
+
"agentSessionId": "<uuid>",
|
|
167
|
+
"agentClientContext": {
|
|
168
|
+
"projectId": "projects/<projectUuid>",
|
|
169
|
+
"clientSessionId": ";<epochMillis>",
|
|
170
|
+
"recaptchaContext": {
|
|
171
|
+
"token": "<recaptcha_token>",
|
|
172
|
+
"applicationType": "RECAPTCHA_APPLICATION_TYPE_WEB"
|
|
173
|
+
},
|
|
174
|
+
"turnNumber": 2
|
|
175
|
+
},
|
|
176
|
+
"userMessage": {
|
|
177
|
+
"userPrompt": {
|
|
178
|
+
"parts": [
|
|
179
|
+
{
|
|
180
|
+
"text": "a red apple"
|
|
181
|
+
}
|
|
182
|
+
]
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Why page-level CDP/Playwright instrumentation recorded 0 entries
|
|
189
|
+
During local capture sessions, page-level request event listeners repeatedly recorded `0` API request entries. This occurred because the agentic UI delegates all `streamChat` network requests to a background **Web/Service Worker** target (`type: worker`). Worker-initiated requests completely bypass page-level network event registration in Playwright/CDP and require worker-level target attachments to capture.
|
|
190
|
+
|
|
191
|
+
### Impact on issue #174 (referenceEntities)
|
|
192
|
+
In the agentic UI, the prompt box is a conversational Slate.js editor. Because generation is driven by the conversation agent stream rather than direct REST inputs, staging library entities (e.g. characters) behaves differently. For the initial implementation phase, driving or injecting into `flowCreationAgent:streamChat` is out of scope. The recommended direction is **Runtime DOM detection + Fail Cleanly** with `FlowAgentUiError` (exit code 23) to prevent automation lockups while the cohort is active.
|
|
@@ -190,6 +190,16 @@ GFLOW_CLI_HISTORY_PROMPTS=redacted gflow image t2i "confidential brief"
|
|
|
190
190
|
**Default:** `true`
|
|
191
191
|
**When to flip to `false`:** if reCAPTCHA Enterprise refuses to mint tokens (Google's bot-detection sometimes refuses headless Chromium but accepts a visible window). Set to `false` and re-run; the browser will appear during generation but the session is still reused from the persistent profile.
|
|
192
192
|
|
|
193
|
+
### `GFLOW_CLI_BROWSER_ENGINE`
|
|
194
|
+
|
|
195
|
+
**What:** Selects the browser-automation engine backing the Playwright API.
|
|
196
|
+
**Values:** `playwright` | `patchright`
|
|
197
|
+
**Default:** `playwright`
|
|
198
|
+
**What `patchright` is:** an opt-in, drop-in patched Playwright (Chromium) that runs page evaluations in an isolated execution context to avoid the `Runtime.enable` CDP leak, for stronger reCAPTCHA-Enterprise evasion on the **headed** path. It is **not** a headless unlock — Google still detects headless Chromium regardless of engine.
|
|
199
|
+
**Install:** `pip install 'gflow-cli[patchright]'` (or `pip install patchright`). Selecting `patchright` without it installed fails fast with exit code 24 and a pip remediation hint. When using system Chrome (the gflow default, `channel=chrome`) you do **not** need `patchright install chromium`.
|
|
200
|
+
**Reverting:** unset the variable (or set `playwright`) — the default path is byte-identical to a build without this feature, with no profile migration.
|
|
201
|
+
**Security note:** the `patchright` extra ships a *patched Chromium driver* that handles your live Google session cookies; it is exact-pinned and treated as a security-review-required dependency. See [SECURITY.md § Dependencies](SECURITY.md).
|
|
202
|
+
|
|
193
203
|
### `GFLOW_CLI_LOCALE`
|
|
194
204
|
|
|
195
205
|
**What:** BCP-47 locale tag passed to Playwright's `launch_persistent_context(locale=...)` — controls the `Accept-Language` HTTP header only.
|
|
@@ -35,6 +35,7 @@ Welcome to the `gflow-cli` documentation. This index is the routing layer: it te
|
|
|
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
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) |
|
|
38
|
+
| **[docs/AGENT_UI_RECON.md](AGENT_UI_RECON.md)** | Agentic Flow UI cohort: classic-vs-agentic DOM signature, server-side + volatile (flapping) A/B gating (no client-readable flag), why `_exit_agent_mode` fails, runtime-DOM-detection recommendation | Touching `_exit_agent_mode` / composer mode-switch, diagnosing exit 23 selector drift, or understanding Flow's agentic-UI A/B (#183/#174) |
|
|
38
39
|
| **[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?" |
|
|
39
40
|
| **[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 |
|
|
40
41
|
| **[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 |
|
|
@@ -111,7 +112,7 @@ Slash commands for Claude Code, stored in `.claude/commands/gflow/`. All prefixe
|
|
|
111
112
|
**"A gflow command hangs / fails — where do I start?"** → [DEBUGGING § Quick reference](DEBUGGING.md#quick-reference)
|
|
112
113
|
**"Flow's UI broke a selector — how do I diagnose it?"** → [DEBUGGING § Inspecting Flow's live UI](DEBUGGING.md#inspecting-flows-live-ui)
|
|
113
114
|
**"What does each `ui_automation.*` log event mean?"** → [DEBUGGING § Listener & HTTP-layer debugging](DEBUGGING.md#listener--http-layer-debugging)
|
|
114
|
-
**"What was actually live-verified for the latest release?"** → latest: [LIVE_VERIFICATION_v0.
|
|
115
|
+
**"What was actually live-verified for the latest release?"** → latest: [LIVE_VERIFICATION_v0.20.0](LIVE_VERIFICATION_v0.20.0.md) (Agentic UI image generation on `denon82`: classic `image t2i` + `video t2v` unaffected, and forced-agentic `image t2i` count 1 & count 3 with **live dedup-by-UUID** under `GFLOW_CLI_FORCE_AGENT_UI=1`; 5-layer ledger; natural-cohort agentic load + content-policy refusal recorded as not-verified-this-cycle). v0.14.0 / v0.15.0 / v0.15.1 shipped without a dedicated doc (CI + the v0.13.0 baseline). Prior: [v0.19.0](LIVE_VERIFICATION_v0.19.0.md) · [v0.18.0](LIVE_VERIFICATION_v0.18.0.md) · [v0.17.0](LIVE_VERIFICATION_v0.17.0.md) · [v0.16.0](LIVE_VERIFICATION_v0.16.0.md) · [v0.13.0](LIVE_VERIFICATION_v0.13.0.md) · [v0.12.0](LIVE_VERIFICATION_v0.12.0.md) · [v0.11.0](LIVE_VERIFICATION_v0.11.0.md) · [v0.10.0](LIVE_VERIFICATION_v0.10.0.md) · [v0.9.1](LIVE_VERIFICATION_v0.9.1.md) · [v0.9.0](LIVE_VERIFICATION_v0.9.0.md) · [v0.8.1](LIVE_VERIFICATION_v0.8.1.md) · [v0.7.0](LIVE_VERIFICATION_v0.7.0.md)
|
|
115
116
|
**"Where is the reverse-engineered wire protocol for a feature?"** → the `*_RECON.md` design docs: [CHARACTER_RECON](CHARACTER_RECON.md) (Flow character entity protocol), [IMAGE_UPSCALE_RECON](IMAGE_UPSCALE_RECON.md) (`/v1/flow/upsampleImage` wire). Naming convention: one `<FEATURE>_RECON.md` per reverse-engineered surface, kept as the durable spec after the feature ships.
|
|
116
117
|
**"What was live-verified for the data layer (PR #58)?"** → [LIVE_VERIFICATION_data_layer](LIVE_VERIFICATION_data_layer.md) — 1 Imagen + 1 Veo credit on denon82, 6-layer ledger (file + magic + Pillow + DB rows + CLI round-trip + structlog)
|
|
117
118
|
**"What was live-verified for the video-download feature (#29)?"** → [LIVE_VERIFICATION_video_download](LIVE_VERIFICATION_video_download.md)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Live Verification — v0.19.0
|
|
2
|
+
|
|
3
|
+
Feature under test: **opt-in Patchright browser engine** (`GFLOW_CLI_BROWSER_ENGINE=patchright`).
|
|
4
|
+
Verified 2026-06-12 against live Google Flow on a real authenticated profile. Image
|
|
5
|
+
generation is credit-free, so the end-to-end run cost **$0**.
|
|
6
|
+
|
|
7
|
+
## Layer 1 — file count
|
|
8
|
+
|
|
9
|
+
One image produced by `gflow image t2i` through the integrated patchright path:
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
scripts/dev/_spike_out/patch_verify/images/2026-06-12/6cb21d9e-e4f9-40ba-897c-eb25c6333892_1.jpg
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
(written to a throwaway `GFLOW_CLI_OUTPUT_DIR`, since cleaned up).
|
|
16
|
+
|
|
17
|
+
## Layer 2 — magic bytes
|
|
18
|
+
|
|
19
|
+
`b[:3] == FF D8 FF` → **valid JPEG**. Size 723,911 bytes.
|
|
20
|
+
|
|
21
|
+
## Layer 3 — dimensions / shape
|
|
22
|
+
|
|
23
|
+
Pillow: `format=JPEG dims=(768, 1376)` → correct **9:16** portrait (the default aspect).
|
|
24
|
+
|
|
25
|
+
## Layer 4 — structlog invariants
|
|
26
|
+
|
|
27
|
+
From the live run (`correlation_id=40877345-0af4-472f-966a-94f88112f0d9`):
|
|
28
|
+
|
|
29
|
+
- `browser.engine_selected engine=patchright` — the **settings resolver** selected
|
|
30
|
+
patchright (the real wired path, not a test monkeypatch).
|
|
31
|
+
- `ui_automation.batch_request_body` — prompt submitted (`bytes=5054`).
|
|
32
|
+
- `ui_automation.batch_response_seen status=200` and
|
|
33
|
+
`ui_automation.batch_response_captured status=200` — `batchGenerateImages`
|
|
34
|
+
returned 200 and the response body parsed under patchright.
|
|
35
|
+
|
|
36
|
+
## Layer 5 — user-confirmable artifact
|
|
37
|
+
|
|
38
|
+
A studio-lit image of "a small green cactus in a terracotta pot" — visually
|
|
39
|
+
inspectable, matches the prompt.
|
|
40
|
+
|
|
41
|
+
## Supporting checks (same release)
|
|
42
|
+
|
|
43
|
+
| Check | Evidence | Result |
|
|
44
|
+
|---|---|---|
|
|
45
|
+
| Default path byte-identical | client-level `navigator.webdriver` read through real `FlowApiClient`: `playwright → None` (masked), `patchright → False` (both non-bot) | ✅ default still stealthed |
|
|
46
|
+
| Missing-dependency UX | `GFLOW_CLI_BROWSER_ENGINE=patchright` with patchright uninstalled → `image t2i` | ✅ **exit 24**, `BrowserEngineUnavailableError` + `pip install patchright` hint, no traceback, no browser launched |
|
|
47
|
+
| Invalid enum setting | `GFLOW_CLI_BROWSER_ENGINE=patchwright` (and `GFLOW_CLI_PROVIDER=bogus`) | ✅ **exit 11**, clean "Configuration error" naming the variable, no raw pydantic traceback |
|
|
48
|
+
| reCAPTCHA mint under patchright | `TokenMinter.mint` with `isolated_context=False` | ✅ returns a real token (the predicted isolated-world break, fixed) |
|
|
49
|
+
| Retry across engine boundary | patchright's distinct `TimeoutError` through the real retry policy | ✅ retried (unit test) |
|
|
50
|
+
|
|
51
|
+
## Premise still pending (documented, not a blocker)
|
|
52
|
+
|
|
53
|
+
The *value* premise — does patchright reduce real 403s vs Playwright — could **not**
|
|
54
|
+
be measured this cycle: the documented hot profile (`denon82`) had cooled, so plain
|
|
55
|
+
Playwright also returned 200 (no 403 to flip). A faithful 403 cannot be emulated
|
|
56
|
+
locally (a route-injected 403 is engine-blind; inflating real WAF heat risks the
|
|
57
|
+
account). The feature ships as a **proven non-breaking, opt-in escape-hatch** with
|
|
58
|
+
the default unchanged. Re-fire trigger: the instant any profile actually 403s, run
|
|
59
|
+
`scripts/dev/spike_patchright.py --profile <hot> --engines playwright,patchright`
|
|
60
|
+
to capture the `WAF_403 → OK_200` differential.
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Live verification — v0.20.0 (Agentic UI driver)
|
|
2
|
+
|
|
3
|
+
> Hand-run against the real Google Flow editor on profile `denon82`. v0.20.0
|
|
4
|
+
> ships the pluggable `FlowUiDriver` strategy and **Agentic-cohort image
|
|
5
|
+
> generation**. The agentic cohort is server-assigned and cannot be forced from
|
|
6
|
+
> the client, so the deterministic trigger `GFLOW_CLI_FORCE_AGENT_UI=1` (clicks
|
|
7
|
+
> the in-input "Agent" toggle) is used to exercise the agentic path. Image
|
|
8
|
+
> generation is credit-free.
|
|
9
|
+
|
|
10
|
+
## Environment
|
|
11
|
+
|
|
12
|
+
| | |
|
|
13
|
+
|---|---|
|
|
14
|
+
| Branch | `feature/agentic-ui-driver` → `develop` (PR [#189](https://github.com/ffroliva/gflow-cli/pull/189)) |
|
|
15
|
+
| Version | `0.19.0` working tree (pre-v0.20.0 bump) |
|
|
16
|
+
| Profile | `denon82` (real-browser Chrome) |
|
|
17
|
+
| Profile dir | `C:\Users\ffrol\AppData\Local\ffroliva\gflow-cli\profile_denon82` |
|
|
18
|
+
| Date | 2026-06-14 |
|
|
19
|
+
| Transport | `ui_automation` |
|
|
20
|
+
| Engine | `playwright` |
|
|
21
|
+
|
|
22
|
+
## How to reproduce
|
|
23
|
+
|
|
24
|
+
```pwsh
|
|
25
|
+
$env:PYTHONUTF8=1
|
|
26
|
+
# Classic cohort (auto-detected):
|
|
27
|
+
uv run gflow image t2i "a red apple on a rustic wooden table" --profile denon82 --aspect 16:9 --json
|
|
28
|
+
uv run gflow video t2v "a golden sunset over calm ocean waves" --profile denon82 --aspect 16:9 --duration 4 --json
|
|
29
|
+
|
|
30
|
+
# Agentic cohort, forced deterministically (clicks the Agent toggle):
|
|
31
|
+
$env:GFLOW_CLI_FORCE_AGENT_UI="1"
|
|
32
|
+
uv run gflow image t2i "a single ripe banana on a white plate" --profile denon82 --aspect 16:9 --json
|
|
33
|
+
uv run gflow image t2i "three colorful macarons on a marble surface" --profile denon82 --count 3 --json
|
|
34
|
+
|
|
35
|
+
# Deterministic harness (verifies forced→agentic→scrape→download, asserts file count):
|
|
36
|
+
.\scripts\e2e\agentic_image_e2e.ps1 -Profile denon82 -Count 3
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Evidence ledger (5-layer)
|
|
40
|
+
|
|
41
|
+
Each run: **file written** + **magic bytes (JPEG/MP4)** + **dimensions/size** +
|
|
42
|
+
**structlog invariants** + **user-confirmable artifact (media UUID + path)**.
|
|
43
|
+
|
|
44
|
+
| # | Path | Cohort | Result | Media UUID(s) | Bytes | Key structlog invariants |
|
|
45
|
+
|---|---|---|---|---|---|---|
|
|
46
|
+
| 1 | image t2i (classic) | classic | exit 0, 1 JPG | `f3e529ca…` | 647 KB | `ui_driver.bound mode=classic`; `crop_16_9` matched; `batchGenerateImages` 200 captured |
|
|
47
|
+
| 2 | video t2v (classic) | classic | exit 0, 1 MP4 | `433a92d5…` | 6.33 MB | `video_mode_entered`; `poll_terminal STATUS_SUCCESSFUL`; `video_saved` |
|
|
48
|
+
| 3 | image t2i (forced agentic, count 1) | agentic | exit 0, 1 JPG | `3722b716…` | 420 KB | `agent_mode_forced activated=true`; `ui_driver.bound mode=agentic`; full-res `getMediaUrlRedirect` URL |
|
|
49
|
+
| 4 | image t2i (forced agentic, count 3) | agentic | exit 0, **3 distinct** JPGs | `0bfb4127…`, `2f5c5f9e…`, `cd8f4781…` | 596 / 590 / 629 KB | `mode=agentic`; **dedup**: multiple `<img>` nodes → 3 distinct media UUIDs |
|
|
50
|
+
|
|
51
|
+
## What this proves
|
|
52
|
+
|
|
53
|
+
- **Classic path unaffected** by the driver refactor (rows 1–2): image + video
|
|
54
|
+
generate and download end-to-end through the new `ClassicFlowUiDriver`.
|
|
55
|
+
- **Agentic image generation works end-to-end** (rows 3–4): the Agent toggle
|
|
56
|
+
flips the composer to agentic, the agentic driver encodes settings into the
|
|
57
|
+
prompt, types into the Slate composer, **scrapes** the generated assets from
|
|
58
|
+
the DOM (page-level network capture is dead in this cohort — Web-Worker
|
|
59
|
+
delegated), **deduplicates by media UUID**, and downloads via the same-origin
|
|
60
|
+
`labs.google/.../getMediaUrlRedirect` redirect (allow-listed for download).
|
|
61
|
+
- **Synthetic `GeneratedImage` fields** (`seed=0`, `workflow_id=""`,
|
|
62
|
+
`dimensions=(0,0)`) flow through the save/report path without error.
|
|
63
|
+
|
|
64
|
+
## Not verified this cycle (recorded honestly)
|
|
65
|
+
|
|
66
|
+
- **A natural (non-forced) agentic load end-to-end:** the cohort flapped to
|
|
67
|
+
classic during the windows tested; the forced path exercises identical driver
|
|
68
|
+
code, but a naturally-served agentic load was not captured generating.
|
|
69
|
+
- **A positive content-policy refusal in the agentic cohort:** detection is
|
|
70
|
+
scoped to alert/dialog regions pending a captured sample (a chat-message-only
|
|
71
|
+
refusal currently misses → timeout). See `docs/AGENT_UI_E2E.md`.
|
|
72
|
+
- **Agentic video:** out of scope — raises `FlowAgentUiError` (exit 25) pending a
|
|
73
|
+
video scraping capture.
|
|
74
|
+
|
|
75
|
+
See [docs/AGENT_UI_E2E.md](AGENT_UI_E2E.md) for the repeatable procedure and the
|
|
76
|
+
three render-race / detection bugs this live testing surfaced and fixed.
|
|
@@ -201,6 +201,26 @@ Audited with `pip-audit` in CI on every push. Major dependency surface:
|
|
|
201
201
|
|
|
202
202
|
No transitive dep with known CVEs at the time of v0.1.0 scaffold.
|
|
203
203
|
|
|
204
|
+
### Optional `patchright` engine — elevated supply-chain trust
|
|
205
|
+
|
|
206
|
+
The `gflow-cli[patchright]` extra (opt-in via `GFLOW_CLI_BROWSER_ENGINE=patchright`)
|
|
207
|
+
installs **Patchright**, a single-maintainer package that ships a **patched
|
|
208
|
+
Chromium driver**. Because that driver *is* the browser process that loads your
|
|
209
|
+
real Google session — it has direct access to session cookies, the OAuth Bearer
|
|
210
|
+
token, and SAPISID — it carries a higher blast radius than an ordinary Python
|
|
211
|
+
dependency: a compromised release could exfiltrate the full Google session.
|
|
212
|
+
|
|
213
|
+
Controls:
|
|
214
|
+
|
|
215
|
+
- **Optional-only** — never in the base `dependencies`; the default install pulls
|
|
216
|
+
neither the package nor its driver.
|
|
217
|
+
- **Exact-pinned** (`patchright==1.60.1`) so a release cannot silently move the
|
|
218
|
+
browser binary underneath you.
|
|
219
|
+
- **Security-review-required on bump** — a Patchright version change is treated
|
|
220
|
+
like a browser-binary change, NOT a routine dependabot auto-merge.
|
|
221
|
+
- Default engine is `playwright` (Microsoft, security-reviewed); patchright is an
|
|
222
|
+
experiment you opt into per the trade-off above.
|
|
223
|
+
|
|
204
224
|
## Reporting
|
|
205
225
|
|
|
206
226
|
| Issue type | How |
|
|
@@ -1031,6 +1031,7 @@ shell scripts can branch on the failure mode without parsing stderr.
|
|
|
1031
1031
|
| `21` | `ChainPartialError` | A video chain stopped mid-way; earlier links completed | Resume from the last completed link shown in the error |
|
|
1032
1032
|
| `22` | `UpscaleUnavailableError` | 4K upscale is gated to Flow **Ultra** accounts (HTTP 403) | Use `--scale 2k`, or upgrade the Flow plan |
|
|
1033
1033
|
| `23` | `UiSelectorDriftError` | A Flow editor control could not be located — Google changed the frontend (issue #183) | Update gflow-cli; file a bug with the probe name + debug screenshot from the error message |
|
|
1034
|
+
| `24` | `BrowserEngineUnavailableError` | `GFLOW_CLI_BROWSER_ENGINE=patchright` but the engine is not installed | `pip install 'gflow-cli[patchright]'`, or unset `GFLOW_CLI_BROWSER_ENGINE` |
|
|
1034
1035
|
| `130`| SIGINT | User-interrupted (Ctrl-C) | — |
|
|
1035
1036
|
|
|
1036
1037
|
**Exit code 16 — data store / migration error.** Fires when:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "gflow-cli"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.20.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" }
|
|
@@ -58,6 +58,15 @@ s3 = [
|
|
|
58
58
|
chain = [
|
|
59
59
|
"av>=12",
|
|
60
60
|
]
|
|
61
|
+
# Patchright engine extra — opt-in, drop-in patched Playwright (Chromium) that
|
|
62
|
+
# avoids the Runtime.enable CDP leak on the headed path. Selected via
|
|
63
|
+
# GFLOW_CLI_BROWSER_ENGINE=patchright. Ships a PATCHED Chromium driver that
|
|
64
|
+
# handles live Google session cookies/Bearer/SAPISID, so it is EXACT-pinned and
|
|
65
|
+
# treated as a security-review-required bump (NOT a dependabot auto-merge) —
|
|
66
|
+
# see SECURITY.md. Not a headless unlock.
|
|
67
|
+
patchright = [
|
|
68
|
+
"patchright==1.60.1",
|
|
69
|
+
]
|
|
61
70
|
|
|
62
71
|
[project.scripts]
|
|
63
72
|
gflow = "gflow_cli.cli:main"
|