mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-09 08:21:50 +00:00
refactor(photon): fold device login into setup, drop standalone login verb
Every other Hermes gateway channel onboards through a single setup surface (paste a token / run the wizard) with no per-platform login command. Photon's device-code flow is unavoidable because Photon mints credentials via API rather than a copy-paste dashboard field, but exposing it as a top-level `hermes photon login` verb broke channel parity. - Remove the `login` subcommand; setup already runs the device flow as its first step. `--no-browser` moves onto `setup`. - Rename `_cmd_login` -> `_run_device_login` (internal helper). - Status / credential-summary hints now point at `hermes photon setup`. - README updated to the one-command onboarding flow.
This commit is contained in:
parent
8f89c4615f
commit
630318e958
3 changed files with 29 additions and 21 deletions
|
|
@ -46,25 +46,29 @@ plugin stays the same.
|
|||
## First-time setup
|
||||
|
||||
```bash
|
||||
# 1. Log in via the device-code flow (opens browser)
|
||||
hermes photon login
|
||||
|
||||
# 2. Full setup: project, user, sidecar deps
|
||||
# 1. One-shot setup: device login (opens browser) + project + user + sidecar deps
|
||||
hermes photon setup --phone +15551234567
|
||||
|
||||
# 3. Expose your webhook URL to the public internet
|
||||
# 2. Expose your webhook URL to the public internet
|
||||
# (cloudflared, ngrok, your gateway's public hostname, etc.)
|
||||
# Then register it with Photon:
|
||||
hermes photon webhook register https://your-host.example.com/photon/webhook
|
||||
|
||||
# 4. Save the signing secret it prints to ~/.hermes/.env
|
||||
# 3. Save the signing secret it prints to ~/.hermes/.env
|
||||
# as PHOTON_WEBHOOK_SECRET=...
|
||||
# Photon only returns it ONCE.
|
||||
|
||||
# 5. Start the gateway
|
||||
# 4. Start the gateway
|
||||
hermes gateway start --platform photon
|
||||
```
|
||||
|
||||
`hermes photon setup` runs the RFC 8628 device-code login as its first
|
||||
step — it opens `https://app.photon.codes/` for approval, then
|
||||
provisions the Spectrum project + iMessage line. There is no separate
|
||||
`login` command; like every other Hermes channel, onboarding goes
|
||||
through one setup surface. Re-running `setup` reuses an existing token
|
||||
and project, so it's safe to run again to finish a partial setup.
|
||||
|
||||
## Credentials
|
||||
|
||||
Stored in `~/.hermes/auth.json` under `credential_pool`:
|
||||
|
|
|
|||
|
|
@ -448,7 +448,7 @@ def print_credential_summary(emit: Any = print) -> None:
|
|||
if load_photon_token():
|
||||
labels["device_token"] = "✓ stored"
|
||||
else:
|
||||
labels["device_token"] = "✗ missing (run `hermes photon login`)"
|
||||
labels["device_token"] = "✗ missing (run `hermes photon setup`)"
|
||||
pid, sec = load_project_credentials()
|
||||
labels["project_id"] = pid if pid else "✗ missing"
|
||||
labels["project_key"] = "✓ stored" if sec else "✗ missing"
|
||||
|
|
@ -477,7 +477,7 @@ def credential_summary() -> Dict[str, str]:
|
|||
function — read-and-bool-cast happens entirely inside the closure.
|
||||
"""
|
||||
def _present_token() -> str:
|
||||
return "✓ stored" if load_photon_token() else "✗ missing (run `hermes photon login`)"
|
||||
return "✓ stored" if load_photon_token() else "✗ missing (run `hermes photon setup`)"
|
||||
|
||||
def _present_project_id() -> str:
|
||||
pid, _sec = load_project_credentials()
|
||||
|
|
|
|||
|
|
@ -4,13 +4,16 @@
|
|||
|
||||
Subcommands:
|
||||
|
||||
login run the device-code OAuth flow
|
||||
setup full first-time setup (login + project + user + sidecar)
|
||||
setup full first-time setup (device login + project + user + sidecar)
|
||||
status show login + project + sidecar dep state
|
||||
install-sidecar npm install inside plugins/platforms/photon/sidecar/
|
||||
webhook register register the local webhook URL with Photon
|
||||
webhook list list registered webhooks
|
||||
webhook delete delete a webhook by id
|
||||
|
||||
The device-code login runs automatically as the first step of ``setup``;
|
||||
there is no standalone ``login`` verb (matching how every other Hermes
|
||||
gateway channel onboards through a single setup surface).
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
|
|
@ -35,17 +38,14 @@ def register_cli(parser: argparse.ArgumentParser) -> None:
|
|||
"""Wire up `hermes photon ...` subcommands."""
|
||||
subs = parser.add_subparsers(dest="photon_command", required=False)
|
||||
|
||||
p_login = subs.add_parser("login", help="Authenticate with Photon (device flow)")
|
||||
p_login.add_argument("--no-browser", action="store_true",
|
||||
help="Don't try to open a browser; print the URL only")
|
||||
|
||||
p_setup = subs.add_parser("setup", help="First-time setup (login + project + user + sidecar)")
|
||||
p_setup = subs.add_parser("setup", help="First-time setup (device login + project + user + sidecar)")
|
||||
p_setup.add_argument("--project-name", default=None, help="Project name (default: 'Hermes Agent')")
|
||||
p_setup.add_argument("--phone", default=None, help="Your E.164 phone number (e.g. +15551234567)")
|
||||
p_setup.add_argument("--first-name", default=None)
|
||||
p_setup.add_argument("--last-name", default=None)
|
||||
p_setup.add_argument("--email", default=None)
|
||||
p_setup.add_argument("--no-browser", action="store_true")
|
||||
p_setup.add_argument("--no-browser", action="store_true",
|
||||
help="Don't try to open a browser for device login; print the URL only")
|
||||
p_setup.add_argument("--skip-sidecar-install", action="store_true",
|
||||
help="Skip `npm install` inside the sidecar directory")
|
||||
|
||||
|
|
@ -71,8 +71,6 @@ def dispatch(args: argparse.Namespace) -> int:
|
|||
if sub is None:
|
||||
# No subcommand given — show status by default.
|
||||
return _cmd_status(args)
|
||||
if sub == "login":
|
||||
return _cmd_login(args)
|
||||
if sub == "setup":
|
||||
return _cmd_setup(args)
|
||||
if sub == "status":
|
||||
|
|
@ -88,7 +86,13 @@ def dispatch(args: argparse.Namespace) -> int:
|
|||
# ---------------------------------------------------------------------------
|
||||
# Subcommand handlers
|
||||
|
||||
def _cmd_login(args: argparse.Namespace) -> int:
|
||||
def _run_device_login(args: argparse.Namespace) -> int:
|
||||
"""Run the RFC 8628 device-code login flow and persist the token.
|
||||
|
||||
Internal helper — invoked as the first step of ``setup``. There is
|
||||
no standalone ``hermes photon login`` command; Photon onboards
|
||||
through the single ``setup`` surface like every other channel.
|
||||
"""
|
||||
def _print_code(code):
|
||||
target = code.verification_uri_complete or code.verification_uri
|
||||
print()
|
||||
|
|
@ -119,7 +123,7 @@ def _cmd_setup(args: argparse.Namespace) -> int:
|
|||
token = photon_auth.load_photon_token()
|
||||
if not token:
|
||||
print("[1/4] No Photon token found — running device login...")
|
||||
rc = _cmd_login(args)
|
||||
rc = _run_device_login(args)
|
||||
if rc != 0:
|
||||
return rc
|
||||
token = photon_auth.load_photon_token()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue