feat(cli): make hermes portal the human-readable Portal onboarding alias

`hermes portal` (no subcommand) now runs the one-shot Nous Portal onboarding
— OAuth login, switch provider to Nous, offer Tool Gateway — identical to
`hermes setup --portal` and the human-readable alias for
`hermes auth add nous --type oauth` (which still works).

The prior status default moves to `hermes portal info`; `status` is kept as a
hidden back-compat alias. `open`/`tools` subcommands are unchanged.

User-facing hints and docs (status.py, conversation_loop 401 guidance,
SystemPage, README, website docs + zh-Hans) now point at `hermes portal` /
`hermes portal info`. `--manual-paste` references keep the explicit auth
command since `hermes portal` does not expose that flag.
This commit is contained in:
kshitijk4poor
2026-06-04 01:19:28 +05:30
parent 39fee4f3bc
commit da4f407e51
21 changed files with 212 additions and 58 deletions

View File

@ -94,7 +94,7 @@ One command from a fresh install:
hermes setup --portal hermes setup --portal
``` ```
That logs you in via OAuth, sets Nous as your provider, and turns on the Tool Gateway. Check what's wired up any time with `hermes portal status`. Full details on the [Tool Gateway docs page](https://hermes-agent.nousresearch.com/docs/user-guide/features/tool-gateway). That logs you in via OAuth, sets Nous as your provider, and turns on the Tool Gateway. Check what's wired up any time with `hermes portal info`. Full details on the [Tool Gateway docs page](https://hermes-agent.nousresearch.com/docs/user-guide/features/tool-gateway).
You can still bring your own keys per-tool whenever you want — the gateway is per-backend, not all-or-nothing. You can still bring your own keys per-tool whenever you want — the gateway is per-backend, not all-or-nothing.

View File

@ -80,7 +80,7 @@ Hermes 始终允许你使用任意服务商,这点不会改变。但如果你
hermes setup --portal hermes setup --portal
``` ```
它会通过 OAuth 登录、把 Nous 设为推理服务商,并启用 Tool Gateway。随时用 `hermes portal status` 查看路由状态。完整说明见 [Tool Gateway 文档](https://hermes-agent.nousresearch.com/docs/user-guide/features/tool-gateway)。 它会通过 OAuth 登录、把 Nous 设为推理服务商,并启用 Tool Gateway。随时用 `hermes portal info` 查看路由状态。完整说明见 [Tool Gateway 文档](https://hermes-agent.nousresearch.com/docs/user-guide/features/tool-gateway)。
你随时可以按工具单独切回自己的 API Key — Gateway 是按工具粒度生效的,不是一刀切。 你随时可以按工具单独切回自己的 API Key — Gateway 是按工具粒度生效的,不是一刀切。

View File

@ -3255,7 +3255,7 @@ def run_conversation(
else: # nous else: # nous
agent._vprint(f"{agent.log_prefix} 💡 Nous Portal OAuth token was rejected (HTTP 401). Your token may be", force=True) agent._vprint(f"{agent.log_prefix} 💡 Nous Portal OAuth token was rejected (HTTP 401). Your token may be", force=True)
agent._vprint(f"{agent.log_prefix} expired, revoked, or your account may be out of credits. To fix:", force=True) agent._vprint(f"{agent.log_prefix} expired, revoked, or your account may be out of credits. To fix:", force=True)
agent._vprint(f"{agent.log_prefix} 1. Re-authenticate: hermes auth add nous --type oauth", force=True) agent._vprint(f"{agent.log_prefix} 1. Re-authenticate: hermes portal", force=True)
agent._vprint(f"{agent.log_prefix} 2. Check your portal account: https://portal.nousresearch.com", force=True) agent._vprint(f"{agent.log_prefix} 2. Check your portal account: https://portal.nousresearch.com", force=True)
# ``:free`` is OpenRouter slug syntax; Nous Portal will reject # ``:free`` is OpenRouter slug syntax; Nous Portal will reject
# the model name even after a successful re-auth. # the model name even after a successful re-auth.

View File

@ -1,12 +1,20 @@
"""``hermes portal`` — small CLI surface for Nous Portal users. """``hermes portal`` — the human-readable entry point for Nous Portal.
Running ``hermes portal`` with no subcommand performs the one-shot Portal
onboarding: OAuth login, switch the inference provider to Nous, and offer to
enable the Tool Gateway. It is the friendly alias for
``hermes auth add nous --type oauth`` (which still works) and is identical to
``hermes setup --portal``.
Subcommands: Subcommands:
status Show Portal auth state + which Tool Gateway tools are routed. (none) Log in to Nous Portal + set it up (one-shot onboarding).
login Explicit alias for the default one-shot onboarding.
info Show Portal auth state + which Tool Gateway tools are routed.
open Open the Portal subscription page in the user's default browser. open Open the Portal subscription page in the user's default browser.
tools List Tool Gateway tools and which are active in the current config. tools List Tool Gateway tools and which are active in the current config.
This command is intentionally minimal — it does not duplicate functionality This command is intentionally minimal — it does not duplicate functionality
already in ``hermes auth`` or ``hermes tools``. It's a discovery + status already in ``hermes auth`` or ``hermes tools``. It's the onboarding + discovery
surface for the Portal subscription itself. surface for the Portal subscription itself.
""" """
from __future__ import annotations from __future__ import annotations
@ -49,7 +57,7 @@ def _cmd_status(args) -> int:
else: else:
print(f" Auth: {color('not logged in', Colors.YELLOW)}") print(f" Auth: {color('not logged in', Colors.YELLOW)}")
print(f" Sign up: {SUBSCRIPTION_URL}") print(f" Sign up: {SUBSCRIPTION_URL}")
print(f" Login: hermes auth add nous --type oauth") print(f" Login: hermes portal")
# Provider selection (independent of auth) # Provider selection (independent of auth)
model_cfg = config.get("model") if isinstance(config.get("model"), dict) else {} model_cfg = config.get("model") if isinstance(config.get("model"), dict) else {}
@ -134,7 +142,7 @@ def _cmd_tools(args) -> int:
print(color(" ────────────────────", Colors.MAGENTA)) print(color(" ────────────────────", Colors.MAGENTA))
if not features.nous_auth_present: if not features.nous_auth_present:
print(color(" Not logged into Nous Portal — sign in with `hermes auth add nous --type oauth`.", Colors.YELLOW)) print(color(" Not logged into Nous Portal — sign in with `hermes portal`.", Colors.YELLOW))
print() print()
label_width = max(len(label) for _, label, _ in catalog) label_width = max(len(label) for _, label, _ in catalog)
@ -158,14 +166,36 @@ def _cmd_tools(args) -> int:
return 0 return 0
def _cmd_login(args) -> int:
"""Run the one-shot Nous Portal onboarding (OAuth + provider + Tool Gateway).
This is the human-readable front door for `hermes auth add nous --type
oauth`. It reuses the exact wiring behind `hermes setup --portal` so the
two commands stay in lockstep: device-code login, switch the inference
provider to Nous, then offer the Tool Gateway opt-in.
"""
from hermes_cli.setup import _run_portal_one_shot
config = load_config() or {}
try:
_run_portal_one_shot(config)
except (KeyboardInterrupt, EOFError):
print()
print("Portal setup cancelled.")
return 1
return 0
def portal_command(args) -> int: def portal_command(args) -> int:
"""Top-level dispatch for `hermes portal <subcommand>`.""" """Top-level dispatch for `hermes portal <subcommand>`."""
sub = getattr(args, "portal_command", None) sub = getattr(args, "portal_command", None)
if sub in {None, ""}: if sub in {None, "", "login"}:
# Default to status — matches gh / kubectl conventions where the # Default to the one-shot onboarding — `hermes portal` is the
# subcommand-less form gives a useful overview. # human-readable alias for `hermes auth add nous --type oauth` /
return _cmd_status(args) # `hermes setup --portal`.
if sub == "status": return _cmd_login(args)
if sub in {"info", "status"}:
# `status` kept as a back-compat alias for the prior default.
return _cmd_status(args) return _cmd_status(args)
if sub == "open": if sub == "open":
return _cmd_open(args) return _cmd_open(args)
@ -180,19 +210,26 @@ def add_parser(subparsers) -> None:
"""Register `hermes portal` on the given argparse subparsers object.""" """Register `hermes portal` on the given argparse subparsers object."""
portal_parser = subparsers.add_parser( portal_parser = subparsers.add_parser(
"portal", "portal",
help="Nous Portal status, subscription, and Tool Gateway routing", help="Set up Nous Portal (OAuth login + Tool Gateway); see also `portal info`",
description=( description=(
"Inspect Nous Portal auth, Tool Gateway routing, and open the " "Run `hermes portal` with no subcommand to log in to Nous Portal "
"Portal subscription page. Subcommands: status (default), " "and set it up (the human-readable alias for `hermes auth add nous "
"open, tools." "--type oauth`, identical to `hermes setup --portal`). Subcommands: "
"login (default), info, open, tools."
), ),
) )
portal_sub = portal_parser.add_subparsers(dest="portal_command") portal_sub = portal_parser.add_subparsers(dest="portal_command")
portal_sub.add_parser( portal_sub.add_parser(
"status", "login",
help="Show Portal auth + Tool Gateway routing summary (default)", help="Log in to Nous Portal + set it up (default; one-shot onboarding)",
) )
portal_sub.add_parser(
"info",
help="Show Portal auth + Tool Gateway routing summary",
)
# `status` retained as a hidden back-compat alias for `info`.
portal_sub.add_parser("status")
portal_sub.add_parser( portal_sub.add_parser(
"open", "open",
help="Open the Portal subscription page in your default browser", help="Open the Portal subscription page in your default browser",

View File

@ -2828,7 +2828,7 @@ def _run_portal_one_shot(config: dict) -> None:
print() print()
print_success("Portal setup complete.") print_success("Portal setup complete.")
print_info(" Run `hermes portal status` to inspect routing.") print_info(" Run `hermes portal info` to inspect routing.")
print_info(" Run `hermes` to start chatting.") print_info(" Run `hermes` to start chatting.")

View File

@ -223,7 +223,7 @@ def show_status(args):
elif nous_inference_present: elif nous_inference_present:
nous_label = "not logged in (Nous inference key configured)" nous_label = "not logged in (Nous inference key configured)"
else: else:
nous_label = "not logged in (run: hermes auth add nous --type oauth)" nous_label = "not logged in (run: hermes portal)"
print( print(
f" {'Nous Portal':<12} {check_mark(nous_logged_in)} " f" {'Nous Portal':<12} {check_mark(nous_logged_in)} "
f"{nous_label}" f"{nous_label}"

View File

@ -48,8 +48,8 @@ def test_nous_401_guidance_strings_present():
# (Nous Portal has no API key path — auth_type=oauth_device_code only). # (Nous Portal has no API key path — auth_type=oauth_device_code only).
assert "Nous Portal OAuth token was rejected" in source assert "Nous Portal OAuth token was rejected" in source
# Must give the exact re-auth command, not a generic "hermes setup". # Must give a concrete re-auth command, not a generic "hermes setup".
assert "hermes auth add nous --type oauth" in source assert "hermes portal" in source
# Must point at the portal so users can check account/credit status. # Must point at the portal so users can check account/credit status.
assert "portal.nousresearch.com" in source assert "portal.nousresearch.com" in source

View File

@ -0,0 +1,111 @@
"""Tests for `hermes portal` dispatch.
`hermes portal` (no subcommand) is the human-readable alias for the Nous Portal
one-shot onboarding (`hermes auth add nous --type oauth` / `hermes setup
--portal`). The prior status default moved to `hermes portal info`, with
`status` retained as a back-compat alias.
"""
from __future__ import annotations
import argparse
from types import SimpleNamespace
import pytest
from hermes_cli import portal_cli
def _args(portal_command):
return SimpleNamespace(portal_command=portal_command)
@pytest.mark.parametrize("sub", [None, "", "login"])
def test_bare_portal_and_login_run_one_shot(monkeypatch, sub):
"""`hermes portal`, `hermes portal login` -> one-shot onboarding."""
calls = {"login": 0, "status": 0}
def fake_one_shot(config):
calls["login"] += 1
def fake_status(args):
calls["status"] += 1
return 0
monkeypatch.setattr(
"hermes_cli.setup._run_portal_one_shot", fake_one_shot
)
monkeypatch.setattr(portal_cli, "_cmd_status", fake_status)
monkeypatch.setattr(portal_cli, "load_config", lambda: {})
rc = portal_cli.portal_command(_args(sub))
assert rc == 0
assert calls["login"] == 1
assert calls["status"] == 0
@pytest.mark.parametrize("sub", ["info", "status"])
def test_info_and_status_alias_run_status(monkeypatch, sub):
"""`hermes portal info` and the `status` back-compat alias -> status."""
calls = {"login": 0, "status": 0}
monkeypatch.setattr(
"hermes_cli.setup._run_portal_one_shot",
lambda config: calls.__setitem__("login", calls["login"] + 1),
)
def fake_status(args):
calls["status"] += 1
return 0
monkeypatch.setattr(portal_cli, "_cmd_status", fake_status)
rc = portal_cli.portal_command(_args(sub))
assert rc == 0
assert calls["status"] == 1
assert calls["login"] == 0
def test_open_and_tools_dispatch(monkeypatch):
seen = []
monkeypatch.setattr(portal_cli, "_cmd_open", lambda a: seen.append("open") or 0)
monkeypatch.setattr(portal_cli, "_cmd_tools", lambda a: seen.append("tools") or 0)
assert portal_cli.portal_command(_args("open")) == 0
assert portal_cli.portal_command(_args("tools")) == 0
assert seen == ["open", "tools"]
def test_unknown_subcommand_returns_error(capsys):
rc = portal_cli.portal_command(_args("bogus"))
assert rc == 1
err = capsys.readouterr().err
assert "Unknown portal subcommand" in err
def test_login_cancelled_returns_one(monkeypatch):
def boom(config):
raise KeyboardInterrupt
monkeypatch.setattr("hermes_cli.setup._run_portal_one_shot", boom)
monkeypatch.setattr(portal_cli, "load_config", lambda: {})
rc = portal_cli.portal_command(_args(None))
assert rc == 1
def test_parser_registers_subcommands():
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest="command")
portal_cli.add_parser(subparsers)
# Bare `portal` resolves to portal_command with no portal_command set.
ns = parser.parse_args(["portal"])
assert ns.func is portal_cli.portal_command
assert getattr(ns, "portal_command", None) in (None, "")
# All documented subcommands parse.
for sub in ("login", "info", "status", "open", "tools"):
ns = parser.parse_args(["portal", sub])
assert ns.portal_command == sub

View File

@ -77,7 +77,7 @@ def test_show_status_reports_nous_auth_error(monkeypatch, capsys, tmp_path):
status_mod.show_status(SimpleNamespace(all=False, deep=False)) status_mod.show_status(SimpleNamespace(all=False, deep=False))
output = capsys.readouterr().out output = capsys.readouterr().out
assert "Nous Portal ✗ not logged in (run: hermes auth add nous --type oauth)" in output assert "Nous Portal ✗ not logged in (run: hermes portal)" in output
assert "Error: Refresh session has been revoked" in output assert "Error: Refresh session has been revoked" in output
assert "Access exp:" in output assert "Access exp:" in output
assert "Key exp:" in output assert "Key exp:" in output

View File

@ -775,7 +775,7 @@ export default function SystemPage() {
)} )}
{!portal?.logged_in && ( {!portal?.logged_in && (
<p className="text-xs text-muted-foreground"> <p className="text-xs text-muted-foreground">
Log in with <span className="font-mono">hermes auth add nous --type oauth</span>. Log in with <span className="font-mono">hermes portal</span>.
</p> </p>
)} )}
</CardContent> </CardContent>

View File

@ -57,7 +57,7 @@ See [OAuth over SSH / Remote Hosts](/guides/oauth-over-ssh) for the full walkthr
## 3. Verify it worked ## 3. Verify it worked
```bash ```bash
hermes portal status hermes portal info
``` ```
You should see: You should see:
@ -178,12 +178,12 @@ For team setups where multiple humans share a machine, each human has their own
## Troubleshooting ## Troubleshooting
### `hermes portal status` shows "not logged in" after `hermes setup --portal` ### `hermes portal info` shows "not logged in" after `hermes setup --portal`
The OAuth flow didn't complete. Re-run it: The OAuth flow didn't complete. Re-run it:
```bash ```bash
hermes auth add nous --type oauth hermes portal
``` ```
If your browser doesn't open or the callback fails, you're likely on a remote/headless host — see [OAuth over SSH](/guides/oauth-over-ssh) for the port-forwarding and manual-paste workarounds. If your browser doesn't open or the callback fails, you're likely on a remote/headless host — see [OAuth over SSH](/guides/oauth-over-ssh) for the port-forwarding and manual-paste workarounds.
@ -203,7 +203,7 @@ hermes model
# pick Nous Portal # pick Nous Portal
``` ```
Re-verify with `hermes portal status`. Re-verify with `hermes portal info`.
### Tool Gateway tools showing partner names instead of "via Nous Portal" ### Tool Gateway tools showing partner names instead of "via Nous Portal"
@ -239,7 +239,7 @@ If a model is genuinely unavailable, [open an issue](https://github.com/NousRese
### Billing not appearing on my Portal account ### Billing not appearing on my Portal account
`hermes portal status` will tell you whether you're actually routing through the Portal or some other provider. Common causes: `hermes portal info` will tell you whether you're actually routing through the Portal or some other provider. Common causes:
- `model.provider` set to `openrouter`/`anthropic`/etc. instead of `nous` - `model.provider` set to `openrouter`/`anthropic`/etc. instead of `nous`
- An OAuth refresh failure that fell back to a different configured provider - An OAuth refresh failure that fell back to a different configured provider

View File

@ -130,12 +130,15 @@ If you use [Hermes profiles](/user-guide/profiles), the Portal refresh token is
### Inspecting what's wired up ### Inspecting what's wired up
```bash ```bash
hermes portal status # login status, subscription info, model + gateway routing hermes portal # log in to Nous Portal + set it up (one-shot onboarding)
hermes portal info # login status, subscription info, model + gateway routing
hermes portal tools # detailed Tool Gateway catalog with per-tool routing hermes portal tools # detailed Tool Gateway catalog with per-tool routing
hermes portal open # open the subscription management page in your browser hermes portal open # open the subscription management page in your browser
``` ```
`hermes portal status` (or just `hermes portal`) gives you the high-level overview: `hermes portal` (with no subcommand) is the human-readable alias for `hermes auth add nous --type oauth` — it logs you in, sets Nous as your inference provider, and offers the Tool Gateway opt-in (identical to `hermes setup --portal`).
`hermes portal info` gives you the high-level overview:
``` ```
Nous Portal Nous Portal
@ -234,12 +237,12 @@ If the Portal invalidates the refresh token (password change, manual revoke, ses
## Troubleshooting ## Troubleshooting
### `hermes portal status` shows "not logged in" ### `hermes portal info` shows "not logged in"
You haven't completed the OAuth flow, or your refresh token was wiped. Run: You haven't completed the OAuth flow, or your refresh token was wiped. Run:
```bash ```bash
hermes auth add nous --type oauth hermes portal
``` ```
or use `hermes model` and re-select Nous Portal. or use `hermes model` and re-select Nous Portal.
@ -260,7 +263,7 @@ If a model is genuinely missing, [open an issue](https://github.com/NousResearch
### Bills not appearing on my Portal account ### Bills not appearing on my Portal account
Check `hermes portal status` first — if it shows you're using a different provider (`Model: currently openrouter` instead of `using Nous as inference provider`), your local config has drifted. Run `hermes model`, pick Nous Portal, and the next request will route through your subscription. Check `hermes portal info` first — if it shows you're using a different provider (`Model: currently openrouter` instead of `using Nous as inference provider`), your local config has drifted. Run `hermes model`, pick Nous Portal, and the next request will route through your subscription.
## See also ## See also

View File

@ -66,7 +66,7 @@ In the `model:` config section, you can use either `default:` or `model:` as the
```bash ```bash
hermes setup --portal # fresh install — OAuth + provider + gateway in one command hermes setup --portal # fresh install — OAuth + provider + gateway in one command
hermes model # existing install — pick "Nous Portal" from the list hermes model # existing install — pick "Nous Portal" from the list
hermes portal status # inspect login + routing at any time hermes portal info # inspect login + routing at any time
``` ```
Don't have a subscription yet? Get one at [portal.nousresearch.com/manage-subscription](https://portal.nousresearch.com/manage-subscription). Don't have a subscription yet? Get one at [portal.nousresearch.com/manage-subscription](https://portal.nousresearch.com/manage-subscription).
@ -89,7 +89,7 @@ Even when using Nous Portal, Codex, or a custom endpoint, some tools (vision, we
::: :::
:::tip Nous Tool Gateway :::tip Nous Tool Gateway
Paid Nous Portal subscribers also get access to the **[Tool Gateway](/user-guide/features/tool-gateway)** — web search, image generation, TTS, and browser automation routed through your subscription. No extra API keys needed. On a fresh install, `hermes setup --portal` logs you in, sets Nous as your provider, and turns the gateway on in one command. Existing users can enable it from `hermes model` or per-tool from `hermes tools`. Inspect routing at any time with `hermes portal status`. Paid Nous Portal subscribers also get access to the **[Tool Gateway](/user-guide/features/tool-gateway)** — web search, image generation, TTS, and browser automation routed through your subscription. No extra API keys needed. On a fresh install, `hermes setup --portal` logs you in, sets Nous as your provider, and turns the gateway on in one command. Existing users can enable it from `hermes model` or per-tool from `hermes tools`. Inspect routing at any time with `hermes portal info`.
::: :::
### Two Commands for Model Management ### Two Commands for Model Management

View File

@ -12,7 +12,7 @@ Hermes uses two kinds of model slots:
This page covers configuring both from the dashboard. If you prefer config files or the CLI, jump to [Alternative methods](#alternative-methods) at the bottom. This page covers configuring both from the dashboard. If you prefer config files or the CLI, jump to [Alternative methods](#alternative-methods) at the bottom.
:::tip Fastest path: Nous Portal :::tip Fastest path: Nous Portal
[Nous Portal](/user-guide/features/tool-gateway) provides 300+ models under one subscription. On a fresh install, run `hermes setup --portal` to log in and set Nous as your provider in one command. Inspect what's wired up with `hermes portal status`. [Nous Portal](/user-guide/features/tool-gateway) provides 300+ models under one subscription. On a fresh install, run `hermes setup --portal` to log in and set Nous as your provider in one command. Inspect what's wired up with `hermes portal info`.
- Portal subscribers also get **10% off token-billed providers**. - Portal subscribers also get **10% off token-billed providers**.
::: :::

View File

@ -29,7 +29,7 @@ proxy when you just want **the model** through your subscription.
### 1. Log into your provider (one-time) ### 1. Log into your provider (one-time)
```bash ```bash
hermes auth add nous hermes portal
``` ```
This opens your browser for the Nous Portal OAuth flow. Hermes stores This opens your browser for the Nous Portal OAuth flow. Hermes stores
@ -88,10 +88,10 @@ Hermes proxy upstream adapters
[nous ] Nous Portal — ready (bearer expires 2026-05-15T06:43:21Z) [nous ] Nous Portal — ready (bearer expires 2026-05-15T06:43:21Z)
``` ```
If you see `not logged in`, run `hermes auth add nous`. If you see If you see `not logged in`, run `hermes portal`. If you see
`credentials need attention`, your refresh token was revoked (rare — `credentials need attention`, your refresh token was revoked (rare —
happens if you signed out from the Portal web UI) — just re-run happens if you signed out from the Portal web UI) — just re-run
`hermes auth add nous`. `hermes portal`.
## Allowed paths ## Allowed paths

View File

@ -60,12 +60,12 @@ hermes tools # Enable the gateway per-tool — pick "Nous Subscript
Check what's active at any time: Check what's active at any time:
```bash ```bash
hermes portal status # Portal auth + Tool Gateway routing summary hermes portal info # Portal auth + Tool Gateway routing summary
hermes portal tools # Gateway catalog with current routing per tool hermes portal tools # Gateway catalog with current routing per tool
hermes status # Full system status (Tool Gateway is one section) hermes status # Full system status (Tool Gateway is one section)
``` ```
`hermes portal status` shows a section like: `hermes portal info` shows a section like:
``` ```
◆ Nous Tool Gateway ◆ Nous Tool Gateway

View File

@ -57,7 +57,7 @@ hermes auth add nous --type oauth --manual-paste
## 3. 验证配置是否成功 ## 3. 验证配置是否成功
```bash ```bash
hermes portal status hermes portal info
``` ```
你应该看到: 你应该看到:
@ -175,12 +175,12 @@ hermes cron add "Daily AI news summary" "every day at 9am" \
## 故障排查 ## 故障排查
### 运行 `hermes setup --portal` 后,`hermes portal status` 显示"not logged in" ### 运行 `hermes setup --portal` 后,`hermes portal info` 显示"not logged in"
OAuth 流程未完成。重新运行: OAuth 流程未完成。重新运行:
```bash ```bash
hermes auth add nous --type oauth hermes portal
``` ```
如果浏览器未打开或回调失败,你可能在远程/无头主机上——参见 [OAuth over SSH](/guides/oauth-over-ssh) 了解端口转发和手动粘贴的解决方案。 如果浏览器未打开或回调失败,你可能在远程/无头主机上——参见 [OAuth over SSH](/guides/oauth-over-ssh) 了解端口转发和手动粘贴的解决方案。
@ -200,7 +200,7 @@ hermes model
# 选择 Nous Portal # 选择 Nous Portal
``` ```
使用 `hermes portal status` 重新验证。 使用 `hermes portal info` 重新验证。
### Tool Gateway 工具显示合作方名称而非"via Nous Portal" ### Tool Gateway 工具显示合作方名称而非"via Nous Portal"
@ -236,7 +236,7 @@ Portal 目录镜像了 OpenRouter 的模型列表300+ 个)。如果某个
### 账单未出现在我的 Portal 账号中 ### 账单未出现在我的 Portal 账号中
`hermes portal status` 会告诉你是否真的在通过 Portal 路由,还是使用了其他 provider。常见原因 `hermes portal info` 会告诉你是否真的在通过 Portal 路由,还是使用了其他 provider。常见原因
- `model.provider` 设置为 `openrouter`/`anthropic`/等,而非 `nous` - `model.provider` 设置为 `openrouter`/`anthropic`/等,而非 `nous`
- OAuth refresh 失败后回退到了其他已配置的 provider - OAuth refresh 失败后回退到了其他已配置的 provider

View File

@ -126,12 +126,15 @@ OAuth 需要浏览器,但回调的 loopback 运行在 Hermes 所在的机器
### 查看当前配置状态 ### 查看当前配置状态
```bash ```bash
hermes portal status # 登录状态、订阅信息、模型与 gateway 路由 hermes portal # 登录 Nous Portal 并完成配置(一键引导)
hermes portal info # 登录状态、订阅信息、模型与 gateway 路由
hermes portal tools # 详细的 Tool Gateway 目录及每个工具的路由信息 hermes portal tools # 详细的 Tool Gateway 目录及每个工具的路由信息
hermes portal open # 在浏览器中打开订阅管理页面 hermes portal open # 在浏览器中打开订阅管理页面
``` ```
`hermes portal status`(或直接 `hermes portal`)给出高层概览: `hermes portal`(不带子命令)是 `hermes auth add nous --type oauth` 的易记别名——它会登录、把 Nous 设为推理服务商,并提供 Tool Gateway 启用选项(与 `hermes setup --portal` 等价)。
`hermes portal info` 给出高层概览:
``` ```
Nous Portal Nous Portal
@ -230,12 +233,12 @@ Hermes 在每次推理调用时从存储的 Portal refresh token 生成短期 JW
## 故障排查 ## 故障排查
### `hermes portal status` 显示"not logged in" ### `hermes portal info` 显示"not logged in"
你尚未完成 OAuth 流程,或 refresh token 已被清除。运行: 你尚未完成 OAuth 流程,或 refresh token 已被清除。运行:
```bash ```bash
hermes auth add nous --type oauth hermes portal
``` ```
或使用 `hermes model` 重新选择 Nous Portal。 或使用 `hermes model` 重新选择 Nous Portal。
@ -256,7 +259,7 @@ Portal 通过 OpenRouter 代理,因此 OpenRouter 支持的所有模型通常
### 账单未出现在我的 Portal 账号中 ### 账单未出现在我的 Portal 账号中
先检查 `hermes portal status`——如果显示你正在使用其他提供商(`Model: currently openrouter` 而非 `using Nous as inference provider`),说明本地配置已偏离。运行 `hermes model`,选择 Nous Portal下一次请求将通过你的订阅路由。 先检查 `hermes portal info`——如果显示你正在使用其他提供商(`Model: currently openrouter` 而非 `using Nous as inference provider`),说明本地配置已偏离。运行 `hermes model`,选择 Nous Portal下一次请求将通过你的订阅路由。
## 另请参阅 ## 另请参阅

View File

@ -58,7 +58,7 @@ sidebar_position: 1
```bash ```bash
hermes setup --portal # 全新安装——一条命令完成 OAuth + 提供商 + 网关配置 hermes setup --portal # 全新安装——一条命令完成 OAuth + 提供商 + 网关配置
hermes model # 已有安装——从列表中选择"Nous Portal" hermes model # 已有安装——从列表中选择"Nous Portal"
hermes portal status # 随时查看登录状态和路由信息 hermes portal info # 随时查看登录状态和路由信息
``` ```
还没有订阅?前往 [portal.nousresearch.com/manage-subscription](https://portal.nousresearch.com/manage-subscription) 购买。 还没有订阅?前往 [portal.nousresearch.com/manage-subscription](https://portal.nousresearch.com/manage-subscription) 购买。
@ -77,7 +77,7 @@ OpenAI Codex 提供商通过设备码device code认证——打开一个 U
::: :::
:::tip Nous Tool Gateway :::tip Nous Tool Gateway
付费 Nous Portal 订阅者还可访问 **[Tool Gateway](/user-guide/features/tool-gateway)**——网页搜索、图像生成、TTS 和浏览器自动化,均通过你的订阅路由。无需额外 API key。全新安装时`hermes setup --portal` 一条命令即可完成登录、设置 Nous 为提供商并开启网关。现有用户可通过 `hermes model``hermes tools` 按工具启用。随时使用 `hermes portal status` 查看路由状态。 付费 Nous Portal 订阅者还可访问 **[Tool Gateway](/user-guide/features/tool-gateway)**——网页搜索、图像生成、TTS 和浏览器自动化,均通过你的订阅路由。无需额外 API key。全新安装时`hermes setup --portal` 一条命令即可完成登录、设置 Nous 为提供商并开启网关。现有用户可通过 `hermes model``hermes tools` 按工具启用。随时使用 `hermes portal info` 查看路由状态。
::: :::
### 模型管理的两个命令 ### 模型管理的两个命令

View File

@ -12,7 +12,7 @@ Hermes 使用两类模型槽位:
本页介绍如何通过仪表板配置上述两类模型。如需使用配置文件或 CLI请跳至底部的[其他方法](#alternative-methods)。 本页介绍如何通过仪表板配置上述两类模型。如需使用配置文件或 CLI请跳至底部的[其他方法](#alternative-methods)。
:::tip 最快路径Nous Portal :::tip 最快路径Nous Portal
[Nous Portal](/user-guide/features/tool-gateway) 在单一订阅下提供 300+ 个模型。全新安装后,运行 `hermes setup --portal` 即可登录并一键将 Nous 设为提供商。使用 `hermes portal status` 查看当前配置。 [Nous Portal](/user-guide/features/tool-gateway) 在单一订阅下提供 300+ 个模型。全新安装后,运行 `hermes setup --portal` 即可登录并一键将 Nous 设为提供商。使用 `hermes portal info` 查看当前配置。
::: :::
## Models 页面 ## Models 页面

View File

@ -24,7 +24,7 @@ description: "将你的 Nous Portal 订阅(或其他 OAuth 提供商)用作
### 1. 登录你的提供商(仅需一次) ### 1. 登录你的提供商(仅需一次)
```bash ```bash
hermes auth add nous hermes portal
``` ```
这会打开浏览器进行 Nous Portal OAuth 流程。Hermes 将刷新令牌存储在 `~/.hermes/auth.json` 中——与所有 Hermes 提供商登录信息存放在同一位置。 这会打开浏览器进行 Nous Portal OAuth 流程。Hermes 将刷新令牌存储在 `~/.hermes/auth.json` 中——与所有 Hermes 提供商登录信息存放在同一位置。
@ -76,7 +76,7 @@ Hermes proxy upstream adapters
[nous ] Nous Portal — ready (bearer expires 2026-05-15T06:43:21Z) [nous ] Nous Portal — ready (bearer expires 2026-05-15T06:43:21Z)
``` ```
如果显示 `not logged in`,请运行 `hermes auth add nous`。如果显示 `credentials need attention`,说明你的刷新令牌已被撤销(较少见——通常发生在你从 Portal Web UI 退出登录时)——重新运行 `hermes auth add nous` 即可。 如果显示 `not logged in`,请运行 `hermes portal`。如果显示 `credentials need attention`,说明你的刷新令牌已被撤销(较少见——通常发生在你从 Portal Web UI 退出登录时)——重新运行 `hermes portal` 即可。
## 允许的路径 ## 允许的路径