mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-06-12 08:51:53 +00:00
Auto-restart gateway after Telegram QR onboarding
This commit is contained in:
parent
298bb93d39
commit
984e69ff62
4 changed files with 124 additions and 10 deletions
|
|
@ -3748,6 +3748,29 @@ async def get_telegram_onboarding_status(pairing_id: str):
|
|||
)
|
||||
|
||||
|
||||
def _restart_gateway_after_telegram_onboarding() -> dict[str, Any]:
|
||||
"""Best-effort gateway restart after saving Telegram QR onboarding.
|
||||
|
||||
The QR flow naturally pulls users into Telegram on another device. If the
|
||||
saved token waits on a separate dashboard restart click, Hermes appears
|
||||
broken from the chat side. Keep the config save authoritative, but report
|
||||
restart failures so the UI can fall back to the existing manual banner.
|
||||
"""
|
||||
try:
|
||||
proc = _spawn_hermes_action(["gateway", "restart"], "gateway-restart")
|
||||
except Exception as exc:
|
||||
_log.exception("Failed to auto-restart gateway after Telegram onboarding")
|
||||
return {
|
||||
"restart_started": False,
|
||||
"restart_error": str(exc),
|
||||
}
|
||||
return {
|
||||
"restart_started": True,
|
||||
"restart_action": "gateway-restart",
|
||||
"restart_pid": proc.pid,
|
||||
}
|
||||
|
||||
|
||||
@app.post("/api/messaging/telegram/onboarding/{pairing_id}/apply")
|
||||
async def apply_telegram_onboarding(
|
||||
pairing_id: str, body: TelegramOnboardingApply
|
||||
|
|
@ -3802,11 +3825,14 @@ async def apply_telegram_onboarding(
|
|||
with _telegram_onboarding_lock:
|
||||
_telegram_onboarding_pairings.pop(pairing_id, None)
|
||||
|
||||
restart_result = _restart_gateway_after_telegram_onboarding()
|
||||
|
||||
return {
|
||||
"ok": True,
|
||||
"platform": "telegram",
|
||||
"bot_username": bot_username,
|
||||
"needs_restart": True,
|
||||
"needs_restart": not restart_result["restart_started"],
|
||||
**restart_result,
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1399,6 +1399,16 @@ class TestWebServerEndpoints:
|
|||
}
|
||||
|
||||
monkeypatch.setattr(ws, "_telegram_onboarding_request_sync", fake_request)
|
||||
restart_calls = []
|
||||
|
||||
class FakeRestartProc:
|
||||
pid = 4242
|
||||
|
||||
def fake_spawn_action(subcommand, name):
|
||||
restart_calls.append((subcommand, name))
|
||||
return FakeRestartProc()
|
||||
|
||||
monkeypatch.setattr(ws, "_spawn_hermes_action", fake_spawn_action)
|
||||
|
||||
start = self.client.post("/api/messaging/telegram/onboarding/start", json={})
|
||||
assert start.status_code == 200
|
||||
|
|
@ -1420,8 +1430,73 @@ class TestWebServerEndpoints:
|
|||
"ok": True,
|
||||
"platform": "telegram",
|
||||
"bot_username": "hermes_pair_ready_bot",
|
||||
"needs_restart": True,
|
||||
"needs_restart": False,
|
||||
"restart_started": True,
|
||||
"restart_action": "gateway-restart",
|
||||
"restart_pid": 4242,
|
||||
}
|
||||
assert restart_calls == [(["gateway", "restart"], "gateway-restart")]
|
||||
env = load_env()
|
||||
assert env["TELEGRAM_BOT_TOKEN"] == "123456:SECRET"
|
||||
assert env["TELEGRAM_ALLOWED_USERS"] == "123456789"
|
||||
assert load_config()["platforms"]["telegram"]["enabled"] is True
|
||||
|
||||
def test_telegram_onboarding_apply_reports_restart_failure_after_save(
|
||||
self, monkeypatch
|
||||
):
|
||||
import hermes_cli.web_server as ws
|
||||
from hermes_cli.config import load_config, load_env
|
||||
|
||||
with ws._telegram_onboarding_lock:
|
||||
ws._telegram_onboarding_pairings.clear()
|
||||
|
||||
def fake_request(method, path, *, body=None, bearer_token=None):
|
||||
if method == "POST":
|
||||
return {
|
||||
"pairing_id": "pair-restart-fails",
|
||||
"poll_token": "poll-secret",
|
||||
"suggested_username": "hermes_pair_restart_fails_bot",
|
||||
"deep_link": "https://t.me/newbot/HermesSetupBot/hermes_pair_restart_fails_bot",
|
||||
"qr_payload": "https://t.me/newbot/HermesSetupBot/hermes_pair_restart_fails_bot",
|
||||
"expires_at": "2027-05-18T00:00:00.000Z",
|
||||
}
|
||||
assert method == "GET"
|
||||
assert path == "/v1/telegram/pairings/pair-restart-fails"
|
||||
assert bearer_token == "poll-secret"
|
||||
return {
|
||||
"status": "ready",
|
||||
"bot_username": "hermes_pair_restart_fails_bot",
|
||||
"owner_user_id": 123456789,
|
||||
"token": "123456:SECRET",
|
||||
}
|
||||
|
||||
monkeypatch.setattr(ws, "_telegram_onboarding_request_sync", fake_request)
|
||||
|
||||
def fail_spawn_action(subcommand, name):
|
||||
assert subcommand == ["gateway", "restart"]
|
||||
assert name == "gateway-restart"
|
||||
raise RuntimeError("supervisor unavailable")
|
||||
|
||||
monkeypatch.setattr(ws, "_spawn_hermes_action", fail_spawn_action)
|
||||
|
||||
start = self.client.post("/api/messaging/telegram/onboarding/start", json={})
|
||||
assert start.status_code == 200
|
||||
ready = self.client.get("/api/messaging/telegram/onboarding/pair-restart-fails")
|
||||
assert ready.status_code == 200
|
||||
assert ready.json()["status"] == "ready"
|
||||
|
||||
applied = self.client.post(
|
||||
"/api/messaging/telegram/onboarding/pair-restart-fails/apply",
|
||||
json={"allowed_user_ids": ["123456789"]},
|
||||
)
|
||||
|
||||
assert applied.status_code == 200
|
||||
applied_data = applied.json()
|
||||
assert applied_data["ok"] is True
|
||||
assert applied_data["needs_restart"] is True
|
||||
assert applied_data["restart_started"] is False
|
||||
assert "supervisor unavailable" in applied_data["restart_error"]
|
||||
assert "token" not in applied_data
|
||||
env = load_env()
|
||||
assert env["TELEGRAM_BOT_TOKEN"] == "123456:SECRET"
|
||||
assert env["TELEGRAM_ALLOWED_USERS"] == "123456789"
|
||||
|
|
|
|||
|
|
@ -1475,7 +1475,11 @@ export interface TelegramOnboardingApplyResponse {
|
|||
ok: boolean;
|
||||
platform: "telegram";
|
||||
bot_username?: string;
|
||||
needs_restart: true;
|
||||
needs_restart: boolean;
|
||||
restart_started?: boolean;
|
||||
restart_action?: string;
|
||||
restart_pid?: number | null;
|
||||
restart_error?: string;
|
||||
}
|
||||
|
||||
export interface SessionMessage {
|
||||
|
|
|
|||
|
|
@ -608,19 +608,28 @@ function TelegramOnboardingPanel({
|
|||
setPhase("applying");
|
||||
setError("");
|
||||
try {
|
||||
await api.applyTelegramOnboarding(setup.pairing_id, {
|
||||
const result = await api.applyTelegramOnboarding(setup.pairing_id, {
|
||||
allowed_user_ids: allowedIds,
|
||||
});
|
||||
resetSetup();
|
||||
showToast("Telegram saved", "success");
|
||||
try {
|
||||
await api.restartGateway();
|
||||
showToast("Gateway restarting…", "success");
|
||||
if (result.restart_started) {
|
||||
showToast("Telegram saved; gateway restarting…", "success");
|
||||
setRestartNeeded(false);
|
||||
setTimeout(() => void onChanged(), 4000);
|
||||
} catch (restartError) {
|
||||
} else if (result.restart_started === undefined && result.needs_restart) {
|
||||
try {
|
||||
await api.restartGateway();
|
||||
showToast("Telegram saved; gateway restarting…", "success");
|
||||
setRestartNeeded(false);
|
||||
setTimeout(() => void onChanged(), 4000);
|
||||
} catch (restartError) {
|
||||
onRestartNeeded();
|
||||
showToast(`Telegram saved; gateway restart failed: ${restartError}`, "error");
|
||||
}
|
||||
} else {
|
||||
onRestartNeeded();
|
||||
showToast(`Telegram saved; restart failed: ${restartError}`, "error");
|
||||
const detail = result.restart_error ? `: ${result.restart_error}` : "";
|
||||
showToast(`Telegram saved; gateway restart failed${detail}`, "error");
|
||||
}
|
||||
await onChanged();
|
||||
} catch (applyError) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue