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
2 changes: 1 addition & 1 deletion .codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "gflow",
"version": "0.77.1",
"version": "0.78.0",
"description": "Reusable development and operations workflows for gflow-cli",
"author": {
"name": "Flavio Oliva",
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ by construction, so Codex / Cursor / Aider / `agy` read exactly what Claude Code
- Python CLI and MCP server for [Google Flow](https://labs.google/fx/tools/flow) — drives Veo (image-to-video, text-to-video) and Imagen (text-to-image) generations from the terminal by reverse-engineering Flow's private REST API at `aisandbox-pa.googleapis.com` — and, for accounts Google has moved to `flow.google.com`, that frontend's `batchexecute` wire (text-to-video, image-to-video from a local `--initial-frame`, reference-to-video from local `--ref` files, and text-to-image/local-file image-to-image, today; `GFLOW_CLI_FLOW_HOST`).
- Python 3.11+ · `uv`-managed · `hatchling` builds · Playwright Chromium transport · `pyright` strict · `ruff` · `pytest`.
- Single-package modular monolith. Top-level modules under `src/gflow_cli/`: `api/`, `auth/`, `data/`, `mcp/`, `services/`, `tools/`, `ui/`, `worker/`, `browser_manager.py`, `cli.py`, `_cli_helpers.py`, `diagnostics.py`, `json_output.py`, `media.py`, `profile_lease.py`, `redaction.py`, `storage.py`, `winsec.py`, `cli_project.py`, `cli_character.py`, `cli_credits.py`, `cli_data.py`, `cli_image.py`, `cli_instructions.py`, `cli_models.py`, `cli_movie.py`, `cli_run.py`, `cli_scene.py`, `cli_tools.py`, `cli_video.py`, `chain.py`, `chain_manifest.py`, `cli_doctor.py`, `cli_update.py`, `composition.py`, `config.py`, `errors.py`, `file_integrity.py`, `flow_selectors/`, `update_check.py`, `exceptions.py`, `image_batch.py`, `movie_manifest.py`, `observability.py`, `paths.py`, `profile_store.py`.
- Command surface: `gflow auth`, `gflow credits` (user/list — read-only Veo balance), `gflow image` (t2i/i2i/batch/upload/upscale), `gflow video` (t2v/i2v/r2v/chain/extend — `extend` continues an existing clip past Flow's 8s ceiling, server-seeded from the source so the join is continuous; no `batch` subcommand; the nonfunctional stub was removed, loop `gflow video t2v`/`i2v` from the shell for multi-clip runs), `gflow character` (create/list/show/rm/voices — reusable project-scoped Flow Character entities), `gflow scene` (create/show — Add Clip / Scenes, with `create --output` for credit-free server-side extended video), `gflow instructions` (persistent Agent-Mode brief cards — add/list/enable/disable/rm/apply/toggle-mode, credits-free, `--project` required), `gflow movie` (run/template — multi-scene manifest pipeline), `gflow tools` (list/show/run — prompt-rewriting tools, also `--tool` on generation commands), `gflow data` (catalog queries), `gflow doctor` (read-only pre-flight diagnostic, exit 33 = findings present), `gflow update` (self-update through the installer that put it here — uv tool / pipx / pip; `--check` only reports; source installs refused, exit 11; deliberately no MCP twin), `gflow project`, `gflow models`, `gflow run`, `gflow mcp` (run/setup — stdio MCP server), and `gflow serve` (Streamable HTTP at `/mcp`; `--transport sse` is deprecated).
- Command surface: `gflow auth`, `gflow credits` (user/list — read-only Veo balance), `gflow image` (t2i/i2i/batch/upload/upscale), `gflow video` (t2v/i2v/r2v/chain/extend — `extend` continues an existing clip past Flow's 8s ceiling, server-seeded from the source so the join is continuous; no `batch` subcommand; the nonfunctional stub was removed, loop `gflow video t2v`/`i2v` from the shell for multi-clip runs), `gflow character` (create/list/show/rm/voices — reusable project-scoped Flow Character entities), `gflow scene` (create/show — Add Clip / Scenes, with `create --output` for credit-free server-side extended video), `gflow instructions` (persistent Agent-Mode brief cards — add/list/enable/disable/rm/apply/toggle-mode, credits-free, `--project` required), `gflow movie` (run/template — multi-scene manifest pipeline), `gflow tools` (list/show/run — prompt-rewriting tools, also `--tool` on generation commands), `gflow data` (catalog queries), `gflow docs` (list/print/search the bundled documentation — read-only, offline, deliberately no MCP twin), `gflow doctor` (read-only pre-flight diagnostic, exit 33 = findings present), `gflow update` (self-update through the installer that put it here — uv tool / pipx / pip; `--check` only reports; source installs refused, exit 11; deliberately no MCP twin), `gflow project`, `gflow models`, `gflow run`, `gflow mcp` (run/setup — stdio MCP server), and `gflow serve` (Streamable HTTP at `/mcp`; `--transport sse` is deprecated).
- Works with any Google account that has Flow access. All generations bill against the user's own Google account.

## Headed-browser dependency (architectural reality)
Expand Down
123 changes: 122 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,126 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.78.0] — 2026-09-17

### Added

- **`--aspect 3:4` images now run on `flow.google.com`.** Flow's image settings there
render a fifth aspect radio (`3:4`) that the 2026-09-08 enumeration did not see, so gflow
refused it with exit 36. All five image aspects are now driven on both hosts.
([#864](https://github.com/ffroliva/gflow-cli/issues/864))

- **`gflow docs` — the documentation, reachable from the terminal.** 126 pages in `docs/`
and nothing in the CLI pointed at any of them, so at the moment of use the knowledge was
unreachable and the reader guessed. `gflow docs` lists the topics, `gflow docs <topic>`
prints one as raw Markdown (it pipes), and `gflow docs --search <term>` returns the
matching lines with `docs/FILE.md:LINE` beside each one. Read-only and offline — no
network, no account, no credits.

**The pages now ship inside the wheel** (`gflow_cli/_docs`; 1.3 MB of Markdown, **+0.58 MB
compressed** — the wheel goes 0.74 MB → 1.32 MB), because the user this was filed for
installed from PyPI and has no checkout; a command that could only print GitHub links
would not solve it. A build hook globs `docs/*.md`, so the topic list cannot drift and
`assets/`/`superpowers/` stay out — measured, after `force-include` + `exclude` silently
shipped 148 files it was told to exclude. The hook **refuses to build** when it finds no
pages: `Path.glob` on a missing directory returns empty, and `release.yml` is `uv build`
then publish, with nothing in between that would notice a wheel whose `gflow docs` is
empty for every user.

Search returns a **line, windowed**, not a file name: the rule that prompted this issue
lives inside a 4 000-character bullet in `MCP.md`, and answering "it is in MCP.md" leaves
the reader where they started. Curated `INDEX.md` § Topic shortcuts rank above raw body
text, all query terms must appear on the line, and a vague query reports how many hits it
held back instead of quietly truncating. A topic resolves from its slug, its file name,
either case or an unambiguous prefix — and never by building a path from what was typed,
so `gflow docs ../../etc/passwd` is an ordinary unknown-topic refusal.

No MCP twin, deliberately (`gflow update` is the precedent); the reasoning is recorded in
`cli_docs.py` rather than left implicit.
([#861](https://github.com/ffroliva/gflow-cli/issues/861))

- **`gflow video i2v --end-frame <local file>` now runs on `flow.google.com`.** Start+end
frame interpolation was the last i2v form the migrated composer refused: it exited 36
("an end frame is not ported yet") on every account Flow has moved. Both frames are now
uploaded through the editor's own Upload entry and bound to the Start and End chips, and
the driver reads back two bound chips before it lets the app submit.

A bound end frame switches Flow to a different submit contract — rpc `nprQif`, an
interpolation model key, and both media ids in the body — which is why watching only the
classic submit rpcs made every start+end run time out *after* Flow had already accepted
the submit and billed it. The submit body is asserted before Flow acts on it, so a frame
that silently failed to bind is a refusal rather than a wrong generation you paid for.

The model key is **cohort-dependent** and matched by shape, not by literal:
`veo_3_1_interpolation_lite` and `omni_flash_i2v_8s_first_last` have both been captured
on live accounts (the second on 2026-09-17, see
`docs/superpowers/spikes/2026-09-17-migrated-end-frame-submit-contract.md`). Frames given
by media UUID or `@Name` are still not ported.
([#639](https://github.com/ffroliva/gflow-cli/issues/639))

### Fixed

- **The Frames picker could not bind the frame it had just uploaded.** On
`flow.google.com`, `video i2v` matched a picker option with an *anchored* regex
(`^\s*<name>\s*$`), but a picker tile is a `mat-icon` ligature followed by the file
name and a locator reads both nodes as one string — `imageshero-ab12cd34.png` for an
asset named `hero-ab12cd34.png`. The anchors could never hold, so every i2v run on this
cohort ended in exit 32, *"the frame picker lists no asset named …"*, while the picker
had been listing it the whole time. The match is now containment on the display name,
which is run-unique by construction ([#792](https://github.com/ffroliva/gflow-cli/issues/792))
and therefore still cannot bind a stale copy of the same file. Covered by a new $0
route-intercepted e2e (`tests/features/frame_picker_binding.feature`) that renders the
icon node for real — a mocked picker passes against the bug.
([#860](https://github.com/ffroliva/gflow-cli/issues/860))

- **Flow's promo modal blocked the first click on a freshly loaded editor.** On
`flow.google.com` a `role="dialog"` panel over a new editor made `image t2i` fail as
exit 23 — *".settings-trigger-button did not accept a click within 5000 ms — it is
covered by div.cdk-overlay-backdrop"* — telling the user to file a selector-drift bug
when one click on the page fixes it permanently.

`_dismiss_dialog` already existed and its selector already matched. It was called at
the one moment it could not see anything: one frame after `domcontentloaded`, before
Angular has rendered the modal. That is why a blocked run logged **neither**
`migrated.dialog_dismissed` nor `migrated.dialog_not_dismissed` — the silence in the
incident's event stream was the evidence, not the absence of it. The dismissal now
also runs in `_open_pane`, where the image and video paths take their first click, the
same place the `glue` consent bar is already cleared. It stays best-effort: a modal
that refuses to go still falls through to the click post-mortem, which names the
backdrop, rather than becoming a second competing error path.
([#859](https://github.com/ffroliva/gflow-cli/issues/859))

- **The migrated `--end-frame` lane rejected omni's interpolation key.** The start+end
submit validator pinned the model key to `veo_3_1_interpolation_lite`, so
`omni_flash_i2v_*_first_last` submits — what the second measured cohort actually sends —
were refused as wire-format errors before Flow could act on them. The check is now
key-shape based: a submit whose model key contains `interpolation` or `first_last`
counts as start+end interpolation for any model, and the submit observer accepts the
bare `batchexecute` reply (no `rpcids` param) that the interpolation rpc answers on.
([#639](https://github.com/ffroliva/gflow-cli/issues/639))
- **Generating without a project works again on accounts Flow serves from
`flow.google.com`, and it no longer tells you to log in.** Every run without `--project`
(CLI) or `project` (MCP) starts by creating a project through labs.google's
`project.createProject`. Google has retired that route: it answers *"Flow RPCs have been
deprecated and disabled"* (404), or 401 for a session holding no labs token — measured
12/12 on four profiles. gflow reported the 401 as *"Authentication expired — run `gflow
auth login`"*, which no login could fix. When that route refuses, gflow now creates the
project on flow.google.com's projects page and reads the new id and title from Flow's own
reply; `gflow project create` and `gflow project rename` work there the same way.
`GFLOW_CLI_FLOW_HOST=labs.google` keeps the labs route and its error. Video runs without a
project now get it created up front, like images, so they route exactly as if you had
passed `--project`; MCP `gflow_generate_video` also honours `project_name`, which it
accepted and then ignored. Thanks to @iceblue03 (#863) and @ChandraLiuswanto (#561) for
the reports. ([#864](https://github.com/ffroliva/gflow-cli/issues/864),
[#561](https://github.com/ffroliva/gflow-cli/issues/561))
- **An MCP server no longer fails a call because another call holds the profile.** Two
calls on one profile inside one server — a client retry, two agents sharing a server —
now run one after the other instead of the second failing with `ProfileLockedError`, and
a profile held by another process (a CLI run) is waited out for up to 180 s. A
`GFLOW_CLI_LEASE_WAIT_SECONDS` you set yourself, in the environment or a `.env`, still
wins. The CLI keeps failing fast. Thanks to @iceblue03 (#862).
([#864](https://github.com/ffroliva/gflow-cli/issues/864))

## [0.77.1] — 2026-09-17

### Fixed
Expand Down Expand Up @@ -5349,7 +5469,8 @@ shell-script template that branches on these codes.

First skeleton. Not functional end-to-end yet.

[Unreleased]: https://github.com/ffroliva/gflow-cli/compare/v0.77.1...HEAD
[Unreleased]: https://github.com/ffroliva/gflow-cli/compare/v0.78.0...HEAD
[0.78.0]: https://github.com/ffroliva/gflow-cli/compare/v0.77.1...v0.78.0
[0.77.1]: https://github.com/ffroliva/gflow-cli/compare/v0.77.0...v0.77.1
[0.77.0]: https://github.com/ffroliva/gflow-cli/compare/v0.76.0...v0.77.0
[0.76.0]: https://github.com/ffroliva/gflow-cli/compare/v0.75.0...v0.76.0
Expand Down
Loading
Loading