Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
instead, since the browser would send the same requests. `GFLOW_CLI_WIRE_READS=0` turns the direct read off; it is off by default on
macOS and while any proxy is configured (#1229).

- **`gflow doctor` and the MCP `gflow_auth_status` tool report whether that direct read works.**
Doctor's new `auth.wire_read` check (default profile) and the tool's new `wire` field (on every session result) give
`accepted` with Flow's build label, or the reason credit reads use the browser
(`signed_out_shell`, `store_unreadable`, `disabled`, `proxy_configured`, `throttled`, …).
Each sends one GET and one POST to flow.google.com unless the direct read is off. The check
is informational: it never changes doctor's exit code. When Flow is throttling the account,
`gflow_auth_status` skips its browser probe and returns `status: throttled` with
`retry_after` (#1229).

### Fixed

- **No gflow process logs an httpx request URL any more.** Starting the MCP server set Python
Expand Down
3 changes: 2 additions & 1 deletion docs/CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -515,8 +515,9 @@ per-command `--proxy` flags (the setting covers one-off runs:
**What:** Whether `gflow credits user`, `gflow credits list` and the MCP `gflow_get_credits` tool first read the balance **directly** from flow.google.com, with no browser ([#1229](https://github.com/ffroliva/gflow-cli/issues/1229)). The direct read takes the profile's saved cookies from its own cookie file, loads the app page once and asks for the balance once. If it fails (Flow does not accept the cookies, Chrome is open on the profile and holds the cookie file, a network error, an unexpected reply), gflow reads the balance in the profile's browser instead, as before. One exception: when Flow throttles the account (HTTP 429 or 503), the command reports that error (exit code as for any rate limit or network failure) rather than loading the page, because the page would send the same requests to the throttled account; further reads of that profile report it again, sending nothing, until the `Retry-After` window has passed (capped at 60 s; 60 s when Flow names none).
**Values:** `1` / `true` — try the direct read first. `0` / `false` — always use the browser.
**Default:** `true` on Windows and Linux; `false` on macOS, where reading Chrome's cookies can raise a Keychain prompt.
**Safety:** the direct read never writes to the profile's cookies, ignores any cookie Flow sends back, and follows no redirect. A profile Flow answers "signed out" (or HTTP 401) is not tried again for the rest of the process; other profiles are unaffected.
**Safety:** the direct read never writes to the profile's cookies, ignores any cookie Flow sends back, and follows no redirect. A profile Flow answers "signed out" (or HTTP 401) is not tried again by credit reads in that process (a browser read succeeding does not change the saved cookies) until `--refresh` renews it, or `gflow doctor` / `gflow_auth_status` asks Flow again and is accepted; other profiles are unaffected.
**Proxy:** while any proxy is configured — `GFLOW_CLI_BROWSER_PROXY`, an `HTTPS_PROXY`/`ALL_PROXY`-style environment variable, or the operating system's proxy setting — the direct read is skipped (reason `proxy_configured`) and the browser reads the balance, so the profile's cookies never leave by a different route than its browser's. The direct read never takes a proxy from the environment.
**Health:** `gflow doctor` (check `auth.wire_read`, the default profile) and the MCP `gflow_auth_status` tool (its `wire` field) report whether the direct read works: `status=accepted` with Flow's build label, or the reason it falls back (`signed_out_shell`, `store_unreadable`, `disabled`, `proxy_configured`, `throttled`, …). Each sends one GET and one POST to flow.google.com, under every rule above, asking afresh even for a profile already marked signed out; with the setting off, any proxy configured, or inside a throttle window, neither sends anything.

### `GFLOW_CLI_AGENT_CONFIRM`

Expand Down
2 changes: 1 addition & 1 deletion docs/MCP.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ The server registers three protocol surfaces:
> because gflow keeps task rows with no expiry.


* `gflow_auth_status(profile)`: Credit-free, non-interactive Flow session probe (#497) — wraps the same fail-closed `verify_flow_profile` check as `gflow auth status`. Returns `{"status": "authenticated", "profile", "user_email"}` or a problem-details error with a `remediation_hint`; a `verification_error` outcome (HTTP 503, `retryable: true`) means a network/endpoint problem, which re-login does not fix, and a `profile_marker_missing` outcome (HTTP 409, `retryable: false`, #796) means the profile lost its `.gflow_browser_strategy` marker so its cookies cannot be read — a retry does not help, and neither does a plain `gflow auth login`; re-run `gflow auth login --browser chrome` for that profile. A `cookie_store_unreadable` outcome (HTTP 409, `retryable: false`, #796) means the profile's Chrome cookie database is corrupt or unreadable — a local file fault, not the network; close any Chrome window using that profile, then re-run `gflow auth login` for it. Any other unauthenticated outcome is a dead session (HTTP 401). Call it before a generation tool to fail fast on dead auth, before a long blocking generation call. Login/logout remain CLI-only (genuinely interactive).
* `gflow_auth_status(profile)`: Credit-free, non-interactive Flow session probe (#497) — wraps the same fail-closed `verify_flow_profile` check as `gflow auth status`. Returns `{"status": "authenticated", "profile", "user_email"}` or a problem-details error with a `remediation_hint`; a `verification_error` outcome (HTTP 503, `retryable: true`) means a network/endpoint problem, which re-login does not fix, and a `profile_marker_missing` outcome (HTTP 409, `retryable: false`, #796) means the profile lost its `.gflow_browser_strategy` marker so its cookies cannot be read — a retry does not help, and neither does a plain `gflow auth login`; re-run `gflow auth login --browser chrome` for that profile. A `cookie_store_unreadable` outcome (HTTP 409, `retryable: false`, #796) means the profile's Chrome cookie database is corrupt or unreadable — a local file fault, not the network; close any Chrome window using that profile, then re-run `gflow auth login` for it. Any other unauthenticated outcome is a dead session (HTTP 401). Call it before a generation tool to fail fast on dead auth, before a long blocking generation call. Every session result also carries `wire: {status, build}` ([#1229](https://github.com/ffroliva/gflow-cli/issues/1229)): the health of the direct, browser-free credits read, probed first with one GET and one POST to flow.google.com unless `GFLOW_CLI_WIRE_READS` is off. `status` is `accepted`, or the reason credit reads fall back to the browser (`signed_out_shell`, `http_401`, `store_unreadable`, `disabled`, `proxy_configured`, `throttled`, `no_chrome_version`, `network`, `redirect`, `http_400`, `http_error`, `unparseable`, `unexpected`); `build` is Flow's build label, else `null`. It never changes the session `status`, except `throttled`: Flow is rate-limiting the account, so the browser probe is skipped and the tool returns `status: "throttled"` with a retryable 429 `error` carrying `retry_after` seconds. `store_unreadable` means the profile's cookies could not be read (Chrome has it open, or it has no saved session). `gflow doctor` reports the same probe for the default profile as its `auth.wire_read` check (doctor itself has no MCP twin, #542). Login/logout remain CLI-only (genuinely interactive).
* `gflow_get_credits(profile, all_profiles, refresh)`: Current Flow balance query; spends no credits. With `all_profiles=true`, inspects every saved profile sequentially and preserves successful balances plus `total_credits` when another profile fails. The balance is read from flow.google.com in the profile's browser: the number the app itself loads, which is the one Flow's account menu shows (#795). That is one browser launch per profile, and the tier fields (`sku`, `service_tier`, `user_paygate_tier`, `subscription_credits`) are always `null`. With `refresh=true`, a profile that lands on a sign-in page is renewed in a headless browser and read again: one more Chrome launch per such profile, no credits. This is the MCP twin of `gflow credits user|list --refresh`. It runs the same renewal as `gflow auth renew`, but only for a profile whose credits read lands on a sign-in page. `auth renew` itself has no MCP tool; see the exemption in `tests/mcp/test_cli_parity.py`. Without `refresh` the call is read-only; with it, an expired profile's saved cookies are rewritten. This tool spends no credits and returns no cookies, bearer tokens, or browser API keys. The balance funds Veo video generation; image generation uses separate per-model daily quotas. A page that loads but never fetches the balance is a `FlowAppError` (exit 31), never an auth error.
* `gflow_character_list(project, profile)`: Lists a project's saved Flow CHARACTER entities with their **entity ids** — read-only, spends no credits, drives a browser session. This is how an agent discovers what it can attach: an `entity_id` goes to `reference_entities`, a `display_name` can be used as an `@Name` mention or goes to `reference_entity_names` at the id's position. On flow.google.com it cannot read the project yet and returns a `wire-format` error (the CLI's exit 7, [#875](https://github.com/ffroliva/gflow-cli/issues/875)): ask the user for the name. An empty list means the project genuinely has none.
* `gflow_character_show(project, entity_id, name, profile)`: Shows one character by id or exact display name (exactly one selector required). An ambiguous name is refused rather than resolved arbitrarily — which is the reason to prefer the id. Read-only; drives a browser session.
Expand Down
7 changes: 5 additions & 2 deletions docs/USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -1732,7 +1732,9 @@ environment. Doctor **diagnoses, never heals**: nothing is migrated, repaired,
or written — not even pending schema migrations. All database access is
strictly read-only (SQLite may create transient `-wal`/`-shm` sidecar files
during the read-only open; the database itself is never modified), so it is
safe to run against a live catalog at any time.
safe to run against a live catalog at any time. One check uses the network:
`auth.wire_read` sends one GET and one POST to flow.google.com for the default
profile, and sends nothing when `GFLOW_CLI_WIRE_READS=0`.

```text
gflow doctor [--json]
Expand All @@ -1742,7 +1744,7 @@ Options:
(experimental; shape may change).
```

Eleven checks run on every invocation:
Twelve checks run on every invocation:

| Check id | Detects | Suggested remediation |
|---|---|---|
Expand All @@ -1757,6 +1759,7 @@ Eleven checks run on every invocation:
| `env.browsers_missing` | Playwright Chromium is not installed | `playwright install chromium` |
| `auth.files_present` | No auth profiles, or profiles without saved cookies | `gflow auth login` |
| `env.browser_proxy` | Reports whether `GFLOW_CLI_BROWSER_PROXY` is set (`scheme://host:port`, never credentials); informational, never changes the exit code | Use the same proxy for `HTTPS_PROXY` and for signing in ([CONFIGURATION](CONFIGURATION.md#gflow_cli_browser_proxy)) |
| `auth.wire_read` | Whether the default profile's direct (browser-free) credits read works: `status=accepted build=<Flow build>` passes; any other status (`signed_out_shell`, `store_unreadable`, `disabled`, `proxy_configured`, `throttled`, `no_profile`, …) is info only and never changes the exit code, because credits then read through the browser. The one network check: one GET and one POST to flow.google.com, nothing with `GFLOW_CLI_WIRE_READS=0` | The finding's own hint (e.g. close Chrome on the profile, or `gflow auth login`) ([CONFIGURATION](CONFIGURATION.md#gflow_cli_wire_reads)) |

**Severity model.** Every check reports `pass`, `info`, `warn`, or `fail`.
`info` is worth knowing but not a defect — it never flips the overall status
Expand Down
Loading
Loading