mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-29 18:46:59 +00:00
fix(plugins/photon): clear stale token and re-enable channel after setup (#72763)
Two related bugs in hermes photon setup / gateway_setup:
Bug 1 -- stale token reused (401)
----------------------------------
_cmd_setup reused an existing dashboard token without validation.
The device token has a short TTL (~3-4 days observed); reusing a
stale token caused every management API call (find_project_by_name,
regenerate_project_secret, etc.) to fail with 401. The operator
saw confusing "spectrum provisioning failed: 401" errors.
Fix: check GET /api/auth/get-session before using the stored token.
On 401/403, clear the stale token with clear_photon_token() and
fall back to a fresh device-login flow automatically.
Bug 2 -- channel left disabled after successful setup
-----------------------------------------------------
After all five provisioning steps completed, config.yaml still had
photon.enabled: false, so the gateway never loaded the Photon
adapter. Every inbound iMessage hit Photon's offline auto-responder
without the operator being notified.
Fix: call write_platform_config_field('photon', 'enabled', True,
raw=True) as a final setup step so the gateway picks up the freshly
configured channel on its next start.
New public API in auth.py:
- clear_photon_token() -- discard stored token from auth.json
- check_photon_token_valid(token) -- lightweight session-check test
References: #72763
This commit is contained in:
parent
b378bc6fab
commit
cd158466d1
2 changed files with 65 additions and 2 deletions
|
|
@ -180,6 +180,45 @@ def store_photon_token(token: str) -> None:
|
|||
_save_auth(auth)
|
||||
|
||||
|
||||
def clear_photon_token() -> None:
|
||||
"""Remove any stored Photon dashboard token from auth.json.
|
||||
|
||||
Used to discard a stale/expired token before re-authentication.
|
||||
"""
|
||||
auth = _load_auth()
|
||||
pool = auth.get("credential_pool", {})
|
||||
photon = pool.get("photon", [])
|
||||
if isinstance(photon, list) and photon:
|
||||
pool["photon"] = []
|
||||
_save_auth(auth)
|
||||
# Also clear the legacy shape if present.
|
||||
providers = auth.get("providers", {})
|
||||
if "photon" in providers:
|
||||
providers["photon"] = {}
|
||||
_save_auth(auth)
|
||||
|
||||
|
||||
def check_photon_token_valid(token: str) -> bool:
|
||||
"""Return True if the token is accepted by the dashboard API.
|
||||
|
||||
Makes a lightweight ``GET /api/auth/get-session`` call. A non-401
|
||||
response (including non-auth errors like network blips) is treated as
|
||||
"probably valid" so transient failures don't force unnecessary re-login.
|
||||
Only a definitive 401 / 403 is treated as stale.
|
||||
"""
|
||||
if not token:
|
||||
return False
|
||||
try:
|
||||
resp = _dashboard_get("/api/auth/get-session", token)
|
||||
if resp.status_code in (401, 403):
|
||||
return False
|
||||
return True
|
||||
except Exception:
|
||||
# Transient error — don't force a re-auth; let the caller's
|
||||
# management-call error propagate if the token really is bad.
|
||||
return True
|
||||
|
||||
|
||||
def load_project_credentials() -> Tuple[Optional[str], Optional[str]]:
|
||||
"""Return the runtime SDK creds ``(spectrum_project_id, project_secret)``.
|
||||
|
||||
|
|
|
|||
|
|
@ -127,10 +127,23 @@ def _run_device_login(args: argparse.Namespace) -> int:
|
|||
|
||||
|
||||
def _cmd_setup(args: argparse.Namespace) -> int:
|
||||
# 1. Login (skip if we already have a token).
|
||||
# 1. Login (skip if we already have a valid token).
|
||||
token = photon_auth.load_photon_token()
|
||||
if token:
|
||||
# Validate the existing token — the dashboard token has a short TTL
|
||||
# and can go stale between runs (observed: ~3-4 days). Reusing a
|
||||
# stale token causes every management call to fail with 401 and
|
||||
# leaves the operator confused about why setup "succeeds" but nothing
|
||||
# works. Check upfront so we fail fast and fall back to fresh login.
|
||||
print("[1/5] Checking existing Photon token...")
|
||||
if photon_auth.check_photon_token_valid(token):
|
||||
print(" ✓ token is valid")
|
||||
else:
|
||||
print(" ✗ token is stale (dashboard rejected it) — re-authenticating")
|
||||
photon_auth.clear_photon_token()
|
||||
token = None
|
||||
if not token:
|
||||
print("[1/5] No Photon token found — running device login...")
|
||||
print("[1/5] No valid Photon token found — running device login...")
|
||||
rc = _run_device_login(args)
|
||||
if rc != 0:
|
||||
return rc
|
||||
|
|
@ -270,6 +283,17 @@ def _cmd_setup(args: argparse.Namespace) -> int:
|
|||
if rc != 0:
|
||||
return rc
|
||||
|
||||
# 7. Ensure the photon platform is enabled in config.yaml so the
|
||||
# gateway loads it on next start. Without this the channel stays
|
||||
# disabled even after a successful provisioning run, silently
|
||||
# keeping iMessage offline.
|
||||
try:
|
||||
from hermes_cli.config import write_platform_config_field
|
||||
write_platform_config_field("photon", "enabled", True, raw=True)
|
||||
print(" ✓ photon platform enabled in config.yaml")
|
||||
except Exception as e:
|
||||
print(f" (could not enable Photon in config: {e})", file=sys.stderr)
|
||||
|
||||
print()
|
||||
print("✓ Photon setup complete.")
|
||||
print(" Start the gateway: hermes gateway start")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue