gflow-cli 0.32.0__tar.gz → 0.33.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.32.0 → gflow_cli-0.33.0}/.env.template +15 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/CHANGELOG.md +35 -1
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/KNOWN_ISSUES.md +12 -3
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/PKG-INFO +1 -1
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/CONFIGURATION.md +8 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/DEBUGGING.md +32 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/INDEX.md +1 -1
- gflow_cli-0.33.0/docs/LIVE_VERIFICATION_v0.32.1.md +46 -0
- gflow_cli-0.33.0/docs/LIVE_VERIFICATION_v0.33.0.md +53 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/USAGE.md +7 -1
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/pyproject.toml +1 -1
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/__init__.py +1 -1
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/api/_engine.py +61 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/api/client.py +51 -18
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/api/image_upscale.py +4 -7
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/api/transports/drivers/agentic.py +17 -4
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/api/transports/experimental/evaluate_fetch.py +3 -3
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/api/transports/ui_automation.py +26 -11
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/api/transports/ui_automation_video.py +885 -52
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/api/video.py +11 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/cli_image.py +51 -18
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/cli_instructions.py +3 -9
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/cli_video.py +73 -1
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/config.py +58 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/data/queries.py +37 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/data/recorder.py +3 -3
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/errors.py +12 -2
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/image_batch.py +57 -8
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/mcp/tools.py +3 -7
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/test_concurrency.py +151 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/transports/drivers/test_agentic.py +84 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/transports/test_ui_automation_video.py +1209 -7
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/cli/test_cli_video.py +135 -0
- gflow_cli-0.33.0/tests/image_batch/test_jitter_config.py +253 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/mcp/test_cli_parity.py +6 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/test_data_queries.py +18 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/uv.lock +1 -1
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/.claude/README.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/.claude/commands/gflow/active.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/.claude/commands/gflow/branch-review.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/.claude/commands/gflow/changelog.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/.claude/commands/gflow/check.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/.claude/commands/gflow/doc-review.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/.claude/commands/gflow/issue-assessment.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/.claude/commands/gflow/issue-resolve.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/.claude/commands/gflow/known-issues.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/.claude/commands/gflow/next.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/.claude/commands/gflow/plan.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/.claude/commands/gflow/pr-council-review.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/.claude/commands/gflow/predict.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/.claude/commands/gflow/release.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/.claude/commands/gflow/scenario.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/.claude/commands/gflow/sonar.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/.claude/commands/gflow/status.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/.gitattributes +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/.github/CODEOWNERS +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/.github/copilot-instructions.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/.github/dependabot.yml +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/.github/workflows/ci.yml +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/.github/workflows/external-pr-triage.yml +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/.github/workflows/governance-advisory.yml +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/.github/workflows/governance-benchmark.yml +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/.github/workflows/main-base-guard.yml +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/.github/workflows/release.yml +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/.gitignore +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/.gitleaks.toml +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/.pre-commit-config.yaml +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/.secrets.baseline +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/AGENTS.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/CLAUDE.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/CONFIGURATION.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/CONTRIBUTING.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/DISCLAIMER.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/GEMINI.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/LICENSE +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/PLAN.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/README.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/RELEASE.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/ROADMAP.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/conftest.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docker-compose.yml +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/AGENT_GUIDE.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/AGENT_UI_E2E.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/AGENT_UI_RECON.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/ARCHITECTURE.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/AUTHENTICATION.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/CHARACTER.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/CHARACTER_RECON.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/DATA_LAYER.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/DEMOS.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/DEVELOPMENT.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/E2E_TESTING.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/EXTERNAL_STORAGE.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/GITHUB.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/GOVERNANCE_BENCHMARK.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/IMAGE_UPSCALE_RECON.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/INSTRUCTIONS.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/LIVE_VERIFICATION_data_layer.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/LIVE_VERIFICATION_image_batch.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/LIVE_VERIFICATION_v0.10.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/LIVE_VERIFICATION_v0.11.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/LIVE_VERIFICATION_v0.12.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/LIVE_VERIFICATION_v0.13.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/LIVE_VERIFICATION_v0.16.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/LIVE_VERIFICATION_v0.17.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/LIVE_VERIFICATION_v0.18.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/LIVE_VERIFICATION_v0.19.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/LIVE_VERIFICATION_v0.20.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/LIVE_VERIFICATION_v0.20.1.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/LIVE_VERIFICATION_v0.21.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/LIVE_VERIFICATION_v0.22.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/LIVE_VERIFICATION_v0.23.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/LIVE_VERIFICATION_v0.24.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/LIVE_VERIFICATION_v0.25.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/LIVE_VERIFICATION_v0.26.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/LIVE_VERIFICATION_v0.27.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/LIVE_VERIFICATION_v0.27.1.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/LIVE_VERIFICATION_v0.28.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/LIVE_VERIFICATION_v0.29.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/LIVE_VERIFICATION_v0.30.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/LIVE_VERIFICATION_v0.31.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/LIVE_VERIFICATION_v0.32.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/LIVE_VERIFICATION_v0.7.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/LIVE_VERIFICATION_v0.8.1.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/LIVE_VERIFICATION_v0.9.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/LIVE_VERIFICATION_v0.9.1.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/LIVE_VERIFICATION_video_download.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/MCP.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/MOVIE.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/PROJECT_STATUS.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/PROMPT_EXPANSION.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/SECURITY.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/TOOLS.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/USER_GUIDE.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/assets/demo-split-pf.gif +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/assets/example-run.gif +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/assets/examples.webp +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/medium_tutorial.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/schemas/movie-handoff.schema.json +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/sonar-cleanup-tracker.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/superpowers/plans/2026-06-12-issue-174-library-ui-attach/PLAN.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/superpowers/plans/2026-06-22-mcp-server/PLAN.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/superpowers/plans/2026-06-22-mcp-server/PREDICT.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/superpowers/plans/2026-06-22-mcp-server/SCENARIO.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/superpowers/plans/2026-06-22-prompt-expansion/PLAN.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/superpowers/plans/2026-06-22-prompt-expansion/PREDICT.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/superpowers/plans/2026-06-22-prompt-expansion/SCENARIO.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/superpowers/plans/2026-06-24-gflow-studio-scaffold/PLAN.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/superpowers/plans/2026-06-24-rest-api-layer/PLAN.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/superpowers/plans/2026-06-26-mcp-e2e-test/PLAN.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/superpowers/plans/2026-07-09-camoufox-adoption/PLAN.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/superpowers/research/2026-06-27-tool-abstraction-evaluation.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/superpowers/specs/2026-06-29-issue-assessment-workflow-design.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/docs/superpowers/spikes/2026-07-09-camoufox-waf-403.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/examples/README.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/examples/batch_from_config.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/examples/multi_prompt_t2i.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/examples/sample_config.json +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/examples/sample_prompts.txt +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/examples/single_image_t2i.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/examples/workflow_chain.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/llms.txt +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/samples/README.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/samples/captured/01_upload_image.json +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/samples/captured/02_batchAsyncGenerateVideoText.json +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/samples/captured/03_batchCheckAsyncVideoGenerationStatus.json +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/samples/captured/04_archive_workflow.json +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/samples/captured/05_createProject.json +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/samples/captured/06_batchGenerateImages.json +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/samples/captured/07_batchGenerateImages_seeded.json +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/samples/captured/08_batchAsyncGenerateVideoStartAndEndImage.json +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/samples/captured/09_batchAsyncGenerateVideoReferenceImages.json +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/samples/captured/10_batchCheckAsyncVideoGenerationStatus_successful.json +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/samples/captured/11_batchCheckAsyncVideoGenerationStatus_failed.json +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/samples/captured/12_create_scene.json +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/samples/captured/13_sceneWorkflows_update.json +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/samples/captured/14_get_scene_workflows.json +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/samples/captured/15_commit_flowWorkflow.json +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/ci/check_doc_links.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/ci/check_materiality.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/ci/check_repo_hygiene.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/debug_editor.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/debug_gen_settings.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/debug_settings.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/dev/_recording_client.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/dev/_spike_common.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/dev/active_plan.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/dev/analyze_agent_ui_capture.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/dev/capture_i2v_frame_slots_dom.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/dev/capture_i2v_intercept_submit.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/dev/capture_i2v_model_select_repro.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/dev/capture_i2v_post_bind_state.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/dev/capture_image_add_media_dom.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/dev/capture_locale_invariants.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/dev/cdp_drive_and_probe.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/dev/character_create_spike.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/dev/character_create_spike_v2.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/dev/dump_character_selectors.js +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/dev/make_project.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/dev/materiality_backtest.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/dev/monitor_pr_38.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/dev/patch_character.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/dev/skillopt/README.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/dev/skillopt/harness.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/dev/skillopt/tasks.json +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/dev/spike_char_editor_dom.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/dev/spike_char_gen_capture.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/dev/spike_image_upscale_capture.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/dev/spike_image_upscale_drive.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/dev/spike_image_upscale_recaptcha_action.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/dev/spike_image_upscale_rest_probe.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/dev/spike_issue170_picker_locale_recon.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/dev/spike_issue174_library_ui_recon.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/dev/spike_movie_attach_payload.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/dev/spike_movie_entity_recon.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/dev/spike_movie_gen_capture.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/dev/spike_movie_picker_select.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/dev/spike_movie_voice_list.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/dev/spike_patch_entity.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/dev/spike_patchright.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/diag/README.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/diag/capture_flow_traffic.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/diag/memory_profile.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/diag/recaptcha_mint.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/e2e/agentic_image_e2e.ps1 +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/e2e/capture_agent_toggle.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/record_demo.ps1 +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/smoke_image.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/smoke_real_chrome_image.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/smoke_video_editor.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/smoke_worker_style.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/spike_waf_camoufox.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/scripts/verify_chrome_auth_viability.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/skills/README.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/skills/gflow-cli/SKILL.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/skills/issue-assessment/SKILL.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/skills/issue-resolve/SKILL.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/skills/plan/SKILL.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/skills/pr-council-review/SKILL.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/skills/predict/SKILL.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/skills/scenario/SKILL.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/skills/status/SKILL.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/sonar-project.properties +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/__main__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/_cli_helpers.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/api/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/api/_retry.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/api/_sapisidhash.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/api/character.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/api/dto.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/api/image.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/api/recaptcha.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/api/routes.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/api/scene.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/api/transports/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/api/transports/_common.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/api/transports/_fingerprint.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/api/transports/base.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/api/transports/drivers/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/api/transports/drivers/base.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/api/transports/drivers/classic.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/api/transports/drivers/factory.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/api/transports/experimental/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/api/transports/experimental/bearer.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/api/transports/experimental/sapisidhash.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/auth/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/auth/base.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/auth/cookies.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/auth/factory.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/auth/internal_chromium.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/auth/real_chrome.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/auth/strategies.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/auth/verification.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/browser_manager.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/chain.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/chain_manifest.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/cli.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/cli_character.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/cli_data.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/cli_models.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/cli_movie.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/cli_run.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/cli_scene.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/cli_tools.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/composition.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/data/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/data/chain_repo.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/data/migrations/0001_initial.sql +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/data/migrations/0002_add_cloud_storage.sql +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/data/migrations/0003_add_scene_tables.sql +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/data/migrations/0004_add_scene_output_path.sql +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/data/migrations/0005_add_chain_links.sql +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/data/migrations/0006_add_operations_metadata.sql +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/data/migrations/0007_queue.sql +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/data/migrations/0008_add_operation_expanded_prompt.sql +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/data/migrations/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/data/models.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/data/redaction.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/data/repository.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/data/store.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/exceptions.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/json_output.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/manifest.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/mcp/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/mcp/prompts.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/mcp/resources.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/mcp/server.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/media.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/movie_manifest.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/observability.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/paths.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/profile_store.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/services/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/services/character_create.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/storage.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/tools/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/tools/banned.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/tools/builtin/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/tools/builtin/creative-director.toml +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/tools/expander.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/tools/invocation.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/tools/loader.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/tools/registry.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/tools/runtime.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/tools/spec.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/ui/app.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/ui/server.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/worker/daemon.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/src/gflow_cli/worker/queue.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tasks/lessons.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/test_assets/sample_batch.json +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/test_assets/sample_batch.tsv +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/test_assets/sample_batch_invalid.tsv +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/fixtures/character_gen_response.json +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/fixtures/patch_entity_response.json +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/test_aisandbox_auth_error.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/test_aisandbox_auth_headers.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/test_bearer_redaction.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/test_character.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/test_client.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/test_client_character.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/test_client_delete_characters.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/test_client_generate_character.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/test_client_image.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/test_client_launch_kwargs.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/test_client_patch_entity.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/test_client_scene.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/test_client_upscale.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/test_dto.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/test_engine.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/test_image.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/test_image_dto.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/test_image_upscale.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/test_post_json_aisandbox_auth.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/test_recaptcha.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/test_retry.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/test_routes.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/test_routes_character.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/test_routes_scene.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/test_sapisidhash_helper.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/test_scene_models.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/test_video.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/test_video_request.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/transports/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/transports/conftest.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/transports/drivers/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/transports/drivers/test_classic.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/transports/drivers/test_factory.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/transports/test_base.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/transports/test_bearer.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/transports/test_common.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/transports/test_evaluate_fetch.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/transports/test_factory.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/transports/test_fingerprint.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/transports/test_sapisidhash.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/transports/test_selector_symmetry.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/transports/test_transport_timeout.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/transports/test_ui_automation.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/transports/test_ui_automation_batch.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/transports/test_ui_automation_image_mode.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/api/transports/test_ui_character_editor.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/auth/strategies/test_factory.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/auth/strategies/test_strategies.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/auth/test_cookies.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/auth/test_verification.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/cli/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/cli/test_cli_auth_list.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/cli/test_cli_character.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/cli/test_cli_character_create.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/cli/test_cli_data.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/cli/test_cli_image.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/cli/test_cli_image_seed_removed.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/cli/test_cli_image_selector_drift.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/cli/test_cli_image_upscale.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/cli/test_cli_instructions.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/cli/test_cli_models.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/cli/test_cli_movie.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/cli/test_cli_run.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/cli/test_cli_scene.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/cli/test_cli_tools.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/cli/test_cli_video_chain.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/cli/test_error_handling.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/cli/test_helpers.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/cli/test_movie_manifest.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/cli/test_movie_manifest_style_variants.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/cli/test_settings_validation.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/cli/test_t2i_multi_prompt.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/composition/test_character.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/composition/test_compose_prompt.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/composition/test_handoff.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/composition/test_style_variants.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/composition/test_style_variants_e2e.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/conftest.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/data/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/data/test_chain_repo.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/data/test_find_incomplete_character.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/data/test_models.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/data/test_packaging.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/data/test_recorder.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/data/test_recorder_character.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/data/test_redaction.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/data/test_repository.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/data/test_scene_persistence.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/data/test_settings_and_errors.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/data/test_store_migrations.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/dev/test_recording_client.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/e2e/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/e2e/conftest.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/e2e/test_aisandbox_auth_live.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/e2e/test_auth_verification_e2e.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/e2e/test_chain_e2e.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/e2e/test_character_create_e2e.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/e2e/test_daemon_e2e.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/e2e/test_data_layer_e2e.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/e2e/test_i2v_flags_e2e.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/e2e/test_image_batch_e2e.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/e2e/test_image_i2i_ref_cap_e2e.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/e2e/test_json_output_e2e.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/e2e/test_live_agentic_instructions.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/e2e/test_locale_selectors_e2e.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/e2e/test_scene_compose_live.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/e2e/test_tools_e2e.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/e2e/test_transports_e2e.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/e2e/test_video_r2v_ref_cap_e2e.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/e2e/test_video_t2v_e2e.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/features/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/features/auth.feature +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/features/auth_login.feature +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/features/character_create.feature +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/features/character_read.feature +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/features/conftest.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/features/image.feature +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/features/image_upscale.feature +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/features/instructions.feature +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/features/locale_picker_include.feature +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/features/test_auth_login_steps.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/features/test_auth_steps.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/features/test_character_create_steps.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/features/test_character_read_steps.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/features/test_image_steps.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/features/test_image_upscale_steps.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/features/test_instructions_steps.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/features/test_locale_picker_include_steps.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/features/test_step_collision_guard.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/features/test_video_agent_ui_steps.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/features/test_video_chain_steps.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/features/video_agent_ui.feature +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/features/video_chain.feature +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/fixtures/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/fixtures/seeded_catalog.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/image_batch/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/image_batch/test_image_manifest.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/image_batch/test_observability_events.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/integration/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/integration/conftest.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/integration/constants.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/integration/test_storage_gcs.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/integration/test_storage_s3.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/mcp/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/mcp/conftest.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/mcp/test_server.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/mcp/test_stdio_transport.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/mcp/test_tools_helpers.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/mcp/test_tools_instructions.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/mcp/test_tools_wired.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/scripts/test_analyze_agent_ui_capture.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/scripts/test_capture_locale_invariants.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/scripts/test_check_materiality.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/scripts/test_check_repo_hygiene.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/scripts/test_materiality_backtest.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/services/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/services/test_character_create_redaction.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/services/test_character_create_saga.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/services/test_character_gen_no_direct_post.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/smoke/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/smoke/test_profile_account_smoke.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/smoke/test_real_flow.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/test_auth.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/test_browser_manager.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/test_chain.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/test_chain_manifest.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/test_cli_data.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/test_config.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/test_conftest_isolation.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/test_documentation_gate.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/test_errors.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/test_errors_403.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/test_json_output.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/test_manifest.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/test_marker_registry.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/test_media.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/test_observability.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/test_paths.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/test_profile_store.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/test_smoke.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/tools/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/tools/test_banned.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/tools/test_expander.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/tools/test_invocation.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/tools/test_loader.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/tools/test_registry.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/tools/test_runtime.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/tools/test_spec.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/ui/test_app.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.33.0}/tests/worker/test_daemon.py +0 -0
|
@@ -26,6 +26,13 @@
|
|
|
26
26
|
# Useful for agent pipelines — set low to surface hung logins as exit 12 fast.
|
|
27
27
|
# GFLOW_CLI_AUTH_LOGIN_TIMEOUT=600
|
|
28
28
|
|
|
29
|
+
# Anti-bot pause between prompt submissions in multi-prompt image runs
|
|
30
|
+
# (image batch, t2i --prompts-file/--stdin, gflow run). 'MIN-MAX' seconds, a
|
|
31
|
+
# single number for 0-N, or 0 to disable. Default 0.5-1.5 (small on purpose);
|
|
32
|
+
# widen (e.g. 10-30) if runs hit WAF 403s. The --jitter flag beats this.
|
|
33
|
+
# See docs/CONFIGURATION.md § GFLOW_CLI_JITTER_RANGE.
|
|
34
|
+
# GFLOW_CLI_JITTER_RANGE=0.5-1.5
|
|
35
|
+
|
|
29
36
|
# -----------------------------------------------------------------------------
|
|
30
37
|
# Output paths
|
|
31
38
|
# -----------------------------------------------------------------------------
|
|
@@ -131,6 +138,14 @@
|
|
|
131
138
|
# language end-to-end (see KNOWN_ISSUES § issue #24).
|
|
132
139
|
# GFLOW_CLI_LOCALE=en-US
|
|
133
140
|
|
|
141
|
+
# Display name of the --project project, used by `gflow video i2v` to select
|
|
142
|
+
# the target project in the media picker's project menu when attaching a
|
|
143
|
+
# media-UUID frame (#287 — the menu lists projects by NAME; unnamed projects
|
|
144
|
+
# show only creation timestamps). Same as the `--project-name` flag, which
|
|
145
|
+
# takes precedence when both are set. Escape hatch when automatic name
|
|
146
|
+
# derivation (tab title / page hints) fails.
|
|
147
|
+
# GFLOW_CLI_PROJECT_NAME=
|
|
148
|
+
|
|
134
149
|
# -----------------------------------------------------------------------------
|
|
135
150
|
# Headless Daemon (gflow serve)
|
|
136
151
|
# -----------------------------------------------------------------------------
|
|
@@ -7,6 +7,38 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.33.0] — 2026-07-12
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **Configurable anti-bot jitter (#241).** The pause between multi-prompt image submissions is now tunable via `--jitter MIN-MAX` on `gflow image t2i` / `gflow image batch` or the `GFLOW_CLI_JITTER_RANGE` setting (env var or `.env`; flag beats env; a single number `N` means uniform 0–N like `video chain --jitter`; `0` disables; bounds must be finite and ≤ 3600 s). The previously **unpaced** paths — `t2i --prompts-file` / `--stdin` / multi-positional and `gflow run` image batches — now pace by default too; field data showed unpaced bursts tripping Flow's WAF (403 `PUBLIC_ERROR_UNUSUAL_ACTIVITY`). WAF cadence behavior and cooldown guidance documented in [DEBUGGING § WAF cadence](docs/DEBUGGING.md#waf-cadence).
|
|
15
|
+
- **`--project-name TEXT` on `gflow video i2v` (env: `GFLOW_CLI_PROJECT_NAME`).** The media picker's project menu lists projects by display NAME only — no ids anywhere in its markup, and unnamed projects show nothing but creation timestamps (live round-4 dump: 80 recency-ordered `menuitem`s). When gflow attaches an in-project asset by media UUID it must first select the right project in that menu, so it needs the project's display name. Automatic derivation is best-effort but live-validated (round 5 confirmed the editor tab title carries the name: `Google Flow - <project name>`); `--project-name` is the explicit, highest-precedence override for when derivation fails on other cohorts/locales — a permanent escape hatch, not a workaround. Threads `GenerateVideoRequest.project_name` through to the picker project-menu match; documented in `.env.template`. CLI-only for now: the MCP `gflow_generate_video` tool has no media-UUID frame inputs yet, so the override has no consumer there (command-level parity unaffected; the field rides along when ref-id support lands) (#287).
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- **Default image-batch jitter lowered from 3–7 s to 0.5–1.5 s (#241).** The default is deliberately minimal — enough to break a perfectly uniform burst signature without wasting wall-clock. Widen (`--jitter 10-30` / `GFLOW_CLI_JITTER_RANGE=10-30`) when runs start hitting WAF 403s, then dial back once the score decays.
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- **`gflow video i2v <media-uuid>` now reaches assets deep in a crowded project's virtualised media grid (#287 — primary fix, part 1: scroll on the RIGHT node, progress-bounded).** The live repro: `TransportTimeoutError` ("Start frame asset ... could not be located in the media picker") on a ~100+-asset project for an asset that WAS in the project and in the local catalog, while the same command worked from a small scratch project. `_select_existing_asset`'s scroll fallback had a fixed budget (12 scrolls x 500 px), capping the reachable depth of the react-virtuoso grid regardless of grid size — and the round-6 audit exposed a second layer: the scroll was a blind hover+wheel over the dialog, but react-virtuoso scrolls its OWN container, so a wheel over the wrong node is a silent no-op that looks exactly like "end of grid". The scroll (shared with `_find_picker_entity_tile` via `_scroll_picker_grid_until_rendered`) now drives the dialog's ACTUAL scrollable element via JS (`[data-virtuoso-scroller]` preferred, then the first overflow container; hover+wheel kept as fallback when the probe fails), and is bounded by evidence of progress: it keeps scrolling while the set of rendered tile identifiers still changes between scrolls — depth proportional to grid size — stops after 3 consecutive no-progress scrolls, retains the legacy 12-scroll budget when the DOM probe yields no evidence, and caps at a 200-scroll hard ceiling. Every scroll probe event reports WHICH node moved (tag + class) and its scrollTop before/after, so a wrong-node no-op (frozen scrollTop) is visible in telemetry. The not-found contract is unchanged: same `TransportTimeoutError` naming the slot and UUID (#287).
|
|
24
|
+
- **CLI-resolved PROMPT search hints surface UUID frame refs via Flow's media search (#287 — primary fix, part 2).** Live rounds proved Flow's picker search does NOT index media UUIDs (both the full-UUID and UUID-stem tiers found nothing), but each picker tile's `alt` text carries the asset's generation PROMPT — which the search does index. New `get_asset_prompt(db_path, media_id)` catalog query (latest output-operation prompt by `flow_media_id`); `gflow video i2v` resolves each UUID frame ref's recorded prompt and passes its first 6 words as `GenerateVideoRequest.search_hints` (best-effort — no catalog, no hints; layering respected: the CLI resolves because it owns catalog access, the transport only consumes). `_select_existing_asset` types each hint into the picker search box after the UUID tiers and still matches tiles by UUID-in-`src` among the results, so an imprecise hint can only surface extra tiles, never select a wrong one; a hint hit skips the scroll fallback entirely. The pre-existing tiers (display name, media UUID, UUID stem) are kept as cheap first attempts (#287).
|
|
25
|
+
- **Hardening: the media picker's library view is verified/aligned to the target project before any UUID asset lookup (#287 — demoted from root cause after round 5).** Round-2's DOM dump appeared to show the picker's library open on a DIFFERENT project ("gflow-cli t2i", 16 tiles, target id nowhere in the dialog), and rounds 2-5 built project-alignment machinery on that reading. The round-5 title telemetry then inverted it: the raw tab title on `/project/f6caf027` was "Google Flow - gflow-cli t2i" — the trigger's "gflow-cli t2i" WAS the target project's actual display name, so the picker had been on the correct project all along and the round-2 evidence was a mirage. The machinery stays as hardening (the picker's library IS per-project with its own selector, so it CAN open on the wrong project — now that state is detected and corrected, and the round-5/6 name derivation gives the match a working source): `_sync_picker_project` runs each time the frame-slot / Add-Media dialog opens: it derives the target project id from the editor URL, resolves the project's display NAME (the menu renders names, not ids; live round-3 run confirmed the previous href/class-only page probes find nothing) — precedence: the `--project-name` / `GFLOW_CLI_PROJECT_NAME` override, then the editor tab title (`document.title` — the editor was navigated to `/project/<id>` before the picker opened; "Flow" AND "Google Flow" branding is stripped tolerantly across separator variants — the live-observed pattern is `Google Flow - <project name>` — branding-only titles are rejected so 'flow' can never contains-match a project like 'gflow-cli i2i', and the raw title is logged on every resolution so the real live pattern stays learnable), then an element whose `href` references the project id, then a project-title-classed element (the local catalog's `projects.title` was considered but the transport layer has no catalog access, and the live page also covers renames and non-gflow projects) — probes the trigger cascade with the live-observed Radix `ProjectDropdownSubTrigger` class first (the generic `aria-haspopup='menu'` fallback tier explicitly excludes the sibling `SortDropdownSubTrigger` — "Recentes" on the observed pt-BR profile — and no tier matches on locale-dependent labels), no-ops when the trigger already shows the target project (by id, or by resolved name — exact OR contains, since the trigger text carries icon-ligature noise; round 5 wasted ~30 menu probes hunting for a project it was already in because the probe demanded exact equality) or when no selector exists (older cohort), and otherwise opens the Radix submenu (click, then hover, then focus+ArrowRight — SubTriggers may ignore plain click — each step verified against the portal-rendered `[role='menu'][data-state='open']`), polls up to ~3s for the portal to POPULATE (live round 3: the open-state flips before the project list renders — matching an empty portal was a guaranteed miss), and clicks the innermost candidate matching the target — anchors whose `href` carries the project id first (round 3 also showed the portal holds ZERO classic menu-item ARIA roles, so the sweep covers generic clickables: `a`, `button`, `li`, `div[role]`, plus the ARIA roles), then the id anywhere in markup, then the normalized name (exact match before contains, so timestamp-labeled scratch entries like "Jul 11, 11:00 PM" can't collide). When the in-view match misses, the open portal itself is scrolled with the same progress-bounded pattern (scroll one step, re-match, stall-terminate when the rendered item set stops changing, hard ceiling) — the recency-ordered menu holds EVERY project and the target's entry can sit below the visible fold. A miss closes the menu (Escape) and proceeds, leaving the asset lookup as the authority.
|
|
26
|
+
- **Every picker-lookup decision point now emits structured telemetry, and each failure mode leaves a bounded DOM dump (#287 diagnosis).** The first live verification failed with zero events from the new code paths, making the failing layer indistinguishable (search tiers vs progress probe vs tile matcher vs wrong library project) — this telemetry is what confirmed the root cause in round 2. New `ui_automation_video.*` events: `picker_project_selector_absent` / `picker_project_already_active` / `picker_project_menu_opened` (opened + method: click/hover/keyboard) / `picker_project_menu_populated` (element count after the population poll) / `picker_project_switched` (matched_by: href/id/name) / `picker_project_switch_miss` (menu_opened, menu_elements, candidate count, dump path) / `picker_project_sync_skipped` / `picker_project_name_override` / `picker_project_name_resolved` / `picker_project_name_unresolved` (both name events carry the raw tab title) / `picker_project_menu_scroll_probe` (per menu scroll: rendered item count + new-item delta) / `picker_project_menu_scroll_done` (reason: found / stall / no_menu / ceiling) (project alignment), `picker_search_tier` (term, found, rendered-tile count) and `picker_search_unavailable`, `picker_scroll_probe` (per scroll: rendered-tile count + new-tile delta, plus WHICH node was scrolled — tag + class — and its scrollTop before/after, so a wrong-node no-op scroll with a frozen scrollTop is visible) and `picker_scroll_done` (termination reason: found / stall / legacy_budget / ceiling, total attempts), and `existing_asset_not_found` (media id, project id, screenshot + dump paths). On a final not-found, `_capture_picker_dom_dump` writes `debug_picker_dom_<uuid8>.json` to the out-dir — tile count, the first 3 tiles' outerHTML truncated to 500 chars (enough to see which attribute carries the media identity in a given cohort), the dialog's aria/role/data attributes, the project-selector candidates' outerHTML, and whether the target project id appears in the dialog at all — plus a `debug_picker_miss_<uuid8>.png` screenshot. On a project-switch miss, the OPEN portal's raw innerHTML (bounded to 4000 chars) plus its child-element count and tag histogram are written to `debug_picker_project_menu_<uuid8>.json` and summarized on the event — round 2's closed-trigger dump and round 3's role-filtered item list both left the menu structure invisible; raw markup can't be blinded by role assumptions. All dumps follow the 0.32.1 None-on-capture-failure contract (never report a file that was not written) (#287).
|
|
27
|
+
|
|
28
|
+
## [0.32.1] — 2026-07-11
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
|
|
32
|
+
- **Browser teardown can no longer hang forever or leak a Chrome tree that locks the profile dir (#293).** A wedged `context.close()` used to be awaited unbounded and its failure swallowed to a warning; stopping the Playwright driver afterwards kills only the Node process, so the detached system-Chrome survived holding the profile — the next run then died at launch with an opaque `TargetClosedError` → "Unexpected error." (exit 1) (observed 3× live, 2026-07-11). Teardown now uses a shared bounded-close helper (generous 30s graceful bound — Playwright hard-kills Chrome mid-profile-flush if a second close arrives during a graceful close, so a slow-but-healthy close must not be escalated; 5s force-close fallback via `context.browser`; 10s driver-stop bound; field resets survive Ctrl-C) across **all three** owned-context teardown paths: `FlowApiClient`, the UI-automation transport's standalone path (its partial-setup guard now also closes a launched context before the driver exits), and the experimental evaluate-fetch transport. A launch-time `TargetClosedError` is now surfaced as `ProfileLockedError` (exit 11) carrying the original error and a kill-the-stale-Chrome remediation, hedged for non-lock startup crashes (#293).
|
|
33
|
+
|
|
34
|
+
- **Picker grid scroll no longer misses a tile rendered by the final scroll (#283 off-by-one).** `_select_existing_asset` checked the tile count only *before* each scroll, so an asset the last scroll brought into the virtualised grid was never re-checked and the picker gave up with the tile on screen. A post-loop re-check closes it (`_find_picker_entity_tile` shares the loop shape but returns the locator unconditionally, so it was unaffected — now documented).
|
|
35
|
+
- **Agentic `await_images` no longer trusts a single exact-count scrape (#283 hardening of the #281 race).** The poll loop breaks only when the new-UUID set is identical across two consecutive scrapes at the expected count (~one extra 0.5s poll); a set that transiently hits the exact count and then grows surfaces as the #281 `MediaAttributionError` instead of being returned as "the" generated media.
|
|
36
|
+
- **Debug screenshots that fail to capture are no longer reported as if they existed.** `_capture_debug_screenshot` returned the target path even when `page.screenshot` raised (observed live 2026-07-11: an error message pointed at a file that was never written). It now returns `None` on capture failure and every error message appends its `Screenshot:` clause conditionally (new `screenshot_clause` helper) (#283).
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
|
|
40
|
+
- **UUID-shape validation consolidated onto `gflow_cli.api.video.is_media_uuid`.** The four per-module private `_UUID_RE` copies (cli_image, cli_instructions, image_upscale, mcp/tools) now delegate to the public helper introduced in #290; no behavior change. `MediaAttributionError` raises in the recorder now carry `route=` provenance, the agentic ambiguity raise no longer duplicates the class remediation text, `image i2i --ref` help uses the same "media UUID" vocabulary as `video i2v`, and a bad value passed via the deprecated `--end-image` alias names `--end-image` (not `--end-frame`) in its usage error (#283).
|
|
41
|
+
|
|
10
42
|
## [0.32.0] — 2026-07-11
|
|
11
43
|
|
|
12
44
|
### Added
|
|
@@ -1919,7 +1951,9 @@ shell-script template that branches on these codes.
|
|
|
1919
1951
|
|
|
1920
1952
|
First skeleton. Not functional end-to-end yet.
|
|
1921
1953
|
|
|
1922
|
-
[Unreleased]: https://github.com/ffroliva/gflow-cli/compare/v0.
|
|
1954
|
+
[Unreleased]: https://github.com/ffroliva/gflow-cli/compare/v0.33.0...HEAD
|
|
1955
|
+
[0.33.0]: https://github.com/ffroliva/gflow-cli/compare/v0.32.1...v0.33.0
|
|
1956
|
+
[0.32.1]: https://github.com/ffroliva/gflow-cli/compare/v0.32.0...v0.32.1
|
|
1923
1957
|
[0.32.0]: https://github.com/ffroliva/gflow-cli/compare/v0.31.0...v0.32.0
|
|
1924
1958
|
[0.31.0]: https://github.com/ffroliva/gflow-cli/compare/v0.30.0...v0.31.0
|
|
1925
1959
|
[0.30.0]: https://github.com/ffroliva/gflow-cli/compare/v0.29.0...v0.30.0
|
|
@@ -450,6 +450,9 @@ batch — so it is not a global incompatibility but a per-profile WAF state.
|
|
|
450
450
|
the score).
|
|
451
451
|
3. **Avoid same-day repeated batch runs** on a profile after a 403 — each
|
|
452
452
|
rejected request can raise the score further.
|
|
453
|
+
4. **Widen the submission jitter** when composing multiple runs — `--jitter 10-30`
|
|
454
|
+
or `GFLOW_CLI_JITTER_RANGE` (#241); cadence guidance in
|
|
455
|
+
[DEBUGGING § WAF cadence](docs/DEBUGGING.md#waf-cadence).
|
|
453
456
|
|
|
454
457
|
**Tracked separately from** the architectural ["first-attempt listener-miss
|
|
455
458
|
flake"](https://github.com/ffroliva/gflow-cli/pull/40) — that one was caused
|
|
@@ -798,8 +801,13 @@ through all three layers: wrong file on disk, exit 0. Mitigations until this
|
|
|
798
801
|
is closed: run generations in a dedicated, low-asset project (fewer
|
|
799
802
|
pre-existing tiles means a smaller lazy-render population to collide with),
|
|
800
803
|
and visually verify anchor/canonical images before referencing them
|
|
801
|
-
downstream in `i2i`.
|
|
802
|
-
|
|
804
|
+
downstream in `i2i`. The stable-break hardening shipped post-v0.32.0 (#283 /
|
|
805
|
+
PR #292): the loop now requires the SAME new-UUID set on two consecutive
|
|
806
|
+
scrapes at the expected count before trusting it, which narrows this window
|
|
807
|
+
to a pre-existing tile that lazy-renders AND holds stable across two 0.5s
|
|
808
|
+
scrapes at exactly the expected count (or first hits the count on the final
|
|
809
|
+
poll before the 180s deadline). Narrowed, not closed — the dedicated-project
|
|
810
|
+
mitigation still applies.
|
|
803
811
|
|
|
804
812
|
**Known gap: the shell multi-prompt path records no history at all.**
|
|
805
813
|
`gflow run --config <file>` and `gflow image t2i` with multiple prompts
|
|
@@ -815,7 +823,8 @@ collision escalation) both depend on local history via
|
|
|
815
823
|
neither guard exists on this path — this is a pre-existing gap, not a
|
|
816
824
|
regression from this fix. `gflow image batch` (the manifest path, via
|
|
817
825
|
`run_manifest_image_batch`) already threads a recorder through and is covered
|
|
818
|
-
by all three layers.
|
|
826
|
+
by all three layers. Accepted low-risk gap (#283 closed with this noted as
|
|
827
|
+
un-shipped remainder; re-file if it bites in practice).
|
|
819
828
|
|
|
820
829
|
**Workaround (pre-fix):** none — on an affected version, manually verify the
|
|
821
830
|
downloaded file matches the prompt before trusting a `t2i`/`i2i` result.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gflow-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.33.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
|
|
@@ -153,6 +153,14 @@ GFLOW_CLI_AUTH_LOGIN_TIMEOUT=120 gflow auth login # abort after 2 minutes
|
|
|
153
153
|
**Default:** `600`
|
|
154
154
|
**Note:** This is a single-request ceiling; the *batch* timeout you experience is sum of all per-clip waits.
|
|
155
155
|
|
|
156
|
+
### `GFLOW_CLI_JITTER_RANGE`
|
|
157
|
+
|
|
158
|
+
**What:** Anti-bot pause between prompt submissions in multi-prompt image runs (`gflow image batch`, `gflow image t2i --prompts-file` / `--stdin` / multiple positional prompts, and `gflow run` image batches). Spaces out the *submission clicks* only — generations still run in parallel inside Flow.
|
|
159
|
+
**Values:** `MIN-MAX` seconds (e.g. `10-30`), a single number `N` (uniform `0`–`N`, mirrors `video chain --jitter`), or `0` to disable.
|
|
160
|
+
**Default:** `0.5-1.5` — deliberately small so runs don't waste wall-clock. **Widen (e.g. `10-30`) when runs start hitting WAF 403s**, then dial back once the score decays.
|
|
161
|
+
**CLI override:** `--jitter` on `image t2i` and `image batch` (flag beats env).
|
|
162
|
+
**Why:** Flow's WAF reacts to cumulative submission cadence — see [DEBUGGING § WAF cadence](DEBUGGING.md#waf-cadence).
|
|
163
|
+
|
|
156
164
|
### `GFLOW_CLI_LOG_LEVEL`
|
|
157
165
|
|
|
158
166
|
**What:** Logging verbosity.
|
|
@@ -173,6 +173,38 @@ $env:PYTHONUTF8 = "1"
|
|
|
173
173
|
|
|
174
174
|
Bash users on Git Bash / WSL inherit a UTF-8 locale and don't need this.
|
|
175
175
|
|
|
176
|
+
## WAF cadence
|
|
177
|
+
|
|
178
|
+
Flow's WAF reacts to **cumulative submission cadence**, not individual
|
|
179
|
+
requests. Field data (issue [#241](https://github.com/ffroliva/gflow-cli/issues/241),
|
|
180
|
+
2026-07-05, real account):
|
|
181
|
+
|
|
182
|
+
- 3-prompt multi-prompt runs spread 30–60 min apart all passed.
|
|
183
|
+
- A burst of ~14 image submissions in ~10 min ended in a **403**
|
|
184
|
+
(`Flow returned 403; blocked by WAF or fingerprint check`,
|
|
185
|
+
`PUBLIC_ERROR_UNUSUAL_ACTIVITY` — see
|
|
186
|
+
[KNOWN_ISSUES § batchGenerateImages HTTP 403](../KNOWN_ISSUES.md)).
|
|
187
|
+
- After ~30 min of cooldown, individually-paced calls (45–120 s apart) on the
|
|
188
|
+
same profile and project passed 4/4.
|
|
189
|
+
|
|
190
|
+
Practical guidance:
|
|
191
|
+
|
|
192
|
+
- **Pace multi-prompt runs — escalate only when needed.** All multi-prompt
|
|
193
|
+
image paths (`image batch`, `image t2i --prompts-file` / `--stdin` /
|
|
194
|
+
multiple prompts, `gflow run` image batches) apply a small random
|
|
195
|
+
0.5–1.5 s pause between submissions by default — enough to break a
|
|
196
|
+
perfectly uniform burst signature without wasting wall-clock. If runs
|
|
197
|
+
start returning 403s, widen with `--jitter 10-30` or
|
|
198
|
+
`GFLOW_CLI_JITTER_RANGE` ([CONFIGURATION § GFLOW_CLI_JITTER_RANGE](CONFIGURATION.md#gflow_cli_jitter_range)),
|
|
199
|
+
then dial back once the score decays. `--jitter 0` disables pacing.
|
|
200
|
+
- **Cool down after a 403.** Wait 30–60 min, then probe with a single small
|
|
201
|
+
generation before batching again.
|
|
202
|
+
- **Reuse a project.** Repeated project churn (`project.createProject` calls
|
|
203
|
+
between generations) adds to the bot-like signature. For repeated
|
|
204
|
+
single-prompt runs (`t2i`, `i2i`), pass `--project` to reuse a standing
|
|
205
|
+
project. Multi-prompt runs already create only **one** shared project per
|
|
206
|
+
run (never one per prompt).
|
|
207
|
+
|
|
176
208
|
## Common failure modes
|
|
177
209
|
|
|
178
210
|
See [`KNOWN_ISSUES.md`](../KNOWN_ISSUES.md) for the canonical list and
|
|
@@ -122,7 +122,7 @@ Slash commands for Claude Code, stored in `.claude/commands/gflow/`. All prefixe
|
|
|
122
122
|
**"A gflow command hangs / fails — where do I start?"** → [DEBUGGING § Quick reference](DEBUGGING.md#quick-reference)
|
|
123
123
|
**"Flow's UI broke a selector — how do I diagnose it?"** → [DEBUGGING § Inspecting Flow's live UI](DEBUGGING.md#inspecting-flows-live-ui)
|
|
124
124
|
**"What does each `ui_automation.*` log event mean?"** → [DEBUGGING § Listener & HTTP-layer debugging](DEBUGGING.md#listener--http-layer-debugging)
|
|
125
|
-
**"What was actually live-verified for the latest release?"** → latest: [LIVE_VERIFICATION_v0.32.0](LIVE_VERIFICATION_v0.32.0.md) (**#287 UUID i2v frames proven live e2e** — an in-project asset selected by media UUID in the frame-slot dialog [frame_ref_attached, startImage bound on the wire, ZERO upload events] produced a real 720×1280 interpolation, resolving the #237 negative prior; foreign UUID → exit 9 pre-generation with screenshot; **#288 duration fail-fast proven live** — exit 23 pre-submit saved a 10-credit generation, and the debug screenshot confirmed the duration control is ABSENT from the cohort's settings popover [locale hypothesis refuted]; upload-400 exit-27 repro deferred-with-reason — the original rejected JPEG no longer exists). Prior: [v0.31.0](LIVE_VERIFICATION_v0.31.0.md) (**#281 wrong-media defenses live-verified in BOTH directions, credit-free** — correct attribution downloads with real wire metadata [seed, 768×1376]; the incident-class lazy-render ambiguity reproduced live and hard-stopped with `MediaAttributionError` exit 26 naming every candidate UUID, no file written; **#282 two-UUID-ref i2i proven e2e** — both freshly generated portrait UUIDs picker-selected into one scene with both canonical identities visually transferred). Prior: [v0.30.0](LIVE_VERIFICATION_v0.30.0.md) (**agentic-cohort image path + both fixes live-verified credit-free** — native 768×1376 stills, i2i-chain + canonical-ref identity hold; the character-create `DataIntegrityError` and the `prefer_classic` WARNING were observed live then fixed [recorder idempotent + INFO log]; MCP video-param feature deferred-with-reason on Veo credits, per the stills-only goal). Prior: [v0.29.0](LIVE_VERIFICATION_v0.29.0.md) (**persistent `gflow instructions` CRUD proven live e2e GREEN, credit-free** — 11-command sequence on a fresh project: add incl. `--ref <image-UUID>` attach, list/enable/disable, master toggle both ways, TOML `apply` destructive full-sync with `enabled=false` honored, `rm` with server card-id preserved across mutations; movie brief-sync code-path-verified via the same primitives, deferred-with-reason on credits; MCP `gflow_instructions_*` tools are thin adapters over the live-verified primitives). Prior: [v0.28.0](LIVE_VERIFICATION_v0.28.0.md) (**agent instructions `-i` now steer agentic generation — crayon e2e GREEN**: a style-neutral prompt + an enabled "crayon" card produced an unmistakable crayon drawing through the real transport; root causes were conversational phrasing + the `project_brief.enabled` master switch). Prior: [v0.27.1](LIVE_VERIFICATION_v0.27.1.md) (**v0.27.0 release follow-up fixes and documentation sync**). Prior: [v0.27.0](LIVE_VERIFICATION_v0.27.0.md) (**`[style]` block with named variants + prompt-aware resume — credit-free CLI verification**: real `movie run --dry-run` 3-way ledger proving resolved-style display, `skip (done)` on matching `style_hash`, and `re-run (style changed)` after a style edit; composition is local, wire path untouched, so no Veo credits spent — first credited styled run will confirm visually). Prior: [v0.26.0](LIVE_VERIFICATION_v0.26.0.md) (**image i2i by generated-image UUID proven live e2e GREEN** — the existing asset is *selected in place* by its media UUID, no duplicate upload, producing an i2i output that references it; plus generated-image `display_name` capture, credited @C1ph3r404). Prior: [v0.25.0](LIVE_VERIFICATION_v0.25.0.md) (**#237 remote-UUID i2v proven live e2e GREEN** — a real 8s 720×1280 interpolation from a generated image's UUID, after the picker-search attach was reworked to local upload; #240 home-`.env` matrix live; plus two silent-failure guards root-caused live: video-as-image download rejection and rejected-upload fail-loud). Prior: [v0.24.0](LIVE_VERIFICATION_v0.24.0.md) (--project parity across CLI + MCP — automated coverage; live gen auth-gated). Prior: [v0.23.0](LIVE_VERIFICATION_v0.23.0.md) (MCP generation wiring proven live end-to-end — tool→worker→real Flow REST→structured-error translation; final image-write blocked by an expired session, environmental. #222 macOS fix reporter-verified e2e on Apple Silicon). Prior: [v0.22.0](LIVE_VERIFICATION_v0.22.0.md) (Tools framework — automated/CI coverage complete; credit/key-gated live feature run is a pending owner gate, see the doc) · [v0.21.0](LIVE_VERIFICATION_v0.21.0.md) (MCP server over stdio + HTTP/SSE — credit-free protocol handshake; 4 tools / 2 prompts / 3 resources confirmed on stdout) · [v0.20.1](LIVE_VERIFICATION_v0.20.1.md) · [v0.20.0](LIVE_VERIFICATION_v0.20.0.md) · [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)
|
|
125
|
+
**"What was actually live-verified for the latest release?"** → latest: [LIVE_VERIFICATION_v0.33.0](LIVE_VERIFICATION_v0.33.0.md) (**#287-follow-up picker fix proven live on the original repro** — the v0.32.1-failing `i2v <uuid> --project <crowded>` command now exits 0 with a real 3.0 MB `ftyp` mp4, `frame_ref_attached` + `video_saved`, zero uploads; **#241 jitter live-verified on both paths** — flag `--jitter 4-6` → `batch_jitter_sleep seconds=4.98`, and the new 0.5–1.5 s default → `seconds=1.47`, each firing exactly once between two t2i submissions; environment note: the denon82 cohort is now natively agentic and round-2 generations hit the pre-existing #281 `MediaAttributionError`, not a regression). Prior: [v0.32.1](LIVE_VERIFICATION_v0.32.1.md) (**#293 teardown fix live-verified credit-free** — error-path aborts leave ZERO chrome.exe holding the profile [3× leak reproduced pre-fix same day]; force-close fallback + ProfileLockedError translation unit-pinned; #283 wave logic fixes mock-verified to exact call counts). Prior: [v0.32.0](LIVE_VERIFICATION_v0.32.0.md) (**#287 UUID i2v frames proven live e2e** — an in-project asset selected by media UUID in the frame-slot dialog [frame_ref_attached, startImage bound on the wire, ZERO upload events] produced a real 720×1280 interpolation, resolving the #237 negative prior; foreign UUID → exit 9 pre-generation with screenshot; **#288 duration fail-fast proven live** — exit 23 pre-submit saved a 10-credit generation, and the debug screenshot confirmed the duration control is ABSENT from the cohort's settings popover [locale hypothesis refuted]; upload-400 exit-27 repro deferred-with-reason — the original rejected JPEG no longer exists). Prior: [v0.31.0](LIVE_VERIFICATION_v0.31.0.md) (**#281 wrong-media defenses live-verified in BOTH directions, credit-free** — correct attribution downloads with real wire metadata [seed, 768×1376]; the incident-class lazy-render ambiguity reproduced live and hard-stopped with `MediaAttributionError` exit 26 naming every candidate UUID, no file written; **#282 two-UUID-ref i2i proven e2e** — both freshly generated portrait UUIDs picker-selected into one scene with both canonical identities visually transferred). Prior: [v0.30.0](LIVE_VERIFICATION_v0.30.0.md) (**agentic-cohort image path + both fixes live-verified credit-free** — native 768×1376 stills, i2i-chain + canonical-ref identity hold; the character-create `DataIntegrityError` and the `prefer_classic` WARNING were observed live then fixed [recorder idempotent + INFO log]; MCP video-param feature deferred-with-reason on Veo credits, per the stills-only goal). Prior: [v0.29.0](LIVE_VERIFICATION_v0.29.0.md) (**persistent `gflow instructions` CRUD proven live e2e GREEN, credit-free** — 11-command sequence on a fresh project: add incl. `--ref <image-UUID>` attach, list/enable/disable, master toggle both ways, TOML `apply` destructive full-sync with `enabled=false` honored, `rm` with server card-id preserved across mutations; movie brief-sync code-path-verified via the same primitives, deferred-with-reason on credits; MCP `gflow_instructions_*` tools are thin adapters over the live-verified primitives). Prior: [v0.28.0](LIVE_VERIFICATION_v0.28.0.md) (**agent instructions `-i` now steer agentic generation — crayon e2e GREEN**: a style-neutral prompt + an enabled "crayon" card produced an unmistakable crayon drawing through the real transport; root causes were conversational phrasing + the `project_brief.enabled` master switch). Prior: [v0.27.1](LIVE_VERIFICATION_v0.27.1.md) (**v0.27.0 release follow-up fixes and documentation sync**). Prior: [v0.27.0](LIVE_VERIFICATION_v0.27.0.md) (**`[style]` block with named variants + prompt-aware resume — credit-free CLI verification**: real `movie run --dry-run` 3-way ledger proving resolved-style display, `skip (done)` on matching `style_hash`, and `re-run (style changed)` after a style edit; composition is local, wire path untouched, so no Veo credits spent — first credited styled run will confirm visually). Prior: [v0.26.0](LIVE_VERIFICATION_v0.26.0.md) (**image i2i by generated-image UUID proven live e2e GREEN** — the existing asset is *selected in place* by its media UUID, no duplicate upload, producing an i2i output that references it; plus generated-image `display_name` capture, credited @C1ph3r404). Prior: [v0.25.0](LIVE_VERIFICATION_v0.25.0.md) (**#237 remote-UUID i2v proven live e2e GREEN** — a real 8s 720×1280 interpolation from a generated image's UUID, after the picker-search attach was reworked to local upload; #240 home-`.env` matrix live; plus two silent-failure guards root-caused live: video-as-image download rejection and rejected-upload fail-loud). Prior: [v0.24.0](LIVE_VERIFICATION_v0.24.0.md) (--project parity across CLI + MCP — automated coverage; live gen auth-gated). Prior: [v0.23.0](LIVE_VERIFICATION_v0.23.0.md) (MCP generation wiring proven live end-to-end — tool→worker→real Flow REST→structured-error translation; final image-write blocked by an expired session, environmental. #222 macOS fix reporter-verified e2e on Apple Silicon). Prior: [v0.22.0](LIVE_VERIFICATION_v0.22.0.md) (Tools framework — automated/CI coverage complete; credit/key-gated live feature run is a pending owner gate, see the doc) · [v0.21.0](LIVE_VERIFICATION_v0.21.0.md) (MCP server over stdio + HTTP/SSE — credit-free protocol handshake; 4 tools / 2 prompts / 3 resources confirmed on stdout) · [v0.20.1](LIVE_VERIFICATION_v0.20.1.md) · [v0.20.0](LIVE_VERIFICATION_v0.20.0.md) · [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)
|
|
126
126
|
**"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.
|
|
127
127
|
**"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)
|
|
128
128
|
**"What was live-verified for the video-download feature (#29)?"** → [LIVE_VERIFICATION_video_download](LIVE_VERIFICATION_video_download.md)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Live verification — v0.32.1 (2026-07-11)
|
|
2
|
+
|
|
3
|
+
Patch release: the #293 teardown fix (PR #294) plus the #283 follow-up wave
|
|
4
|
+
(PR #292). All verification credit-free.
|
|
5
|
+
|
|
6
|
+
## 1. Browser teardown — no leaked Chrome after error exits (#293, PR #294)
|
|
7
|
+
|
|
8
|
+
Baseline (pre-fix, same day): three separate error-exit runs on the `denon82`
|
|
9
|
+
profile left a full Chrome tree holding the profile dir; the next run died at
|
|
10
|
+
context launch with `TargetClosedError` → "Unexpected error." exit 1.
|
|
11
|
+
|
|
12
|
+
Post-fix live repro, run twice (pre- and post-review-refactor):
|
|
13
|
+
|
|
14
|
+
1. Trigger an error-path abort: `gflow video i2v --initial-frame
|
|
15
|
+
<foreign-UUID> … --project dd9e498c-… --profile denon82` → **exit 9**
|
|
16
|
+
(typed picker-miss abort, pre-credit).
|
|
17
|
+
2. Immediately after process exit:
|
|
18
|
+
`Get-CimInstance Win32_Process -Filter "Name='chrome.exe'"` filtered on
|
|
19
|
+
`profile_denon82` in the command line → **0 processes**, both runs.
|
|
20
|
+
3. Follow-up launches in the same session succeeded cleanly (no
|
|
21
|
+
`TargetClosedError` at launch).
|
|
22
|
+
|
|
23
|
+
Honest caveat: in both post-fix runs the *graceful* close succeeded within the
|
|
24
|
+
bound, so the force-close fallback did not fire live — its trigger (a wedged
|
|
25
|
+
close) is nondeterministic. The fallback branch is pinned by unit tests
|
|
26
|
+
(`tests/api/test_concurrency.py`: force-close ordering before driver stop,
|
|
27
|
+
double-failure last resort, hang-timeout path, both `_is_target_closed`
|
|
28
|
+
branches), and `context.browser` being non-None for persistent contexts was
|
|
29
|
+
verified against the pinned Playwright 1.59.0 package source.
|
|
30
|
+
|
|
31
|
+
The `ProfileLockedError` (exit 11) launch translation is unit-verified; a live
|
|
32
|
+
sample requires deliberately wedging a browser and was not manufactured.
|
|
33
|
+
|
|
34
|
+
## 2. #283 wave (PR #292) — logic-level fixes, unit-pinned
|
|
35
|
+
|
|
36
|
+
The picker scroll off-by-one, `await_images` stable-break, and phantom
|
|
37
|
+
screenshot paths are pure logic around mocked DOM scrapes — the unit tests are
|
|
38
|
+
the affected surface (mock-verified to the exact call counts: 12 pre-scroll
|
|
39
|
+
checks + 1 post-loop re-check). The phantom-screenshot fix's real-world
|
|
40
|
+
trigger was itself observed live on 2026-07-11 (an error message named a
|
|
41
|
+
`debug_no_mode_trigger.png` that was never written — the empty directory is
|
|
42
|
+
the evidence); post-fix, capture failures return `None` and every message
|
|
43
|
+
appends its `Screenshot:` clause conditionally.
|
|
44
|
+
|
|
45
|
+
Deferred-with-reason: no credited generation was needed for this patch — no
|
|
46
|
+
generation-path behavior changed.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Live verification — v0.33.0 (2026-07-12)
|
|
2
|
+
|
|
3
|
+
Both user-facing changes exercised against live Flow on the merged `develop`
|
|
4
|
+
tip (`669884c`), profile `denon82`, before tagging. Evidence follows the
|
|
5
|
+
5-layer ledger (file count · magic bytes · dimensions/shape · structlog
|
|
6
|
+
invariants · user-confirmable artifact).
|
|
7
|
+
|
|
8
|
+
## 1. #287 follow-up (PR #296) — picker reaches assets deep in a crowded project
|
|
9
|
+
|
|
10
|
+
**Command (the original repro, verbatim surface):**
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
gflow video i2v 2f72aa2a-5081-4df8-a3b1-64b6a859274c "slow dolly-in, soft light shifting across the scene" \
|
|
14
|
+
--project f6caf027-ad68-49e3-aac9-ee32f1582bf3 --profile denon82 --out-dir <scratch>/330-e2e-i2v
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
- **Exit code:** `0` (v0.32.1 failed this exact command with `TransportTimeoutError`, exit 9).
|
|
18
|
+
- **File count:** 1 mp4 produced.
|
|
19
|
+
- **Magic bytes:** `ftyp` at offset 4 — real ISO-BMFF video.
|
|
20
|
+
- **Size:** 3.0 MB.
|
|
21
|
+
- **Structlog invariants:** `picker_project_name_resolved` → `picker_project_already_active`
|
|
22
|
+
(alignment machinery confirms the picker was on the target project; no switch needed)
|
|
23
|
+
→ `frame_ref_attached` → `video_saved`. Zero upload events — the in-project asset
|
|
24
|
+
was referenced, not re-uploaded.
|
|
25
|
+
- **Artifact:** `497d687d-86cc-4bcc-aeab-878a1d1c4fae.mp4` (scratch dir; user-inspectable).
|
|
26
|
+
|
|
27
|
+
## 2. #241 (PR #297) — anti-bot jitter: configurable, small default, t2i path paced
|
|
28
|
+
|
|
29
|
+
**Round 1 (pre-merge, flag path):** `gflow image t2i "<p1>" "<p2>" --jitter 4-6` →
|
|
30
|
+
`batch_jitter_sleep {"seconds": 4.98, "index": 1}` — exactly one pause, between the
|
|
31
|
+
two submissions (never before the first), within the requested 4–6 s. Prompt 1
|
|
32
|
+
saved a real jpg.
|
|
33
|
+
|
|
34
|
+
**Round 2 (post-merge, default path):** same 2-prompt t2i with NO `--jitter` flag →
|
|
35
|
+
`batch_jitter_sleep {"seconds": 1.47, "index": 1}` — within the new 0.5–1.5 s
|
|
36
|
+
default, fired once. This is the release-defining behavior: the previously
|
|
37
|
+
unpaced t2i multi-prompt path now paces, at the minimal default.
|
|
38
|
+
|
|
39
|
+
**Environment finding (pre-existing, NOT a regression):** both round-2 generations
|
|
40
|
+
failed post-submission with the #281 `MediaAttributionError` (exit 26), and
|
|
41
|
+
`prefer_classic.cohort_natively_agentic` fired — the `denon82` cohort now mounts
|
|
42
|
+
Flow's agentic UI unconditionally and `GFLOW_CLI_PREFER_CLASSIC=1` cannot exit it.
|
|
43
|
+
The identical failure mode exists on released v0.32.1 (same cohort); the jitter
|
|
44
|
+
feature under verification is upstream of generation and verified by the sleep
|
|
45
|
+
event + the round-1 saved jpg. Tracked in KNOWN_ISSUES (agentic attribution) —
|
|
46
|
+
candidate for the next issue wave.
|
|
47
|
+
|
|
48
|
+
## Not verified this cycle
|
|
49
|
+
|
|
50
|
+
- `GFLOW_CLI_JITTER_RANGE` via a real `.env` file on a live run (covered by unit
|
|
51
|
+
tests through pydantic-settings; env-var form is equivalent at the Settings layer).
|
|
52
|
+
- `gflow run --config` image-batch pacing (same `run_sequential_batch` code path
|
|
53
|
+
as t2i, verified live above; `gflow run` adds only config parsing on top).
|
|
@@ -163,6 +163,11 @@ Options:
|
|
|
163
163
|
--continue-on-error /
|
|
164
164
|
--fail-fast Continue after per-prompt failures or stop at the
|
|
165
165
|
first failed prompt. [default: continue-on-error]
|
|
166
|
+
--jitter SPEC Anti-bot pause between multi-prompt submissions:
|
|
167
|
+
'MIN-MAX' seconds, a single number for 0-N, or 0
|
|
168
|
+
to disable. Widen (e.g. 10-30) if runs hit WAF
|
|
169
|
+
403s. [default: 0.5-1.5; GFLOW_CLI_JITTER_RANGE
|
|
170
|
+
overrides the default]
|
|
166
171
|
--model [nano2|nano-pro|image4]
|
|
167
172
|
Image model alias. [default: nano2]
|
|
168
173
|
--aspect [9:16|16:9|1:1|4:3|3:4]
|
|
@@ -394,11 +399,12 @@ Example: [`test_assets/sample_batch.json`](../test_assets/sample_batch.json).
|
|
|
394
399
|
|
|
395
400
|
### Session behaviour
|
|
396
401
|
|
|
397
|
-
All prompts in a batch share one Flow project. The editor is opened once; each prompt is submitted in turn with a random
|
|
402
|
+
All prompts in a batch share one Flow project. The editor is opened once; each prompt is submitted in turn with a small random pause between submissions (default 0.5–1.5 seconds; tune with `--jitter` or `GFLOW_CLI_JITTER_RANGE`). This jitter is a **submission-cadence anti-bot-detection measure** — it spaces out the submission clicks, not the generation wait. All generations run in parallel inside Flow; only the click timing is jittered. The command returns once every submitted generation has resolved (success or failure), not after the last click. See [DEBUGGING § WAF cadence](DEBUGGING.md#waf-cadence) for when to widen the range.
|
|
398
403
|
|
|
399
404
|
### Flags
|
|
400
405
|
|
|
401
406
|
- `--continue-on-error` / `--fail-fast` — keep going past row failures or stop at the first one (default: `--fail-fast`). On fail-fast, already-completed images are downloaded before the error is surfaced.
|
|
407
|
+
- `--jitter SPEC` — anti-bot pause between submissions: `MIN-MAX` seconds (e.g. `10-30`), a single number for `0`–`N`, or `0` to disable. Default `0.5-1.5`; widen if runs hit WAF 403s. `GFLOW_CLI_JITTER_RANGE` overrides the default, the flag beats both.
|
|
402
408
|
|
|
403
409
|
### Limits
|
|
404
410
|
|
|
@@ -25,6 +25,7 @@ This module is the single place that reconciles the two engines' differences:
|
|
|
25
25
|
|
|
26
26
|
from __future__ import annotations
|
|
27
27
|
|
|
28
|
+
import asyncio
|
|
28
29
|
import importlib
|
|
29
30
|
from typing import TYPE_CHECKING, Any, cast
|
|
30
31
|
|
|
@@ -48,6 +49,66 @@ _PATCHRIGHT_PIP_HINT = (
|
|
|
48
49
|
)
|
|
49
50
|
|
|
50
51
|
|
|
52
|
+
# Teardown deadlines (issue #293). The graceful bound is generous on purpose:
|
|
53
|
+
# for a persistent context, close() is Chrome flushing profile state (the
|
|
54
|
+
# cookie DB is this product's auth store) and Playwright's driver hard-kills
|
|
55
|
+
# the process if a second close arrives mid-graceful-close — so escalating a
|
|
56
|
+
# merely-slow close would risk corrupting the profile. The force bound is
|
|
57
|
+
# short: it only matters when the graceful close already failed, and a dead
|
|
58
|
+
# driver connection would otherwise burn the full window as pure delay.
|
|
59
|
+
_CONTEXT_CLOSE_TIMEOUT_S = 30.0
|
|
60
|
+
_FORCE_CLOSE_TIMEOUT_S = 5.0
|
|
61
|
+
DRIVER_STOP_TIMEOUT_S = 10.0
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
async def _force_close_browser(context: Any, *, owner: str) -> None:
|
|
65
|
+
"""Force-close ``context.browser``, bounded — never raises Exception."""
|
|
66
|
+
try:
|
|
67
|
+
browser = context.browser
|
|
68
|
+
if browser is not None:
|
|
69
|
+
await asyncio.wait_for(browser.close(), timeout=_FORCE_CLOSE_TIMEOUT_S)
|
|
70
|
+
logger.warning(
|
|
71
|
+
"browser_teardown.force_close_returned",
|
|
72
|
+
owner=owner,
|
|
73
|
+
note=(
|
|
74
|
+
"graceful context close failed; browser.close() returned "
|
|
75
|
+
"(the browser may also simply have been gone already)"
|
|
76
|
+
),
|
|
77
|
+
)
|
|
78
|
+
except Exception:
|
|
79
|
+
# ponytail: an OS-level tree kill needs a chrome PID that
|
|
80
|
+
# playwright-python does not expose for persistent contexts.
|
|
81
|
+
logger.error(
|
|
82
|
+
"browser_teardown.force_close_failed",
|
|
83
|
+
owner=owner,
|
|
84
|
+
remediation=(
|
|
85
|
+
"a browser may still hold this profile dir — close Chrome "
|
|
86
|
+
"windows using it, or kill the Chrome processes (chrome.exe "
|
|
87
|
+
"on Windows) whose command line names the profile dir"
|
|
88
|
+
),
|
|
89
|
+
exc_info=True,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
async def close_context_bounded(context: Any, *, owner: str) -> None:
|
|
94
|
+
"""Close a persistent BrowserContext without hanging or leaking chrome.
|
|
95
|
+
|
|
96
|
+
Issue #293: an unbounded ``context.close()`` can hang forever on a wedged
|
|
97
|
+
page, and a close that FAILS leaves the system-Chrome tree alive holding
|
|
98
|
+
the profile dir once the driver stops — the graceful close is what asks
|
|
99
|
+
Chrome to exit. Bounds the graceful close, then force-closes the browser
|
|
100
|
+
on any failure. On cancellation (Ctrl-C mid-teardown) the force-close is
|
|
101
|
+
still attempted best-effort before the cancellation propagates.
|
|
102
|
+
"""
|
|
103
|
+
try:
|
|
104
|
+
await asyncio.wait_for(context.close(), timeout=_CONTEXT_CLOSE_TIMEOUT_S)
|
|
105
|
+
except BaseException as exc:
|
|
106
|
+
logger.warning("browser_teardown.context_close_error", owner=owner, exc_info=True)
|
|
107
|
+
await _force_close_browser(context, owner=owner)
|
|
108
|
+
if not isinstance(exc, Exception):
|
|
109
|
+
raise
|
|
110
|
+
|
|
111
|
+
|
|
51
112
|
def active_engine() -> BrowserEngine:
|
|
52
113
|
"""Return the configured browser engine (validated ``BrowserEngine`` enum)."""
|
|
53
114
|
return get_settings().browser_engine
|
|
@@ -29,7 +29,9 @@ from playwright.async_api import BrowserContext, Page, Playwright, async_playwri
|
|
|
29
29
|
|
|
30
30
|
from gflow_cli.api import routes
|
|
31
31
|
from gflow_cli.api._engine import (
|
|
32
|
+
DRIVER_STOP_TIMEOUT_S,
|
|
32
33
|
active_engine,
|
|
34
|
+
close_context_bounded,
|
|
33
35
|
log_engine_selected,
|
|
34
36
|
mint_evaluate_kwargs,
|
|
35
37
|
resolve_async_playwright,
|
|
@@ -57,6 +59,7 @@ from gflow_cli.errors import (
|
|
|
57
59
|
ContentPolicyError,
|
|
58
60
|
FlowApiError, # re-exported via gflow_cli.api.__init__
|
|
59
61
|
NetworkError,
|
|
62
|
+
ProfileLockedError,
|
|
60
63
|
RateLimitError,
|
|
61
64
|
SceneConcatError,
|
|
62
65
|
TransportTimeoutError,
|
|
@@ -547,7 +550,34 @@ class FlowApiClient:
|
|
|
547
550
|
await self._preread_flow_session_cookies()
|
|
548
551
|
kwargs = self._persistent_context_kwargs()
|
|
549
552
|
self._log_and_guard_launch(kwargs)
|
|
550
|
-
|
|
553
|
+
try:
|
|
554
|
+
self._context = await self._pw.chromium.launch_persistent_context(**kwargs)
|
|
555
|
+
except Exception as exc:
|
|
556
|
+
if _is_target_closed(exc):
|
|
557
|
+
# A TargetClosedError at LAUNCH usually means the profile dir
|
|
558
|
+
# is held by another Chrome — most commonly a stale browser
|
|
559
|
+
# leaked by a crashed prior run (issue #293) or a concurrent
|
|
560
|
+
# gflow run. It CAN also be an unrelated startup crash, so
|
|
561
|
+
# the original error is carried in the detail and the wording
|
|
562
|
+
# hedges rather than asserts.
|
|
563
|
+
first_line = str(exc).splitlines()[0] if str(exc) else type(exc).__name__
|
|
564
|
+
raise ProfileLockedError(
|
|
565
|
+
detail=(
|
|
566
|
+
f"the browser exited immediately while launching on profile "
|
|
567
|
+
f"dir {self.profile_dir} — most likely another Chrome holds "
|
|
568
|
+
f"it (original error: {first_line})"
|
|
569
|
+
),
|
|
570
|
+
remediation_hint=(
|
|
571
|
+
"Another gflow run — or a stale Chrome left by a crashed one "
|
|
572
|
+
"— may hold this profile. Close Chrome windows using this "
|
|
573
|
+
"profile (or kill the Chrome processes — chrome.exe on "
|
|
574
|
+
"Windows — whose command line names the profile dir) and "
|
|
575
|
+
"re-run, or use a different --profile. If no such process "
|
|
576
|
+
"exists, the browser crashed at startup for another reason; "
|
|
577
|
+
"see the original error above."
|
|
578
|
+
),
|
|
579
|
+
) from exc
|
|
580
|
+
raise
|
|
551
581
|
# Hide the automation flag so reCAPTCHA Enterprise doesn't score
|
|
552
582
|
# the session as a bot — navigator.webdriver=true causes low-score
|
|
553
583
|
# tokens and HTTP 403 on batchGenerateImages.
|
|
@@ -655,23 +685,26 @@ class FlowApiClient:
|
|
|
655
685
|
silently swallow). Resets the browser fields so a reused client never
|
|
656
686
|
holds references to a dead BrowserContext.
|
|
657
687
|
"""
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
688
|
+
try:
|
|
689
|
+
if self._context is not None:
|
|
690
|
+
# Bounded close + force-close fallback (issue #293) — shared
|
|
691
|
+
# with the transports' own-context teardowns via _engine.
|
|
692
|
+
await close_context_bounded(self._context, owner="client")
|
|
693
|
+
if self._pw is not None:
|
|
694
|
+
try:
|
|
695
|
+
# pw.stop() awaits the Node driver's exit with no deadline
|
|
696
|
+
# of its own — a wedged driver would hang teardown forever.
|
|
697
|
+
await asyncio.wait_for(self._pw.stop(), timeout=DRIVER_STOP_TIMEOUT_S)
|
|
698
|
+
except Exception:
|
|
699
|
+
logger.warning("playwright_stop_error", exc_info=True)
|
|
700
|
+
finally:
|
|
701
|
+
# Field resets must survive even cancellation (Ctrl-C mid-close),
|
|
702
|
+
# or a reused client holds references to a dead BrowserContext.
|
|
703
|
+
self._pages = []
|
|
704
|
+
self._page_queue = None
|
|
705
|
+
self._page = None
|
|
706
|
+
self._context = None
|
|
707
|
+
self._pw = None
|
|
675
708
|
|
|
676
709
|
async def _checkout_page(self) -> Page:
|
|
677
710
|
"""Block until a Page is available from the pool; FIFO.
|
|
@@ -19,12 +19,13 @@ Wire facts (reverse-engineered, see ``docs/IMAGE_UPSCALE_RECON.md``)::
|
|
|
19
19
|
|
|
20
20
|
from __future__ import annotations
|
|
21
21
|
|
|
22
|
-
import re
|
|
23
22
|
from dataclasses import dataclass, field
|
|
24
23
|
from enum import StrEnum
|
|
25
24
|
from types import MappingProxyType
|
|
26
25
|
from typing import Any
|
|
27
26
|
|
|
27
|
+
from gflow_cli.api.video import is_media_uuid
|
|
28
|
+
|
|
28
29
|
__all__ = [
|
|
29
30
|
"DEFAULT_PAYGATE_TIER",
|
|
30
31
|
"TargetResolution",
|
|
@@ -45,10 +46,6 @@ DEFAULT_PAYGATE_TIER = "PAYGATE_TIER_ONE"
|
|
|
45
46
|
# Strict UUID allowlist for the source mediaId and the owning projectId. Both
|
|
46
47
|
# are interpolated into the request body (not a URL path), but validating up
|
|
47
48
|
# front rejects malformed input before any reCAPTCHA mint or network call fires
|
|
48
|
-
# (scenario #9). Flow media + project ids are both 8-4-4-4-12 hex UUIDs.
|
|
49
|
-
_UUID_RE = re.compile(
|
|
50
|
-
r"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"
|
|
51
|
-
)
|
|
52
49
|
|
|
53
50
|
|
|
54
51
|
class TargetResolution(StrEnum):
|
|
@@ -114,13 +111,13 @@ class UpsampleImageRequest:
|
|
|
114
111
|
recaptcha_token: str = field(default="")
|
|
115
112
|
|
|
116
113
|
def __post_init__(self) -> None:
|
|
117
|
-
if not
|
|
114
|
+
if not is_media_uuid(self.media_id):
|
|
118
115
|
msg = (
|
|
119
116
|
f"UpsampleImageRequest.media_id must be a bare UUID "
|
|
120
117
|
f"(8-4-4-4-12 hex, no whitespace); got {self.media_id!r}"
|
|
121
118
|
)
|
|
122
119
|
raise ValueError(msg)
|
|
123
|
-
if not
|
|
120
|
+
if not is_media_uuid(self.project_id):
|
|
124
121
|
msg = (
|
|
125
122
|
f"UpsampleImageRequest.project_id must be a bare UUID "
|
|
126
123
|
f"(8-4-4-4-12 hex, no whitespace); got {self.project_id!r}"
|