gflow-cli 0.31.0__tar.gz → 0.32.1__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.31.0 → gflow_cli-0.32.1}/AGENTS.md +1 -1
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/CHANGELOG.md +29 -1
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/KNOWN_ISSUES.md +66 -3
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/PKG-INFO +1 -1
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/INDEX.md +1 -1
- gflow_cli-0.32.1/docs/LIVE_VERIFICATION_v0.32.0.md +74 -0
- gflow_cli-0.32.1/docs/LIVE_VERIFICATION_v0.32.1.md +46 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/MCP.md +1 -1
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/USAGE.md +24 -9
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/pyproject.toml +1 -1
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/dev/spike_movie_attach_payload.py +1 -1
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/skills/gflow-cli/SKILL.md +2 -2
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/__init__.py +1 -1
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/api/_engine.py +61 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/api/client.py +51 -18
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/api/image_upscale.py +4 -7
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/api/transports/drivers/agentic.py +17 -4
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/api/transports/drivers/classic.py +1 -1
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/api/transports/experimental/evaluate_fetch.py +3 -3
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/api/transports/ui_automation.py +26 -11
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/api/transports/ui_automation_video.py +101 -17
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/api/video.py +44 -2
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/cli_image.py +9 -14
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/cli_instructions.py +3 -9
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/cli_video.py +57 -25
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/data/recorder.py +3 -3
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/errors.py +37 -2
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/mcp/tools.py +3 -7
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/test_concurrency.py +151 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/test_video.py +47 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/transports/drivers/test_agentic.py +84 -0
- gflow_cli-0.32.1/tests/api/transports/drivers/test_classic.py +68 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/transports/test_ui_automation_video.py +205 -3
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/cli/test_cli_video.py +84 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/test_errors.py +11 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/uv.lock +1 -1
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/.claude/README.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/.claude/commands/gflow/active.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/.claude/commands/gflow/branch-review.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/.claude/commands/gflow/changelog.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/.claude/commands/gflow/check.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/.claude/commands/gflow/doc-review.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/.claude/commands/gflow/issue-assessment.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/.claude/commands/gflow/issue-resolve.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/.claude/commands/gflow/known-issues.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/.claude/commands/gflow/next.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/.claude/commands/gflow/plan.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/.claude/commands/gflow/pr-council-review.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/.claude/commands/gflow/predict.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/.claude/commands/gflow/release.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/.claude/commands/gflow/scenario.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/.claude/commands/gflow/sonar.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/.claude/commands/gflow/status.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/.env.template +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/.gitattributes +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/.github/CODEOWNERS +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/.github/copilot-instructions.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/.github/dependabot.yml +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/.github/workflows/ci.yml +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/.github/workflows/external-pr-triage.yml +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/.github/workflows/governance-advisory.yml +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/.github/workflows/governance-benchmark.yml +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/.github/workflows/main-base-guard.yml +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/.github/workflows/release.yml +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/.gitignore +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/.gitleaks.toml +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/.pre-commit-config.yaml +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/.secrets.baseline +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/CLAUDE.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/CONFIGURATION.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/CONTRIBUTING.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/DISCLAIMER.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/GEMINI.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/LICENSE +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/PLAN.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/README.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/RELEASE.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/ROADMAP.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/conftest.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docker-compose.yml +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/AGENT_GUIDE.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/AGENT_UI_E2E.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/AGENT_UI_RECON.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/ARCHITECTURE.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/AUTHENTICATION.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/CHARACTER.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/CHARACTER_RECON.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/CONFIGURATION.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/DATA_LAYER.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/DEBUGGING.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/DEMOS.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/DEVELOPMENT.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/E2E_TESTING.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/EXTERNAL_STORAGE.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/GITHUB.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/GOVERNANCE_BENCHMARK.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/IMAGE_UPSCALE_RECON.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/INSTRUCTIONS.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_data_layer.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_image_batch.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.10.0.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.11.0.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.12.0.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.13.0.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.16.0.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.17.0.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.18.0.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.19.0.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.20.0.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.20.1.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.21.0.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.22.0.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.23.0.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.24.0.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.25.0.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.26.0.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.27.0.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.27.1.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.28.0.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.29.0.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.30.0.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.31.0.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.7.0.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.8.1.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.9.0.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.9.1.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_video_download.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/MOVIE.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/PROJECT_STATUS.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/PROMPT_EXPANSION.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/SECURITY.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/TOOLS.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/USER_GUIDE.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/assets/demo-split-pf.gif +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/assets/example-run.gif +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/assets/examples.webp +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/medium_tutorial.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/schemas/movie-handoff.schema.json +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/sonar-cleanup-tracker.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/superpowers/plans/2026-06-12-issue-174-library-ui-attach/PLAN.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/superpowers/plans/2026-06-22-mcp-server/PLAN.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/superpowers/plans/2026-06-22-mcp-server/PREDICT.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/superpowers/plans/2026-06-22-mcp-server/SCENARIO.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/superpowers/plans/2026-06-22-prompt-expansion/PLAN.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/superpowers/plans/2026-06-22-prompt-expansion/PREDICT.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/superpowers/plans/2026-06-22-prompt-expansion/SCENARIO.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/superpowers/plans/2026-06-24-gflow-studio-scaffold/PLAN.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/superpowers/plans/2026-06-24-rest-api-layer/PLAN.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/superpowers/plans/2026-06-26-mcp-e2e-test/PLAN.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/superpowers/plans/2026-07-09-camoufox-adoption/PLAN.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/superpowers/research/2026-06-27-tool-abstraction-evaluation.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/superpowers/specs/2026-06-29-issue-assessment-workflow-design.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/docs/superpowers/spikes/2026-07-09-camoufox-waf-403.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/examples/README.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/examples/batch_from_config.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/examples/multi_prompt_t2i.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/examples/sample_config.json +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/examples/sample_prompts.txt +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/examples/single_image_t2i.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/examples/workflow_chain.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/llms.txt +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/samples/README.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/samples/captured/01_upload_image.json +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/samples/captured/02_batchAsyncGenerateVideoText.json +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/samples/captured/03_batchCheckAsyncVideoGenerationStatus.json +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/samples/captured/04_archive_workflow.json +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/samples/captured/05_createProject.json +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/samples/captured/06_batchGenerateImages.json +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/samples/captured/07_batchGenerateImages_seeded.json +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/samples/captured/08_batchAsyncGenerateVideoStartAndEndImage.json +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/samples/captured/09_batchAsyncGenerateVideoReferenceImages.json +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/samples/captured/10_batchCheckAsyncVideoGenerationStatus_successful.json +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/samples/captured/11_batchCheckAsyncVideoGenerationStatus_failed.json +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/samples/captured/12_create_scene.json +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/samples/captured/13_sceneWorkflows_update.json +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/samples/captured/14_get_scene_workflows.json +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/samples/captured/15_commit_flowWorkflow.json +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/ci/check_doc_links.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/ci/check_materiality.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/ci/check_repo_hygiene.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/debug_editor.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/debug_gen_settings.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/debug_settings.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/dev/_recording_client.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/dev/_spike_common.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/dev/active_plan.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/dev/analyze_agent_ui_capture.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/dev/capture_i2v_frame_slots_dom.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/dev/capture_i2v_intercept_submit.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/dev/capture_i2v_model_select_repro.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/dev/capture_i2v_post_bind_state.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/dev/capture_image_add_media_dom.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/dev/capture_locale_invariants.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/dev/cdp_drive_and_probe.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/dev/character_create_spike.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/dev/character_create_spike_v2.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/dev/dump_character_selectors.js +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/dev/make_project.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/dev/materiality_backtest.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/dev/monitor_pr_38.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/dev/patch_character.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/dev/skillopt/README.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/dev/skillopt/harness.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/dev/skillopt/tasks.json +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/dev/spike_char_editor_dom.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/dev/spike_char_gen_capture.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/dev/spike_image_upscale_capture.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/dev/spike_image_upscale_drive.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/dev/spike_image_upscale_recaptcha_action.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/dev/spike_image_upscale_rest_probe.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/dev/spike_issue170_picker_locale_recon.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/dev/spike_issue174_library_ui_recon.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/dev/spike_movie_entity_recon.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/dev/spike_movie_gen_capture.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/dev/spike_movie_picker_select.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/dev/spike_movie_voice_list.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/dev/spike_patch_entity.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/dev/spike_patchright.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/diag/README.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/diag/capture_flow_traffic.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/diag/memory_profile.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/diag/recaptcha_mint.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/e2e/agentic_image_e2e.ps1 +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/e2e/capture_agent_toggle.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/record_demo.ps1 +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/smoke_image.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/smoke_real_chrome_image.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/smoke_video_editor.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/smoke_worker_style.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/spike_waf_camoufox.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/scripts/verify_chrome_auth_viability.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/skills/README.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/skills/issue-assessment/SKILL.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/skills/issue-resolve/SKILL.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/skills/plan/SKILL.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/skills/pr-council-review/SKILL.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/skills/predict/SKILL.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/skills/scenario/SKILL.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/skills/status/SKILL.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/sonar-project.properties +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/__main__.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/_cli_helpers.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/api/__init__.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/api/_retry.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/api/_sapisidhash.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/api/character.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/api/dto.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/api/image.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/api/recaptcha.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/api/routes.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/api/scene.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/api/transports/__init__.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/api/transports/_common.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/api/transports/_fingerprint.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/api/transports/base.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/api/transports/drivers/__init__.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/api/transports/drivers/base.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/api/transports/drivers/factory.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/api/transports/experimental/__init__.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/api/transports/experimental/bearer.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/api/transports/experimental/sapisidhash.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/auth/__init__.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/auth/base.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/auth/cookies.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/auth/factory.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/auth/internal_chromium.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/auth/real_chrome.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/auth/strategies.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/auth/verification.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/browser_manager.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/chain.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/chain_manifest.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/cli.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/cli_character.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/cli_data.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/cli_models.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/cli_movie.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/cli_run.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/cli_scene.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/cli_tools.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/composition.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/config.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/data/__init__.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/data/chain_repo.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/data/migrations/0001_initial.sql +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/data/migrations/0002_add_cloud_storage.sql +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/data/migrations/0003_add_scene_tables.sql +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/data/migrations/0004_add_scene_output_path.sql +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/data/migrations/0005_add_chain_links.sql +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/data/migrations/0006_add_operations_metadata.sql +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/data/migrations/0007_queue.sql +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/data/migrations/0008_add_operation_expanded_prompt.sql +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/data/migrations/__init__.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/data/models.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/data/queries.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/data/redaction.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/data/repository.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/data/store.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/exceptions.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/image_batch.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/json_output.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/manifest.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/mcp/__init__.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/mcp/prompts.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/mcp/resources.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/mcp/server.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/media.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/movie_manifest.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/observability.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/paths.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/profile_store.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/services/__init__.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/services/character_create.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/storage.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/tools/__init__.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/tools/banned.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/tools/builtin/__init__.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/tools/builtin/creative-director.toml +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/tools/expander.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/tools/invocation.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/tools/loader.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/tools/registry.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/tools/runtime.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/tools/spec.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/ui/app.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/ui/server.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/worker/daemon.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/src/gflow_cli/worker/queue.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tasks/lessons.md +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/test_assets/sample_batch.json +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/test_assets/sample_batch.tsv +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/test_assets/sample_batch_invalid.tsv +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/__init__.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/__init__.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/fixtures/character_gen_response.json +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/fixtures/patch_entity_response.json +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/test_aisandbox_auth_error.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/test_aisandbox_auth_headers.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/test_bearer_redaction.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/test_character.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/test_client.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/test_client_character.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/test_client_delete_characters.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/test_client_generate_character.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/test_client_image.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/test_client_launch_kwargs.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/test_client_patch_entity.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/test_client_scene.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/test_client_upscale.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/test_dto.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/test_engine.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/test_image.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/test_image_dto.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/test_image_upscale.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/test_post_json_aisandbox_auth.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/test_recaptcha.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/test_retry.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/test_routes.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/test_routes_character.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/test_routes_scene.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/test_sapisidhash_helper.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/test_scene_models.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/test_video_request.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/transports/__init__.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/transports/conftest.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/transports/drivers/__init__.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/transports/drivers/test_factory.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/transports/test_base.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/transports/test_bearer.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/transports/test_common.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/transports/test_evaluate_fetch.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/transports/test_factory.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/transports/test_fingerprint.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/transports/test_sapisidhash.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/transports/test_selector_symmetry.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/transports/test_transport_timeout.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/transports/test_ui_automation.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/transports/test_ui_automation_batch.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/transports/test_ui_automation_image_mode.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/api/transports/test_ui_character_editor.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/auth/strategies/test_factory.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/auth/strategies/test_strategies.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/auth/test_cookies.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/auth/test_verification.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/cli/__init__.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/cli/test_cli_auth_list.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/cli/test_cli_character.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/cli/test_cli_character_create.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/cli/test_cli_data.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/cli/test_cli_image.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/cli/test_cli_image_seed_removed.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/cli/test_cli_image_selector_drift.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/cli/test_cli_image_upscale.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/cli/test_cli_instructions.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/cli/test_cli_models.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/cli/test_cli_movie.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/cli/test_cli_run.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/cli/test_cli_scene.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/cli/test_cli_tools.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/cli/test_cli_video_chain.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/cli/test_error_handling.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/cli/test_helpers.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/cli/test_movie_manifest.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/cli/test_movie_manifest_style_variants.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/cli/test_settings_validation.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/cli/test_t2i_multi_prompt.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/composition/test_character.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/composition/test_compose_prompt.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/composition/test_handoff.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/composition/test_style_variants.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/composition/test_style_variants_e2e.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/conftest.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/data/__init__.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/data/test_chain_repo.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/data/test_find_incomplete_character.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/data/test_models.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/data/test_packaging.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/data/test_recorder.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/data/test_recorder_character.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/data/test_redaction.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/data/test_repository.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/data/test_scene_persistence.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/data/test_settings_and_errors.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/data/test_store_migrations.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/dev/test_recording_client.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/e2e/__init__.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/e2e/conftest.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/e2e/test_aisandbox_auth_live.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/e2e/test_auth_verification_e2e.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/e2e/test_chain_e2e.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/e2e/test_character_create_e2e.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/e2e/test_daemon_e2e.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/e2e/test_data_layer_e2e.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/e2e/test_i2v_flags_e2e.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/e2e/test_image_batch_e2e.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/e2e/test_image_i2i_ref_cap_e2e.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/e2e/test_json_output_e2e.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/e2e/test_live_agentic_instructions.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/e2e/test_locale_selectors_e2e.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/e2e/test_scene_compose_live.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/e2e/test_tools_e2e.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/e2e/test_transports_e2e.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/e2e/test_video_r2v_ref_cap_e2e.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/e2e/test_video_t2v_e2e.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/features/__init__.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/features/auth.feature +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/features/auth_login.feature +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/features/character_create.feature +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/features/character_read.feature +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/features/conftest.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/features/image.feature +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/features/image_upscale.feature +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/features/instructions.feature +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/features/locale_picker_include.feature +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/features/test_auth_login_steps.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/features/test_auth_steps.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/features/test_character_create_steps.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/features/test_character_read_steps.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/features/test_image_steps.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/features/test_image_upscale_steps.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/features/test_instructions_steps.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/features/test_locale_picker_include_steps.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/features/test_step_collision_guard.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/features/test_video_agent_ui_steps.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/features/test_video_chain_steps.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/features/video_agent_ui.feature +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/features/video_chain.feature +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/fixtures/__init__.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/fixtures/seeded_catalog.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/image_batch/__init__.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/image_batch/test_image_manifest.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/image_batch/test_observability_events.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/integration/__init__.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/integration/conftest.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/integration/constants.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/integration/test_storage_gcs.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/integration/test_storage_s3.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/mcp/__init__.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/mcp/conftest.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/mcp/test_cli_parity.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/mcp/test_server.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/mcp/test_stdio_transport.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/mcp/test_tools_helpers.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/mcp/test_tools_instructions.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/mcp/test_tools_wired.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/scripts/test_analyze_agent_ui_capture.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/scripts/test_capture_locale_invariants.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/scripts/test_check_materiality.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/scripts/test_check_repo_hygiene.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/scripts/test_materiality_backtest.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/services/__init__.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/services/test_character_create_redaction.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/services/test_character_create_saga.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/services/test_character_gen_no_direct_post.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/smoke/__init__.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/smoke/test_profile_account_smoke.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/smoke/test_real_flow.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/test_auth.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/test_browser_manager.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/test_chain.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/test_chain_manifest.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/test_cli_data.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/test_config.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/test_conftest_isolation.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/test_data_queries.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/test_documentation_gate.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/test_errors_403.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/test_json_output.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/test_manifest.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/test_marker_registry.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/test_media.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/test_observability.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/test_paths.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/test_profile_store.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/test_smoke.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/tools/__init__.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/tools/test_banned.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/tools/test_expander.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/tools/test_invocation.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/tools/test_loader.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/tools/test_registry.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/tools/test_runtime.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/tools/test_spec.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/ui/test_app.py +0 -0
- {gflow_cli-0.31.0 → gflow_cli-0.32.1}/tests/worker/test_daemon.py +0 -0
|
@@ -57,7 +57,7 @@ Or invoke the wrapper: `/gflow:check`.
|
|
|
57
57
|
|
|
58
58
|
- Type hints everywhere; `pyright` strict on `src/gflow_cli`.
|
|
59
59
|
- Structured logging only (`structlog`) — **never** raw `print()` or `import logging` in `src/`.
|
|
60
|
-
- Errors as RFC 9457 Problem Details with stable per-class exit codes (3–
|
|
60
|
+
- Errors as RFC 9457 Problem Details with stable per-class exit codes (3–27, e.g. 16 is the `DataStoreError` family, 19 `SceneConcatError`, 20 `FrameExtractionError`, 21 `ChainPartialError`, 22 `UpscaleUnavailableError`, 25 `FlowAgentUiError`). See `src/gflow_cli/errors.py::EXIT_CODE_MAP` for the complete mapping.
|
|
61
61
|
- 100-char line length, `ruff` configured. Imports sorted by `ruff` (isort rules).
|
|
62
62
|
- **MCP & CLI Schema Symmetry**: Any updates or additions to user-facing CLI command parameters (e.g., `gflow image t2i`, `gflow video`) must be mirrored in the corresponding MCP tool definitions. Never add option/argument fields to Click commands without updating the MCP server implementation. This symmetry is enforced programmatically in CI via `tests/mcp/test_cli_parity.py` (every CLI leaf command needs a mapped MCP tool or an explicit, reasoned exemption) plus the schema checks in `tests/mcp/test_server.py`.
|
|
63
63
|
|
|
@@ -7,6 +7,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.32.1] — 2026-07-11
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **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).
|
|
15
|
+
|
|
16
|
+
- **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).
|
|
17
|
+
- **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.
|
|
18
|
+
- **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).
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
- **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).
|
|
23
|
+
|
|
24
|
+
## [0.32.0] — 2026-07-11
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- **`gflow video i2v` accepts an in-project asset media UUID for `--initial-frame` / `--end-frame` (and the positional IMAGE).** A UUID-shaped value selects the already-existing Flow asset in place via the same `_select_existing_asset` picker the image `--ref` flow uses (#282 scroll/search fixes included) instead of forcing a duplicate local-file upload — the duplicate-asset pileup and per-run re-upload from the 2026-07-11 chalkboard pilot. Pair with `--project` so the asset's project is the one generated in; a UUID that can't be located in the picker fails with `TransportTimeoutError` (exit 9) naming the slot and UUID (#287).
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
|
|
32
|
+
- **A Flow upload-endpoint rejection is now a typed error instead of "Unexpected error." (exit 1).** An `uploadImage` 4xx during frame/reference attach (observed live: one JPEG rejected with HTTP 400 while byte-identical-format siblings uploaded fine) raised a bare `RuntimeError` that fell through to the generic handler with no hint the *input image* was refused. It now raises the new `MediaUploadRejectedError` (**exit code 27**, RFC 9457 type `media-upload-rejected`) with a re-encode remediation hint (`ffmpeg -q:v 2 -map_metadata -1`) (#287).
|
|
33
|
+
- **An explicit `--duration` that cannot be applied now fails fast instead of silently producing a clip of Flow's default length.** When the video settings panel's duration tab probe missed (observed 3/3 on a live 2026-07-11 Frames-submode run: `--duration 4` returned an 8-second clip and the JSON result reported success), `_select_video_duration` demoted the failure to a warning and generation continued on Flow's default. Duration is a contract parameter — downstream timeline math sizes cuts from the requested value — so a probe miss with an explicit `--duration` now raises `UiSelectorDriftError` (**exit code 23**, the #183 selector-drift semantics) with a `debug_no_duration_tab.png` viewport screenshot and an omit-`--duration` remediation hint. Omitting `--duration` is unaffected. Root cause confirmed live 2026-07-11: the duration control is absent from the affected cohort's settings popover (#288, #289).
|
|
34
|
+
|
|
10
35
|
## [0.31.0] — 2026-07-10
|
|
11
36
|
|
|
12
37
|
### Fixed
|
|
@@ -1908,7 +1933,10 @@ shell-script template that branches on these codes.
|
|
|
1908
1933
|
|
|
1909
1934
|
First skeleton. Not functional end-to-end yet.
|
|
1910
1935
|
|
|
1911
|
-
[Unreleased]: https://github.com/ffroliva/gflow-cli/compare/v0.
|
|
1936
|
+
[Unreleased]: https://github.com/ffroliva/gflow-cli/compare/v0.32.1...HEAD
|
|
1937
|
+
[0.32.1]: https://github.com/ffroliva/gflow-cli/compare/v0.32.0...v0.32.1
|
|
1938
|
+
[0.32.0]: https://github.com/ffroliva/gflow-cli/compare/v0.31.0...v0.32.0
|
|
1939
|
+
[0.31.0]: https://github.com/ffroliva/gflow-cli/compare/v0.30.0...v0.31.0
|
|
1912
1940
|
[0.30.0]: https://github.com/ffroliva/gflow-cli/compare/v0.29.0...v0.30.0
|
|
1913
1941
|
[0.29.0]: https://github.com/ffroliva/gflow-cli/compare/v0.28.0...v0.29.0
|
|
1914
1942
|
[0.28.0]: https://github.com/ffroliva/gflow-cli/compare/v0.27.1...v0.28.0
|
|
@@ -14,6 +14,63 @@ Living list of behaviour that's broken, surprising, or limited by design — alo
|
|
|
14
14
|
|
|
15
15
|
## Open
|
|
16
16
|
|
|
17
|
+
### Flow's `uploadImage` endpoint rejects some JPEGs with HTTP 400 (metadata-sensitive)
|
|
18
|
+
|
|
19
|
+
- **Status:** Open ([#287](https://github.com/ffroliva/gflow-cli/issues/287))
|
|
20
|
+
- **Severity:** Low (typed + remediable) · **Affected:** i2v frame / reference uploads
|
|
21
|
+
|
|
22
|
+
Observed live 2026-07-11: one JPEG was rejected with HTTP 400 while
|
|
23
|
+
byte-identical-format siblings uploaded fine; re-encoding with
|
|
24
|
+
`ffmpeg -q:v 2 -map_metadata -1` fixed it, implicating a metadata segment.
|
|
25
|
+
Since #290 the rejection raises `MediaUploadRejectedError` (exit 27) with that
|
|
26
|
+
remediation instead of a generic "Unexpected error." (exit 1). **Workaround:**
|
|
27
|
+
re-encode the file, or reference the already-in-project asset by media UUID
|
|
28
|
+
(`--initial-frame <UUID> --project <id>`). Root cause of Flow's metadata
|
|
29
|
+
sensitivity is unidentified.
|
|
30
|
+
|
|
31
|
+
### i2v frame-slot picker selection by UUID — RESOLVED live 2026-07-11
|
|
32
|
+
|
|
33
|
+
- **Status:** **Verified live** ([#287](https://github.com/ffroliva/gflow-cli/issues/287) / PR [#290](https://github.com/ffroliva/gflow-cli/pull/290))
|
|
34
|
+
- **Affected:** `gflow video i2v --initial-frame/--end-frame <UUID>`
|
|
35
|
+
|
|
36
|
+
#290 routes i2v frame slots through `_select_existing_asset` (the UUID picker
|
|
37
|
+
live-proven in the **Add-Media** dialog: v0.26.0 i2i-by-UUID, #282 scroll
|
|
38
|
+
fixes). The **frame-slot** dialog carried a negative prior — #237's
|
|
39
|
+
name-search there never surfaced generated media (v0.25.0 rework) — but the
|
|
40
|
+
thumbnail-URL tile match succeeds where name search failed. Live evidence
|
|
41
|
+
(2026-07-11, denon82): `frame_ref_attached {slot: Start}` → wire capture on
|
|
42
|
+
`batchAsyncGenerateVideoStartImage` with the asset's `startImage` bound →
|
|
43
|
+
SUCCESSFUL 720×1280 mp4; **zero** `image_uploaded` events (no duplicate
|
|
44
|
+
upload). Negative check: a foreign UUID exits 9 pre-generation with a
|
|
45
|
+
`debug_frame_ref_miss_start.png` screenshot. Caveat that stands: the asset's
|
|
46
|
+
project must be entered via `--project`, and projects that open in the
|
|
47
|
+
full-page media-library UI (#174) can fail earlier at `mode_switch_trigger`.
|
|
48
|
+
|
|
49
|
+
### Video duration tab probe misses on the current Frames-submode DOM
|
|
50
|
+
|
|
51
|
+
- **Status:** Open ([#288](https://github.com/ffroliva/gflow-cli/issues/288))
|
|
52
|
+
- **Severity:** Medium · **Affected:** `gflow video` with an explicit `--duration`
|
|
53
|
+
on at least some Classic-profile cohorts (3/3 miss on a live 2026-07-11 run)
|
|
54
|
+
|
|
55
|
+
The `duration_tab` selector cascade (`[role='tab']:text-is('4s')` /
|
|
56
|
+
`:has-text('4s')`) fails to match on Flow's current Frames-submode settings
|
|
57
|
+
panel. Pre-fix this silently produced a clip of Flow's default length; since
|
|
58
|
+
the #289 fix the run fails fast with `UiSelectorDriftError` (exit 23) and a
|
|
59
|
+
`debug_no_duration_tab.png` screenshot — live-verified 2026-07-11 on the
|
|
60
|
+
denon82 profile (aborted pre-submit, saving the 10-credit generation).
|
|
61
|
+
**Workaround:** omit `--duration` to accept Flow's default.
|
|
62
|
+
|
|
63
|
+
**Root cause (confirmed live 2026-07-11, screenshot evidence on #288): the
|
|
64
|
+
duration control is ABSENT from this cohort's settings popover** — the panel
|
|
65
|
+
renders mode tabs (Imagem/Video), sub-mode (Frames/Elementos), aspect
|
|
66
|
+
(9:16/16:9), count (1x–x4), and the model dropdown (Veo 3.1 - Lite), and
|
|
67
|
+
nothing else. The earlier locale hypothesis is refuted: the UI renders in
|
|
68
|
+
Portuguese and the sibling count tabs (`1x`/`x2`) match fine; there is simply
|
|
69
|
+
no duration row to click. Whether Flow removed clip-length selection for this
|
|
70
|
+
cohort/model or moved it elsewhere is unknown — until that's answered,
|
|
71
|
+
`--duration` cannot be honored on affected cohorts and the fail-fast is the
|
|
72
|
+
correct behavior, not a selector bug to patch.
|
|
73
|
+
|
|
17
74
|
### macOS: generation runs logged-out → HTTP 401, even with `--browser chrome`
|
|
18
75
|
|
|
19
76
|
- **Status:** **RESOLVED in v0.23.0** ([#222](https://github.com/ffroliva/gflow-cli/issues/222),
|
|
@@ -741,8 +798,13 @@ through all three layers: wrong file on disk, exit 0. Mitigations until this
|
|
|
741
798
|
is closed: run generations in a dedicated, low-asset project (fewer
|
|
742
799
|
pre-existing tiles means a smaller lazy-render population to collide with),
|
|
743
800
|
and visually verify anchor/canonical images before referencing them
|
|
744
|
-
downstream in `i2i`.
|
|
745
|
-
|
|
801
|
+
downstream in `i2i`. The stable-break hardening shipped post-v0.32.0 (#283 /
|
|
802
|
+
PR #292): the loop now requires the SAME new-UUID set on two consecutive
|
|
803
|
+
scrapes at the expected count before trusting it, which narrows this window
|
|
804
|
+
to a pre-existing tile that lazy-renders AND holds stable across two 0.5s
|
|
805
|
+
scrapes at exactly the expected count (or first hits the count on the final
|
|
806
|
+
poll before the 180s deadline). Narrowed, not closed — the dedicated-project
|
|
807
|
+
mitigation still applies.
|
|
746
808
|
|
|
747
809
|
**Known gap: the shell multi-prompt path records no history at all.**
|
|
748
810
|
`gflow run --config <file>` and `gflow image t2i` with multiple prompts
|
|
@@ -758,7 +820,8 @@ collision escalation) both depend on local history via
|
|
|
758
820
|
neither guard exists on this path — this is a pre-existing gap, not a
|
|
759
821
|
regression from this fix. `gflow image batch` (the manifest path, via
|
|
760
822
|
`run_manifest_image_batch`) already threads a recorder through and is covered
|
|
761
|
-
by all three layers.
|
|
823
|
+
by all three layers. Accepted low-risk gap (#283 closed with this noted as
|
|
824
|
+
un-shipped remainder; re-file if it bites in practice).
|
|
762
825
|
|
|
763
826
|
**Workaround (pre-fix):** none — on an affected version, manually verify the
|
|
764
827
|
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.32.1
|
|
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
|
|
@@ -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.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.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,74 @@
|
|
|
1
|
+
# Live verification — v0.32.0 (2026-07-11)
|
|
2
|
+
|
|
3
|
+
Scope: the two user-facing changes of this release, exercised against live Flow
|
|
4
|
+
on the `denon82` profile (Classic strategy, Chrome, Windows 11; Flow renders in
|
|
5
|
+
Portuguese for this profile — `/fx/pt/` redirect). Credits spent: **1 image +
|
|
6
|
+
1 video generation**; every failure-path check completed pre-submit at zero
|
|
7
|
+
credit cost.
|
|
8
|
+
|
|
9
|
+
## 1. `gflow video i2v --initial-frame <UUID>` — in-project asset selection (#287, PR #290)
|
|
10
|
+
|
|
11
|
+
The declared-riskiest surface: the frame-slot dialog carried a negative prior
|
|
12
|
+
(#237's name-search never surfaced generated media there — see
|
|
13
|
+
[LIVE_VERIFICATION_v0.25.0](LIVE_VERIFICATION_v0.25.0.md)). The thumbnail-URL
|
|
14
|
+
tile match succeeds where name search failed.
|
|
15
|
+
|
|
16
|
+
Run: seed `gflow image t2i` (asset `0bbcdf05-19e0-43c3-bbeb-ba0ff7246e54`,
|
|
17
|
+
project `dd9e498c-22b1-4d73-a748-0bc0eac14c8b`), then
|
|
18
|
+
`gflow video i2v --initial-frame 0bbcdf05-… "slow cinematic push-in toward the
|
|
19
|
+
mug, steam rising gently" --project dd9e498c-… --profile denon82 --json`
|
|
20
|
+
(correlation `92b99c8c-5d69-4e00-905f-734f8eb6dadc`).
|
|
21
|
+
|
|
22
|
+
5-layer ledger:
|
|
23
|
+
|
|
24
|
+
1. **File count:** exactly 1 mp4 in `--out-dir` (`b61bcce6-baca-4934-b6e5-5ca84e10b49a.mp4`, 3,031,564 bytes).
|
|
25
|
+
2. **Magic bytes:** `00 00 00 20 66 74 79 70 69 73 6f 6d` — valid `ftypisom` MP4 header.
|
|
26
|
+
3. **Dimensions/shape:** ffprobe `720×1280`, duration `8.000000` s (Flow default — no `--duration` passed, correct).
|
|
27
|
+
4. **Structlog invariants:**
|
|
28
|
+
- `ui_automation_video.frame_ref_attached {slot: Start, media_id: 0bbcdf05…}` — UUID picker selection in the frame-slot dialog;
|
|
29
|
+
- `ui_automation_video.generate_captured {url: …/video:batchAsyncGenerateVideoStartImage, image_inputs.startImage: "0bbcdf05"}` — the asset is bound in the actual wire request (no #125 T2V fallback);
|
|
30
|
+
- **zero `image_uploaded` events** in the whole run — no duplicate upload (the founding goal of #287);
|
|
31
|
+
- `poll_terminal {status: MEDIA_GENERATION_STATUS_SUCCESSFUL}` → `video_saved`.
|
|
32
|
+
5. **User-confirmable artifact:** video `b61bcce6-…` in Flow project `dd9e498c-…` (denon82 account) — a genuine motion interpolation of the seeded red-mug still.
|
|
33
|
+
|
|
34
|
+
Negative check (free): a foreign UUID (`00000000-0000-4000-8000-000000000001`)
|
|
35
|
+
in the same project → **exit 9** (`TransportTimeoutError`) pre-generation, the
|
|
36
|
+
message naming slot + UUID + the `--project` hint, with
|
|
37
|
+
`debug_frame_ref_miss_start.png` captured (valid PNG).
|
|
38
|
+
|
|
39
|
+
Not verified this cycle: a live `uploadImage` HTTP-400 → exit 27
|
|
40
|
+
(`MediaUploadRejectedError`) repro — the original Flow-rejected JPEG from the
|
|
41
|
+
2026-07-11 pilot no longer exists on disk (only thumbnails survive). The typed
|
|
42
|
+
raise is pinned by a unit test that drives the real `_upload_via_open_dialog`
|
|
43
|
+
listener with a 400 response (`tests/api/transports/test_ui_automation_video.py::TestUploadRejectionTypedError`).
|
|
44
|
+
The KNOWN_ISSUES entry on metadata-sensitive rejections tracks the live case.
|
|
45
|
+
|
|
46
|
+
## 2. `--duration` fail-fast (#288, PR #289)
|
|
47
|
+
|
|
48
|
+
Run: `gflow video i2v <local file> "slow pan across the mug" --duration 4
|
|
49
|
+
--profile denon82 --json` (correlation `b8e66830-58bf-4928-9afe-1eecbbfcdb60`).
|
|
50
|
+
|
|
51
|
+
- `duration_tab` probe missed (reproducing the pilot's 3/3) and the run
|
|
52
|
+
**aborted pre-submit with exit 23** (`UiSelectorDriftError`) — the settings
|
|
53
|
+
popover showed "A geração vai usar 10 créditos", so the fail-fast saved that
|
|
54
|
+
spend instead of delivering a silent 8-second clip reported as success.
|
|
55
|
+
- `debug_no_duration_tab.png` captured (valid PNG, `89 50 4E 47` header) and
|
|
56
|
+
**answers the #288 investigation: the duration control is absent from this
|
|
57
|
+
cohort's Frames-submode settings popover** (rows rendered: Imagem/Vídeo →
|
|
58
|
+
Frames/Elementos → 9:16/16:9 → 1x/x2/x3/x4 → model dropdown; no duration
|
|
59
|
+
row). The council's locale hypothesis is refuted — the UI is Portuguese and
|
|
60
|
+
the sibling text-matched count tabs match fine.
|
|
61
|
+
- Happy-path caveat, recorded per the honesty rule: "duration tab found →
|
|
62
|
+
clicked" is unverifiable on this cohort because the control does not exist.
|
|
63
|
+
If a cohort still renders duration tabs, the pre-existing selector cascade
|
|
64
|
+
should match them unchanged; the fail-fast only fires on a miss.
|
|
65
|
+
|
|
66
|
+
## Incidental live findings (logged on their issues)
|
|
67
|
+
|
|
68
|
+
- Project `f6caf027-…` opens in the full-page media-library UI (#174 shape,
|
|
69
|
+
"Agente" composer pill) where even `mode_switch_trigger` fails — fresh
|
|
70
|
+
projects still get the classic composer and work. Posted to #288/#174 trail.
|
|
71
|
+
- Error-path browser teardown can leave Chrome holding the profile dir
|
|
72
|
+
(subsequent runs die with `TargetClosedError` until the process is killed),
|
|
73
|
+
and `_capture_debug_screenshot` can report a screenshot path it never wrote
|
|
74
|
+
when capture fails. Both noted as #283-wave follow-ups.
|
|
@@ -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.
|
|
@@ -46,7 +46,7 @@ The server registers three protocol surfaces:
|
|
|
46
46
|
|
|
47
47
|
### Tools (Executable actions)
|
|
48
48
|
* `gflow_generate_image(prompt, model, aspect, count, seed, reference_images, tools, profile, project, instructions)`: Triggers text-to-image / image-to-image (Imagen / Nano Banana). `instructions` is an optional list of ephemeral agent-instruction strings (agentic cohort only). `reference_images` switches to i2i and accepts **either a local file path or a generated image's Flow media UUID**. A UUID reference is attached by **selecting the already-existing asset in Flow's reference picker — no duplicate copy is uploaded** (locating the tile by the media id in its thumbnail URL, and searching the recorded display name to surface it when needed); gflow falls back to uploading the asset's on-disk local file only when it can't be located in place (e.g. it lives in a different project's picker). `project` generates into an existing Flow project id (mirrors CLI `--project`) — pass the reference's project to keep it selectable in place.
|
|
49
|
-
* `gflow_generate_video(prompt, mode, aspect, initial_frame, end_frame, reference_images, model, duration, count, tools, profile, project)`: Triggers vertical or landscape video generation (Veo). `mode` is `t2v`/`i2v`/`r2v`; `model` (`veo_lite`/`veo_fast`/`veo_quality`/`omni_flash`, aliases accepted), `duration` (seconds), and `count` mirror the CLI `gflow video` flags — an omitted `model` lets the transport apply its i2v veo-lite default (issue #125), and `omni_flash` is rejected for i2v-with-frames; `i2v` requires `initial_frame`, `r2v` requires `reference_images`; `project` generates into an existing Flow project id (mirrors CLI `--project`). `initial_frame`, `end_frame`, and `reference_images` each accept **either a local file path or the Flow image UUID of a generated asset** — pass a generated image's id straight in to chain image→video, and gflow attaches it for you (the UUID is resolved to the asset's already-on-disk local file and uploaded as the frame; no manual download/re-upload step). A UUID that isn't in your local asset catalog is rejected up front with a clear "Reference Not Found" error; a catalogued asset whose local file is no longer on disk gives a "Reference Not On Disk" error (re-generate it or pass a local path).
|
|
49
|
+
* `gflow_generate_video(prompt, mode, aspect, initial_frame, end_frame, reference_images, model, duration, count, tools, profile, project)`: Triggers vertical or landscape video generation (Veo). `mode` is `t2v`/`i2v`/`r2v`; `model` (`veo_lite`/`veo_fast`/`veo_quality`/`omni_flash`, aliases accepted), `duration` (seconds), and `count` mirror the CLI `gflow video` flags — an omitted `model` lets the transport apply its i2v veo-lite default (issue #125), and `omni_flash` is rejected for i2v-with-frames; `i2v` requires `initial_frame`, `r2v` requires `reference_images`; `project` generates into an existing Flow project id (mirrors CLI `--project`). `initial_frame`, `end_frame`, and `reference_images` each accept **either a local file path or the Flow image UUID of a generated asset** — pass a generated image's id straight in to chain image→video, and gflow attaches it for you (the UUID is resolved to the asset's already-on-disk local file and uploaded as the frame; no manual download/re-upload step). A UUID that isn't in your local asset catalog is rejected up front with a clear "Reference Not Found" error; a catalogued asset whose local file is no longer on disk gives a "Reference Not On Disk" error (re-generate it or pass a local path). Note the CLI's semantics diverge here (#287): `gflow video i2v --initial-frame <UUID>` selects the asset **in the project's media picker with no upload** and needs no local catalog entry — so the same UUID input can succeed on one surface and fail on the other.
|
|
50
50
|
* `gflow_list_projects(profile, limit)`: Queries SQLite catalog for recent generation folders.
|
|
51
51
|
* `gflow_list_characters(profile)`: Lists Flow Character entities (requires an active browser session).
|
|
52
52
|
* `gflow_list_tools()`: Lists the prompt tools (name/title/description/category) accepted by the generate tools' `tools` param.
|
|
@@ -67,7 +67,7 @@ See [AUTHENTICATION § Commands](AUTHENTICATION.md#commands).
|
|
|
67
67
|
|
|
68
68
|
## `gflow image upload`
|
|
69
69
|
|
|
70
|
-
Upload a local PNG/JPEG/WebP/GIF into a fresh Flow project and print the asset UUID + dimensions Flow inferred. The UUID is what later subcommands (`gflow image i2i --ref UUID`, `video i2v`) accept as an initial frame.
|
|
70
|
+
Upload a local PNG/JPEG/WebP/GIF into a fresh Flow project and print the asset UUID + dimensions Flow inferred. The UUID is what later subcommands (`gflow image i2i --ref UUID`, `video i2v`) accept as an initial frame. Note for `video i2v`: the UUID is selected from the generation project's media picker, and `upload` puts the asset in a *fresh scratch project* — so pass `--project <id>` of the project that holds the asset, or the picker lookup fails with exit 9 (#287).
|
|
71
71
|
|
|
72
72
|
```text
|
|
73
73
|
gflow image upload PATH [OPTIONS]
|
|
@@ -460,9 +460,12 @@ gflow video t2v "a dog surfing" --tool creative-director:style=cinematic
|
|
|
460
460
|
## `gflow video i2v`
|
|
461
461
|
|
|
462
462
|
Generate a video from an INITIAL frame (+ optional END frame) and a motion prompt.
|
|
463
|
-
Each
|
|
464
|
-
|
|
465
|
-
|
|
463
|
+
Each frame is a local PNG/JPEG **or the media UUID of an existing in-project asset**
|
|
464
|
+
(#287 — the asset is selected in place, no duplicate upload; pair with `--project`
|
|
465
|
+
so the UUID's project is the one being generated in). A local file is bound into
|
|
466
|
+
the editor's frame slot via the media dialog, then Flow fires
|
|
467
|
+
`batchAsyncGenerateVideoStartImage` (initial only) or `…StartAndEndImage`
|
|
468
|
+
(initial+end interpolation).
|
|
466
469
|
|
|
467
470
|
```text
|
|
468
471
|
gflow video i2v --initial-frame INITIAL [--end-frame LAST] PROMPT [--model] [--duration] [--count] [--aspect] [...]
|
|
@@ -474,19 +477,28 @@ Arguments:
|
|
|
474
477
|
PROMPT Motion prompt. [required]
|
|
475
478
|
|
|
476
479
|
Options:
|
|
477
|
-
--initial-frame PATH Initial frame to animate
|
|
478
|
-
|
|
479
|
-
--
|
|
480
|
-
|
|
480
|
+
--initial-frame PATH|UUID Initial frame to animate: local image path or in-project
|
|
481
|
+
asset media UUID. Canonical form; replaces the positional IMAGE.
|
|
482
|
+
--end-frame PATH|UUID Optional end frame — Flow interpolates initial frame -> end frame.
|
|
483
|
+
--project ID Generate in this EXISTING Flow project instead of a
|
|
484
|
+
scratch project (see "Sharing one project across calls").
|
|
481
485
|
```
|
|
482
486
|
|
|
483
487
|
```bash
|
|
484
488
|
gflow video i2v --initial-frame ./hero.png "Slow camera arc, soft golden light"
|
|
485
489
|
gflow video i2v --initial-frame ./first.png --end-frame ./last.png "morph between scenes" --model veo-quality
|
|
490
|
+
# Reference an existing in-project asset by media UUID (no re-upload, #287):
|
|
491
|
+
gflow video i2v --initial-frame d6f1927a-3eae-4626-bc90-9a6ea7637bab "pan left" --project f6caf027-...
|
|
486
492
|
# Back-compat positional form:
|
|
487
493
|
gflow video i2v ./hero.png "Slow camera arc, soft golden light"
|
|
488
494
|
```
|
|
489
495
|
|
|
496
|
+
Exit codes specific to this surface: **27** — Flow's upload endpoint rejected a
|
|
497
|
+
local frame file (`MediaUploadRejectedError`; try re-encoding, e.g.
|
|
498
|
+
`ffmpeg -i in.jpg -q:v 2 -map_metadata -1 out.jpg`, or reference the asset by
|
|
499
|
+
UUID instead); **9** — a frame UUID could not be located in the project's media
|
|
500
|
+
picker (`TransportTimeoutError`; wrong `--project` or foreign UUID).
|
|
501
|
+
|
|
490
502
|
## `gflow video r2v`
|
|
491
503
|
|
|
492
504
|
Reference-to-video (Flow "ingredients"): condition a generation on reference
|
|
@@ -1212,7 +1224,7 @@ shell scripts can branch on the failure mode without parsing stderr.
|
|
|
1212
1224
|
| `6` | `NetworkError` | Network failure persisted across 3 attempts | Check connectivity |
|
|
1213
1225
|
| `7` | `WireFormatError` | Unexpected response shape — Flow API changed | File a bug (do NOT include captured tokens or signed URLs) |
|
|
1214
1226
|
| `8` | `AuthMissingError` | Required auth credential is absent from profile | `gflow auth login --profile <name>` |
|
|
1215
|
-
| `9` | `TransportTimeoutError` | Browser/API operation exceeded its timeout
|
|
1227
|
+
| `9` | `TransportTimeoutError` | Browser/API operation exceeded its timeout (incl. an i2v frame UUID not found in the media picker, #287) | Retry; raise the relevant timeout — or, for a frame-UUID miss, verify the UUID belongs to the `--project` passed |
|
|
1216
1228
|
| `10` | `WafRejectionError` | Flow security layer rejected the request | Change prompt/request and retry |
|
|
1217
1229
|
| `11` | `ConfigurationError` | Local configuration or browser mode is invalid | Fix the option/env var shown in the error |
|
|
1218
1230
|
| `12` | `AuthLoginTimeoutError` | Browser sign-in was not completed in time | Re-run login or raise `GFLOW_CLI_AUTH_LOGIN_TIMEOUT` |
|
|
@@ -1228,6 +1240,9 @@ shell scripts can branch on the failure mode without parsing stderr.
|
|
|
1228
1240
|
| `22` | `UpscaleUnavailableError` | 4K upscale is gated to Flow **Ultra** accounts (HTTP 403) | Use `--scale 2k`, or upgrade the Flow plan |
|
|
1229
1241
|
| `23` | `UiSelectorDriftError` | A Flow editor control could not be located — Google changed the frontend (issue #183) | Update gflow-cli; file a bug with the probe name + debug screenshot from the error message |
|
|
1230
1242
|
| `24` | `BrowserEngineUnavailableError` | `GFLOW_CLI_BROWSER_ENGINE=patchright` but the engine is not installed | `pip install 'gflow-cli[patchright]'`, or unset `GFLOW_CLI_BROWSER_ENGINE` |
|
|
1243
|
+
| `25` | `FlowAgentUiError` | The profile is on Flow's Agentic UI cohort and the classic media panel is unrecoverable for this operation | Use a Classic-cohort profile; see KNOWN_ISSUES on the agentic cohort |
|
|
1244
|
+
| `26` | `MediaAttributionError` | Generated media could not be reliably attributed to this request (issue #281) | Re-run; a dedicated project with fewer pre-existing assets avoids the ambiguity |
|
|
1245
|
+
| `27` | `MediaUploadRejectedError` | Flow's upload endpoint refused the input file (`uploadImage` 4xx, issue #287) | Re-encode the image (`ffmpeg -q:v 2 -map_metadata -1`), or reference the asset by its media UUID |
|
|
1231
1246
|
| `130`| SIGINT | User-interrupted (Ctrl-C) | — |
|
|
1232
1247
|
|
|
1233
1248
|
**Exit code 16 — data store / migration error.** Fires when:
|
|
@@ -106,7 +106,7 @@ async def _run(
|
|
|
106
106
|
if full:
|
|
107
107
|
await VideoGenerationMixin._select_video_aspect(page, Aspect.PORTRAIT) # noqa: SLF001
|
|
108
108
|
await VideoGenerationMixin._set_output_count(page, 1) # noqa: SLF001
|
|
109
|
-
await VideoGenerationMixin._select_video_duration(page, 8) # noqa: SLF001
|
|
109
|
+
await VideoGenerationMixin._select_video_duration(page, 8, out_dir=None) # noqa: SLF001
|
|
110
110
|
await page.keyboard.press("Escape")
|
|
111
111
|
await page.wait_for_timeout(600)
|
|
112
112
|
await page.wait_for_timeout(800)
|
|
@@ -68,7 +68,7 @@ gflow image i2i "<prompt>" --ref PATH_OR_UUID [--ref ...] [...same as t2i]
|
|
|
68
68
|
|
|
69
69
|
# Video generation (Veo 3.1)
|
|
70
70
|
gflow video t2v "<prompt>" [--out-dir DIR] [--aspect ...] [--seed N]
|
|
71
|
-
gflow video i2v --initial-frame <image> "<prompt>" [--out-dir DIR] [...same as t2v]
|
|
71
|
+
gflow video i2v --initial-frame <image|media-UUID> "<prompt>" [--out-dir DIR] [...same as t2v] # UUID = in-project asset, no re-upload (#287; pair with --project)
|
|
72
72
|
gflow video batch <manifest.tsv> [--out-dir DIR]
|
|
73
73
|
gflow video chain <manifest.jsonl> [--out-dir DIR] [--dry-run] \
|
|
74
74
|
[--max-links N] [--resume-from N] # last-frame I2V chaining; veo models only
|
|
@@ -259,7 +259,7 @@ Documented errors agents commonly make — negative examples for the SkillOpt tr
|
|
|
259
259
|
| `playwright install` or `playwright install --all` | `uvx --from gflow-cli playwright install chromium` (Chromium only, ~150 MB) |
|
|
260
260
|
| Running two generations on the same `--profile` in parallel | Use different `--profile` names — Chromium refuses two persistent contexts on the same dir |
|
|
261
261
|
| `GFLOW_CLI_HEADLESS=true` to fix reCAPTCHA failures | `GFLOW_CLI_HEADLESS=false` — headless mode *causes* bot-detection, not prevents it |
|
|
262
|
-
| Calling `gflow image upload` again for an already-uploaded UUID | Pass the UUID directly to `--ref UUID` — no re-upload needed |
|
|
262
|
+
| Calling `gflow image upload` again for an already-uploaded UUID | Pass the UUID directly to `--ref UUID` (i2i) or `--initial-frame/--end-frame UUID` (i2v, with `--project`) — no re-upload needed |
|
|
263
263
|
| `--model imagen` / `--model quality` / `--model high` | `--model image4` (Imagen 3.5), `--model nano-pro` (Gem Pix 2), `--model nano2` (Narwhal) |
|
|
264
264
|
| Python: `client = FlowApiClient(...)` then method calls | Must use `async with FlowApiClient(...) as client:` — it's an async context manager |
|
|
265
265
|
| Python: `from gflow_cli import FlowApiClient` | `from gflow_cli.api.client import FlowApiClient` |
|
|
@@ -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.
|