fix(xai): accept Grok Build code during loopback wait + tiny screenshot guard
xAI's consent page renders the authorization code in-page instead of redirecting to the loopback callback, so the listener just hangs and the manual-paste flow demands a callback URL that never contains the token. - auth.py: poll stdin non-blockingly while waiting for the xAI loopback callback; accept a pasted bare Grok Build code and substitute the locally generated state (PKCE code_verifier still binds the exchange). No need to wait for timeout or re-run with --manual-paste. - computer_use: parse PNG/JPEG dimensions from base64 and fall back to the text/AX/SOM payload when the screenshot is below the provider minimum (8x8), which xAI rejects with HTTP 400. - model_setup_flows.py: xAI credential reuse prompt uses the standard radio picker via a shared _prompt_auth_credentials_choice helper. - main.py: thread a title through _prompt_provider_choice; re-home the helper import (flows live in model_setup_flows.py post-decomposition). Salvaged from #36781 onto current main (contributor's main.py edits re-homed to model_setup_flows.py, where the flows were extracted since the PR opened).
This commit is contained in:
+10
-4
@@ -499,6 +499,7 @@ from hermes_cli import __version__, __release_date__
|
||||
# (god-file decomposition Phase 2). Re-imported here so select_provider_and_model and
|
||||
# existing test monkeypatches (hermes_cli.main._model_flow_*) keep resolving unchanged.
|
||||
from hermes_cli.model_setup_flows import (
|
||||
_prompt_auth_credentials_choice,
|
||||
_model_flow_openrouter,
|
||||
_model_flow_nous,
|
||||
_model_flow_openai_codex,
|
||||
@@ -2830,7 +2831,12 @@ def select_provider_and_model(args=None):
|
||||
member_labels = [
|
||||
provider_labels.get(m, m) for m in selected_members
|
||||
]
|
||||
member_idx = _prompt_provider_choice(member_labels, default=member_default)
|
||||
group_label = ordered[provider_idx][1].split(" ▸", 1)[0]
|
||||
member_idx = _prompt_provider_choice(
|
||||
member_labels,
|
||||
default=member_default,
|
||||
title=f"Select {group_label} provider:",
|
||||
)
|
||||
if member_idx is None:
|
||||
print("No change.")
|
||||
return
|
||||
@@ -3331,7 +3337,7 @@ def _aux_flow_custom_endpoint(task: str, task_cfg: dict) -> None:
|
||||
print(f"{display_name}: custom ({short_url})" + (f" · {model}" if model else ""))
|
||||
|
||||
|
||||
def _prompt_provider_choice(choices, *, default=0):
|
||||
def _prompt_provider_choice(choices, *, default=0, title="Select provider:"):
|
||||
"""Show provider selection menu with curses arrow-key navigation.
|
||||
|
||||
Falls back to a numbered list when curses is unavailable (e.g. piped
|
||||
@@ -3341,7 +3347,7 @@ def _prompt_provider_choice(choices, *, default=0):
|
||||
try:
|
||||
from hermes_cli.setup import _curses_prompt_choice
|
||||
|
||||
idx = _curses_prompt_choice("Select provider:", choices, default)
|
||||
idx = _curses_prompt_choice(title, choices, default)
|
||||
if idx >= 0:
|
||||
print()
|
||||
return idx
|
||||
@@ -3349,7 +3355,7 @@ def _prompt_provider_choice(choices, *, default=0):
|
||||
pass
|
||||
|
||||
# Fallback: numbered list
|
||||
print("Select provider:")
|
||||
print(title)
|
||||
for i, c in enumerate(choices, 1):
|
||||
marker = "→" if i - 1 == default else " "
|
||||
print(f" {marker} {i}. {c}")
|
||||
|
||||
Reference in New Issue
Block a user