gflow-cli 0.32.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.32.0 → gflow_cli-0.32.1}/CHANGELOG.md +16 -1
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/KNOWN_ISSUES.md +9 -3
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/PKG-INFO +1 -1
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/INDEX.md +1 -1
- gflow_cli-0.32.1/docs/LIVE_VERIFICATION_v0.32.1.md +46 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/pyproject.toml +1 -1
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/__init__.py +1 -1
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/api/_engine.py +61 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/api/client.py +51 -18
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/api/image_upscale.py +4 -7
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/api/transports/drivers/agentic.py +17 -4
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/api/transports/experimental/evaluate_fetch.py +3 -3
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/api/transports/ui_automation.py +26 -11
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/api/transports/ui_automation_video.py +22 -7
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/cli_image.py +9 -14
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/cli_instructions.py +3 -9
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/cli_video.py +3 -1
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/data/recorder.py +3 -3
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/errors.py +12 -2
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/mcp/tools.py +3 -7
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/test_concurrency.py +151 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/transports/drivers/test_agentic.py +84 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/transports/test_ui_automation_video.py +45 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/cli/test_cli_video.py +11 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/uv.lock +1 -1
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/.claude/README.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/.claude/commands/gflow/active.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/.claude/commands/gflow/branch-review.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/.claude/commands/gflow/changelog.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/.claude/commands/gflow/check.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/.claude/commands/gflow/doc-review.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/.claude/commands/gflow/issue-assessment.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/.claude/commands/gflow/issue-resolve.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/.claude/commands/gflow/known-issues.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/.claude/commands/gflow/next.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/.claude/commands/gflow/plan.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/.claude/commands/gflow/pr-council-review.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/.claude/commands/gflow/predict.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/.claude/commands/gflow/release.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/.claude/commands/gflow/scenario.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/.claude/commands/gflow/sonar.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/.claude/commands/gflow/status.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/.env.template +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/.gitattributes +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/.github/CODEOWNERS +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/.github/copilot-instructions.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/.github/dependabot.yml +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/.github/workflows/ci.yml +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/.github/workflows/external-pr-triage.yml +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/.github/workflows/governance-advisory.yml +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/.github/workflows/governance-benchmark.yml +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/.github/workflows/main-base-guard.yml +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/.github/workflows/release.yml +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/.gitignore +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/.gitleaks.toml +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/.pre-commit-config.yaml +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/.secrets.baseline +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/AGENTS.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/CLAUDE.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/CONFIGURATION.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/CONTRIBUTING.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/DISCLAIMER.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/GEMINI.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/LICENSE +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/PLAN.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/README.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/RELEASE.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/ROADMAP.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/conftest.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docker-compose.yml +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/AGENT_GUIDE.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/AGENT_UI_E2E.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/AGENT_UI_RECON.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/ARCHITECTURE.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/AUTHENTICATION.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/CHARACTER.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/CHARACTER_RECON.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/CONFIGURATION.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/DATA_LAYER.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/DEBUGGING.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/DEMOS.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/DEVELOPMENT.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/E2E_TESTING.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/EXTERNAL_STORAGE.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/GITHUB.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/GOVERNANCE_BENCHMARK.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/IMAGE_UPSCALE_RECON.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/INSTRUCTIONS.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_data_layer.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_image_batch.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.10.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.11.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.12.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.13.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.16.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.17.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.18.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.19.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.20.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.20.1.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.21.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.22.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.23.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.24.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.25.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.26.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.27.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.27.1.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.28.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.29.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.30.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.31.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.32.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.7.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.8.1.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.9.0.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_v0.9.1.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/LIVE_VERIFICATION_video_download.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/MCP.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/MOVIE.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/PROJECT_STATUS.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/PROMPT_EXPANSION.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/SECURITY.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/TOOLS.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/USAGE.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/USER_GUIDE.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/assets/demo-split-pf.gif +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/assets/example-run.gif +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/assets/examples.webp +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/medium_tutorial.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/schemas/movie-handoff.schema.json +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/sonar-cleanup-tracker.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/superpowers/plans/2026-06-12-issue-174-library-ui-attach/PLAN.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/superpowers/plans/2026-06-22-mcp-server/PLAN.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/superpowers/plans/2026-06-22-mcp-server/PREDICT.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/superpowers/plans/2026-06-22-mcp-server/SCENARIO.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/superpowers/plans/2026-06-22-prompt-expansion/PLAN.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/superpowers/plans/2026-06-22-prompt-expansion/PREDICT.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/superpowers/plans/2026-06-22-prompt-expansion/SCENARIO.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/superpowers/plans/2026-06-24-gflow-studio-scaffold/PLAN.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/superpowers/plans/2026-06-24-rest-api-layer/PLAN.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/superpowers/plans/2026-06-26-mcp-e2e-test/PLAN.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/superpowers/plans/2026-07-09-camoufox-adoption/PLAN.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/superpowers/research/2026-06-27-tool-abstraction-evaluation.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/superpowers/specs/2026-06-29-issue-assessment-workflow-design.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/docs/superpowers/spikes/2026-07-09-camoufox-waf-403.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/examples/README.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/examples/batch_from_config.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/examples/multi_prompt_t2i.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/examples/sample_config.json +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/examples/sample_prompts.txt +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/examples/single_image_t2i.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/examples/workflow_chain.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/llms.txt +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/samples/README.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/samples/captured/01_upload_image.json +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/samples/captured/02_batchAsyncGenerateVideoText.json +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/samples/captured/03_batchCheckAsyncVideoGenerationStatus.json +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/samples/captured/04_archive_workflow.json +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/samples/captured/05_createProject.json +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/samples/captured/06_batchGenerateImages.json +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/samples/captured/07_batchGenerateImages_seeded.json +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/samples/captured/08_batchAsyncGenerateVideoStartAndEndImage.json +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/samples/captured/09_batchAsyncGenerateVideoReferenceImages.json +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/samples/captured/10_batchCheckAsyncVideoGenerationStatus_successful.json +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/samples/captured/11_batchCheckAsyncVideoGenerationStatus_failed.json +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/samples/captured/12_create_scene.json +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/samples/captured/13_sceneWorkflows_update.json +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/samples/captured/14_get_scene_workflows.json +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/samples/captured/15_commit_flowWorkflow.json +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/ci/check_doc_links.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/ci/check_materiality.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/ci/check_repo_hygiene.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/debug_editor.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/debug_gen_settings.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/debug_settings.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/dev/_recording_client.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/dev/_spike_common.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/dev/active_plan.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/dev/analyze_agent_ui_capture.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/dev/capture_i2v_frame_slots_dom.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/dev/capture_i2v_intercept_submit.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/dev/capture_i2v_model_select_repro.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/dev/capture_i2v_post_bind_state.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/dev/capture_image_add_media_dom.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/dev/capture_locale_invariants.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/dev/cdp_drive_and_probe.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/dev/character_create_spike.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/dev/character_create_spike_v2.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/dev/dump_character_selectors.js +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/dev/make_project.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/dev/materiality_backtest.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/dev/monitor_pr_38.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/dev/patch_character.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/dev/skillopt/README.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/dev/skillopt/harness.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/dev/skillopt/tasks.json +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/dev/spike_char_editor_dom.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/dev/spike_char_gen_capture.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/dev/spike_image_upscale_capture.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/dev/spike_image_upscale_drive.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/dev/spike_image_upscale_recaptcha_action.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/dev/spike_image_upscale_rest_probe.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/dev/spike_issue170_picker_locale_recon.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/dev/spike_issue174_library_ui_recon.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/dev/spike_movie_attach_payload.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/dev/spike_movie_entity_recon.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/dev/spike_movie_gen_capture.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/dev/spike_movie_picker_select.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/dev/spike_movie_voice_list.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/dev/spike_patch_entity.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/dev/spike_patchright.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/diag/README.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/diag/capture_flow_traffic.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/diag/memory_profile.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/diag/recaptcha_mint.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/e2e/agentic_image_e2e.ps1 +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/e2e/capture_agent_toggle.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/record_demo.ps1 +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/smoke_image.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/smoke_real_chrome_image.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/smoke_video_editor.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/smoke_worker_style.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/spike_waf_camoufox.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/scripts/verify_chrome_auth_viability.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/skills/README.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/skills/gflow-cli/SKILL.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/skills/issue-assessment/SKILL.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/skills/issue-resolve/SKILL.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/skills/plan/SKILL.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/skills/pr-council-review/SKILL.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/skills/predict/SKILL.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/skills/scenario/SKILL.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/skills/status/SKILL.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/sonar-project.properties +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/__main__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/_cli_helpers.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/api/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/api/_retry.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/api/_sapisidhash.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/api/character.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/api/dto.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/api/image.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/api/recaptcha.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/api/routes.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/api/scene.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/api/transports/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/api/transports/_common.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/api/transports/_fingerprint.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/api/transports/base.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/api/transports/drivers/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/api/transports/drivers/base.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/api/transports/drivers/classic.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/api/transports/drivers/factory.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/api/transports/experimental/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/api/transports/experimental/bearer.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/api/transports/experimental/sapisidhash.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/api/video.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/auth/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/auth/base.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/auth/cookies.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/auth/factory.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/auth/internal_chromium.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/auth/real_chrome.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/auth/strategies.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/auth/verification.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/browser_manager.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/chain.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/chain_manifest.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/cli.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/cli_character.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/cli_data.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/cli_models.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/cli_movie.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/cli_run.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/cli_scene.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/cli_tools.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/composition.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/config.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/data/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/data/chain_repo.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/data/migrations/0001_initial.sql +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/data/migrations/0002_add_cloud_storage.sql +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/data/migrations/0003_add_scene_tables.sql +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/data/migrations/0004_add_scene_output_path.sql +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/data/migrations/0005_add_chain_links.sql +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/data/migrations/0006_add_operations_metadata.sql +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/data/migrations/0007_queue.sql +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/data/migrations/0008_add_operation_expanded_prompt.sql +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/data/migrations/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/data/models.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/data/queries.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/data/redaction.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/data/repository.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/data/store.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/exceptions.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/image_batch.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/json_output.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/manifest.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/mcp/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/mcp/prompts.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/mcp/resources.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/mcp/server.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/media.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/movie_manifest.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/observability.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/paths.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/profile_store.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/services/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/services/character_create.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/storage.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/tools/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/tools/banned.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/tools/builtin/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/tools/builtin/creative-director.toml +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/tools/expander.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/tools/invocation.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/tools/loader.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/tools/registry.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/tools/runtime.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/tools/spec.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/ui/app.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/ui/server.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/worker/daemon.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/worker/queue.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tasks/lessons.md +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/test_assets/sample_batch.json +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/test_assets/sample_batch.tsv +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/test_assets/sample_batch_invalid.tsv +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/fixtures/character_gen_response.json +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/fixtures/patch_entity_response.json +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/test_aisandbox_auth_error.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/test_aisandbox_auth_headers.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/test_bearer_redaction.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/test_character.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/test_client.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/test_client_character.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/test_client_delete_characters.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/test_client_generate_character.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/test_client_image.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/test_client_launch_kwargs.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/test_client_patch_entity.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/test_client_scene.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/test_client_upscale.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/test_dto.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/test_engine.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/test_image.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/test_image_dto.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/test_image_upscale.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/test_post_json_aisandbox_auth.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/test_recaptcha.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/test_retry.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/test_routes.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/test_routes_character.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/test_routes_scene.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/test_sapisidhash_helper.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/test_scene_models.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/test_video.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/test_video_request.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/transports/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/transports/conftest.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/transports/drivers/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/transports/drivers/test_classic.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/transports/drivers/test_factory.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/transports/test_base.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/transports/test_bearer.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/transports/test_common.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/transports/test_evaluate_fetch.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/transports/test_factory.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/transports/test_fingerprint.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/transports/test_sapisidhash.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/transports/test_selector_symmetry.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/transports/test_transport_timeout.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/transports/test_ui_automation.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/transports/test_ui_automation_batch.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/transports/test_ui_automation_image_mode.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/api/transports/test_ui_character_editor.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/auth/strategies/test_factory.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/auth/strategies/test_strategies.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/auth/test_cookies.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/auth/test_verification.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/cli/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/cli/test_cli_auth_list.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/cli/test_cli_character.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/cli/test_cli_character_create.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/cli/test_cli_data.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/cli/test_cli_image.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/cli/test_cli_image_seed_removed.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/cli/test_cli_image_selector_drift.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/cli/test_cli_image_upscale.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/cli/test_cli_instructions.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/cli/test_cli_models.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/cli/test_cli_movie.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/cli/test_cli_run.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/cli/test_cli_scene.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/cli/test_cli_tools.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/cli/test_cli_video_chain.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/cli/test_error_handling.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/cli/test_helpers.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/cli/test_movie_manifest.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/cli/test_movie_manifest_style_variants.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/cli/test_settings_validation.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/cli/test_t2i_multi_prompt.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/composition/test_character.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/composition/test_compose_prompt.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/composition/test_handoff.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/composition/test_style_variants.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/composition/test_style_variants_e2e.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/conftest.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/data/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/data/test_chain_repo.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/data/test_find_incomplete_character.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/data/test_models.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/data/test_packaging.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/data/test_recorder.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/data/test_recorder_character.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/data/test_redaction.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/data/test_repository.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/data/test_scene_persistence.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/data/test_settings_and_errors.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/data/test_store_migrations.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/dev/test_recording_client.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/e2e/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/e2e/conftest.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/e2e/test_aisandbox_auth_live.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/e2e/test_auth_verification_e2e.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/e2e/test_chain_e2e.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/e2e/test_character_create_e2e.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/e2e/test_daemon_e2e.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/e2e/test_data_layer_e2e.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/e2e/test_i2v_flags_e2e.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/e2e/test_image_batch_e2e.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/e2e/test_image_i2i_ref_cap_e2e.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/e2e/test_json_output_e2e.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/e2e/test_live_agentic_instructions.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/e2e/test_locale_selectors_e2e.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/e2e/test_scene_compose_live.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/e2e/test_tools_e2e.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/e2e/test_transports_e2e.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/e2e/test_video_r2v_ref_cap_e2e.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/e2e/test_video_t2v_e2e.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/features/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/features/auth.feature +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/features/auth_login.feature +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/features/character_create.feature +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/features/character_read.feature +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/features/conftest.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/features/image.feature +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/features/image_upscale.feature +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/features/instructions.feature +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/features/locale_picker_include.feature +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/features/test_auth_login_steps.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/features/test_auth_steps.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/features/test_character_create_steps.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/features/test_character_read_steps.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/features/test_image_steps.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/features/test_image_upscale_steps.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/features/test_instructions_steps.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/features/test_locale_picker_include_steps.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/features/test_step_collision_guard.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/features/test_video_agent_ui_steps.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/features/test_video_chain_steps.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/features/video_agent_ui.feature +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/features/video_chain.feature +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/fixtures/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/fixtures/seeded_catalog.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/image_batch/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/image_batch/test_image_manifest.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/image_batch/test_observability_events.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/integration/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/integration/conftest.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/integration/constants.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/integration/test_storage_gcs.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/integration/test_storage_s3.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/mcp/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/mcp/conftest.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/mcp/test_cli_parity.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/mcp/test_server.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/mcp/test_stdio_transport.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/mcp/test_tools_helpers.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/mcp/test_tools_instructions.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/mcp/test_tools_wired.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/scripts/test_analyze_agent_ui_capture.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/scripts/test_capture_locale_invariants.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/scripts/test_check_materiality.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/scripts/test_check_repo_hygiene.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/scripts/test_materiality_backtest.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/services/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/services/test_character_create_redaction.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/services/test_character_create_saga.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/services/test_character_gen_no_direct_post.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/smoke/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/smoke/test_profile_account_smoke.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/smoke/test_real_flow.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/test_auth.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/test_browser_manager.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/test_chain.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/test_chain_manifest.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/test_cli_data.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/test_config.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/test_conftest_isolation.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/test_data_queries.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/test_documentation_gate.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/test_errors.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/test_errors_403.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/test_json_output.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/test_manifest.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/test_marker_registry.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/test_media.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/test_observability.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/test_paths.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/test_profile_store.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/test_smoke.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/tools/__init__.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/tools/test_banned.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/tools/test_expander.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/tools/test_invocation.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/tools/test_loader.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/tools/test_registry.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/tools/test_runtime.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/tools/test_spec.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/ui/test_app.py +0 -0
- {gflow_cli-0.32.0 → gflow_cli-0.32.1}/tests/worker/test_daemon.py +0 -0
|
@@ -7,6 +7,20 @@ 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
|
+
|
|
10
24
|
## [0.32.0] — 2026-07-11
|
|
11
25
|
|
|
12
26
|
### Added
|
|
@@ -1919,7 +1933,8 @@ shell-script template that branches on these codes.
|
|
|
1919
1933
|
|
|
1920
1934
|
First skeleton. Not functional end-to-end yet.
|
|
1921
1935
|
|
|
1922
|
-
[Unreleased]: https://github.com/ffroliva/gflow-cli/compare/v0.32.
|
|
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
|
|
1923
1938
|
[0.32.0]: https://github.com/ffroliva/gflow-cli/compare/v0.31.0...v0.32.0
|
|
1924
1939
|
[0.31.0]: https://github.com/ffroliva/gflow-cli/compare/v0.30.0...v0.31.0
|
|
1925
1940
|
[0.30.0]: https://github.com/ffroliva/gflow-cli/compare/v0.29.0...v0.30.0
|
|
@@ -798,8 +798,13 @@ through all three layers: wrong file on disk, exit 0. Mitigations until this
|
|
|
798
798
|
is closed: run generations in a dedicated, low-asset project (fewer
|
|
799
799
|
pre-existing tiles means a smaller lazy-render population to collide with),
|
|
800
800
|
and visually verify anchor/canonical images before referencing them
|
|
801
|
-
downstream in `i2i`.
|
|
802
|
-
|
|
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.
|
|
803
808
|
|
|
804
809
|
**Known gap: the shell multi-prompt path records no history at all.**
|
|
805
810
|
`gflow run --config <file>` and `gflow image t2i` with multiple prompts
|
|
@@ -815,7 +820,8 @@ collision escalation) both depend on local history via
|
|
|
815
820
|
neither guard exists on this path — this is a pre-existing gap, not a
|
|
816
821
|
regression from this fix. `gflow image batch` (the manifest path, via
|
|
817
822
|
`run_manifest_image_batch`) already threads a recorder through and is covered
|
|
818
|
-
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).
|
|
819
825
|
|
|
820
826
|
**Workaround (pre-fix):** none — on an affected version, manually verify the
|
|
821
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.32.
|
|
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.32.0](LIVE_VERIFICATION_v0.32.0.md) (**#287 UUID i2v frames proven live e2e** — an in-project asset selected by media UUID in the frame-slot dialog [frame_ref_attached, startImage bound on the wire, ZERO upload events] produced a real 720×1280 interpolation, resolving the #237 negative prior; foreign UUID → exit 9 pre-generation with screenshot; **#288 duration fail-fast proven live** — exit 23 pre-submit saved a 10-credit generation, and the debug screenshot confirmed the duration control is ABSENT from the cohort's settings popover [locale hypothesis refuted]; upload-400 exit-27 repro deferred-with-reason — the original rejected JPEG no longer exists). Prior: [v0.31.0](LIVE_VERIFICATION_v0.31.0.md) (**#281 wrong-media defenses live-verified in BOTH directions, credit-free** — correct attribution downloads with real wire metadata [seed, 768×1376]; the incident-class lazy-render ambiguity reproduced live and hard-stopped with `MediaAttributionError` exit 26 naming every candidate UUID, no file written; **#282 two-UUID-ref i2i proven e2e** — both freshly generated portrait UUIDs picker-selected into one scene with both canonical identities visually transferred). Prior: [v0.30.0](LIVE_VERIFICATION_v0.30.0.md) (**agentic-cohort image path + both fixes live-verified credit-free** — native 768×1376 stills, i2i-chain + canonical-ref identity hold; the character-create `DataIntegrityError` and the `prefer_classic` WARNING were observed live then fixed [recorder idempotent + INFO log]; MCP video-param feature deferred-with-reason on Veo credits, per the stills-only goal). Prior: [v0.29.0](LIVE_VERIFICATION_v0.29.0.md) (**persistent `gflow instructions` CRUD proven live e2e GREEN, credit-free** — 11-command sequence on a fresh project: add incl. `--ref <image-UUID>` attach, list/enable/disable, master toggle both ways, TOML `apply` destructive full-sync with `enabled=false` honored, `rm` with server card-id preserved across mutations; movie brief-sync code-path-verified via the same primitives, deferred-with-reason on credits; MCP `gflow_instructions_*` tools are thin adapters over the live-verified primitives). Prior: [v0.28.0](LIVE_VERIFICATION_v0.28.0.md) (**agent instructions `-i` now steer agentic generation — crayon e2e GREEN**: a style-neutral prompt + an enabled "crayon" card produced an unmistakable crayon drawing through the real transport; root causes were conversational phrasing + the `project_brief.enabled` master switch). Prior: [v0.27.1](LIVE_VERIFICATION_v0.27.1.md) (**v0.27.0 release follow-up fixes and documentation sync**). Prior: [v0.27.0](LIVE_VERIFICATION_v0.27.0.md) (**`[style]` block with named variants + prompt-aware resume — credit-free CLI verification**: real `movie run --dry-run` 3-way ledger proving resolved-style display, `skip (done)` on matching `style_hash`, and `re-run (style changed)` after a style edit; composition is local, wire path untouched, so no Veo credits spent — first credited styled run will confirm visually). Prior: [v0.26.0](LIVE_VERIFICATION_v0.26.0.md) (**image i2i by generated-image UUID proven live e2e GREEN** — the existing asset is *selected in place* by its media UUID, no duplicate upload, producing an i2i output that references it; plus generated-image `display_name` capture, credited @C1ph3r404). Prior: [v0.25.0](LIVE_VERIFICATION_v0.25.0.md) (**#237 remote-UUID i2v proven live e2e GREEN** — a real 8s 720×1280 interpolation from a generated image's UUID, after the picker-search attach was reworked to local upload; #240 home-`.env` matrix live; plus two silent-failure guards root-caused live: video-as-image download rejection and rejected-upload fail-loud). Prior: [v0.24.0](LIVE_VERIFICATION_v0.24.0.md) (--project parity across CLI + MCP — automated coverage; live gen auth-gated). Prior: [v0.23.0](LIVE_VERIFICATION_v0.23.0.md) (MCP generation wiring proven live end-to-end — tool→worker→real Flow REST→structured-error translation; final image-write blocked by an expired session, environmental. #222 macOS fix reporter-verified e2e on Apple Silicon). Prior: [v0.22.0](LIVE_VERIFICATION_v0.22.0.md) (Tools framework — automated/CI coverage complete; credit/key-gated live feature run is a pending owner gate, see the doc) · [v0.21.0](LIVE_VERIFICATION_v0.21.0.md) (MCP server over stdio + HTTP/SSE — credit-free protocol handshake; 4 tools / 2 prompts / 3 resources confirmed on stdout) · [v0.20.1](LIVE_VERIFICATION_v0.20.1.md) · [v0.20.0](LIVE_VERIFICATION_v0.20.0.md) · [v0.19.0](LIVE_VERIFICATION_v0.19.0.md) · [v0.18.0](LIVE_VERIFICATION_v0.18.0.md) · [v0.17.0](LIVE_VERIFICATION_v0.17.0.md) · [v0.16.0](LIVE_VERIFICATION_v0.16.0.md) · [v0.13.0](LIVE_VERIFICATION_v0.13.0.md) · [v0.12.0](LIVE_VERIFICATION_v0.12.0.md) · [v0.11.0](LIVE_VERIFICATION_v0.11.0.md) · [v0.10.0](LIVE_VERIFICATION_v0.10.0.md) · [v0.9.1](LIVE_VERIFICATION_v0.9.1.md) · [v0.9.0](LIVE_VERIFICATION_v0.9.0.md) · [v0.8.1](LIVE_VERIFICATION_v0.8.1.md) · [v0.7.0](LIVE_VERIFICATION_v0.7.0.md)
|
|
125
|
+
**"What was actually live-verified for the latest release?"** → latest: [LIVE_VERIFICATION_v0.32.1](LIVE_VERIFICATION_v0.32.1.md) (**#293 teardown fix live-verified credit-free** — error-path aborts leave ZERO chrome.exe holding the profile [3× leak reproduced pre-fix same day]; force-close fallback + ProfileLockedError translation unit-pinned; #283 wave logic fixes mock-verified to exact call counts). Prior: [v0.32.0](LIVE_VERIFICATION_v0.32.0.md) (**#287 UUID i2v frames proven live e2e** — an in-project asset selected by media UUID in the frame-slot dialog [frame_ref_attached, startImage bound on the wire, ZERO upload events] produced a real 720×1280 interpolation, resolving the #237 negative prior; foreign UUID → exit 9 pre-generation with screenshot; **#288 duration fail-fast proven live** — exit 23 pre-submit saved a 10-credit generation, and the debug screenshot confirmed the duration control is ABSENT from the cohort's settings popover [locale hypothesis refuted]; upload-400 exit-27 repro deferred-with-reason — the original rejected JPEG no longer exists). Prior: [v0.31.0](LIVE_VERIFICATION_v0.31.0.md) (**#281 wrong-media defenses live-verified in BOTH directions, credit-free** — correct attribution downloads with real wire metadata [seed, 768×1376]; the incident-class lazy-render ambiguity reproduced live and hard-stopped with `MediaAttributionError` exit 26 naming every candidate UUID, no file written; **#282 two-UUID-ref i2i proven e2e** — both freshly generated portrait UUIDs picker-selected into one scene with both canonical identities visually transferred). Prior: [v0.30.0](LIVE_VERIFICATION_v0.30.0.md) (**agentic-cohort image path + both fixes live-verified credit-free** — native 768×1376 stills, i2i-chain + canonical-ref identity hold; the character-create `DataIntegrityError` and the `prefer_classic` WARNING were observed live then fixed [recorder idempotent + INFO log]; MCP video-param feature deferred-with-reason on Veo credits, per the stills-only goal). Prior: [v0.29.0](LIVE_VERIFICATION_v0.29.0.md) (**persistent `gflow instructions` CRUD proven live e2e GREEN, credit-free** — 11-command sequence on a fresh project: add incl. `--ref <image-UUID>` attach, list/enable/disable, master toggle both ways, TOML `apply` destructive full-sync with `enabled=false` honored, `rm` with server card-id preserved across mutations; movie brief-sync code-path-verified via the same primitives, deferred-with-reason on credits; MCP `gflow_instructions_*` tools are thin adapters over the live-verified primitives). Prior: [v0.28.0](LIVE_VERIFICATION_v0.28.0.md) (**agent instructions `-i` now steer agentic generation — crayon e2e GREEN**: a style-neutral prompt + an enabled "crayon" card produced an unmistakable crayon drawing through the real transport; root causes were conversational phrasing + the `project_brief.enabled` master switch). Prior: [v0.27.1](LIVE_VERIFICATION_v0.27.1.md) (**v0.27.0 release follow-up fixes and documentation sync**). Prior: [v0.27.0](LIVE_VERIFICATION_v0.27.0.md) (**`[style]` block with named variants + prompt-aware resume — credit-free CLI verification**: real `movie run --dry-run` 3-way ledger proving resolved-style display, `skip (done)` on matching `style_hash`, and `re-run (style changed)` after a style edit; composition is local, wire path untouched, so no Veo credits spent — first credited styled run will confirm visually). Prior: [v0.26.0](LIVE_VERIFICATION_v0.26.0.md) (**image i2i by generated-image UUID proven live e2e GREEN** — the existing asset is *selected in place* by its media UUID, no duplicate upload, producing an i2i output that references it; plus generated-image `display_name` capture, credited @C1ph3r404). Prior: [v0.25.0](LIVE_VERIFICATION_v0.25.0.md) (**#237 remote-UUID i2v proven live e2e GREEN** — a real 8s 720×1280 interpolation from a generated image's UUID, after the picker-search attach was reworked to local upload; #240 home-`.env` matrix live; plus two silent-failure guards root-caused live: video-as-image download rejection and rejected-upload fail-loud). Prior: [v0.24.0](LIVE_VERIFICATION_v0.24.0.md) (--project parity across CLI + MCP — automated coverage; live gen auth-gated). Prior: [v0.23.0](LIVE_VERIFICATION_v0.23.0.md) (MCP generation wiring proven live end-to-end — tool→worker→real Flow REST→structured-error translation; final image-write blocked by an expired session, environmental. #222 macOS fix reporter-verified e2e on Apple Silicon). Prior: [v0.22.0](LIVE_VERIFICATION_v0.22.0.md) (Tools framework — automated/CI coverage complete; credit/key-gated live feature run is a pending owner gate, see the doc) · [v0.21.0](LIVE_VERIFICATION_v0.21.0.md) (MCP server over stdio + HTTP/SSE — credit-free protocol handshake; 4 tools / 2 prompts / 3 resources confirmed on stdout) · [v0.20.1](LIVE_VERIFICATION_v0.20.1.md) · [v0.20.0](LIVE_VERIFICATION_v0.20.0.md) · [v0.19.0](LIVE_VERIFICATION_v0.19.0.md) · [v0.18.0](LIVE_VERIFICATION_v0.18.0.md) · [v0.17.0](LIVE_VERIFICATION_v0.17.0.md) · [v0.16.0](LIVE_VERIFICATION_v0.16.0.md) · [v0.13.0](LIVE_VERIFICATION_v0.13.0.md) · [v0.12.0](LIVE_VERIFICATION_v0.12.0.md) · [v0.11.0](LIVE_VERIFICATION_v0.11.0.md) · [v0.10.0](LIVE_VERIFICATION_v0.10.0.md) · [v0.9.1](LIVE_VERIFICATION_v0.9.1.md) · [v0.9.0](LIVE_VERIFICATION_v0.9.0.md) · [v0.8.1](LIVE_VERIFICATION_v0.8.1.md) · [v0.7.0](LIVE_VERIFICATION_v0.7.0.md)
|
|
126
126
|
**"Where is the reverse-engineered wire protocol for a feature?"** → the `*_RECON.md` design docs: [CHARACTER_RECON](CHARACTER_RECON.md) (Flow character entity protocol), [IMAGE_UPSCALE_RECON](IMAGE_UPSCALE_RECON.md) (`/v1/flow/upsampleImage` wire). Naming convention: one `<FEATURE>_RECON.md` per reverse-engineered surface, kept as the durable spec after the feature ships.
|
|
127
127
|
**"What was live-verified for the data layer (PR #58)?"** → [LIVE_VERIFICATION_data_layer](LIVE_VERIFICATION_data_layer.md) — 1 Imagen + 1 Veo credit on denon82, 6-layer ledger (file + magic + Pillow + DB rows + CLI round-trip + structlog)
|
|
128
128
|
**"What was live-verified for the video-download feature (#29)?"** → [LIVE_VERIFICATION_video_download](LIVE_VERIFICATION_video_download.md)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Live verification — v0.32.1 (2026-07-11)
|
|
2
|
+
|
|
3
|
+
Patch release: the #293 teardown fix (PR #294) plus the #283 follow-up wave
|
|
4
|
+
(PR #292). All verification credit-free.
|
|
5
|
+
|
|
6
|
+
## 1. Browser teardown — no leaked Chrome after error exits (#293, PR #294)
|
|
7
|
+
|
|
8
|
+
Baseline (pre-fix, same day): three separate error-exit runs on the `denon82`
|
|
9
|
+
profile left a full Chrome tree holding the profile dir; the next run died at
|
|
10
|
+
context launch with `TargetClosedError` → "Unexpected error." exit 1.
|
|
11
|
+
|
|
12
|
+
Post-fix live repro, run twice (pre- and post-review-refactor):
|
|
13
|
+
|
|
14
|
+
1. Trigger an error-path abort: `gflow video i2v --initial-frame
|
|
15
|
+
<foreign-UUID> … --project dd9e498c-… --profile denon82` → **exit 9**
|
|
16
|
+
(typed picker-miss abort, pre-credit).
|
|
17
|
+
2. Immediately after process exit:
|
|
18
|
+
`Get-CimInstance Win32_Process -Filter "Name='chrome.exe'"` filtered on
|
|
19
|
+
`profile_denon82` in the command line → **0 processes**, both runs.
|
|
20
|
+
3. Follow-up launches in the same session succeeded cleanly (no
|
|
21
|
+
`TargetClosedError` at launch).
|
|
22
|
+
|
|
23
|
+
Honest caveat: in both post-fix runs the *graceful* close succeeded within the
|
|
24
|
+
bound, so the force-close fallback did not fire live — its trigger (a wedged
|
|
25
|
+
close) is nondeterministic. The fallback branch is pinned by unit tests
|
|
26
|
+
(`tests/api/test_concurrency.py`: force-close ordering before driver stop,
|
|
27
|
+
double-failure last resort, hang-timeout path, both `_is_target_closed`
|
|
28
|
+
branches), and `context.browser` being non-None for persistent contexts was
|
|
29
|
+
verified against the pinned Playwright 1.59.0 package source.
|
|
30
|
+
|
|
31
|
+
The `ProfileLockedError` (exit 11) launch translation is unit-verified; a live
|
|
32
|
+
sample requires deliberately wedging a browser and was not manufactured.
|
|
33
|
+
|
|
34
|
+
## 2. #283 wave (PR #292) — logic-level fixes, unit-pinned
|
|
35
|
+
|
|
36
|
+
The picker scroll off-by-one, `await_images` stable-break, and phantom
|
|
37
|
+
screenshot paths are pure logic around mocked DOM scrapes — the unit tests are
|
|
38
|
+
the affected surface (mock-verified to the exact call counts: 12 pre-scroll
|
|
39
|
+
checks + 1 post-loop re-check). The phantom-screenshot fix's real-world
|
|
40
|
+
trigger was itself observed live on 2026-07-11 (an error message named a
|
|
41
|
+
`debug_no_mode_trigger.png` that was never written — the empty directory is
|
|
42
|
+
the evidence); post-fix, capture failures return `None` and every message
|
|
43
|
+
appends its `Screenshot:` clause conditionally.
|
|
44
|
+
|
|
45
|
+
Deferred-with-reason: no credited generation was needed for this patch — no
|
|
46
|
+
generation-path behavior changed.
|
|
@@ -25,6 +25,7 @@ This module is the single place that reconciles the two engines' differences:
|
|
|
25
25
|
|
|
26
26
|
from __future__ import annotations
|
|
27
27
|
|
|
28
|
+
import asyncio
|
|
28
29
|
import importlib
|
|
29
30
|
from typing import TYPE_CHECKING, Any, cast
|
|
30
31
|
|
|
@@ -48,6 +49,66 @@ _PATCHRIGHT_PIP_HINT = (
|
|
|
48
49
|
)
|
|
49
50
|
|
|
50
51
|
|
|
52
|
+
# Teardown deadlines (issue #293). The graceful bound is generous on purpose:
|
|
53
|
+
# for a persistent context, close() is Chrome flushing profile state (the
|
|
54
|
+
# cookie DB is this product's auth store) and Playwright's driver hard-kills
|
|
55
|
+
# the process if a second close arrives mid-graceful-close — so escalating a
|
|
56
|
+
# merely-slow close would risk corrupting the profile. The force bound is
|
|
57
|
+
# short: it only matters when the graceful close already failed, and a dead
|
|
58
|
+
# driver connection would otherwise burn the full window as pure delay.
|
|
59
|
+
_CONTEXT_CLOSE_TIMEOUT_S = 30.0
|
|
60
|
+
_FORCE_CLOSE_TIMEOUT_S = 5.0
|
|
61
|
+
DRIVER_STOP_TIMEOUT_S = 10.0
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
async def _force_close_browser(context: Any, *, owner: str) -> None:
|
|
65
|
+
"""Force-close ``context.browser``, bounded — never raises Exception."""
|
|
66
|
+
try:
|
|
67
|
+
browser = context.browser
|
|
68
|
+
if browser is not None:
|
|
69
|
+
await asyncio.wait_for(browser.close(), timeout=_FORCE_CLOSE_TIMEOUT_S)
|
|
70
|
+
logger.warning(
|
|
71
|
+
"browser_teardown.force_close_returned",
|
|
72
|
+
owner=owner,
|
|
73
|
+
note=(
|
|
74
|
+
"graceful context close failed; browser.close() returned "
|
|
75
|
+
"(the browser may also simply have been gone already)"
|
|
76
|
+
),
|
|
77
|
+
)
|
|
78
|
+
except Exception:
|
|
79
|
+
# ponytail: an OS-level tree kill needs a chrome PID that
|
|
80
|
+
# playwright-python does not expose for persistent contexts.
|
|
81
|
+
logger.error(
|
|
82
|
+
"browser_teardown.force_close_failed",
|
|
83
|
+
owner=owner,
|
|
84
|
+
remediation=(
|
|
85
|
+
"a browser may still hold this profile dir — close Chrome "
|
|
86
|
+
"windows using it, or kill the Chrome processes (chrome.exe "
|
|
87
|
+
"on Windows) whose command line names the profile dir"
|
|
88
|
+
),
|
|
89
|
+
exc_info=True,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
async def close_context_bounded(context: Any, *, owner: str) -> None:
|
|
94
|
+
"""Close a persistent BrowserContext without hanging or leaking chrome.
|
|
95
|
+
|
|
96
|
+
Issue #293: an unbounded ``context.close()`` can hang forever on a wedged
|
|
97
|
+
page, and a close that FAILS leaves the system-Chrome tree alive holding
|
|
98
|
+
the profile dir once the driver stops — the graceful close is what asks
|
|
99
|
+
Chrome to exit. Bounds the graceful close, then force-closes the browser
|
|
100
|
+
on any failure. On cancellation (Ctrl-C mid-teardown) the force-close is
|
|
101
|
+
still attempted best-effort before the cancellation propagates.
|
|
102
|
+
"""
|
|
103
|
+
try:
|
|
104
|
+
await asyncio.wait_for(context.close(), timeout=_CONTEXT_CLOSE_TIMEOUT_S)
|
|
105
|
+
except BaseException as exc:
|
|
106
|
+
logger.warning("browser_teardown.context_close_error", owner=owner, exc_info=True)
|
|
107
|
+
await _force_close_browser(context, owner=owner)
|
|
108
|
+
if not isinstance(exc, Exception):
|
|
109
|
+
raise
|
|
110
|
+
|
|
111
|
+
|
|
51
112
|
def active_engine() -> BrowserEngine:
|
|
52
113
|
"""Return the configured browser engine (validated ``BrowserEngine`` enum)."""
|
|
53
114
|
return get_settings().browser_engine
|
|
@@ -29,7 +29,9 @@ from playwright.async_api import BrowserContext, Page, Playwright, async_playwri
|
|
|
29
29
|
|
|
30
30
|
from gflow_cli.api import routes
|
|
31
31
|
from gflow_cli.api._engine import (
|
|
32
|
+
DRIVER_STOP_TIMEOUT_S,
|
|
32
33
|
active_engine,
|
|
34
|
+
close_context_bounded,
|
|
33
35
|
log_engine_selected,
|
|
34
36
|
mint_evaluate_kwargs,
|
|
35
37
|
resolve_async_playwright,
|
|
@@ -57,6 +59,7 @@ from gflow_cli.errors import (
|
|
|
57
59
|
ContentPolicyError,
|
|
58
60
|
FlowApiError, # re-exported via gflow_cli.api.__init__
|
|
59
61
|
NetworkError,
|
|
62
|
+
ProfileLockedError,
|
|
60
63
|
RateLimitError,
|
|
61
64
|
SceneConcatError,
|
|
62
65
|
TransportTimeoutError,
|
|
@@ -547,7 +550,34 @@ class FlowApiClient:
|
|
|
547
550
|
await self._preread_flow_session_cookies()
|
|
548
551
|
kwargs = self._persistent_context_kwargs()
|
|
549
552
|
self._log_and_guard_launch(kwargs)
|
|
550
|
-
|
|
553
|
+
try:
|
|
554
|
+
self._context = await self._pw.chromium.launch_persistent_context(**kwargs)
|
|
555
|
+
except Exception as exc:
|
|
556
|
+
if _is_target_closed(exc):
|
|
557
|
+
# A TargetClosedError at LAUNCH usually means the profile dir
|
|
558
|
+
# is held by another Chrome — most commonly a stale browser
|
|
559
|
+
# leaked by a crashed prior run (issue #293) or a concurrent
|
|
560
|
+
# gflow run. It CAN also be an unrelated startup crash, so
|
|
561
|
+
# the original error is carried in the detail and the wording
|
|
562
|
+
# hedges rather than asserts.
|
|
563
|
+
first_line = str(exc).splitlines()[0] if str(exc) else type(exc).__name__
|
|
564
|
+
raise ProfileLockedError(
|
|
565
|
+
detail=(
|
|
566
|
+
f"the browser exited immediately while launching on profile "
|
|
567
|
+
f"dir {self.profile_dir} — most likely another Chrome holds "
|
|
568
|
+
f"it (original error: {first_line})"
|
|
569
|
+
),
|
|
570
|
+
remediation_hint=(
|
|
571
|
+
"Another gflow run — or a stale Chrome left by a crashed one "
|
|
572
|
+
"— may hold this profile. Close Chrome windows using this "
|
|
573
|
+
"profile (or kill the Chrome processes — chrome.exe on "
|
|
574
|
+
"Windows — whose command line names the profile dir) and "
|
|
575
|
+
"re-run, or use a different --profile. If no such process "
|
|
576
|
+
"exists, the browser crashed at startup for another reason; "
|
|
577
|
+
"see the original error above."
|
|
578
|
+
),
|
|
579
|
+
) from exc
|
|
580
|
+
raise
|
|
551
581
|
# Hide the automation flag so reCAPTCHA Enterprise doesn't score
|
|
552
582
|
# the session as a bot — navigator.webdriver=true causes low-score
|
|
553
583
|
# tokens and HTTP 403 on batchGenerateImages.
|
|
@@ -655,23 +685,26 @@ class FlowApiClient:
|
|
|
655
685
|
silently swallow). Resets the browser fields so a reused client never
|
|
656
686
|
holds references to a dead BrowserContext.
|
|
657
687
|
"""
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
688
|
+
try:
|
|
689
|
+
if self._context is not None:
|
|
690
|
+
# Bounded close + force-close fallback (issue #293) — shared
|
|
691
|
+
# with the transports' own-context teardowns via _engine.
|
|
692
|
+
await close_context_bounded(self._context, owner="client")
|
|
693
|
+
if self._pw is not None:
|
|
694
|
+
try:
|
|
695
|
+
# pw.stop() awaits the Node driver's exit with no deadline
|
|
696
|
+
# of its own — a wedged driver would hang teardown forever.
|
|
697
|
+
await asyncio.wait_for(self._pw.stop(), timeout=DRIVER_STOP_TIMEOUT_S)
|
|
698
|
+
except Exception:
|
|
699
|
+
logger.warning("playwright_stop_error", exc_info=True)
|
|
700
|
+
finally:
|
|
701
|
+
# Field resets must survive even cancellation (Ctrl-C mid-close),
|
|
702
|
+
# or a reused client holds references to a dead BrowserContext.
|
|
703
|
+
self._pages = []
|
|
704
|
+
self._page_queue = None
|
|
705
|
+
self._page = None
|
|
706
|
+
self._context = None
|
|
707
|
+
self._pw = None
|
|
675
708
|
|
|
676
709
|
async def _checkout_page(self) -> Page:
|
|
677
710
|
"""Block until a Page is available from the pool; FIFO.
|
|
@@ -19,12 +19,13 @@ Wire facts (reverse-engineered, see ``docs/IMAGE_UPSCALE_RECON.md``)::
|
|
|
19
19
|
|
|
20
20
|
from __future__ import annotations
|
|
21
21
|
|
|
22
|
-
import re
|
|
23
22
|
from dataclasses import dataclass, field
|
|
24
23
|
from enum import StrEnum
|
|
25
24
|
from types import MappingProxyType
|
|
26
25
|
from typing import Any
|
|
27
26
|
|
|
27
|
+
from gflow_cli.api.video import is_media_uuid
|
|
28
|
+
|
|
28
29
|
__all__ = [
|
|
29
30
|
"DEFAULT_PAYGATE_TIER",
|
|
30
31
|
"TargetResolution",
|
|
@@ -45,10 +46,6 @@ DEFAULT_PAYGATE_TIER = "PAYGATE_TIER_ONE"
|
|
|
45
46
|
# Strict UUID allowlist for the source mediaId and the owning projectId. Both
|
|
46
47
|
# are interpolated into the request body (not a URL path), but validating up
|
|
47
48
|
# front rejects malformed input before any reCAPTCHA mint or network call fires
|
|
48
|
-
# (scenario #9). Flow media + project ids are both 8-4-4-4-12 hex UUIDs.
|
|
49
|
-
_UUID_RE = re.compile(
|
|
50
|
-
r"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"
|
|
51
|
-
)
|
|
52
49
|
|
|
53
50
|
|
|
54
51
|
class TargetResolution(StrEnum):
|
|
@@ -114,13 +111,13 @@ class UpsampleImageRequest:
|
|
|
114
111
|
recaptcha_token: str = field(default="")
|
|
115
112
|
|
|
116
113
|
def __post_init__(self) -> None:
|
|
117
|
-
if not
|
|
114
|
+
if not is_media_uuid(self.media_id):
|
|
118
115
|
msg = (
|
|
119
116
|
f"UpsampleImageRequest.media_id must be a bare UUID "
|
|
120
117
|
f"(8-4-4-4-12 hex, no whitespace); got {self.media_id!r}"
|
|
121
118
|
)
|
|
122
119
|
raise ValueError(msg)
|
|
123
|
-
if not
|
|
120
|
+
if not is_media_uuid(self.project_id):
|
|
124
121
|
msg = (
|
|
125
122
|
f"UpsampleImageRequest.project_id must be a bare UUID "
|
|
126
123
|
f"(8-4-4-4-12 hex, no whitespace); got {self.project_id!r}"
|
|
@@ -478,6 +478,14 @@ class AgenticFlowUiDriver:
|
|
|
478
478
|
interval of wall time up front; the total poll timeout budget
|
|
479
479
|
(``_AWAIT_TIMEOUT_S``) is unchanged.
|
|
480
480
|
|
|
481
|
+
**Stable break (issue #283):** reaching ``expected_count`` on a single
|
|
482
|
+
scrape is not trusted — a lazily-rendered pre-existing tile can
|
|
483
|
+
transiently hit the exact count. The loop breaks only when the same
|
|
484
|
+
new-UUID set holds across two consecutive scrapes (~one extra 0.5s
|
|
485
|
+
poll). Deliberate fall-through: a set that first reaches the exact
|
|
486
|
+
count on the final poll before the deadline is returned unconfirmed —
|
|
487
|
+
better a last-second success than a spurious timeout.
|
|
488
|
+
|
|
481
489
|
**Ambiguity fail-fast (issue #281):** if more new UUIDs appear than
|
|
482
490
|
were requested, there is no reliable way to tell which ones belong to
|
|
483
491
|
this generation. Rather than arbitrarily slice the unordered set (the
|
|
@@ -503,6 +511,7 @@ class AgenticFlowUiDriver:
|
|
|
503
511
|
|
|
504
512
|
deadline = asyncio.get_event_loop().time() + _AWAIT_TIMEOUT_S
|
|
505
513
|
new_uuids: set[str] = set()
|
|
514
|
+
prev_new_uuids: set[str] | None = None
|
|
506
515
|
|
|
507
516
|
while asyncio.get_event_loop().time() < deadline:
|
|
508
517
|
await asyncio.sleep(_POLL_INTERVAL_S)
|
|
@@ -522,8 +531,14 @@ class AgenticFlowUiDriver:
|
|
|
522
531
|
current_uuids = _extract_uuids(current_srcs)
|
|
523
532
|
new_uuids = current_uuids - baseline_uuids
|
|
524
533
|
|
|
525
|
-
if len(new_uuids)
|
|
534
|
+
if len(new_uuids) > expected_count:
|
|
535
|
+
break # ambiguity — fail fast below (#281)
|
|
536
|
+
# Stable-break (#283): exact-count on a single scrape can still be
|
|
537
|
+
# a lazy-render race; require the SAME set on two consecutive
|
|
538
|
+
# scrapes (~one extra poll interval) before trusting it.
|
|
539
|
+
if len(new_uuids) == expected_count and new_uuids == prev_new_uuids:
|
|
526
540
|
break
|
|
541
|
+
prev_new_uuids = set(new_uuids)
|
|
527
542
|
|
|
528
543
|
if len(new_uuids) < expected_count:
|
|
529
544
|
if new_uuids:
|
|
@@ -543,9 +558,7 @@ class AgenticFlowUiDriver:
|
|
|
543
558
|
raise MediaAttributionError(
|
|
544
559
|
detail=(
|
|
545
560
|
f"Cannot attribute the generation among {len(candidates)} candidate "
|
|
546
|
-
f"media UUIDs (expected {expected_count}): {candidates}.
|
|
547
|
-
"generation; a dedicated project with fewer pre-existing assets "
|
|
548
|
-
"avoids lazy-render ambiguity."
|
|
561
|
+
f"media UUIDs (expected {expected_count}): {candidates}."
|
|
549
562
|
),
|
|
550
563
|
route="agentic:await_images",
|
|
551
564
|
)
|
{gflow_cli-0.32.0 → gflow_cli-0.32.1}/src/gflow_cli/api/transports/experimental/evaluate_fetch.py
RENAMED
|
@@ -267,10 +267,10 @@ class EvaluateFetchTransport:
|
|
|
267
267
|
"""
|
|
268
268
|
if self._owns_playwright:
|
|
269
269
|
if self._ctx is not None:
|
|
270
|
+
from gflow_cli.api._engine import close_context_bounded # noqa: PLC0415
|
|
271
|
+
|
|
270
272
|
try:
|
|
271
|
-
await self._ctx
|
|
272
|
-
except Exception:
|
|
273
|
-
log.warning("evaluate_fetch.teardown: ctx.close() failed", exc_info=True)
|
|
273
|
+
await close_context_bounded(self._ctx, owner="evaluate_fetch")
|
|
274
274
|
finally:
|
|
275
275
|
self._ctx = None
|
|
276
276
|
self._page = None
|
|
@@ -35,6 +35,7 @@ from gflow_cli.api.transports.ui_automation_video import (
|
|
|
35
35
|
ENTITY_ATTACH_DRIFT_HINT,
|
|
36
36
|
MODE_SWITCH_TRIGGER_SELECTORS,
|
|
37
37
|
VideoGenerationMixin,
|
|
38
|
+
screenshot_clause,
|
|
38
39
|
selector_drift_detail,
|
|
39
40
|
zip_entity_refs,
|
|
40
41
|
)
|
|
@@ -178,6 +179,7 @@ async def _capture_debug_screenshot(
|
|
|
178
179
|
)
|
|
179
180
|
except Exception as e:
|
|
180
181
|
log.debug("ui_automation.screenshot_capture_failed", error=str(e))
|
|
182
|
+
return None # never report a path that was not written (#283)
|
|
181
183
|
return shot_path
|
|
182
184
|
|
|
183
185
|
|
|
@@ -741,6 +743,7 @@ class UiAutomationTransport(VideoGenerationMixin):
|
|
|
741
743
|
# opt-in patchright engine routes through the resolver.
|
|
742
744
|
from gflow_cli.api._engine import (
|
|
743
745
|
active_engine,
|
|
746
|
+
close_context_bounded,
|
|
744
747
|
log_engine_selected,
|
|
745
748
|
resolve_async_playwright,
|
|
746
749
|
)
|
|
@@ -766,6 +769,7 @@ class UiAutomationTransport(VideoGenerationMixin):
|
|
|
766
769
|
# ``.chromium.launch_persistent_context`` surface; type as Any so this
|
|
767
770
|
# standalone path is engine-agnostic without per-engine stubs.
|
|
768
771
|
pw: Any = await pw_cm.__aenter__()
|
|
772
|
+
ctx = None
|
|
769
773
|
try:
|
|
770
774
|
import os
|
|
771
775
|
|
|
@@ -805,7 +809,11 @@ class UiAutomationTransport(VideoGenerationMixin):
|
|
|
805
809
|
profile_dir=str(profile_dir),
|
|
806
810
|
)
|
|
807
811
|
except Exception:
|
|
808
|
-
# Partial-setup leak guard.
|
|
812
|
+
# Partial-setup leak guard. The context must be closed BEFORE the
|
|
813
|
+
# driver exits — stopping only the driver leaves the detached
|
|
814
|
+
# system-Chrome alive holding the profile dir (issue #293).
|
|
815
|
+
if ctx is not None:
|
|
816
|
+
await close_context_bounded(ctx, owner="ui_automation")
|
|
809
817
|
await pw_cm.__aexit__(None, None, None)
|
|
810
818
|
raise
|
|
811
819
|
|
|
@@ -1039,8 +1047,8 @@ class UiAutomationTransport(VideoGenerationMixin):
|
|
|
1039
1047
|
|
|
1040
1048
|
shot_path = await _capture_debug_screenshot(page, out_dir, "debug_new_project.png")
|
|
1041
1049
|
msg = (
|
|
1042
|
-
f"Could not find 'New project' CTA on Flow gallery.
|
|
1043
|
-
f"
|
|
1050
|
+
f"Could not find 'New project' CTA on Flow gallery. "
|
|
1051
|
+
f"URL: {page.url}.{screenshot_clause(shot_path)}"
|
|
1044
1052
|
)
|
|
1045
1053
|
raise RuntimeError(
|
|
1046
1054
|
msg,
|
|
@@ -1182,7 +1190,7 @@ class UiAutomationTransport(VideoGenerationMixin):
|
|
|
1182
1190
|
continue
|
|
1183
1191
|
|
|
1184
1192
|
shot_path = await _capture_debug_screenshot(page, out_dir, "debug_prompt_not_found.png")
|
|
1185
|
-
msg = f"Prompt input not found in Flow UI. URL: {page.url}.
|
|
1193
|
+
msg = f"Prompt input not found in Flow UI. URL: {page.url}.{screenshot_clause(shot_path)}"
|
|
1186
1194
|
raise RuntimeError(msg)
|
|
1187
1195
|
|
|
1188
1196
|
async def _click_submit(self, page: Page) -> None:
|
|
@@ -2767,7 +2775,7 @@ class UiAutomationTransport(VideoGenerationMixin):
|
|
|
2767
2775
|
)
|
|
2768
2776
|
msg = (
|
|
2769
2777
|
f"Character editor not ready: prompt textbox not visible "
|
|
2770
|
-
f"within 20 s. URL: {page.url}.
|
|
2778
|
+
f"within 20 s. URL: {page.url}.{screenshot_clause(shot)}"
|
|
2771
2779
|
)
|
|
2772
2780
|
raise RuntimeError(msg) from exc
|
|
2773
2781
|
|
|
@@ -3026,13 +3034,20 @@ class UiAutomationTransport(VideoGenerationMixin):
|
|
|
3026
3034
|
if not self._setup_done:
|
|
3027
3035
|
return
|
|
3028
3036
|
if self._owns_playwright and self._pw_cm is not None:
|
|
3037
|
+
from gflow_cli.api._engine import ( # noqa: PLC0415
|
|
3038
|
+
DRIVER_STOP_TIMEOUT_S,
|
|
3039
|
+
close_context_bounded,
|
|
3040
|
+
)
|
|
3041
|
+
|
|
3042
|
+
if self._ctx is not None:
|
|
3043
|
+
# Bounded close + force-close fallback (issue #293) — same
|
|
3044
|
+
# helper as FlowApiClient's teardown; this standalone path had
|
|
3045
|
+
# the identical unbounded-close-and-swallow gap.
|
|
3046
|
+
await close_context_bounded(self._ctx, owner="ui_automation")
|
|
3029
3047
|
try:
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
log.warning("ui_automation.context_close_failed", error=str(e))
|
|
3034
|
-
try:
|
|
3035
|
-
await self._pw_cm.__aexit__(None, None, None)
|
|
3048
|
+
await asyncio.wait_for(
|
|
3049
|
+
self._pw_cm.__aexit__(None, None, None), timeout=DRIVER_STOP_TIMEOUT_S
|
|
3050
|
+
)
|
|
3036
3051
|
except Exception as e:
|
|
3037
3052
|
log.warning("ui_automation.playwright_exit_failed", error=str(e))
|
|
3038
3053
|
self._pw_cm = None
|
|
@@ -423,9 +423,16 @@ async def _capture_debug_screenshot(page: Any, out_dir: Path | None, filename: s
|
|
|
423
423
|
)
|
|
424
424
|
except Exception as e:
|
|
425
425
|
log.debug("ui_automation_video.screenshot_capture_failed", error=str(e))
|
|
426
|
+
return None # never report a path that was not written (#283)
|
|
426
427
|
return shot_path
|
|
427
428
|
|
|
428
429
|
|
|
430
|
+
def screenshot_clause(shot: Path | None) -> str:
|
|
431
|
+
"""' Screenshot: <path>' when one was captured, else '' — a capture
|
|
432
|
+
failure must not put a phantom path (or 'None') in the error text."""
|
|
433
|
+
return f" Screenshot: {shot}" if shot is not None else ""
|
|
434
|
+
|
|
435
|
+
|
|
429
436
|
def selector_drift_detail(probe: str, what: str, shot: Path | None) -> str:
|
|
430
437
|
"""Build a :class:`UiSelectorDriftError` detail string for a probe miss.
|
|
431
438
|
|
|
@@ -1050,7 +1057,7 @@ class VideoGenerationMixin:
|
|
|
1050
1057
|
msg = (
|
|
1051
1058
|
f"model picker trigger not found; cannot select {model.value!r} "
|
|
1052
1059
|
f"for i2v. Refusing to proceed (Flow's default would drop the "
|
|
1053
|
-
f"frames to T2V — issue #125).
|
|
1060
|
+
f"frames to T2V — issue #125).{screenshot_clause(shot)}"
|
|
1054
1061
|
)
|
|
1055
1062
|
raise VideoModelSelectionError(detail=msg, route="model_picker_trigger")
|
|
1056
1063
|
return
|
|
@@ -1092,7 +1099,7 @@ class VideoGenerationMixin:
|
|
|
1092
1099
|
f"could not select video model {model.value!r} for i2v after 2 "
|
|
1093
1100
|
f"attempts. Refusing to proceed — Flow's default model "
|
|
1094
1101
|
f"({VideoModel.OMNI_FLASH.value}) silently drops the start/end "
|
|
1095
|
-
f"frames and routes to T2V (issue #125).
|
|
1102
|
+
f"frames and routes to T2V (issue #125).{screenshot_clause(shot)}"
|
|
1096
1103
|
)
|
|
1097
1104
|
raise VideoModelSelectionError(detail=msg, route="model_option")
|
|
1098
1105
|
|
|
@@ -1280,7 +1287,7 @@ class VideoGenerationMixin:
|
|
|
1280
1287
|
shot = await _capture_debug_screenshot(
|
|
1281
1288
|
page, out_dir, f"debug_no_{label.lower()}_slot.png"
|
|
1282
1289
|
)
|
|
1283
|
-
msg = f"frame slot {label!r} not found on the Flow editor.
|
|
1290
|
+
msg = f"frame slot {label!r} not found on the Flow editor.{screenshot_clause(shot)}"
|
|
1284
1291
|
raise RuntimeError(msg) from e
|
|
1285
1292
|
|
|
1286
1293
|
struct_count = await structs.count()
|
|
@@ -1423,12 +1430,13 @@ class VideoGenerationMixin:
|
|
|
1423
1430
|
msg = (
|
|
1424
1431
|
f"Neither the icon nor the text 'Upload media' selector opened a file "
|
|
1425
1432
|
f"chooser for {log_label!r} — Google likely changed the media dialog "
|
|
1426
|
-
f"(issue #56).
|
|
1433
|
+
f"(issue #56). Workaround: set the CHROME BROWSER "
|
|
1427
1434
|
f"PROFILE's language to English (chrome://settings/languages). NOTE: "
|
|
1428
1435
|
f"this is the Chrome PROFILE language, NOT the Google ACCOUNT language "
|
|
1429
1436
|
f"— changing only the Google account language does NOT work, because "
|
|
1430
1437
|
f"Flow follows the Chrome profile locale (and the --lang=en-US launch "
|
|
1431
1438
|
f"arg cannot override an already-configured profile)."
|
|
1439
|
+
f"{screenshot_clause(shot)}"
|
|
1432
1440
|
)
|
|
1433
1441
|
raise RuntimeError(
|
|
1434
1442
|
msg,
|
|
@@ -1488,7 +1496,8 @@ class VideoGenerationMixin:
|
|
|
1488
1496
|
except Exception as e:
|
|
1489
1497
|
if i == 0:
|
|
1490
1498
|
shot = await _capture_debug_screenshot(page, out_dir, "debug_no_add_media.png")
|
|
1491
|
-
|
|
1499
|
+
clause = screenshot_clause(shot)
|
|
1500
|
+
msg = f"'Add Media' button not found for first reference.{clause}"
|
|
1492
1501
|
raise RuntimeError(
|
|
1493
1502
|
msg,
|
|
1494
1503
|
) from e
|
|
@@ -1611,6 +1620,10 @@ class VideoGenerationMixin:
|
|
|
1611
1620
|
visible = True
|
|
1612
1621
|
break
|
|
1613
1622
|
await VideoGenerationMixin._scroll_picker_grid(page)
|
|
1623
|
+
# Final re-check (#283 off-by-one): the count runs BEFORE each
|
|
1624
|
+
# scroll, so a tile rendered by the LAST scroll was never seen.
|
|
1625
|
+
if not visible and await tile.count():
|
|
1626
|
+
visible = True
|
|
1614
1627
|
if visible:
|
|
1615
1628
|
try:
|
|
1616
1629
|
await tile.wait_for(state="visible", timeout=4000)
|
|
@@ -1728,7 +1741,9 @@ class VideoGenerationMixin:
|
|
|
1728
1741
|
"""Locate the Personagens-tab tile for a character entity. Each tile is
|
|
1729
1742
|
keyed by the entity id as `data-tile-id="fe_id_<entityId>"` (exact — no
|
|
1730
1743
|
display-name ambiguity). Scroll the grid until it renders, then return
|
|
1731
|
-
the locator (the caller still waits for visibility
|
|
1744
|
+
the locator (the caller still waits for visibility — which also covers
|
|
1745
|
+
a tile rendered by the final scroll, so the #283 off-by-one fixed in
|
|
1746
|
+
`_select_existing_asset` has no effect here)."""
|
|
1732
1747
|
tile = page.locator(f"[data-tile-id='fe_id_{entity_id}']").first
|
|
1733
1748
|
for _ in range(PICKER_GRID_SCROLL_ATTEMPTS):
|
|
1734
1749
|
if await tile.count():
|
|
@@ -1781,7 +1796,7 @@ class VideoGenerationMixin:
|
|
|
1781
1796
|
await page.keyboard.press("Escape")
|
|
1782
1797
|
raise TransportTimeoutError(
|
|
1783
1798
|
f"{detail} include action did not appear (no selector tier "
|
|
1784
|
-
f"matched on surface {surface!r}).
|
|
1799
|
+
f"matched on surface {surface!r}).{screenshot_clause(shot)}",
|
|
1785
1800
|
remediation_hint=(
|
|
1786
1801
|
"Flow's resource picker may have changed, or your account's "
|
|
1787
1802
|
"UI language is not yet covered by the selector fallbacks. "
|