Skip to content

feat(oauth): add --manual-paste fallback for browser-only remote consoles (closes #26923)#28358

Merged
teknium1 merged 3 commits into
mainfrom
hermes/hermes-6063e704
May 19, 2026
Merged

feat(oauth): add --manual-paste fallback for browser-only remote consoles (closes #26923)#28358
teknium1 merged 3 commits into
mainfrom
hermes/hermes-6063e704

Conversation

@teknium1

Copy link
Copy Markdown
Contributor

Salvage of #26929 (@xxxigm).

Summary

Adds a --manual-paste fallback to hermes auth add xai-oauth and hermes model so users on browser-only remote consoles — GCP Cloud Shell, GitHub Codespaces, AWS Instance Connect, Gitpod, StackBlitz, Replit — can complete xAI Grok OAuth login without an SSH tunnel. Also unblocks WSL2 users hit by the Hyper-V Firewall default-block on the loopback callback.

Changes

  • hermes_cli/auth.py:
    • _xai_oauth_loopback_login(manual_paste=True) skips the HTTP listener entirely. Redirect URI, PKCE verifier, state, and nonce are byte-identical to the loopback path — purely a transport change for the callback hop, not a security downgrade.
    • New helpers _parse_pasted_callback and _prompt_manual_callback_paste (designed to be reused by Spotify in a follow-up).
    • _is_remote_session() broadened to recognize CLOUD_SHELL, CODESPACES, CODESPACE_NAME, GITPOD_WORKSPACE_ID, REPL_ID, STACKBLITZ in addition to SSH_CLIENT / SSH_TTY. Existing tunnel hint also mentions --manual-paste.
  • hermes_cli/auth_commands.py + hermes_cli/main.py: register --manual-paste on both hermes auth add and hermes model. hermes model now also forwards --no-browser and --timeout.
  • tests/hermes_cli/test_auth_manual_paste.py (new): pins manual-paste semantics.
  • website/docs/guides/oauth-over-ssh.md + website/docs/guides/xai-grok-oauth.md: documents the browser-only console path.

Validation

  • scripts/run_tests.sh tests/hermes_cli/test_auth_manual_paste.py tests/hermes_cli/test_auth_xai_oauth_provider.py tests/hermes_cli/test_auth_commands.py tests/hermes_cli/test_auth_loopback_ssh_hint.py -q → 157/157 passing.
  • scripts/run_tests.sh tests/hermes_cli/test_argparse_flag_propagation.py -q → 14/14 passing.

Authorship preserved via cherry-pick across 3 commits.

Note for contributor: this also closes the same gap addressed by #27523 (@levi951) and #27556 (@WinterArc21) — both closed in favor of this PR which was first-submitted and broader in scope (remote-env detection broadening + tests + docs).

xxxigm added 3 commits May 18, 2026 20:09
xAI Grok OAuth (and Spotify) use a loopback redirect to
``http://127.0.0.1:<port>/callback`` to capture the authorization
code. That works when the browser and Hermes run on the same
machine, and the SSH tunnel recipe handles the regular remote
case. It breaks completely on **browser-only remote consoles**
(GCP Cloud Shell, GitHub Codespaces, AWS EC2 Instance Connect,
Gitpod, Replit, …) where the user has a browser but no real SSH
client to forward a port — the redirect to 127.0.0.1 on the
remote VM simply isn't reachable from the laptop, and there's
nothing the existing flow can do about it (#26923).

This commit adds the foundation for a manual-paste fallback:

* ``_is_remote_session`` now also recognises Cloud Shell,
  Codespaces, Gitpod, Replit, StackBlitz (in addition to SSH),
  so the existing tunnel hint at least fires in those
  environments.
* ``_parse_pasted_callback`` accepts any of: a full
  ``http(s)://...?code=...&state=...`` URL, a bare ``?code=...``
  query string, a bare ``code=...&state=...`` fragment, or a
  bare opaque code value.  Returns the same dict shape the HTTP
  callback handler produces, so the caller's state / error
  validation works unchanged (no CSRF bypass).
* ``_prompt_manual_callback_paste`` reads stdin with a clear
  multi-line explanation of what's happening and what to paste.
* ``_xai_oauth_loopback_login`` gains a ``manual_paste`` kwarg
  that skips the HTTP listener entirely.  The redirect_uri,
  PKCE verifier, state, and nonce are byte-identical to the
  loopback path so xAI's token endpoint can't tell the
  difference at the protocol level.
* ``_print_loopback_ssh_hint`` now also mentions
  ``--manual-paste`` so users without a real SSH client see a
  path forward instead of a dead-end tunnel recipe.
* ``_login_xai_oauth`` threads ``args.manual_paste`` into the
  loopback helper.
…model``

Register the new ``--manual-paste`` flag on both entry points and
thread it through to the xAI loopback login:

* ``hermes auth add xai-oauth --manual-paste`` — pool-add path,
  forwarded inside ``auth_commands.handle_auth_add``.
* ``hermes model --manual-paste`` — model-picker path, forwarded
  by ``_model_flow_xai_oauth`` into the synthetic ``argparse.Namespace``
  it passes to ``_login_xai_oauth``.  The picker also now forwards
  ``--no-browser`` and ``--timeout`` for consistency (previously
  hardcoded to defaults regardless of CLI flags).

Help text on both flags points at #26923 and names the
browser-only remote consoles (Cloud Shell, Codespaces, EC2
Instance Connect) so users searching ``hermes --help`` can find
the workaround.
…y path (#26923)

Tests (``tests/hermes_cli/test_auth_manual_paste.py``):

* 9 parametrised + scalar cases for ``_is_remote_session`` covering
  the new Cloud Shell / Codespaces / Gitpod / Replit / StackBlitz
  env vars (plus the existing SSH ones).
* 9 cases for ``_parse_pasted_callback`` covering every paste form
  (full URL, https URL with extra params, bare ``?code=...``, bare
  ``code=...`` fragment, bare opaque value, error+description,
  empty, whitespace-only, malformed URL).
* 3 cases for ``_prompt_manual_callback_paste`` (happy path, EOF,
  Ctrl-C).
* 3 end-to-end ``_xai_oauth_loopback_login(manual_paste=True)``
  cases: the HTTP server MUST NOT be started (asserted via a
  callable that raises if invoked), wrong state still rejected
  with ``xai_state_mismatch`` (no CSRF bypass), and empty paste
  surfaces ``xai_code_missing``.
* SSH-hint mention test ensures the ``--manual-paste`` instruction
  is printed in the remote-session hint.

Docs:

* ``oauth-over-ssh.md`` — new "Browser-only remote (Cloud Shell /
  Codespaces / EC2 Instance Connect)" section with the
  ``--manual-paste`` recipe, plus a TL;DR note for the new flag.
* ``xai-grok-oauth.md`` — short subsection pointing at the same
  recipe and the OAuth-over-SSH guide anchor.
@teknium1 teknium1 merged commit 817e1d6 into main May 19, 2026
17 of 18 checks passed
@teknium1 teknium1 deleted the hermes/hermes-6063e704 branch May 19, 2026 03:10
@github-actions

Copy link
Copy Markdown
Contributor

🔎 Lint report: hermes/hermes-6063e704 vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 8792 on HEAD, 8791 on base (🆕 +1)

🆕 New issues (1):

Rule Count
unresolved-import 1
First entries
tests/hermes_cli/test_auth_manual_paste.py:30: [unresolved-import] unresolved-import: Cannot resolve imported module `pytest`

✅ Fixed issues: none

Unchanged: 4626 pre-existing issues carried over.

Diagnostics are surfaced as warnings — this check never fails the build.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants