gflow-cli 0.6.0a2__tar.gz → 0.6.0a3__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.6.0a2 → gflow_cli-0.6.0a3}/.env.template +4 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/CHANGELOG.md +41 -22
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/PKG-INFO +1 -1
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/docs/CONFIGURATION.md +12 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/pyproject.toml +1 -1
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/src/gflow_cli/__init__.py +1 -1
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/src/gflow_cli/api/client.py +2 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/src/gflow_cli/api/transports/ui_automation.py +2 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/src/gflow_cli/auth/__init__.py +5 -1
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/src/gflow_cli/auth/factory.py +12 -9
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/src/gflow_cli/auth/internal_chromium.py +24 -3
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/src/gflow_cli/auth/real_chrome.py +10 -6
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/src/gflow_cli/browser_manager.py +34 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/src/gflow_cli/config.py +10 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/src/gflow_cli/errors.py +1 -1
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/auth/strategies/test_strategies.py +10 -2
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/features/auth_login.feature +1 -1
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/features/test_auth_login_steps.py +6 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/test_smoke.py +1 -1
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/.claude/README.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/.claude/commands/release.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/.gitattributes +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/.github/workflows/ci.yml +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/.github/workflows/release.yml +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/.gitignore +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/.planning/todos/pending/2026-05-11-add-project-logo-and-docs-site-promotion-plan.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/CLAUDE.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/CONFIGURATION.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/CONTRIBUTING.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/DISCLAIMER.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/KNOWN_ISSUES.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/LICENSE +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/PLAN.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/README.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/RELEASE.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/docs/ARCHITECTURE.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/docs/AUTHENTICATION.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/docs/INDEX.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/docs/SECURITY.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/docs/USAGE.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/docs/USER_GUIDE.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/docs/assets/example-run.gif +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/docs/superpowers/plans/2026-05-09-image-mvp-orchestration.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/docs/superpowers/plans/2026-05-09-image-mvp.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/docs/superpowers/plans/2026-05-09-video-mvp-orchestration.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/docs/superpowers/plans/2026-05-09-video-mvp.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/docs/superpowers/plans/2026-05-10-phase-4-hardening-orchestration.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/docs/superpowers/plans/2026-05-10-phase-4-hardening.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/docs/superpowers/plans/2026-05-14-shell-multi-prompt/2026-05-14-shell-multi-prompt-orchestration.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/docs/superpowers/plans/2026-05-14-shell-multi-prompt/COUNCIL_FINAL_ARCH.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/docs/superpowers/plans/2026-05-14-shell-multi-prompt/COUNCIL_FINAL_SEC_UX.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/docs/superpowers/plans/2026-05-14-shell-multi-prompt/COUNCIL_REVIEW_CODE.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/docs/superpowers/plans/2026-05-14-shell-multi-prompt/COUNCIL_REVIEW_GEMINI.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/docs/superpowers/plans/2026-05-14-shell-multi-prompt/COUNCIL_REVIEW_SECURITY.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/docs/superpowers/plans/2026-05-14-shell-multi-prompt/IMPLEMENTATION_REVIEW_PYTHON.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/docs/superpowers/plans/2026-05-14-shell-multi-prompt/IMPLEMENTATION_REVIEW_SECURITY.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/docs/superpowers/plans/2026-05-14-shell-multi-prompt/PLAN.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/docs/superpowers/plans/2026-05-14-shell-multi-prompt/PLAN_REVIEW_CODE.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/docs/superpowers/plans/2026-05-14-shell-multi-prompt/PLAN_REVIEW_FOLLOWUP.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/docs/superpowers/plans/2026-05-14-shell-multi-prompt/PLAN_REVIEW_PLANNER.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/docs/superpowers/plans/2026-05-14-shell-multi-prompt/PLAN_REVIEW_SECURITY.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/docs/superpowers/plans/2026-05-14-shell-multi-prompt/PLAN_REVIEW_SECURITY_FOLLOWUP.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/docs/superpowers/plans/2026-05-15-auth-login-real-chrome/COUNCIL_FINAL_SEC_UX_VERIFIED.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/docs/superpowers/plans/2026-05-15-auth-login-real-chrome/COUNCIL_REVIEW_PLAN_SECURITY.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/docs/superpowers/plans/2026-05-15-auth-login-real-chrome/COUNCIL_REVIEW_SPEC_SECURITY.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/docs/superpowers/plans/2026-05-15-auth-login-real-chrome/PLAN.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/docs/superpowers/plans/2026-05-15-auth-login-real-chrome/orchestration.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/docs/superpowers/specs/2026-05-10-phase-4-hardening-design.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/docs/superpowers/specs/2026-05-14-shell-multi-prompt-design.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/docs/superpowers/specs/2026-05-15-auth-login-real-chrome-design.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/docs/superpowers/verifications/2026-05-11-phase-4-stage-g.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/examples/README.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/examples/batch_from_config.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/examples/multi_prompt_t2i.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/examples/sample_config.json +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/examples/sample_prompts.txt +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/examples/single_image_t2i.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/samples/README.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/samples/captured/01_upload_image.json +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/samples/captured/02_batchAsyncGenerateVideoText.json +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/samples/captured/03_batchCheckAsyncVideoGenerationStatus.json +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/samples/captured/04_archive_workflow.json +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/samples/captured/05_createProject.json +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/samples/captured/06_batchGenerateImages.json +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/samples/captured/07_batchGenerateImages_seeded.json +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/scripts/diag_capture_flow_traffic.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/scripts/diag_recaptcha_mint.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/scripts/record_demo.ps1 +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/scripts/smoke_e2e.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/scripts/smoke_image.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/scripts/smoke_real_chrome_image.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/scripts/smoke_worker_style.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/scripts/verify_chrome_auth_viability.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/skills/README.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/skills/gflow-cli/SKILL.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/src/gflow_cli/__main__.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/src/gflow_cli/_cli_helpers.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/src/gflow_cli/api/__init__.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/src/gflow_cli/api/_retry.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/src/gflow_cli/api/dto.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/src/gflow_cli/api/image.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/src/gflow_cli/api/recaptcha.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/src/gflow_cli/api/routes.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/src/gflow_cli/api/transports/__init__.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/src/gflow_cli/api/transports/_common.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/src/gflow_cli/api/transports/_fingerprint.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/src/gflow_cli/api/transports/base.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/src/gflow_cli/api/transports/experimental/__init__.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/src/gflow_cli/api/transports/experimental/bearer.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/src/gflow_cli/api/transports/experimental/evaluate_fetch.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/src/gflow_cli/api/transports/experimental/sapisidhash.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/src/gflow_cli/api/video.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/src/gflow_cli/auth/base.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/src/gflow_cli/auth/strategies.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/src/gflow_cli/cli.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/src/gflow_cli/cli_image.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/src/gflow_cli/cli_run.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/src/gflow_cli/cli_video.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/src/gflow_cli/image_batch.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/src/gflow_cli/manifest.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/src/gflow_cli/observability.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/src/gflow_cli/paths.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/src/gflow_cli/profile_store.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tasks/lessons.md +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/__init__.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/api/__init__.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/api/test_client.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/api/test_client_generate_video.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/api/test_client_image.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/api/test_concurrency.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/api/test_dto.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/api/test_image.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/api/test_image_dto.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/api/test_recaptcha.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/api/test_retry.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/api/test_routes.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/api/test_video.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/api/transports/__init__.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/api/transports/test_base.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/api/transports/test_bearer.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/api/transports/test_common.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/api/transports/test_evaluate_fetch.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/api/transports/test_factory.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/api/transports/test_fingerprint.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/api/transports/test_sapisidhash.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/api/transports/test_ui_automation.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/auth/strategies/test_factory.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/cli/__init__.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/cli/test_cli_image.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/cli/test_cli_run.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/cli/test_error_handling.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/cli/test_helpers.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/cli/test_t2i_multi_prompt.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/conftest.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/e2e/__init__.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/e2e/test_transports_e2e.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/features/__init__.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/features/auth.feature +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/features/conftest.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/features/image.feature +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/features/test_auth_steps.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/features/test_image_steps.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/features/test_step_collision_guard.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/features/test_video_steps.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/features/video.feature +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/smoke/__init__.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/smoke/test_real_flow.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/test_auth.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/test_browser_manager.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/test_cli_video.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/test_config.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/test_errors.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/test_manifest.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/test_observability.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/test_paths.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/tests/test_profile_store.py +0 -0
- {gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/uv.lock +0 -0
|
@@ -21,6 +21,10 @@
|
|
|
21
21
|
# `--profile <name>`.
|
|
22
22
|
# GFLOW_CLI_PROFILE=default
|
|
23
23
|
|
|
24
|
+
# Maximum wait time (seconds) for `gflow auth login` to complete Google sign-in.
|
|
25
|
+
# Useful for agent pipelines — set low to surface hung logins as exit 12 fast.
|
|
26
|
+
# GFLOW_CLI_AUTH_LOGIN_TIMEOUT=600
|
|
27
|
+
|
|
24
28
|
# -----------------------------------------------------------------------------
|
|
25
29
|
# Output paths
|
|
26
30
|
# -----------------------------------------------------------------------------
|
|
@@ -7,47 +7,66 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.6.0a3] — 2026-05-17
|
|
11
|
+
|
|
12
|
+
> **Deterministic timeouts + agent-friendly exit codes.** This release hardens
|
|
13
|
+
> the auth login flow for unattended / agentic use: timeouts now raise distinct
|
|
14
|
+
> errors with dedicated exit codes instead of silently swallowing failures.
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- **`AuthLoginTimeoutError`** (exit code **12**) — raised by both strategies
|
|
19
|
+
when the user/agent does not complete sign-in within `timeout_seconds`.
|
|
20
|
+
Distinct from `ConfigurationError` (11) and `SecurityError` (13) so agents
|
|
21
|
+
can branch on failure type without parsing stderr.
|
|
22
|
+
- **`SecurityError`** exit code **13** — now registered in `EXIT_CODE_MAP`.
|
|
23
|
+
- **`timeout_seconds=600` parameter** on both `RealChromeStrategy` and
|
|
24
|
+
`InternalChromiumStrategy` — configurable upper bound for the login window.
|
|
25
|
+
- **Broad `GFlowError` catch** in `auth_login` CLI command — previously only
|
|
26
|
+
caught `ConfigurationError`; now looks up any `GFlowError` subclass in
|
|
27
|
+
`EXIT_CODE_MAP` and exits with the correct code plus a `remediation_hint`.
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- `InternalChromiumStrategy` had an infinite `while True:` polling loop that
|
|
32
|
+
never timed out; replaced with a bounded loop that raises
|
|
33
|
+
`AuthLoginTimeoutError` on expiry.
|
|
34
|
+
- `auth login --browser chrome` when Chrome is missing now exits with code
|
|
35
|
+
**11** (ConfigurationError) instead of 1.
|
|
36
|
+
|
|
10
37
|
## [0.6.0a2] — 2026-05-16
|
|
11
38
|
|
|
12
39
|
> **Real Chrome auth strategy — G12 block resolved.** This release restores
|
|
13
|
-
> `gflow auth login` reliability by
|
|
14
|
-
>
|
|
15
|
-
>
|
|
16
|
-
>
|
|
40
|
+
> `gflow auth login` reliability by implementing a new **Passive Capture**
|
|
41
|
+
> strategy. This method providing a 100% clean browser environment by launching
|
|
42
|
+
> your system's real Google Chrome as a standard process, completely bypassing
|
|
43
|
+
> Google's bot-detection.
|
|
17
44
|
|
|
18
45
|
### Added
|
|
19
46
|
|
|
20
47
|
- **`--browser [auto|chrome|internal]` flag** on `gflow auth login` — selects
|
|
21
|
-
the browser strategy. `chrome` uses real system Chrome (
|
|
22
|
-
falls back to bundled Chromium. `auto` (default) probes for real
|
|
23
|
-
falls back gracefully.
|
|
48
|
+
the browser strategy. `chrome` uses real system Chrome (**Passive Capture**).
|
|
49
|
+
`internal` falls back to bundled Chromium. `auto` (default) probes for real
|
|
50
|
+
Chrome and falls back gracefully.
|
|
24
51
|
- **`GFLOW_CLI_AUTH_BROWSER` env var** — overrides the browser strategy without
|
|
25
52
|
a CLI flag.
|
|
26
|
-
- **`RealChromeStrategy`** (`src/gflow_cli/auth/real_chrome.py`) —
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
`navigator.webdriver`.
|
|
53
|
+
- **`RealChromeStrategy`** (`src/gflow_cli/auth/real_chrome.py`) — zero-automation
|
|
54
|
+
login flow: launches clean Chrome, waits for user to close window, then extracts
|
|
55
|
+
the session.
|
|
30
56
|
- **`InternalChromiumStrategy`** — extracted from the previous `auth.py` monolith
|
|
31
57
|
as an explicit fallback strategy.
|
|
32
58
|
- **`AuthStrategyFactory`** — routes `auto`/`chrome`/`internal` to the
|
|
33
59
|
appropriate strategy based on system state.
|
|
34
|
-
- **`is_chrome_available()`** in `browser_manager.py` — non-raising probe for
|
|
35
|
-
system Chrome presence.
|
|
36
|
-
- **4 new BDD scenarios** in `tests/features/auth_login.feature` covering all
|
|
37
|
-
`--browser` modes.
|
|
38
60
|
|
|
39
61
|
### Fixed
|
|
40
62
|
|
|
41
63
|
- **G12 bot-detection block** — Google's "browser not secure" rejection (`/v3/signin/rejected`)
|
|
42
|
-
is bypassed by the
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
JS init script can run, making `Object.defineProperty` overrides silently fail.
|
|
46
|
-
- **`add_init_script` timing** — registration now occurs before any page is
|
|
47
|
-
accessed, ensuring the stealth script fires on every navigation including the
|
|
48
|
-
first `goto()`.
|
|
64
|
+
is bypassed by the Passive Capture workflow. By removing all automation signals
|
|
65
|
+
(CDP, WebDriver flags) during login, the browser is indistinguishable from a
|
|
66
|
+
regular user session.
|
|
49
67
|
- **Privacy Guard** — `RealChromeStrategy` validates that `profile_dir` is inside
|
|
50
68
|
`GFLOW_CLI_HOME` and raises `SecurityError` if it is not, preventing accidental
|
|
69
|
+
interference with your primary personal Chrome profile.
|
|
51
70
|
use of the user's primary system Chrome profile.
|
|
52
71
|
- **`ConfigurationError` on missing Chrome** — clear "Chrome binary not found"
|
|
53
72
|
message with install guidance when `--browser chrome` is requested but Chrome
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gflow-cli
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.0a3
|
|
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
|
|
@@ -68,6 +68,18 @@ A profile maps to a directory `$GFLOW_CLI_HOME/profile_<name>/`. Profiles are is
|
|
|
68
68
|
**Default:** unset
|
|
69
69
|
**Get one:** <https://aistudio.google.com/apikey>
|
|
70
70
|
|
|
71
|
+
### `GFLOW_CLI_AUTH_LOGIN_TIMEOUT`
|
|
72
|
+
|
|
73
|
+
**What:** Maximum time (seconds) that `gflow auth login` waits for the user to complete the Google sign-in flow in the browser.
|
|
74
|
+
**Default:** `600` (10 minutes)
|
|
75
|
+
**Range:** 1–86400
|
|
76
|
+
**Exit code on expiry:** 12 (`AuthLoginTimeoutError`)
|
|
77
|
+
**Note:** Useful for CI/CD or agent pipelines where a hung login should surface as a definite failure rather than blocking indefinitely. Set to a large value (e.g. `3600`) for interactive sessions over slow connections.
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
GFLOW_CLI_AUTH_LOGIN_TIMEOUT=120 gflow auth login # abort after 2 minutes
|
|
81
|
+
```
|
|
82
|
+
|
|
71
83
|
### `GFLOW_CLI_TIMEOUT_SECONDS`
|
|
72
84
|
|
|
73
85
|
**What:** Per-request HTTP timeout. Veo videos can take 60–180 s each.
|
|
@@ -148,12 +148,14 @@ class FlowApiClient:
|
|
|
148
148
|
# opening a second Playwright process against the same profile dir
|
|
149
149
|
# (which would conflict on the Chromium lockfile — spec § 5.4.4).
|
|
150
150
|
self._pw = await async_playwright().start()
|
|
151
|
+
from gflow_cli.browser_manager import channel_for_profile
|
|
151
152
|
self._context = await self._pw.chromium.launch_persistent_context(
|
|
152
153
|
user_data_dir=str(self.profile_dir),
|
|
153
154
|
headless=self.headless,
|
|
154
155
|
viewport={"width": 1280, "height": 720},
|
|
155
156
|
locale="en-US",
|
|
156
157
|
extra_http_headers={"Accept-Language": "en-US,en;q=0.9"},
|
|
158
|
+
channel=channel_for_profile(self.profile_dir),
|
|
157
159
|
)
|
|
158
160
|
# Open ``Settings.concurrency`` Pages inside the one persistent
|
|
159
161
|
# BrowserContext. ``launch_persistent_context`` opens one Page by
|
|
@@ -208,11 +208,13 @@ class UiAutomationTransport:
|
|
|
208
208
|
pw_cm = async_playwright()
|
|
209
209
|
pw = await pw_cm.__aenter__()
|
|
210
210
|
try:
|
|
211
|
+
from gflow_cli.browser_manager import channel_for_profile # noqa: PLC0415
|
|
211
212
|
ctx = await pw.chromium.launch_persistent_context(
|
|
212
213
|
str(profile_dir),
|
|
213
214
|
headless=False,
|
|
214
215
|
viewport=cast("ViewportSize", _VIEWPORT),
|
|
215
216
|
locale="en-US",
|
|
217
|
+
channel=channel_for_profile(profile_dir),
|
|
216
218
|
)
|
|
217
219
|
self._pw_cm = pw_cm
|
|
218
220
|
self._ctx = ctx
|
|
@@ -61,7 +61,11 @@ def status(name: str = "default") -> dict[str, object]:
|
|
|
61
61
|
"""Lightweight check — does the profile dir exist and have cookies file?"""
|
|
62
62
|
pdir = profile_dir(name)
|
|
63
63
|
cookies_file: Path | None = None
|
|
64
|
-
for candidate in (
|
|
64
|
+
for candidate in (
|
|
65
|
+
pdir / "Default" / "Network" / "Cookies", # Chrome 130+ (new location)
|
|
66
|
+
pdir / "Default" / "Cookies", # Chrome < 130 / legacy
|
|
67
|
+
pdir / "Cookies", # Playwright bundled Chromium
|
|
68
|
+
):
|
|
65
69
|
if candidate.exists():
|
|
66
70
|
cookies_file = candidate
|
|
67
71
|
break
|
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import structlog
|
|
4
4
|
|
|
5
|
+
from gflow_cli.config import get_settings
|
|
5
6
|
from gflow_cli.errors import ConfigurationError
|
|
6
7
|
|
|
7
8
|
from .base import AuthStrategy
|
|
@@ -22,28 +23,30 @@ class AuthStrategyFactory:
|
|
|
22
23
|
|
|
23
24
|
def create(self, mode: str) -> AuthStrategy:
|
|
24
25
|
"""Create an authentication strategy based on the requested mode and system state."""
|
|
26
|
+
timeout = get_settings().auth_login_timeout
|
|
25
27
|
# T2.4: auto mode probes for Real Chrome; falls back to internal if missing.
|
|
26
28
|
if mode == "auto":
|
|
27
29
|
if self._is_chrome_available():
|
|
28
|
-
return RealChromeStrategy()
|
|
30
|
+
return RealChromeStrategy(timeout_seconds=timeout)
|
|
29
31
|
logger.warning(
|
|
30
32
|
"chrome_missing_falling_back_to_internal",
|
|
31
33
|
reason="Google Chrome was not detected on this system.",
|
|
32
34
|
)
|
|
33
|
-
return InternalChromiumStrategy()
|
|
35
|
+
return InternalChromiumStrategy(timeout_seconds=timeout)
|
|
34
36
|
|
|
35
|
-
|
|
36
|
-
if not strategy_cls:
|
|
37
|
+
if mode not in self._strategies:
|
|
37
38
|
raise ConfigurationError(
|
|
38
39
|
f"Unknown auth browser mode '{mode}'. Supported: auto, chrome, internal."
|
|
39
40
|
)
|
|
40
41
|
|
|
41
|
-
if mode == "chrome"
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
if mode == "chrome":
|
|
43
|
+
if not self._is_chrome_available():
|
|
44
|
+
raise ConfigurationError(
|
|
45
|
+
"Chrome binary not found. Install Google Chrome or use '--browser internal'."
|
|
46
|
+
)
|
|
47
|
+
return RealChromeStrategy(timeout_seconds=timeout)
|
|
45
48
|
|
|
46
|
-
return
|
|
49
|
+
return InternalChromiumStrategy(timeout_seconds=timeout)
|
|
47
50
|
|
|
48
51
|
def _is_chrome_available(self) -> bool:
|
|
49
52
|
"""Probe for Real Chrome via browser_manager or Playwright."""
|
|
@@ -7,7 +7,8 @@ from typing import TYPE_CHECKING
|
|
|
7
7
|
import structlog
|
|
8
8
|
from rich.console import Console
|
|
9
9
|
|
|
10
|
-
from gflow_cli.
|
|
10
|
+
from gflow_cli.config import get_settings
|
|
11
|
+
from gflow_cli.errors import AuthLoginTimeoutError, SecurityError
|
|
11
12
|
|
|
12
13
|
from .base import AuthStrategy
|
|
13
14
|
|
|
@@ -34,6 +35,15 @@ class InternalChromiumStrategy(AuthStrategy):
|
|
|
34
35
|
|
|
35
36
|
async def login(self, profile_dir: Path, headless: bool) -> None:
|
|
36
37
|
"""Execute the login flow using internal Chromium."""
|
|
38
|
+
settings = get_settings()
|
|
39
|
+
try:
|
|
40
|
+
profile_dir.resolve(strict=False).relative_to(settings.home.resolve())
|
|
41
|
+
except ValueError:
|
|
42
|
+
raise SecurityError(
|
|
43
|
+
f"Profile directory {profile_dir} is outside of GFLOW_CLI_HOME "
|
|
44
|
+
f"({settings.home}) boundaries."
|
|
45
|
+
) from None
|
|
46
|
+
|
|
37
47
|
# Deferred import to avoid circular dependency and support test patching
|
|
38
48
|
from .strategies import async_playwright
|
|
39
49
|
|
|
@@ -60,6 +70,7 @@ class InternalChromiumStrategy(AuthStrategy):
|
|
|
60
70
|
|
|
61
71
|
# Polling for success (SAPISID cookie + UI signal).
|
|
62
72
|
timeout_at = asyncio.get_running_loop().time() + self._timeout_seconds
|
|
73
|
+
success = False
|
|
63
74
|
|
|
64
75
|
while asyncio.get_running_loop().time() < timeout_at:
|
|
65
76
|
try:
|
|
@@ -73,9 +84,10 @@ class InternalChromiumStrategy(AuthStrategy):
|
|
|
73
84
|
or await page.get_by_text("Your projects").is_visible()
|
|
74
85
|
):
|
|
75
86
|
logger.info("auth_login_success_detected", strategy=self.name)
|
|
87
|
+
success = True
|
|
76
88
|
break
|
|
77
89
|
except Exception:
|
|
78
|
-
#
|
|
90
|
+
# Browser or context is gone — exit loop without success
|
|
79
91
|
break
|
|
80
92
|
|
|
81
93
|
await asyncio.sleep(1)
|
|
@@ -84,11 +96,20 @@ class InternalChromiumStrategy(AuthStrategy):
|
|
|
84
96
|
f"Sign-in not completed within {self._timeout_seconds}s.",
|
|
85
97
|
remediation_hint=(
|
|
86
98
|
"Run `gflow auth login` again and complete sign-in promptly. "
|
|
87
|
-
f"Set
|
|
99
|
+
f"Set GFLOW_CLI_AUTH_LOGIN_TIMEOUT to a higher value if needed "
|
|
88
100
|
f"(current: {self._timeout_seconds}s)."
|
|
89
101
|
),
|
|
90
102
|
)
|
|
91
103
|
|
|
104
|
+
if not success:
|
|
105
|
+
raise AuthLoginTimeoutError(
|
|
106
|
+
"Browser closed before authentication was verified.",
|
|
107
|
+
remediation_hint=(
|
|
108
|
+
"Complete the full sign-in flow before closing the browser. "
|
|
109
|
+
"Run `gflow auth login` to try again."
|
|
110
|
+
),
|
|
111
|
+
)
|
|
112
|
+
|
|
92
113
|
# Small delay to ensure state is flushed to disk
|
|
93
114
|
await asyncio.sleep(1)
|
|
94
115
|
|
|
@@ -10,7 +10,7 @@ import structlog
|
|
|
10
10
|
from rich.console import Console
|
|
11
11
|
|
|
12
12
|
from gflow_cli.config import get_settings
|
|
13
|
-
from gflow_cli.errors import AuthLoginTimeoutError, SecurityError
|
|
13
|
+
from gflow_cli.errors import AuthLoginTimeoutError, AuthMissingError, SecurityError
|
|
14
14
|
|
|
15
15
|
from .base import AuthStrategy
|
|
16
16
|
|
|
@@ -68,7 +68,7 @@ class RealChromeStrategy(AuthStrategy):
|
|
|
68
68
|
"""Execute the login flow using Passive Capture on Real Chrome."""
|
|
69
69
|
settings = get_settings()
|
|
70
70
|
try:
|
|
71
|
-
profile_dir.relative_to(settings.home)
|
|
71
|
+
profile_dir.resolve(strict=False).relative_to(settings.home.resolve())
|
|
72
72
|
except ValueError:
|
|
73
73
|
raise SecurityError(
|
|
74
74
|
f"Profile directory {profile_dir} is outside of GFLOW_CLI_HOME "
|
|
@@ -134,10 +134,10 @@ class RealChromeStrategy(AuthStrategy):
|
|
|
134
134
|
except subprocess.TimeoutExpired:
|
|
135
135
|
proc.kill()
|
|
136
136
|
raise AuthLoginTimeoutError(
|
|
137
|
-
f"Sign-in
|
|
137
|
+
f"Sign-in timed out after {self._timeout_seconds}s; Chrome was stopped.",
|
|
138
138
|
remediation_hint=(
|
|
139
139
|
"Run `gflow auth login` again and complete sign-in before the time limit. "
|
|
140
|
-
f"Set
|
|
140
|
+
f"Set GFLOW_CLI_AUTH_LOGIN_TIMEOUT to raise the limit "
|
|
141
141
|
f"(current: {self._timeout_seconds}s)."
|
|
142
142
|
),
|
|
143
143
|
) from None
|
|
@@ -161,10 +161,14 @@ class RealChromeStrategy(AuthStrategy):
|
|
|
161
161
|
|
|
162
162
|
if has_sapisid:
|
|
163
163
|
logger.info("auth_login_success_verified", strategy=self.name)
|
|
164
|
-
|
|
164
|
+
# Write strategy marker before any output that might fail on
|
|
165
|
+
# narrow Windows codepages — FlowApiClient reads this to select
|
|
166
|
+
# the matching Chrome channel for launch_persistent_context.
|
|
167
|
+
(profile_dir / ".gflow_browser_strategy").write_text("chrome", encoding="utf-8")
|
|
168
|
+
_console.print("[green][OK] Session captured and verified.[/green]")
|
|
165
169
|
else:
|
|
166
170
|
logger.warning("auth_login_no_cookies", strategy=self.name)
|
|
167
|
-
raise
|
|
171
|
+
raise AuthMissingError(
|
|
168
172
|
"No session cookies found after sign-in. "
|
|
169
173
|
"Did you complete the sign-in before closing Chrome?"
|
|
170
174
|
)
|
|
@@ -172,6 +172,40 @@ def is_chrome_available() -> bool:
|
|
|
172
172
|
return False
|
|
173
173
|
|
|
174
174
|
|
|
175
|
+
def channel_for_profile(profile_dir: Path) -> str | None:
|
|
176
|
+
"""Return the Playwright channel to use for ``profile_dir``, or None.
|
|
177
|
+
|
|
178
|
+
Reads the ``.gflow_browser_strategy`` marker written by
|
|
179
|
+
:class:`~gflow_cli.auth.real_chrome.RealChromeStrategy`. When the marker
|
|
180
|
+
is ``"chrome"`` and system Chrome is available, returns ``"chrome"`` so
|
|
181
|
+
callers can pass it to ``launch_persistent_context(channel=...)`` —
|
|
182
|
+
avoiding the downgrade-cleanup exit-33 that occurs when Playwright's
|
|
183
|
+
bundled Chromium opens a profile created by Chrome 130+.
|
|
184
|
+
|
|
185
|
+
Logs a warning when the marker requests Chrome but Chrome is no longer
|
|
186
|
+
available, as the resulting launch against bundled Chromium will likely
|
|
187
|
+
fail with the same exit-33 error.
|
|
188
|
+
"""
|
|
189
|
+
import structlog as _structlog
|
|
190
|
+
|
|
191
|
+
_log = _structlog.get_logger(__name__)
|
|
192
|
+
marker = profile_dir / ".gflow_browser_strategy"
|
|
193
|
+
if not marker.exists():
|
|
194
|
+
return None
|
|
195
|
+
strategy = marker.read_text(encoding="utf-8").strip()
|
|
196
|
+
if strategy != "chrome":
|
|
197
|
+
return None
|
|
198
|
+
if is_chrome_available():
|
|
199
|
+
return "chrome"
|
|
200
|
+
_log.warning(
|
|
201
|
+
"browser_manager.chrome_marker_but_unavailable",
|
|
202
|
+
profile_dir=str(profile_dir),
|
|
203
|
+
hint="Profile was captured with system Chrome but Chrome is not found. "
|
|
204
|
+
"Re-run `gflow auth login --browser chrome` after installing Chrome.",
|
|
205
|
+
)
|
|
206
|
+
return None
|
|
207
|
+
|
|
208
|
+
|
|
175
209
|
# ---------------------------------------------------------------------------
|
|
176
210
|
# Health check
|
|
177
211
|
# ---------------------------------------------------------------------------
|
|
@@ -129,6 +129,16 @@ class Settings(BaseSettings):
|
|
|
129
129
|
|
|
130
130
|
# --- runtime ----------------------------------------------------------
|
|
131
131
|
timeout_seconds: int = Field(default=600, ge=1, le=3600)
|
|
132
|
+
auth_login_timeout: int = Field(
|
|
133
|
+
default=600,
|
|
134
|
+
ge=1,
|
|
135
|
+
le=86400,
|
|
136
|
+
description=(
|
|
137
|
+
"Seconds to wait for the user to complete interactive sign-in. "
|
|
138
|
+
"Applies to both Real Chrome (Passive Capture) and Internal Chromium strategies. "
|
|
139
|
+
"Override via GFLOW_CLI_AUTH_LOGIN_TIMEOUT."
|
|
140
|
+
),
|
|
141
|
+
)
|
|
132
142
|
concurrency: int = Field(default=1, ge=1, le=16)
|
|
133
143
|
headless: bool = Field(
|
|
134
144
|
default=True,
|
|
@@ -270,7 +270,7 @@ class AuthLoginTimeoutError(GFlowError):
|
|
|
270
270
|
_default_remediation = (
|
|
271
271
|
"The sign-in was not completed within the allowed time. "
|
|
272
272
|
"Run `gflow auth login` again and complete sign-in promptly. "
|
|
273
|
-
"Increase
|
|
273
|
+
"Increase GFLOW_CLI_AUTH_LOGIN_TIMEOUT (seconds) if you need more time."
|
|
274
274
|
)
|
|
275
275
|
|
|
276
276
|
|
|
@@ -171,7 +171,9 @@ class TestInternalChromiumStrategy:
|
|
|
171
171
|
async def test_internal_chromium_standard_behavior(self, tmp_path: Path) -> None:
|
|
172
172
|
"""Verify Internal Chromium uses standard Playwright (no stealth flags)."""
|
|
173
173
|
strategy = InternalChromiumStrategy()
|
|
174
|
-
|
|
174
|
+
gflow_home = tmp_path / "gflow_home"
|
|
175
|
+
gflow_home.mkdir()
|
|
176
|
+
profile_dir = gflow_home / "profile_internal"
|
|
175
177
|
|
|
176
178
|
mock_success_loc = MagicMock()
|
|
177
179
|
mock_success_loc.is_visible = AsyncMock(return_value=True)
|
|
@@ -196,9 +198,11 @@ class TestInternalChromiumStrategy:
|
|
|
196
198
|
mock_ap = MagicMock(name="async_playwright", return_value=mock_cm)
|
|
197
199
|
|
|
198
200
|
with (
|
|
201
|
+
patch("gflow_cli.auth.internal_chromium.get_settings") as mock_settings,
|
|
199
202
|
patch("gflow_cli.auth.strategies.async_playwright", mock_ap),
|
|
200
203
|
patch("asyncio.sleep", AsyncMock()),
|
|
201
204
|
):
|
|
205
|
+
mock_settings.return_value.home = gflow_home
|
|
202
206
|
await strategy.login(profile_dir, headless=False)
|
|
203
207
|
|
|
204
208
|
_, kwargs = mock_launch_pctx.call_args
|
|
@@ -209,7 +213,9 @@ class TestInternalChromiumStrategy:
|
|
|
209
213
|
async def test_internal_chromium_timeout_raises(self, tmp_path: Path) -> None:
|
|
210
214
|
"""Verify AuthLoginTimeoutError is raised when polling loop exceeds deadline."""
|
|
211
215
|
strategy = InternalChromiumStrategy(timeout_seconds=0)
|
|
212
|
-
|
|
216
|
+
gflow_home = tmp_path / "gflow_home"
|
|
217
|
+
gflow_home.mkdir()
|
|
218
|
+
profile_dir = gflow_home / "profile_internal"
|
|
213
219
|
|
|
214
220
|
mock_success_loc = MagicMock()
|
|
215
221
|
mock_success_loc.is_visible = AsyncMock(return_value=False)
|
|
@@ -233,9 +239,11 @@ class TestInternalChromiumStrategy:
|
|
|
233
239
|
mock_ap = MagicMock(name="async_playwright", return_value=mock_cm)
|
|
234
240
|
|
|
235
241
|
with (
|
|
242
|
+
patch("gflow_cli.auth.internal_chromium.get_settings") as mock_settings,
|
|
236
243
|
patch("gflow_cli.auth.strategies.async_playwright", mock_ap),
|
|
237
244
|
patch("asyncio.sleep", AsyncMock()),
|
|
238
245
|
):
|
|
246
|
+
mock_settings.return_value.home = gflow_home
|
|
239
247
|
with pytest.raises(AuthLoginTimeoutError) as excinfo:
|
|
240
248
|
await strategy.login(profile_dir, headless=False)
|
|
241
249
|
|
|
@@ -27,5 +27,5 @@ Feature: Auth Login via Real Chrome
|
|
|
27
27
|
Given Chrome is NOT installed on the system
|
|
28
28
|
And the profile root is empty
|
|
29
29
|
When I run "gflow auth login --browser chrome"
|
|
30
|
-
Then the exit code is
|
|
30
|
+
Then the exit code is 11
|
|
31
31
|
And the output contains "Chrome binary not found"
|
|
@@ -109,6 +109,12 @@ def _check_exit_1(cli_result_holder: dict[str, Any]) -> None:
|
|
|
109
109
|
assert result.exit_code == 1, result.output
|
|
110
110
|
|
|
111
111
|
|
|
112
|
+
@then("the exit code is 11")
|
|
113
|
+
def _check_exit_11(cli_result_holder: dict[str, Any]) -> None:
|
|
114
|
+
result = cli_result_holder["result"]
|
|
115
|
+
assert result.exit_code == 11, result.output
|
|
116
|
+
|
|
117
|
+
|
|
112
118
|
@then('the output contains "Launching real Chrome"')
|
|
113
119
|
def _check_launching_chrome(cli_result_holder: dict[str, Any]) -> None:
|
|
114
120
|
result = cli_result_holder["result"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/docs/superpowers/plans/2026-05-09-image-mvp-orchestration.md
RENAMED
|
File without changes
|
|
File without changes
|
{gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/docs/superpowers/plans/2026-05-09-video-mvp-orchestration.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/docs/superpowers/plans/2026-05-10-phase-4-hardening.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/docs/superpowers/plans/2026-05-14-shell-multi-prompt/PLAN.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/docs/superpowers/verifications/2026-05-11-phase-4-stage-g.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/samples/captured/02_batchAsyncGenerateVideoText.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/src/gflow_cli/api/transports/experimental/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/src/gflow_cli/api/transports/experimental/evaluate_fetch.py
RENAMED
|
File without changes
|
{gflow_cli-0.6.0a2 → gflow_cli-0.6.0a3}/src/gflow_cli/api/transports/experimental/sapisidhash.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|