diff --git a/src/hai_agents_cli/app.py b/src/hai_agents_cli/app.py index 196dc515..7c42d15b 100644 --- a/src/hai_agents_cli/app.py +++ b/src/hai_agents_cli/app.py @@ -95,12 +95,12 @@ def login( ) -> None: """Sign in through the browser and store an API key in ~/.config/hai/.env.""" state = _state(ctx) - if key: - _store_pasted_key(state.base_url) - return if credentials.current_api_key() and not force: console.print("Already signed in. Pass --force to rotate the key.") return + if key: + _store_pasted_key(state.base_url) + return if not sys.stdin.isatty(): _raise_cli_error(RuntimeError(f"login needs an interactive terminal and a browser. {auth.KEY_FALLBACK}")) @@ -118,7 +118,8 @@ def login( def _store_pasted_key(base_url: str | None) -> None: - pasted = typer.prompt("API key", hide_input=True) if sys.stdin.isatty() else sys.stdin.readline() + prompt = f"API key (create one at {credentials.API_KEYS_PAGE})" + pasted = typer.prompt(prompt, hide_input=True) if sys.stdin.isatty() else sys.stdin.readline() pasted = pasted.strip() if not pasted: _raise_cli_error(RuntimeError(f"no key given; create one at {credentials.API_KEYS_PAGE}.")) diff --git a/tests/test_credentials.py b/tests/test_credentials.py index d7538334..8632ae48 100644 --- a/tests/test_credentials.py +++ b/tests/test_credentials.py @@ -111,7 +111,6 @@ def get_session_quota(self): raise ApiError(status_code=status, body={"detail": "Invalid API key"}) monkeypatch.setattr(app_module, "make_client", lambda **_: type("C", (), {"sessions": _Sessions()})()) - monkeypatch.setattr(app_module.credentials, "current_api_key", lambda *_: "hk-existing") result = runner.invoke(app, ["login", "--key"], input="hk-pasted\n") monkeypatch.delenv(credentials.API_KEY_VAR, raising=False) @@ -125,3 +124,29 @@ def get_session_quota(self): def _error_text(result) -> str: return "\n".join(part for part in (result.output, result.stderr, str(result.exception)) if part) + + +def test_login_key_is_a_no_op_when_a_key_is_already_stored(monkeypatch): + """Pasted snippets start with `hai login --key`; a second paste must not ask again.""" + monkeypatch.setattr(app_module.credentials, "current_api_key", lambda *_: "hk-existing") + monkeypatch.setattr(app_module, "make_client", lambda **_: pytest.fail("must not validate or store anything")) + + result = runner.invoke(app, ["login", "--key"], input="hk-pasted\n") + + assert result.exit_code == 0 + assert "Already signed in" in result.output + + +def test_login_key_force_replaces_a_stored_key(monkeypatch): + class _Sessions: + def get_session_quota(self): + pass + + monkeypatch.setattr(app_module, "make_client", lambda **_: type("C", (), {"sessions": _Sessions()})()) + credentials.save_api_key("hk-existing") + + result = runner.invoke(app, ["login", "--key", "--force"], input="hk-new\n") + monkeypatch.delenv(credentials.API_KEY_VAR, raising=False) + + assert result.exit_code == 0, _error_text(result) + assert "hk-new" in credentials.GLOBAL_ENV_PATH.read_text()