Skip to content
Open
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 agent-test-flows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ and reports the results. Keep these short — the agent works out the details.
## How to use
Point an agent at this folder and ask for a **smoke** pass (quick, ~10–15 min) or a **full** pass
(thorough, can take hours). The agent plans the tasks, picks models, runs them via `mobilerun run …`
(and `mobilerun device` / `mobilerun macro` / `mobilerun <provider> login` as needed), and writes a
(and `mobilerun device` / `mobilerun macro` / `mobilerun configure <provider>` as needed), and writes a
comparison table with screenshots. Smoke = one cheap model + a few core checks; full = sweep the
areas below.

Expand Down
2 changes: 1 addition & 1 deletion mobilerun/agent/utils/llm_picker.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ def _validate_gemini_oauth_model(model: object) -> None:
raise ValueError(
f"Model '{model_id}' is from the deprecated gemini-cli Code Assist "
f"path, which stops serving Google One / individual tiers on "
f"2026-06-18. Re-run `mobilerun gemini login` and pick one of: "
f"2026-06-18. Re-run `mobilerun configure gemini` and pick one of: "
f"{supported}."
)

Expand Down
132 changes: 20 additions & 112 deletions mobilerun/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -414,12 +414,6 @@ def _run_anthropic_oauth_login(credential_path: str, **kwargs) -> None:
_print_oauth_login_success("Anthropic", credential_path)


def _prompt_anthropic_setup_token(token: str | None) -> str:
if token:
return token
return click.prompt("Paste your Anthropic setup token", hide_input=True)


try:
_available_agents = list_agents()
except Exception:
Expand Down Expand Up @@ -826,65 +820,7 @@ def tui():
run_tui()


@cli.command(name="setup-token")
@click.option(
"--timeout",
type=float,
default=300.0,
show_default=True,
help="Max seconds to wait for the browser callback.",
)
@click.option(
"--callback-host",
default="127.0.0.1",
show_default=True,
help="Host to bind the local OAuth callback server.",
)
@click.option(
"--callback-port",
type=int,
default=0,
show_default=True,
help="Port to bind the local OAuth callback server. Use 0 for auto.",
)
@click.option(
"--callback-path",
default="/callback",
show_default=True,
help="Callback path for the local OAuth server.",
)
@click.option(
"--open-browser/--no-browser",
default=True,
show_default=True,
help="Open the authorization URL automatically.",
)
def setup_token(
timeout: float,
callback_host: str,
callback_port: int,
callback_path: str,
open_browser: bool,
):
"""Create a long-lived Anthropic setup token using Mobilerun's native OAuth flow."""
console.print(
"This will guide you through long-lived (1-year) auth token setup for your Claude account."
)
token = run_anthropic_setup_token_oauth(
timeout=timeout,
callback_host=callback_host,
callback_port=callback_port,
callback_path=callback_path,
open_browser=open_browser,
)
console.print("\n[green]Setup token created.[/]")
console.print(
"Paste this token into `mobilerun configure` or `mobilerun anthropic login`."
)
click.echo(token)


@cli.command(name="configure")
@cli.group(name="configure", invoke_without_command=True)
@click.option(
"--provider",
type=str,
Expand All @@ -907,14 +843,22 @@ def setup_token(
default=None,
help="Base URL override for compatible providers.",
)
@click.pass_context
def configure(
ctx: click.Context,
provider: str | None,
auth_mode: str | None,
model: str | None,
api_key: str | None,
base_url: str | None,
):
"""Configure LLM provider, auth mode, and model."""
"""Configure LLM provider, auth mode, and model.

Run without a subcommand for the interactive wizard, or use a provider
subcommand (e.g. `mobilerun configure anthropic`) to log in via OAuth.
"""
if ctx.invoked_subcommand is not None:
return
run_configure_wizard(
console,
ConfigureWizardCallbacks(
Expand All @@ -930,13 +874,7 @@ def configure(
)


@cli.group()
def openai():
"""OpenAI OAuth commands."""
pass


@openai.command("login")
@configure.command("openai")
@click.option(
"--credential-path",
default=str(DEFAULT_OPENAI_OAUTH_CREDENTIAL_PATH),
Expand Down Expand Up @@ -978,7 +916,7 @@ def openai():
show_default=True,
help="Open the authorization URL automatically.",
)
def openai_login(
def configure_openai(
credential_path: str,
model: str | None,
timeout: float,
Expand All @@ -987,7 +925,7 @@ def openai_login(
callback_path: str,
open_browser: bool,
):
"""Login with ChatGPT/OpenAI OAuth and save credentials locally."""
"""Log in to ChatGPT/OpenAI via OAuth and save credentials locally."""
_run_openai_oauth_login(
credential_path=credential_path,
model=model,
Expand All @@ -999,58 +937,28 @@ def openai_login(
)


@cli.group()
def anthropic():
"""Anthropic authentication commands."""
pass


@anthropic.command("login")
@configure.command("anthropic")
@click.option(
"--credential-path",
default=str(ANTHROPIC_OAUTH_CREDENTIAL_PATH),
show_default=True,
help="Where to store the Anthropic setup-token.",
help="Where to store the Anthropic credentials.",
)
@click.option(
"--token",
default=None,
help="Anthropic setup-token value. If provided, skips the OAuth flow.",
)
def anthropic_login(credential_path: str, token: str | None):
"""Login with Anthropic OAuth. Pass --token to save a setup-token without OAuth."""
def configure_anthropic(credential_path: str, token: str | None):
"""Log in to Anthropic via OAuth (or pass --token to save a setup-token)."""
if token:
save_anthropic_setup_token(credential_path, token)
_print_oauth_login_success("Anthropic", credential_path)
else:
_run_anthropic_oauth_login(credential_path=credential_path)


@anthropic.command("setup-token")
@click.option(
"--credential-path",
default=str(ANTHROPIC_OAUTH_CREDENTIAL_PATH),
show_default=True,
help="Where to store the Anthropic setup-token.",
)
@click.option(
"--token",
default=None,
help="Setup-token value. If omitted, you will be prompted.",
)
def anthropic_setup_token(credential_path: str, token: str | None):
"""Paste and save an Anthropic setup-token."""
save_anthropic_setup_token(credential_path, _prompt_anthropic_setup_token(token))
_print_oauth_login_success("Anthropic setup-token", credential_path)


@cli.group(name="gemini")
def gemini_group():
"""Gemini OAuth commands."""
pass


@gemini_group.command("login")
@configure.command("gemini")
@click.option(
"--credential-path",
default=str(GEMINI_OAUTH_CREDENTIAL_PATH),
Expand Down Expand Up @@ -1092,7 +1000,7 @@ def gemini_group():
show_default=True,
help="Open the authorization URL automatically.",
)
def gemini_login(
def configure_gemini(
credential_path: str,
model: str | None,
timeout: float,
Expand All @@ -1101,7 +1009,7 @@ def gemini_login(
callback_path: str,
open_browser: bool,
):
"""Login with Gemini Code Assist OAuth and save credentials locally."""
"""Log in to Gemini Code Assist via OAuth and save credentials locally."""
_run_gemini_oauth_login(
credential_path=credential_path,
model=model,
Expand Down
Loading