gflow-cli 0.15.0__tar.gz → 0.15.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.15.0 → gflow_cli-0.15.1}/AGENTS.md +3 -1
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/CHANGELOG.md +17 -1
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/PKG-INFO +9 -9
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/README.md +8 -8
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/docs/INDEX.md +6 -1
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/pyproject.toml +1 -1
- gflow_cli-0.15.1/scripts/diag/README.md +29 -0
- gflow_cli-0.15.0/scripts/diag_capture_flow_traffic.py → gflow_cli-0.15.1/scripts/diag/capture_flow_traffic.py +1 -1
- gflow_cli-0.15.1/scripts/diag/memory_profile.py +235 -0
- gflow_cli-0.15.0/scripts/diag_recaptcha_mint.py → gflow_cli-0.15.1/scripts/diag/recaptcha_mint.py +1 -1
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/__init__.py +1 -1
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/api/client.py +1 -1
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/api/transports/ui_automation.py +1 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/api/transports/ui_automation_video.py +9 -0
- gflow_cli-0.15.1/tests/api/test_client_launch_kwargs.py +77 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/api/transports/test_ui_automation_video.py +26 -1
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/dev/test_recording_client.py +4 -1
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/uv.lock +1 -1
- gflow_cli-0.15.0/tests/api/test_client_launch_kwargs.py +0 -35
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/.claude/README.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/.claude/commands/gflow/active.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/.claude/commands/gflow/branch-review.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/.claude/commands/gflow/changelog.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/.claude/commands/gflow/check.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/.claude/commands/gflow/doc-review.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/.claude/commands/gflow/known-issues.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/.claude/commands/gflow/next.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/.claude/commands/gflow/plan.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/.claude/commands/gflow/pr-council-review.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/.claude/commands/gflow/predict.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/.claude/commands/gflow/release.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/.claude/commands/gflow/scenario.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/.claude/commands/gflow/status.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/.continue-here.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/.env.template +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/.gitattributes +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/.github/CODEOWNERS +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/.github/copilot-instructions.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/.github/dependabot.yml +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/.github/workflows/ci.yml +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/.github/workflows/external-pr-triage.yml +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/.github/workflows/governance-advisory.yml +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/.github/workflows/governance-benchmark.yml +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/.github/workflows/release.yml +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/.gitignore +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/.gitleaks.toml +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/.pre-commit-config.yaml +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/.secrets.baseline +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/CLAUDE.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/CONFIGURATION.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/CONTRIBUTING.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/DISCLAIMER.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/GEMINI.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/KNOWN_ISSUES.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/LICENSE +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/PLAN.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/PR162_MOVIE_CHARACTER_REVIEW.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/RELEASE.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/ROADMAP.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/conftest.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/docker-compose.yml +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/docs/AGENT_GUIDE.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/docs/ARCHITECTURE.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/docs/AUTHENTICATION.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/docs/CHARACTER.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/docs/CHARACTER_RECON.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/docs/CONFIGURATION.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/docs/DATA_LAYER.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/docs/DEBUGGING.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/docs/DEMOS.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/docs/DEVELOPMENT.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/docs/E2E_TESTING.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/docs/EXTERNAL_STORAGE.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/docs/GITHUB.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/docs/GOVERNANCE_BENCHMARK.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/docs/LIVE_VERIFICATION_data_layer.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/docs/LIVE_VERIFICATION_image_batch.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/docs/LIVE_VERIFICATION_v0.10.0.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/docs/LIVE_VERIFICATION_v0.11.0.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/docs/LIVE_VERIFICATION_v0.12.0.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/docs/LIVE_VERIFICATION_v0.13.0.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/docs/LIVE_VERIFICATION_v0.7.0.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/docs/LIVE_VERIFICATION_v0.8.1.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/docs/LIVE_VERIFICATION_v0.9.0.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/docs/LIVE_VERIFICATION_v0.9.1.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/docs/LIVE_VERIFICATION_video_download.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/docs/MOVIE.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/docs/PROJECT_STATUS.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/docs/SECURITY.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/docs/USAGE.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/docs/USER_GUIDE.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/docs/assets/demo-split-pf.gif +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/docs/assets/example-run.gif +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/docs/schemas/movie-handoff.schema.json +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/docs/superpowers/2026-05-17-issue-15-handover.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/docs/superpowers/character-scenario.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/docs/superpowers/plans/2026-06-01-governance-enforcement-advisory/PLAN.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/docs/superpowers/plans/2026-06-04-character-create-recording.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/docs/superpowers/plans/2026-06-06-movie-p0-asyncio-hotfix.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/docs/superpowers/plans/2026-06-06-movie-p1-composition-manifest.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/docs/superpowers/plans/2026-06-06-movie-p2-native-identity-voice.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/docs/superpowers/specs/2026-06-06-movie-consistency-design.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/docs/superpowers/verifications/2026-05-11-phase-4-stage-g.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/examples/README.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/examples/batch_from_config.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/examples/multi_prompt_t2i.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/examples/sample_config.json +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/examples/sample_prompts.txt +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/examples/single_image_t2i.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/examples/workflow_chain.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/llms.txt +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/samples/README.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/samples/captured/01_upload_image.json +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/samples/captured/02_batchAsyncGenerateVideoText.json +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/samples/captured/03_batchCheckAsyncVideoGenerationStatus.json +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/samples/captured/04_archive_workflow.json +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/samples/captured/05_createProject.json +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/samples/captured/06_batchGenerateImages.json +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/samples/captured/07_batchGenerateImages_seeded.json +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/samples/captured/08_batchAsyncGenerateVideoStartAndEndImage.json +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/samples/captured/09_batchAsyncGenerateVideoReferenceImages.json +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/samples/captured/10_batchCheckAsyncVideoGenerationStatus_successful.json +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/samples/captured/11_batchCheckAsyncVideoGenerationStatus_failed.json +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/samples/captured/12_create_scene.json +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/samples/captured/13_sceneWorkflows_update.json +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/samples/captured/14_get_scene_workflows.json +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/samples/captured/15_commit_flowWorkflow.json +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/scripts/ci/check_doc_links.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/scripts/ci/check_materiality.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/scripts/ci/check_repo_hygiene.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/scripts/debug_editor.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/scripts/debug_gen_settings.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/scripts/debug_settings.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/scripts/dev/_recording_client.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/scripts/dev/_spike_common.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/scripts/dev/active_plan.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/scripts/dev/capture_i2v_frame_slots_dom.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/scripts/dev/capture_i2v_intercept_submit.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/scripts/dev/capture_i2v_model_select_repro.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/scripts/dev/capture_i2v_post_bind_state.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/scripts/dev/capture_image_add_media_dom.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/scripts/dev/capture_locale_invariants.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/scripts/dev/cdp_drive_and_probe.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/scripts/dev/character_create_spike.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/scripts/dev/character_create_spike_v2.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/scripts/dev/dump_character_selectors.js +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/scripts/dev/make_project.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/scripts/dev/materiality_backtest.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/scripts/dev/monitor_pr_38.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/scripts/dev/patch_character.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/scripts/dev/record_flow_capture.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/scripts/dev/skillopt/README.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/scripts/dev/skillopt/harness.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/scripts/dev/skillopt/tasks.json +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/scripts/dev/spike_char_editor_dom.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/scripts/dev/spike_char_gen_capture.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/scripts/dev/spike_movie_attach_payload.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/scripts/dev/spike_movie_entity_recon.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/scripts/dev/spike_movie_gen_capture.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/scripts/dev/spike_movie_picker_select.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/scripts/dev/spike_movie_voice_list.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/scripts/dev/spike_patch_entity.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/scripts/record_demo.ps1 +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/scripts/smoke_image.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/scripts/smoke_real_chrome_image.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/scripts/smoke_video_editor.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/scripts/smoke_worker_style.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/scripts/verify_chrome_auth_viability.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/skills/README.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/skills/gflow-cli/SKILL.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/skills/plan/SKILL.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/skills/pr-council-review/SKILL.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/skills/predict/SKILL.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/skills/scenario/SKILL.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/skills/status/SKILL.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/sonar-project.properties +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/__main__.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/_cli_helpers.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/api/__init__.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/api/_retry.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/api/_sapisidhash.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/api/character.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/api/dto.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/api/image.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/api/recaptcha.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/api/routes.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/api/scene.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/api/transports/__init__.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/api/transports/_common.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/api/transports/_fingerprint.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/api/transports/base.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/api/transports/experimental/__init__.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/api/transports/experimental/bearer.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/api/transports/experimental/evaluate_fetch.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/api/transports/experimental/sapisidhash.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/api/video.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/auth/__init__.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/auth/base.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/auth/factory.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/auth/internal_chromium.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/auth/real_chrome.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/auth/strategies.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/auth/verification.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/browser_manager.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/chain.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/chain_manifest.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/cli.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/cli_character.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/cli_data.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/cli_image.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/cli_models.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/cli_movie.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/cli_run.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/cli_scene.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/cli_video.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/composition.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/config.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/data/__init__.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/data/chain_repo.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/data/migrations/0001_initial.sql +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/data/migrations/0002_add_cloud_storage.sql +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/data/migrations/0003_add_scene_tables.sql +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/data/migrations/0004_add_scene_output_path.sql +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/data/migrations/0005_add_chain_links.sql +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/data/migrations/0006_add_operations_metadata.sql +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/data/migrations/__init__.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/data/models.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/data/queries.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/data/recorder.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/data/redaction.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/data/repository.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/data/store.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/errors.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/exceptions.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/image_batch.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/json_output.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/manifest.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/media.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/movie_manifest.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/observability.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/paths.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/profile_store.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/services/__init__.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/services/character_create.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/src/gflow_cli/storage.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tasks/lessons.md +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/test_assets/sample_batch.json +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/test_assets/sample_batch.tsv +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/test_assets/sample_batch_invalid.tsv +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/__init__.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/api/__init__.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/api/fixtures/character_gen_response.json +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/api/fixtures/patch_entity_response.json +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/api/test_aisandbox_auth_error.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/api/test_aisandbox_auth_headers.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/api/test_bearer_redaction.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/api/test_character.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/api/test_client.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/api/test_client_character.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/api/test_client_delete_characters.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/api/test_client_generate_character.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/api/test_client_image.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/api/test_client_patch_entity.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/api/test_client_scene.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/api/test_concurrency.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/api/test_dto.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/api/test_image.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/api/test_image_dto.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/api/test_post_json_aisandbox_auth.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/api/test_recaptcha.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/api/test_retry.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/api/test_routes.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/api/test_routes_character.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/api/test_routes_scene.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/api/test_sapisidhash_helper.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/api/test_scene_models.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/api/test_video.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/api/test_video_request.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/api/transports/__init__.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/api/transports/test_base.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/api/transports/test_bearer.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/api/transports/test_common.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/api/transports/test_evaluate_fetch.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/api/transports/test_factory.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/api/transports/test_fingerprint.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/api/transports/test_sapisidhash.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/api/transports/test_transport_timeout.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/api/transports/test_ui_automation.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/api/transports/test_ui_automation_batch.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/api/transports/test_ui_automation_image_mode.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/api/transports/test_ui_character_editor.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/auth/strategies/test_factory.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/auth/strategies/test_strategies.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/auth/test_verification.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/cli/__init__.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/cli/test_cli_auth_list.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/cli/test_cli_character.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/cli/test_cli_character_create.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/cli/test_cli_data.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/cli/test_cli_image.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/cli/test_cli_image_seed_removed.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/cli/test_cli_models.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/cli/test_cli_movie.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/cli/test_cli_run.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/cli/test_cli_scene.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/cli/test_cli_video.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/cli/test_cli_video_chain.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/cli/test_error_handling.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/cli/test_helpers.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/cli/test_movie_manifest.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/cli/test_t2i_multi_prompt.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/composition/test_character.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/composition/test_compose_prompt.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/composition/test_handoff.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/conftest.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/data/__init__.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/data/test_chain_repo.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/data/test_find_incomplete_character.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/data/test_models.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/data/test_packaging.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/data/test_recorder.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/data/test_recorder_character.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/data/test_redaction.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/data/test_repository.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/data/test_scene_persistence.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/data/test_settings_and_errors.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/data/test_store_migrations.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/dev/test_record_flow_capture.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/e2e/__init__.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/e2e/conftest.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/e2e/test_aisandbox_auth_live.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/e2e/test_auth_verification_e2e.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/e2e/test_chain_e2e.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/e2e/test_character_create_e2e.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/e2e/test_data_layer_e2e.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/e2e/test_i2v_flags_e2e.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/e2e/test_image_batch_e2e.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/e2e/test_image_i2i_ref_cap_e2e.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/e2e/test_json_output_e2e.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/e2e/test_locale_selectors_e2e.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/e2e/test_scene_compose_live.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/e2e/test_transports_e2e.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/e2e/test_video_r2v_ref_cap_e2e.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/e2e/test_video_t2v_e2e.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/features/__init__.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/features/auth.feature +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/features/auth_login.feature +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/features/character_create.feature +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/features/character_read.feature +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/features/conftest.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/features/image.feature +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/features/test_auth_login_steps.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/features/test_auth_steps.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/features/test_character_create_steps.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/features/test_character_read_steps.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/features/test_image_steps.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/features/test_step_collision_guard.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/features/test_video_chain_steps.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/features/video_chain.feature +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/fixtures/__init__.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/fixtures/seeded_catalog.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/image_batch/__init__.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/image_batch/test_image_manifest.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/image_batch/test_observability_events.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/integration/__init__.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/integration/conftest.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/integration/constants.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/integration/test_storage_gcs.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/integration/test_storage_s3.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/scripts/test_capture_locale_invariants.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/scripts/test_check_materiality.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/scripts/test_check_repo_hygiene.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/scripts/test_materiality_backtest.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/services/__init__.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/services/test_character_create_redaction.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/services/test_character_create_saga.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/services/test_character_gen_no_direct_post.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/smoke/__init__.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/smoke/test_profile_account_smoke.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/smoke/test_real_flow.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/test_auth.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/test_browser_manager.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/test_chain.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/test_chain_manifest.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/test_cli_data.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/test_config.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/test_conftest_isolation.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/test_data_queries.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/test_documentation_gate.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/test_errors.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/test_errors_403.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/test_json_output.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/test_manifest.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/test_marker_registry.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/test_media.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/test_observability.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/test_paths.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/test_profile_store.py +0 -0
- {gflow_cli-0.15.0 → gflow_cli-0.15.1}/tests/test_smoke.py +0 -0
|
@@ -76,10 +76,12 @@ The `skills/` directory ships installable agent skill docs in plain Markdown wit
|
|
|
76
76
|
| `gflow-cli` | [`skills/gflow-cli/SKILL.md`](skills/gflow-cli/SKILL.md) | User wants to run any `gflow` command — auth, T2V, I2V, T2I, I2I, batch |
|
|
77
77
|
| `predict` | [`skills/predict/SKILL.md`](skills/predict/SKILL.md) | Pre-implementation adversarial analysis before any high-stakes change |
|
|
78
78
|
| `scenario` | [`skills/scenario/SKILL.md`](skills/scenario/SKILL.md) | Edge-case explorer after a predict GO/CAUTION |
|
|
79
|
+
| `plan` | [`skills/plan/SKILL.md`](skills/plan/SKILL.md) | Create a structured task-by-task implementation plan for a feature |
|
|
80
|
+
| `status` | [`skills/status/SKILL.md`](skills/status/SKILL.md) | Show current plan state, progress, and next unchecked task |
|
|
79
81
|
| `pr-council-review` | [`skills/pr-council-review/SKILL.md`](skills/pr-council-review/SKILL.md) | Multi-dimensional PR council review |
|
|
80
82
|
|
|
81
83
|
**Cursor / Aider / Codex / Gemini CLI:** paste or include the relevant `SKILL.md` in your system context.
|
|
82
|
-
**Claude Code:**
|
|
84
|
+
**Claude Code:** the `/gflow:` slash commands in `.claude/commands/gflow/` are auto-discovered when the project is open — no extra setup needed. To register a skill globally, copy the command file to `~/.claude/commands/`.
|
|
83
85
|
**Custom agents:** fetch `skills/gflow-cli/SKILL.md` into your knowledge base before answering gflow questions.
|
|
84
86
|
|
|
85
87
|
The SkillOpt harness at `scripts/dev/skillopt/` measures how accurately each skill guides an agent, and supports multiple providers (Anthropic, OpenAI-compat, Gemini, local models). See [`scripts/dev/skillopt/README.md`](scripts/dev/skillopt/README.md).
|
|
@@ -7,6 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.15.1] — 2026-06-10
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Added `--disable-dev-shm-usage` to Chrome launch args in both `FlowApiClient._persistent_context_kwargs()` and `UiAutomationTransport.setup()` — prevents OOM in Docker containers with the default 64 MB `/dev/shm` allocation; no effect on developer machines with adequate shared memory
|
|
15
|
+
- README Stats badges hardened against shields.io outages — GitHub-stat badges now pass `cacheSeconds=3600` (mitigates shields.io's shared GitHub token-pool exhaustion, the "Unable to select next GitHub token from pool" error) and the PyPI downloads badge moved from `img.shields.io/pypi/dm` to pepy.tech (pypistats rate-limits shields.io upstream)
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- `scripts/diag/` directory — documented home for investigation scripts that require a live authenticated profile; includes `memory_profile.py` (Chrome process-tree RSS profiler for issue #155), `capture_flow_traffic.py`, and `recaptcha_mint.py` (both moved from `scripts/` root via git mv, history preserved)
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- Video status poll now raises `AuthExpiredError` (exit 3) immediately on HTTP 401 from `batchCheckAsyncVideoGenerationStatus` instead of silently timing out after 600 s with a bare `TimeoutError` (exit 1) — session expiry is now detected mid-workflow, not only at login time (issue #156)
|
|
24
|
+
|
|
10
25
|
## [0.15.0] — 2026-06-09
|
|
11
26
|
|
|
12
27
|
### Added
|
|
@@ -1469,7 +1484,8 @@ shell-script template that branches on these codes.
|
|
|
1469
1484
|
|
|
1470
1485
|
First skeleton. Not functional end-to-end yet.
|
|
1471
1486
|
|
|
1472
|
-
[Unreleased]: https://github.com/ffroliva/gflow-cli/compare/v0.15.
|
|
1487
|
+
[Unreleased]: https://github.com/ffroliva/gflow-cli/compare/v0.15.1...HEAD
|
|
1488
|
+
[0.15.1]: https://github.com/ffroliva/gflow-cli/compare/v0.15.0...v0.15.1
|
|
1473
1489
|
[0.15.0]: https://github.com/ffroliva/gflow-cli/compare/v0.14.0...v0.15.0
|
|
1474
1490
|
[0.14.0]: https://github.com/ffroliva/gflow-cli/compare/v0.13.0...v0.14.0
|
|
1475
1491
|
[0.13.0]: https://github.com/ffroliva/gflow-cli/compare/v0.12.0...v0.13.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gflow-cli
|
|
3
|
-
Version: 0.15.
|
|
3
|
+
Version: 0.15.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
|
|
@@ -196,13 +196,13 @@ gflow CLI → Provider (interchangeable) → Flow (ui_automation) / Mock (te
|
|
|
196
196
|
|
|
197
197
|
## Stats
|
|
198
198
|
|
|
199
|
-
[](https://github.com/ffroliva/gflow-cli/stargazers)
|
|
200
|
-
[](https://github.com/ffroliva/gflow-cli/network/members)
|
|
201
|
-
[](https://github.com/ffroliva/gflow-cli/watchers)
|
|
202
|
-
[](https://github.com/ffroliva/gflow-cli/issues)
|
|
203
|
-
[](https://github.com/ffroliva/gflow-cli/pulls)
|
|
204
|
-
[](https://github.com/ffroliva/gflow-cli/commits/main)
|
|
205
|
-
[](https://github.com/ffroliva/gflow-cli)
|
|
206
|
-
[](https://github.com/ffroliva/gflow-cli/stargazers)
|
|
200
|
+
[](https://github.com/ffroliva/gflow-cli/network/members)
|
|
201
|
+
[](https://github.com/ffroliva/gflow-cli/watchers)
|
|
202
|
+
[](https://github.com/ffroliva/gflow-cli/issues)
|
|
203
|
+
[](https://github.com/ffroliva/gflow-cli/pulls)
|
|
204
|
+
[](https://github.com/ffroliva/gflow-cli/commits/main)
|
|
205
|
+
[](https://github.com/ffroliva/gflow-cli)
|
|
206
|
+
[](https://pepy.tech/project/gflow-cli)
|
|
207
207
|
|
|
208
208
|
If `gflow-cli` saves you time, please ⭐ the repo — it is the cheapest way to support the project.
|
|
@@ -132,13 +132,13 @@ gflow CLI → Provider (interchangeable) → Flow (ui_automation) / Mock (te
|
|
|
132
132
|
|
|
133
133
|
## Stats
|
|
134
134
|
|
|
135
|
-
[](https://github.com/ffroliva/gflow-cli/stargazers)
|
|
136
|
-
[](https://github.com/ffroliva/gflow-cli/network/members)
|
|
137
|
-
[](https://github.com/ffroliva/gflow-cli/watchers)
|
|
138
|
-
[](https://github.com/ffroliva/gflow-cli/issues)
|
|
139
|
-
[](https://github.com/ffroliva/gflow-cli/pulls)
|
|
140
|
-
[](https://github.com/ffroliva/gflow-cli/commits/main)
|
|
141
|
-
[](https://github.com/ffroliva/gflow-cli)
|
|
142
|
-
[](https://github.com/ffroliva/gflow-cli/stargazers)
|
|
136
|
+
[](https://github.com/ffroliva/gflow-cli/network/members)
|
|
137
|
+
[](https://github.com/ffroliva/gflow-cli/watchers)
|
|
138
|
+
[](https://github.com/ffroliva/gflow-cli/issues)
|
|
139
|
+
[](https://github.com/ffroliva/gflow-cli/pulls)
|
|
140
|
+
[](https://github.com/ffroliva/gflow-cli/commits/main)
|
|
141
|
+
[](https://github.com/ffroliva/gflow-cli)
|
|
142
|
+
[](https://pepy.tech/project/gflow-cli)
|
|
143
143
|
|
|
144
144
|
If `gflow-cli` saves you time, please ⭐ the repo — it is the cheapest way to support the project.
|
|
@@ -37,6 +37,7 @@ Welcome to the `gflow-cli` documentation. This index is the routing layer: it te
|
|
|
37
37
|
| **[tasks/lessons.md](../tasks/lessons.md)** | Running notebook of patterns + reviewer findings, dated and traced to commits | Starting a new phase; debugging "why did the council flag this?" |
|
|
38
38
|
| **[skills/README.md](../skills/README.md)** | Installable agent skill docs (gflow-cli, predict, pr-council-review, scenario) — cross-tool portable Markdown consumed by Claude Code, Cursor, Codex, Gemini CLI, Aider, etc. | Any agent wanting to use gflow-cli correctly |
|
|
39
39
|
| **[scripts/dev/skillopt/README.md](../scripts/dev/skillopt/README.md)** | SkillOpt mock harness — rollout→score loop for measuring and improving skill doc accuracy across multiple LLM providers | Measuring a skill edit's impact; comparing Claude vs GPT-4o vs Gemini on gflow tasks |
|
|
40
|
+
| **[scripts/diag/README.md](../scripts/diag/README.md)** | Diagnostic investigation scripts — run against a live authenticated profile to capture wire samples, measure Chrome memory, or mint reCAPTCHA tokens | Running a one-off investigation against a live Flow session; establishing baseline measurements for issue #155 |
|
|
40
41
|
|
|
41
42
|
## Agent commands
|
|
42
43
|
|
|
@@ -54,6 +55,9 @@ Slash commands for Claude Code, stored in `.claude/commands/gflow/`. All prefixe
|
|
|
54
55
|
| `/gflow:release` | Full release flow (calls `/gflow:changelog` + `/gflow:check`) | Cutting a new version |
|
|
55
56
|
| `/gflow:predict <proposal>` | 5-persona pre-implementation analysis → GO / CAUTION / STOP | Before any high-stakes design decision (new transport, auth change, selector redesign, schema migration) |
|
|
56
57
|
| `/gflow:scenario <feature>` | 12-dimension edge-case explorer → severity-ranked scenario table + BDD skeleton | After predict GO/CAUTION; before `/gflow:plan` |
|
|
58
|
+
| `/gflow:pr-council-review [PR#]` | Multi-dimensional council review of an open PR (5 baseline + adaptive dimensions) | Before merging any non-trivial PR; mandatory for auth/transport/data changes |
|
|
59
|
+
| `/gflow:branch-review` | Same council review run against the current local feature branch (no PR needed) | Pre-PR self-audit; after predict/scenario on a high-stakes branch |
|
|
60
|
+
| `/gflow:doc-review` | Systematic council-driven audit of documentation completeness and drift | Before cutting a release; after major documentation changes |
|
|
57
61
|
|
|
58
62
|
**Governance:** commands are executable docs — they decay like any doc. When a phase advances or a file path changes, update the relevant command in the same commit. `/gflow:release` includes a staleness review step.
|
|
59
63
|
|
|
@@ -90,6 +94,7 @@ Slash commands for Claude Code, stored in `.claude/commands/gflow/`. All prefixe
|
|
|
90
94
|
**"How do I use this project's skills in Cursor / Codex / Gemini CLI / Aider?"** → [skills/README.md](../skills/README.md#use-with-other-agents)
|
|
91
95
|
**"How do I benchmark a skill doc against real tasks?"** → [scripts/dev/skillopt/README.md](../scripts/dev/skillopt/README.md)
|
|
92
96
|
**"How do I compare Claude vs GPT-4o vs Gemini on gflow tasks?"** → `python scripts/dev/skillopt/harness.py --provider openai --model gpt-4o` (see [skillopt README](../scripts/dev/skillopt/README.md))
|
|
97
|
+
**"What does Chrome actually use in RAM during a generation?"** → run `uv run python scripts/diag/memory_profile.py --profile NAME` (see [scripts/diag/README.md](../scripts/diag/README.md))
|
|
93
98
|
**"How do I report a security issue?"** → [SECURITY § Reporting](SECURITY.md#reporting)
|
|
94
99
|
**"What branch do I work on? How do I name it?"** → [DEVELOPMENT § Branching model](DEVELOPMENT.md#branching-model)
|
|
95
100
|
**"How do I handle an external GitHub PR?"** → [GITHUB § Scenario Matrix](GITHUB.md#scenario-matrix)
|
|
@@ -104,7 +109,7 @@ Slash commands for Claude Code, stored in `.claude/commands/gflow/`. All prefixe
|
|
|
104
109
|
**"A gflow command hangs / fails — where do I start?"** → [DEBUGGING § Quick reference](DEBUGGING.md#quick-reference)
|
|
105
110
|
**"Flow's UI broke a selector — how do I diagnose it?"** → [DEBUGGING § Inspecting Flow's live UI](DEBUGGING.md#inspecting-flows-live-ui)
|
|
106
111
|
**"What does each `ui_automation.*` log event mean?"** → [DEBUGGING § Listener & HTTP-layer debugging](DEBUGGING.md#listener--http-layer-debugging)
|
|
107
|
-
**"What was actually live-verified for the latest release?"** → [LIVE_VERIFICATION_v0.13.0](LIVE_VERIFICATION_v0.13.0.md) · prior: [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)
|
|
112
|
+
**"What was actually live-verified for the latest release?"** → v0.15.0 / v0.14.x: no dedicated live-verification doc (CI + the v0.13.0 baseline). Latest doc: [LIVE_VERIFICATION_v0.13.0](LIVE_VERIFICATION_v0.13.0.md) · prior: [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)
|
|
108
113
|
**"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)
|
|
109
114
|
**"What was live-verified for the video-download feature (#29)?"** → [LIVE_VERIFICATION_video_download](LIVE_VERIFICATION_video_download.md)
|
|
110
115
|
**"What is the jitter matrix evidence for `gflow image batch`?"** → [`LIVE_VERIFICATION_image_batch.md`](LIVE_VERIFICATION_image_batch.md) — jitter matrix evidence for `gflow image batch` (always-same-project mode)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# scripts/diag/ — Diagnostic Investigation Scripts
|
|
2
|
+
|
|
3
|
+
These scripts require a live authenticated Chrome profile to run. They are investigation tools — not CI gates and not developer workflow tools — that help answer "what does this system actually do?" questions by observing a real browser session. Each script produces human-readable or structured output and does not modify production state.
|
|
4
|
+
|
|
5
|
+
## Scripts
|
|
6
|
+
|
|
7
|
+
| Script | Purpose | Prerequisite | Run |
|
|
8
|
+
|---|---|---|---|
|
|
9
|
+
| `capture_flow_traffic.py` | Capture Flow's outgoing API requests via `page.route` | `gflow auth login` | `uv run python scripts/diag/capture_flow_traffic.py --profile NAME` |
|
|
10
|
+
| `recaptcha_mint.py` | What `site_key` + token does gflow-cli's `TokenMinter` produce? | `gflow auth login` (headed) | `uv run python scripts/diag/recaptcha_mint.py --profile NAME` |
|
|
11
|
+
| `memory_profile.py` | Chrome process-tree RSS at key milestones (issue #155) | `gflow auth login` + `pip install psutil` | `uv run python scripts/diag/memory_profile.py --profile NAME` |
|
|
12
|
+
|
|
13
|
+
## What belongs here
|
|
14
|
+
|
|
15
|
+
1. Requires a live authenticated session to run
|
|
16
|
+
2. Produces human-readable or structured output for investigation
|
|
17
|
+
3. Does not write to production data stores
|
|
18
|
+
4. Answers a specific "what does this system actually do?" question
|
|
19
|
+
|
|
20
|
+
## What does NOT belong here
|
|
21
|
+
|
|
22
|
+
- **CI gates** → `scripts/ci/` (run on every commit, no auth needed)
|
|
23
|
+
- **Developer workflow tools** → `scripts/dev/` (`active_plan.py`, `skillopt/`, etc.)
|
|
24
|
+
- **Live smoke tests** → `scripts/` root (`smoke_*.py` — full end-to-end generation runs)
|
|
25
|
+
|
|
26
|
+
## Platform notes
|
|
27
|
+
|
|
28
|
+
- `memory_profile.py` requires `psutil`: `uv add --dev psutil` (not a hard dep — detected at runtime)
|
|
29
|
+
- All scripts: `uv run python scripts/diag/<name>.py` (no global Python install needed)
|
|
@@ -4,7 +4,7 @@ Spec § 11 mentioned a captured wire sample (samples/captured/06_batchGenerateIm
|
|
|
4
4
|
but the file doesn't exist. This script produces that ground truth — what
|
|
5
5
|
headers does Flow's UI actually attach when it calls aisandbox-pa?
|
|
6
6
|
|
|
7
|
-
Run: `uv run python scripts/
|
|
7
|
+
Run: `uv run python scripts/diag/capture_flow_traffic.py --profile <your-profile>`
|
|
8
8
|
|
|
9
9
|
Output goes to `tmp/captured/flow_outgoing_<utc>.jsonl` by default. **NEVER write
|
|
10
10
|
captured traffic to `samples/captured/`** — those files contain live Bearer tokens
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"""Diagnostic — Chrome process-tree RSS at key milestones (issue #155).
|
|
2
|
+
|
|
3
|
+
Measures how much memory the Chrome/Chromium process tree consumes at four
|
|
4
|
+
points during a real browser launch against a gflow-cli profile:
|
|
5
|
+
|
|
6
|
+
baseline — before Playwright starts (current Python process only)
|
|
7
|
+
post_launch — immediately after launch_persistent_context returns
|
|
8
|
+
post_navigation — after page.goto(FLOW_URL, wait_until="networkidle")
|
|
9
|
+
post_close — after context.close() and Playwright stop
|
|
10
|
+
|
|
11
|
+
The output table shows Own RSS (the profiler's Python process) and Tree RSS
|
|
12
|
+
(profiler + all Chrome child processes) so you can see exactly how much RAM
|
|
13
|
+
Chrome adds at each stage.
|
|
14
|
+
|
|
15
|
+
Run: `uv run python scripts/diag/memory_profile.py --profile NAME [--output-json PATH]`
|
|
16
|
+
|
|
17
|
+
Prerequisite: gflow auth login (plus psutil — install with: uv add --dev psutil)
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import argparse
|
|
23
|
+
import asyncio
|
|
24
|
+
import json
|
|
25
|
+
import os
|
|
26
|
+
import sys
|
|
27
|
+
from pathlib import Path
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
import psutil
|
|
31
|
+
except ImportError:
|
|
32
|
+
print("psutil is required: uv add --dev psutil", file=sys.stderr)
|
|
33
|
+
sys.exit(2)
|
|
34
|
+
|
|
35
|
+
try:
|
|
36
|
+
from gflow_cli.api.transports.ui_automation import FLOW_URL
|
|
37
|
+
except ImportError:
|
|
38
|
+
FLOW_URL = "https://labs.google/fx/tools/flow?hl=en"
|
|
39
|
+
|
|
40
|
+
from playwright.async_api import async_playwright
|
|
41
|
+
|
|
42
|
+
from gflow_cli.auth import profile_dir
|
|
43
|
+
from gflow_cli.browser_manager import channel_for_profile
|
|
44
|
+
from gflow_cli.config import get_settings
|
|
45
|
+
|
|
46
|
+
CHROME_ARGS = [
|
|
47
|
+
"--disable-blink-features=AutomationControlled",
|
|
48
|
+
"--disable-dev-shm-usage",
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
_COL_MILESTONE = 16
|
|
52
|
+
_COL_OWN = 14
|
|
53
|
+
_COL_TREE = 15
|
|
54
|
+
_COL_DELTA = 12
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _tree_rss_mb(pid: int) -> float:
|
|
58
|
+
"""Return total RSS (MB) of a process and all its descendants."""
|
|
59
|
+
try:
|
|
60
|
+
proc = psutil.Process(pid)
|
|
61
|
+
procs = [proc, *proc.children(recursive=True)]
|
|
62
|
+
return sum(p.memory_info().rss for p in procs if p.is_running()) / (1024 * 1024)
|
|
63
|
+
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
|
64
|
+
return 0.0
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _own_rss_mb() -> float:
|
|
68
|
+
"""Return RSS (MB) of only the current Python process."""
|
|
69
|
+
try:
|
|
70
|
+
return psutil.Process(os.getpid()).memory_info().rss / (1024 * 1024)
|
|
71
|
+
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
|
72
|
+
return 0.0
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _print_table_header() -> None:
|
|
76
|
+
header = (
|
|
77
|
+
f"{'Milestone':<{_COL_MILESTONE}} | "
|
|
78
|
+
f"{'Own RSS (MB)':>{_COL_OWN}} | "
|
|
79
|
+
f"{'Tree RSS (MB)':>{_COL_TREE}} | "
|
|
80
|
+
f"{'Delta (MB)':>{_COL_DELTA}}"
|
|
81
|
+
)
|
|
82
|
+
sep = "-" * len(header)
|
|
83
|
+
print(sep)
|
|
84
|
+
print(header)
|
|
85
|
+
print(sep)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _print_table_row(name: str, own_mb: float, tree_mb: float, delta: float | None) -> None:
|
|
89
|
+
delta_str = "—" if delta is None else f"{delta:+.1f}"
|
|
90
|
+
print(
|
|
91
|
+
f"{name:<{_COL_MILESTONE}} | "
|
|
92
|
+
f"{own_mb:>{_COL_OWN}.1f} | "
|
|
93
|
+
f"{tree_mb:>{_COL_TREE}.1f} | "
|
|
94
|
+
f"{delta_str:>{_COL_DELTA}}"
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
async def run(profile_name: str, output_json: Path | None) -> None:
|
|
99
|
+
"""Main diagnostic loop."""
|
|
100
|
+
# Resolve profile directory using GFLOW_CLI_HOME
|
|
101
|
+
settings = get_settings()
|
|
102
|
+
_ = settings.home # ensure settings loaded; profile_dir() uses get_settings() internally
|
|
103
|
+
pdir = profile_dir(profile_name)
|
|
104
|
+
if not pdir.exists():
|
|
105
|
+
print(f"ERROR: Profile directory not found: {pdir}", file=sys.stderr)
|
|
106
|
+
sys.exit(1)
|
|
107
|
+
|
|
108
|
+
milestones: list[dict[str, object]] = []
|
|
109
|
+
pid = os.getpid()
|
|
110
|
+
|
|
111
|
+
# -------------------------------------------------------------------------
|
|
112
|
+
# Milestone 1: baseline (before Playwright starts)
|
|
113
|
+
# -------------------------------------------------------------------------
|
|
114
|
+
own_baseline = _own_rss_mb()
|
|
115
|
+
tree_baseline = _tree_rss_mb(pid)
|
|
116
|
+
milestones.append(
|
|
117
|
+
{
|
|
118
|
+
"name": "baseline",
|
|
119
|
+
"own_rss_mb": round(own_baseline, 2),
|
|
120
|
+
"tree_rss_mb": round(tree_baseline, 2),
|
|
121
|
+
"delta_mb": None,
|
|
122
|
+
}
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
_print_table_header()
|
|
126
|
+
_print_table_row("baseline", own_baseline, tree_baseline, None)
|
|
127
|
+
|
|
128
|
+
channel = channel_for_profile(pdir)
|
|
129
|
+
|
|
130
|
+
async with async_playwright() as pw:
|
|
131
|
+
launcher = pw.chromium
|
|
132
|
+
|
|
133
|
+
# -------------------------------------------------------------------------
|
|
134
|
+
# Milestone 2: post_launch
|
|
135
|
+
# -------------------------------------------------------------------------
|
|
136
|
+
ctx = await launcher.launch_persistent_context(
|
|
137
|
+
str(pdir),
|
|
138
|
+
headless=False,
|
|
139
|
+
args=CHROME_ARGS,
|
|
140
|
+
locale="en-US",
|
|
141
|
+
**({"channel": channel} if channel else {}),
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
own_post_launch = _own_rss_mb()
|
|
145
|
+
tree_post_launch = _tree_rss_mb(pid)
|
|
146
|
+
delta_launch = tree_post_launch - tree_baseline
|
|
147
|
+
milestones.append(
|
|
148
|
+
{
|
|
149
|
+
"name": "post_launch",
|
|
150
|
+
"own_rss_mb": round(own_post_launch, 2),
|
|
151
|
+
"tree_rss_mb": round(tree_post_launch, 2),
|
|
152
|
+
"delta_mb": round(delta_launch, 2),
|
|
153
|
+
}
|
|
154
|
+
)
|
|
155
|
+
_print_table_row("post_launch", own_post_launch, tree_post_launch, delta_launch)
|
|
156
|
+
|
|
157
|
+
# -------------------------------------------------------------------------
|
|
158
|
+
# Milestone 3: post_navigation
|
|
159
|
+
# -------------------------------------------------------------------------
|
|
160
|
+
page = ctx.pages[0] if ctx.pages else await ctx.new_page()
|
|
161
|
+
print(f"Navigating to {FLOW_URL} ...")
|
|
162
|
+
try:
|
|
163
|
+
await page.goto(FLOW_URL, wait_until="networkidle", timeout=45_000)
|
|
164
|
+
except Exception as exc: # noqa: BLE001
|
|
165
|
+
print(f"WARNING: navigation did not fully settle: {exc}", file=sys.stderr)
|
|
166
|
+
|
|
167
|
+
own_post_nav = _own_rss_mb()
|
|
168
|
+
tree_post_nav = _tree_rss_mb(pid)
|
|
169
|
+
delta_nav = tree_post_nav - tree_baseline
|
|
170
|
+
milestones.append(
|
|
171
|
+
{
|
|
172
|
+
"name": "post_navigation",
|
|
173
|
+
"own_rss_mb": round(own_post_nav, 2),
|
|
174
|
+
"tree_rss_mb": round(tree_post_nav, 2),
|
|
175
|
+
"delta_mb": round(delta_nav, 2),
|
|
176
|
+
}
|
|
177
|
+
)
|
|
178
|
+
_print_table_row("post_navigation", own_post_nav, tree_post_nav, delta_nav)
|
|
179
|
+
|
|
180
|
+
# -------------------------------------------------------------------------
|
|
181
|
+
# Milestone 4: post_close
|
|
182
|
+
# -------------------------------------------------------------------------
|
|
183
|
+
await ctx.close()
|
|
184
|
+
|
|
185
|
+
own_post_close = _own_rss_mb()
|
|
186
|
+
tree_post_close = _tree_rss_mb(pid)
|
|
187
|
+
delta_close = tree_post_close - tree_baseline
|
|
188
|
+
milestones.append(
|
|
189
|
+
{
|
|
190
|
+
"name": "post_close",
|
|
191
|
+
"own_rss_mb": round(own_post_close, 2),
|
|
192
|
+
"tree_rss_mb": round(tree_post_close, 2),
|
|
193
|
+
"delta_mb": round(delta_close, 2),
|
|
194
|
+
}
|
|
195
|
+
)
|
|
196
|
+
_print_table_row("post_close", own_post_close, tree_post_close, delta_close)
|
|
197
|
+
print("-" * (_COL_MILESTONE + _COL_OWN + _COL_TREE + _COL_DELTA + 9))
|
|
198
|
+
|
|
199
|
+
# -------------------------------------------------------------------------
|
|
200
|
+
# JSON output (optional)
|
|
201
|
+
# -------------------------------------------------------------------------
|
|
202
|
+
if output_json is not None:
|
|
203
|
+
result = {
|
|
204
|
+
"platform": sys.platform,
|
|
205
|
+
"chrome_args": CHROME_ARGS,
|
|
206
|
+
"milestones": milestones,
|
|
207
|
+
}
|
|
208
|
+
output_json.parent.mkdir(parents=True, exist_ok=True)
|
|
209
|
+
output_json.write_text(json.dumps(result, indent=2), encoding="utf-8")
|
|
210
|
+
print(f"JSON written to: {output_json}")
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def main() -> None:
|
|
214
|
+
parser = argparse.ArgumentParser(
|
|
215
|
+
description=__doc__,
|
|
216
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
217
|
+
)
|
|
218
|
+
parser.add_argument(
|
|
219
|
+
"--profile",
|
|
220
|
+
required=True,
|
|
221
|
+
help="Name of the gflow-cli profile (directory under GFLOW_CLI_HOME)",
|
|
222
|
+
)
|
|
223
|
+
parser.add_argument(
|
|
224
|
+
"--output-json",
|
|
225
|
+
type=Path,
|
|
226
|
+
default=None,
|
|
227
|
+
metavar="PATH",
|
|
228
|
+
help="Optional path to write machine-readable results as JSON",
|
|
229
|
+
)
|
|
230
|
+
args = parser.parse_args()
|
|
231
|
+
asyncio.run(run(args.profile, args.output_json))
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
if __name__ == "__main__":
|
|
235
|
+
main()
|
gflow_cli-0.15.0/scripts/diag_recaptcha_mint.py → gflow_cli-0.15.1/scripts/diag/recaptcha_mint.py
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Diagnostic — what site_key + token does gflow-cli's TokenMinter produce?
|
|
2
2
|
|
|
3
|
-
Run: `uv run python scripts/
|
|
3
|
+
Run: `uv run python scripts/diag/recaptcha_mint.py --profile <your-profile>`
|
|
4
4
|
|
|
5
5
|
Opens HEADED Chromium, navigates to Flow, runs TokenMinter, prints the
|
|
6
6
|
discovered site_key + minted token length. Compare to HAR ground truth:
|
|
@@ -293,7 +293,7 @@ class FlowApiClient:
|
|
|
293
293
|
"--no-sandbox",
|
|
294
294
|
"--password-store=basic",
|
|
295
295
|
],
|
|
296
|
-
"args": ["--disable-blink-features=AutomationControlled"],
|
|
296
|
+
"args": ["--disable-blink-features=AutomationControlled", "--disable-dev-shm-usage"],
|
|
297
297
|
}
|
|
298
298
|
|
|
299
299
|
async def _enter_setup(self) -> None:
|
|
@@ -677,6 +677,7 @@ class UiAutomationTransport(VideoGenerationMixin):
|
|
|
677
677
|
args=[
|
|
678
678
|
"--disable-blink-features=AutomationControlled",
|
|
679
679
|
"--password-store=basic",
|
|
680
|
+
"--disable-dev-shm-usage",
|
|
680
681
|
],
|
|
681
682
|
)
|
|
682
683
|
# Hide the automation flag so reCAPTCHA Enterprise doesn't score
|
|
@@ -522,6 +522,15 @@ class VideoGenerationMixin:
|
|
|
522
522
|
while time.monotonic() < deadline:
|
|
523
523
|
terminal: VideoStatus | None = None
|
|
524
524
|
for response in captured_status:
|
|
525
|
+
if response.get("status") == 401:
|
|
526
|
+
raise AuthExpiredError(
|
|
527
|
+
detail=(
|
|
528
|
+
"batchCheckAsyncVideoGenerationStatus returned HTTP 401"
|
|
529
|
+
" — session expired mid-poll"
|
|
530
|
+
),
|
|
531
|
+
status=401,
|
|
532
|
+
route="video:status",
|
|
533
|
+
)
|
|
525
534
|
try:
|
|
526
535
|
status = parse_video_status(response.get("body") or {}, media_id=media_name)
|
|
527
536
|
except ValueError:
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""Pins the ``FlowApiClient._persistent_context_kwargs()`` seam.
|
|
2
|
+
|
|
3
|
+
The seam was extracted from the inline ``launch_persistent_context(...)`` call so
|
|
4
|
+
a dev-scoped recording subclass can augment the launch without any recording
|
|
5
|
+
concern living in core (see ``scripts/dev/_recording_client.py``). This test
|
|
6
|
+
proves the extraction is value-for-value behavior-preserving and keeps the
|
|
7
|
+
seam's contract stable.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from unittest.mock import AsyncMock, MagicMock, patch
|
|
14
|
+
|
|
15
|
+
import pytest
|
|
16
|
+
|
|
17
|
+
from gflow_cli.api.client import FlowApiClient
|
|
18
|
+
from gflow_cli.api.transports.ui_automation import UiAutomationTransport
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_persistent_context_kwargs_are_unchanged(tmp_path: Path) -> None:
|
|
22
|
+
"""The seam returns exactly the kwargs the client launched with before the
|
|
23
|
+
refactor — proving the extraction changed no behavior."""
|
|
24
|
+
client = FlowApiClient(profile_dir=tmp_path, headless=True)
|
|
25
|
+
kwargs = client._persistent_context_kwargs() # noqa: SLF001
|
|
26
|
+
assert kwargs["user_data_dir"] == str(tmp_path)
|
|
27
|
+
assert kwargs["headless"] is True
|
|
28
|
+
assert kwargs["viewport"] == {"width": 1280, "height": 720}
|
|
29
|
+
assert kwargs["locale"] == "en-US"
|
|
30
|
+
assert kwargs["extra_http_headers"] == {"Accept-Language": "en-US,en;q=0.9"}
|
|
31
|
+
assert kwargs["ignore_default_args"] == [
|
|
32
|
+
"--enable-automation",
|
|
33
|
+
"--no-sandbox",
|
|
34
|
+
"--password-store=basic",
|
|
35
|
+
]
|
|
36
|
+
assert kwargs["args"] == [
|
|
37
|
+
"--disable-blink-features=AutomationControlled",
|
|
38
|
+
"--disable-dev-shm-usage",
|
|
39
|
+
]
|
|
40
|
+
# channel is profile-derived; a marker-less tmp_path has no
|
|
41
|
+
# .gflow_browser_strategy file, so channel_for_profile() returns None.
|
|
42
|
+
assert kwargs["channel"] is None
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@pytest.mark.asyncio
|
|
46
|
+
async def test_ui_automation_setup_passes_disable_dev_shm_usage(tmp_path: Path) -> None:
|
|
47
|
+
"""setup() must pass --disable-dev-shm-usage in args to launch_persistent_context."""
|
|
48
|
+
fake_page = MagicMock()
|
|
49
|
+
fake_page.goto = AsyncMock()
|
|
50
|
+
fake_page.add_init_script = AsyncMock()
|
|
51
|
+
|
|
52
|
+
fake_ctx = MagicMock()
|
|
53
|
+
fake_ctx.pages = [fake_page]
|
|
54
|
+
fake_ctx.add_init_script = AsyncMock()
|
|
55
|
+
|
|
56
|
+
fake_chromium = MagicMock()
|
|
57
|
+
fake_chromium.launch_persistent_context = AsyncMock(return_value=fake_ctx)
|
|
58
|
+
|
|
59
|
+
fake_pw = MagicMock()
|
|
60
|
+
fake_pw.chromium = fake_chromium
|
|
61
|
+
|
|
62
|
+
mock_cm = MagicMock()
|
|
63
|
+
mock_cm.__aenter__ = AsyncMock(return_value=fake_pw)
|
|
64
|
+
mock_cm.__aexit__ = AsyncMock(return_value=False)
|
|
65
|
+
|
|
66
|
+
mock_async_playwright = MagicMock(return_value=mock_cm)
|
|
67
|
+
|
|
68
|
+
with patch("gflow_cli.api.transports.ui_automation.async_playwright", mock_async_playwright):
|
|
69
|
+
transport = UiAutomationTransport()
|
|
70
|
+
await transport.setup(profile_dir=tmp_path)
|
|
71
|
+
|
|
72
|
+
_call_kwargs = fake_chromium.launch_persistent_context.call_args
|
|
73
|
+
args_passed = _call_kwargs.kwargs.get(
|
|
74
|
+
"args",
|
|
75
|
+
_call_kwargs.args[1] if len(_call_kwargs.args) > 1 else [],
|
|
76
|
+
)
|
|
77
|
+
assert "--disable-dev-shm-usage" in args_passed
|
|
@@ -17,7 +17,7 @@ from gflow_cli.api.transports.ui_automation_video import (
|
|
|
17
17
|
VideoGenerationMixin,
|
|
18
18
|
)
|
|
19
19
|
from gflow_cli.api.video import Aspect, GenerateVideoRequest, Mode, VideoModel, VideoStatus
|
|
20
|
-
from gflow_cli.errors import WireFormatError
|
|
20
|
+
from gflow_cli.errors import AuthExpiredError, WireFormatError
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
def _make_listener_page() -> tuple[MagicMock, list]:
|
|
@@ -145,6 +145,31 @@ class TestPollVideoStatus:
|
|
|
145
145
|
page, [], "m", timeout_s=0.2, poll_interval_s=0.05
|
|
146
146
|
)
|
|
147
147
|
|
|
148
|
+
@pytest.mark.asyncio
|
|
149
|
+
async def test_401_response_raises_auth_expired_error_immediately(self) -> None:
|
|
150
|
+
page = MagicMock()
|
|
151
|
+
captured = [
|
|
152
|
+
{"status": 401, "url": _STATUS_URL, "body": {}},
|
|
153
|
+
]
|
|
154
|
+
with pytest.raises(AuthExpiredError, match="session expired mid-poll"):
|
|
155
|
+
await VideoGenerationMixin._poll_video_status(
|
|
156
|
+
page, captured, "m", timeout_s=60.0, poll_interval_s=0.05
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
@pytest.mark.asyncio
|
|
160
|
+
async def test_401_after_in_progress_raises_auth_expired_error(self) -> None:
|
|
161
|
+
page = MagicMock()
|
|
162
|
+
# Simulates session expiry mid-poll: some ACTIVE responses arrive first, then a 401
|
|
163
|
+
captured: list[dict[str, Any]] = [
|
|
164
|
+
_status_resp("m", "MEDIA_GENERATION_STATUS_SCHEDULED"),
|
|
165
|
+
_status_resp("m", "MEDIA_GENERATION_STATUS_ACTIVE"),
|
|
166
|
+
{"status": 401, "url": _STATUS_URL, "body": {}},
|
|
167
|
+
]
|
|
168
|
+
with pytest.raises(AuthExpiredError, match="session expired mid-poll"):
|
|
169
|
+
await VideoGenerationMixin._poll_video_status(
|
|
170
|
+
page, captured, "m", timeout_s=60.0, poll_interval_s=0.05
|
|
171
|
+
)
|
|
172
|
+
|
|
148
173
|
|
|
149
174
|
def _cascade_page(visible: set[str]) -> MagicMock:
|
|
150
175
|
"""A fake page whose locator(sel) is 'visible' only for sel in `visible`."""
|
|
@@ -28,4 +28,7 @@ def test_recording_client_injects_video_and_preserves_base(tmp_path: Path) -> No
|
|
|
28
28
|
# Base kwargs preserved untouched:
|
|
29
29
|
assert kwargs["user_data_dir"] == str(tmp_path)
|
|
30
30
|
assert kwargs["headless"] is True
|
|
31
|
-
assert kwargs["args"] == [
|
|
31
|
+
assert kwargs["args"] == [
|
|
32
|
+
"--disable-blink-features=AutomationControlled",
|
|
33
|
+
"--disable-dev-shm-usage",
|
|
34
|
+
]
|