feat(cli): deliver the Antigravity credential straight to the remote install

`omniroute login antigravity` already runs the OAuth where it actually works — the
operator's own machine, the only place Google's firstparty/nativeapp loopback
resolves — but it stopped at PRINTING a credential blob to copy into the remote
dashboard by hand.

Every piece needed to close that loop was already in place:

- `omniroute connect <host>` saves an admin-scoped token in the active context;
- `apiFetch()` injects that context's baseUrl + Bearer automatically;
- `/api/oauth` requires admin scope (src/server/authz/accessScopes.ts) and stays
  remote-reachable — routeGuard.ts loopback-gates only `/api/oauth/cursor/auto-import`,
  with an explicit comment that the rest must remain reachable;
- `isAuthenticated()` accepts a Bearer management token;
- `/api/oauth/<provider>/paste-credentials` already decodes a blob and persists.

So the CLI now POSTs the blob itself whenever the active context points at another
machine. No paste, and no SSH tunnel.

What it deliberately does NOT do is make the push mandatory. This helper exists
because it needs no route to the VPS at all — it talks only to Google, so it works
from behind a firewall. A failed push therefore falls back to printing the blob
rather than discarding an authorization the operator just completed in a browser.
`--no-push` forces the old behaviour, `--push` forces delivery, `--context <name>`
targets a specific install.

A successful push does not echo the blob: it wraps a refresh token and it already
landed. Printing it would only widen the exposure.

Note for the follow-up: `oauth start --provider antigravity` cannot substitute for
this. `runBrowserFlow` asks the SERVER to start the flow and then polls the server,
so the callback still has to reach the server's loopback — it hangs on a remote
install exactly like the dashboard does.
This commit is contained in:
diegosouzapw
2026-07-28 04:07:13 -03:00
parent ec150a0069
commit a6735cfb16
3 changed files with 353 additions and 13 deletions

View File

@@ -121,29 +121,51 @@ There are two supported ways to connect Antigravity to a remote OmniRoute.
### Option A — local login helper (recommended)
Run the OAuth on **your own computer**, where `127.0.0.1` is reachable, and paste
the result into the remote dashboard. The helper talks only to Google — it does
**not** need network access to your VPS, so it works even behind firewalls.
Run the OAuth on **your own computer**, where `127.0.0.1` is reachable. The helper
talks to Google directly, so the consent completes where the dashboard's version
cannot.
**If you are already connected** (`omniroute connect <host>`), there is nothing to
copy — the helper delivers the credential to that install for you:
```bash
# On your LOCAL machine (needs Node.js + a browser):
omniroute connect 192.168.0.15 # once — mints an admin-scoped context token
npx omniroute login antigravity
# ↳ opens the Google consent in your browser, captures the callback on a local
# loopback port, exchanges it, and prints a one-line credential blob:
# ↳ opens the Google consent, captures the callback on a local loopback port,
# exchanges it, and POSTs the credential to the active context:
#
# Antigravity connected on http://192.168.0.15:20128 (connection abc123).
# Nothing to paste — you can close this terminal.
```
The push happens automatically whenever the active context points at another
machine. Force it either way with `--push` / `--no-push`, or aim at a specific
context with `--context <name>`.
**If your machine cannot reach the VPS** (firewalled, no SSH, air-gapped desk), the
helper still works — it only ever _needs_ Google. Use `--no-push`, or just let the
push fail: it falls back to printing the blob rather than discarding an
authorization you already completed.
```bash
npx omniroute login antigravity --no-push
# omniroute-cred-v1.eyJ2IjoxLCJ...
```
Then, in the **remote** dashboard: **Providers → Antigravity → Connect**, and
paste the `omniroute-cred-v1.…` blob into the **Step 2** field (it accepts either
a callback URL or a credential blob). OmniRoute decodes it, runs the Cloud Code
Then, in the **remote** dashboard: **Providers → Antigravity → Connect**, and paste
the `omniroute-cred-v1.…` blob into the **Step 2** field (it accepts either a
callback URL or a credential blob). OmniRoute decodes it, runs the Cloud Code
onboarding server-side, and persists the connection.
> The blob contains a refresh token — treat it like a password. It is sent once
> over your dashboard connection and stored encrypted at rest.
> The blob contains a refresh token — treat it like a password. On the push path it
> is sent once over your context's authenticated connection; on the paste path, over
> your dashboard connection. Either way it is stored encrypted at rest, and a
> successful push never prints it to your terminal.
Flags: `--no-browser` (print the URL instead of auto-opening), `--port <n>`
(pin the loopback port), `--timeout <ms>`.
(pin the loopback port), `--timeout <ms>`, `--push` / `--no-push` (override the
automatic delivery), `--context <name>` (target a specific context).
### Option B — SSH local-forward tunnel