feat: improve health checks (telegram tokenFile + hints)

This commit is contained in:
Josh Palmer
2025-12-20 21:32:17 +01:00
parent b7363f7c18
commit 5d19afd422
4 changed files with 120 additions and 5 deletions

View File

@@ -1,3 +1,7 @@
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { afterEach, describe, expect, it, vi } from "vitest";
import type { HealthSummary } from "./health.js";
@@ -99,6 +103,57 @@ describe("getHealthSnapshot", () => {
expect(calls.some((c) => c.includes("/getWebhookInfo"))).toBe(true);
});
it("treats telegram.tokenFile as configured", async () => {
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "clawdis-health-"));
const tokenFile = path.join(tmpDir, "telegram-token");
fs.writeFileSync(tokenFile, "t-file\n", "utf-8");
testConfig = { telegram: { tokenFile } };
testStore = {};
const calls: string[] = [];
vi.stubGlobal(
"fetch",
vi.fn(async (url: string) => {
calls.push(url);
if (url.includes("/getMe")) {
return {
ok: true,
status: 200,
json: async () => ({
ok: true,
result: { id: 1, username: "bot" },
}),
} as unknown as Response;
}
if (url.includes("/getWebhookInfo")) {
return {
ok: true,
status: 200,
json: async () => ({
ok: true,
result: {
url: "https://example.com/h",
has_custom_certificate: false,
},
}),
} as unknown as Response;
}
return {
ok: false,
status: 404,
json: async () => ({ ok: false, description: "nope" }),
} as unknown as Response;
}),
);
const snap = await getHealthSnapshot(25);
expect(snap.telegram.configured).toBe(true);
expect(snap.telegram.probe?.ok).toBe(true);
expect(calls.some((c) => c.includes("bott-file/getMe"))).toBe(true);
fs.rmSync(tmpDir, { recursive: true, force: true });
});
it("returns a structured telegram probe error when getMe fails", async () => {
testConfig = { telegram: { botToken: "bad-token" } };
testStore = {};

View File

@@ -1,3 +1,5 @@
import fs from "node:fs";
import { loadConfig } from "../config/config.js";
import { loadSessionStore, resolveStorePath } from "../config/sessions.js";
import { type DiscordProbe, probeDiscord } from "../discord/probe.js";
@@ -53,6 +55,25 @@ export type HealthSummary = {
const DEFAULT_TIMEOUT_MS = 10_000;
function loadTelegramToken(cfg: ReturnType<typeof loadConfig>): string {
const env = process.env.TELEGRAM_BOT_TOKEN?.trim();
if (env) return env;
const tokenFile = cfg.telegram?.tokenFile?.trim();
if (tokenFile) {
try {
if (fs.existsSync(tokenFile)) {
const token = fs.readFileSync(tokenFile, "utf-8").trim();
if (token) return token;
}
} catch {
// Ignore errors; health should be non-fatal.
}
}
return cfg.telegram?.botToken?.trim() ?? "";
}
export async function getHealthSnapshot(
timeoutMs?: number,
): Promise<HealthSummary> {
@@ -74,8 +95,7 @@ export async function getHealthSnapshot(
const start = Date.now();
const cappedTimeout = Math.max(1000, timeoutMs ?? DEFAULT_TIMEOUT_MS);
const telegramToken =
process.env.TELEGRAM_BOT_TOKEN ?? cfg.telegram?.botToken ?? "";
const telegramToken = loadTelegramToken(cfg);
const telegramConfigured = telegramToken.trim().length > 0;
const telegramProxy = cfg.telegram?.proxy;
const telegramProbe = telegramConfigured

View File

@@ -1,3 +1,4 @@
import fs from "node:fs";
import chalk from "chalk";
import { type ClawdisConfig, loadConfig } from "../config/config.js";
import { normalizeE164 } from "../utils.js";
@@ -36,8 +37,12 @@ export async function buildProviderSummary(
} else {
const telegramToken =
process.env.TELEGRAM_BOT_TOKEN ?? effective.telegram?.botToken;
const telegramTokenFile = effective.telegram?.tokenFile?.trim();
const telegramConfigured =
Boolean(telegramToken) ||
Boolean(telegramTokenFile ? fs.existsSync(telegramTokenFile) : false);
lines.push(
telegramToken
telegramConfigured
? chalk.green("Telegram: configured")
: chalk.cyan("Telegram: not configured"),
);