mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-10 05:12:43 +00:00
Mac: launch gateway and add relay installer
This commit is contained in:
@@ -4,6 +4,7 @@ import path from "node:path";
|
||||
import { loadConfig } from "../config/config.js";
|
||||
import { loadSessionStore, resolveStorePath } from "../config/sessions.js";
|
||||
import { info } from "../globals.js";
|
||||
import { makeProxyFetch } from "../telegram/proxy.js";
|
||||
import type { RuntimeEnv } from "../runtime.js";
|
||||
import { resolveHeartbeatSeconds } from "../web/reconnect.js";
|
||||
import {
|
||||
@@ -22,6 +23,15 @@ type HealthConnect = {
|
||||
elapsedMs: number;
|
||||
};
|
||||
|
||||
type TelegramProbe = {
|
||||
ok: boolean;
|
||||
status?: number | null;
|
||||
error?: string | null;
|
||||
elapsedMs: number;
|
||||
bot?: { id?: number | null; username?: string | null };
|
||||
webhook?: { url?: string | null; hasCustomCert?: boolean | null };
|
||||
};
|
||||
|
||||
export type HealthSummary = {
|
||||
ts: number;
|
||||
durationMs: number;
|
||||
@@ -30,6 +40,10 @@ export type HealthSummary = {
|
||||
authAgeMs: number | null;
|
||||
connect?: HealthConnect;
|
||||
};
|
||||
telegram: {
|
||||
configured: boolean;
|
||||
probe?: TelegramProbe;
|
||||
};
|
||||
heartbeatSeconds: number;
|
||||
sessions: {
|
||||
path: string;
|
||||
@@ -44,6 +58,7 @@ export type HealthSummary = {
|
||||
};
|
||||
|
||||
const DEFAULT_TIMEOUT_MS = 10_000;
|
||||
const TELEGRAM_API_BASE = "https://api.telegram.org";
|
||||
|
||||
async function probeWebConnect(timeoutMs: number): Promise<HealthConnect> {
|
||||
const started = Date.now();
|
||||
@@ -77,6 +92,93 @@ async function probeWebConnect(timeoutMs: number): Promise<HealthConnect> {
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchWithTimeout(
|
||||
url: string,
|
||||
timeoutMs: number,
|
||||
fetcher: typeof fetch,
|
||||
): Promise<Response> {
|
||||
const controller = new AbortController();
|
||||
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
||||
try {
|
||||
return await fetcher(url, { signal: controller.signal });
|
||||
} finally {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
}
|
||||
|
||||
async function probeTelegram(
|
||||
token: string,
|
||||
timeoutMs: number,
|
||||
proxyUrl?: string,
|
||||
): Promise<TelegramProbe> {
|
||||
const started = Date.now();
|
||||
const fetcher = proxyUrl ? makeProxyFetch(proxyUrl) : fetch;
|
||||
const base = `${TELEGRAM_API_BASE}/bot${token}`;
|
||||
|
||||
const result: TelegramProbe = {
|
||||
ok: false,
|
||||
status: null,
|
||||
error: null,
|
||||
elapsedMs: 0,
|
||||
};
|
||||
|
||||
try {
|
||||
const meRes = await fetchWithTimeout(`${base}/getMe`, timeoutMs, fetcher);
|
||||
const meJson = (await meRes.json()) as {
|
||||
ok?: boolean;
|
||||
description?: string;
|
||||
result?: { id?: number; username?: string };
|
||||
};
|
||||
if (!meRes.ok || !meJson?.ok) {
|
||||
result.status = meRes.status;
|
||||
result.error = meJson?.description ?? `getMe failed (${meRes.status})`;
|
||||
return { ...result, elapsedMs: Date.now() - started };
|
||||
}
|
||||
|
||||
result.bot = {
|
||||
id: meJson.result?.id ?? null,
|
||||
username: meJson.result?.username ?? null,
|
||||
};
|
||||
|
||||
// Try to fetch webhook info, but don't fail health if it errors
|
||||
try {
|
||||
const webhookRes = await fetchWithTimeout(
|
||||
`${base}/getWebhookInfo`,
|
||||
timeoutMs,
|
||||
fetcher,
|
||||
);
|
||||
const webhookJson = (await webhookRes.json()) as {
|
||||
ok?: boolean;
|
||||
result?: {
|
||||
url?: string;
|
||||
has_custom_certificate?: boolean;
|
||||
};
|
||||
};
|
||||
if (webhookRes.ok && webhookJson?.ok) {
|
||||
result.webhook = {
|
||||
url: webhookJson.result?.url ?? null,
|
||||
hasCustomCert: webhookJson.result?.has_custom_certificate ?? null,
|
||||
};
|
||||
}
|
||||
} catch {
|
||||
// ignore webhook errors for health
|
||||
}
|
||||
|
||||
result.ok = true;
|
||||
result.status = null;
|
||||
result.error = null;
|
||||
result.elapsedMs = Date.now() - started;
|
||||
return result;
|
||||
} catch (err) {
|
||||
return {
|
||||
...result,
|
||||
status: err instanceof Response ? err.status : result.status,
|
||||
error: err instanceof Error ? err.message : String(err),
|
||||
elapsedMs: Date.now() - started,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export async function getHealthSnapshot(
|
||||
timeoutMs?: number,
|
||||
): Promise<HealthSummary> {
|
||||
@@ -103,10 +205,19 @@ export async function getHealthSnapshot(
|
||||
const cappedTimeout = Math.max(1000, timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
||||
const connect = linked ? await probeWebConnect(cappedTimeout) : undefined;
|
||||
|
||||
const telegramToken =
|
||||
process.env.TELEGRAM_BOT_TOKEN ?? cfg.telegram?.botToken ?? "";
|
||||
const telegramConfigured = telegramToken.trim().length > 0;
|
||||
const telegramProxy = cfg.telegram?.proxy;
|
||||
const telegramProbe = telegramConfigured
|
||||
? await probeTelegram(telegramToken.trim(), cappedTimeout, telegramProxy)
|
||||
: undefined;
|
||||
|
||||
const summary: HealthSummary = {
|
||||
ts: Date.now(),
|
||||
durationMs: Date.now() - start,
|
||||
web: { linked, authAgeMs, connect },
|
||||
telegram: { configured: telegramConfigured, probe: telegramProbe },
|
||||
heartbeatSeconds,
|
||||
sessions: {
|
||||
path: storePath,
|
||||
@@ -125,7 +236,11 @@ export async function healthCommand(
|
||||
) {
|
||||
const summary = await getHealthSnapshot(opts.timeoutMs);
|
||||
const fatal =
|
||||
!summary.web.linked || (summary.web.connect && !summary.web.connect.ok);
|
||||
!summary.web.linked ||
|
||||
(summary.web.connect && !summary.web.connect.ok) ||
|
||||
(summary.telegram.configured &&
|
||||
summary.telegram.probe &&
|
||||
!summary.telegram.probe.ok);
|
||||
|
||||
if (opts.json) {
|
||||
runtime.log(JSON.stringify(summary, null, 2));
|
||||
@@ -147,6 +262,19 @@ export async function healthCommand(
|
||||
(summary.web.connect.error ? ` - ${summary.web.connect.error}` : ""),
|
||||
);
|
||||
}
|
||||
|
||||
const tgLabel = summary.telegram.configured
|
||||
? summary.telegram.probe?.ok
|
||||
? info(
|
||||
`Telegram: ok${summary.telegram.probe.bot?.username ? ` (@${summary.telegram.probe.bot.username})` : ""} (${summary.telegram.probe.elapsedMs}ms)` +
|
||||
(summary.telegram.probe.webhook?.url
|
||||
? ` - webhook ${summary.telegram.probe.webhook.url}`
|
||||
: ""),
|
||||
)
|
||||
: `Telegram: failed (${summary.telegram.probe?.status ?? "unknown"})${summary.telegram.probe?.error ? ` - ${summary.telegram.probe.error}` : ""}`
|
||||
: "Telegram: not configured";
|
||||
runtime.log(tgLabel);
|
||||
|
||||
runtime.log(info(`Heartbeat interval: ${summary.heartbeatSeconds}s`));
|
||||
runtime.log(
|
||||
info(
|
||||
|
||||
Reference in New Issue
Block a user