UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

159 lines (158 loc) 5.44 kB
import { C as resolveExpiresAtMsFromDurationMs, j as resolveTimerTimeoutMs, o as asDateTimestampMs } from "./number-coercion-CJQ8TR--.js"; import { i as formatErrorMessage } from "./errors-BXgSefBE.js"; import "./error-runtime-C8vbtAJt.js"; import "./number-runtime-DBLVDypr.js"; import { r as createFeishuClient } from "./client-DmDgwXmZ.js"; //#region extensions/feishu/src/async.ts const RACE_TIMEOUT = Symbol("race-timeout"); const RACE_ABORT = Symbol("race-abort"); async function raceWithTimeoutAndAbort(promise, options = {}) { if (options.abortSignal?.aborted) return { status: "aborted" }; if (options.timeoutMs === void 0 && !options.abortSignal) return { status: "resolved", value: await promise }; let timeoutHandle; let abortHandler; const contenders = [promise]; if (options.timeoutMs !== void 0) { const timeoutMs = resolveTimerTimeoutMs(options.timeoutMs, 1); contenders.push(new Promise((resolve) => { timeoutHandle = setTimeout(() => resolve(RACE_TIMEOUT), timeoutMs); })); } if (options.abortSignal) contenders.push(new Promise((resolve) => { abortHandler = () => resolve(RACE_ABORT); options.abortSignal?.addEventListener("abort", abortHandler, { once: true }); })); try { const result = await Promise.race(contenders); if (result === RACE_TIMEOUT) return { status: "timeout" }; if (result === RACE_ABORT) return { status: "aborted" }; return { status: "resolved", value: result }; } finally { if (timeoutHandle) clearTimeout(timeoutHandle); if (abortHandler) options.abortSignal?.removeEventListener("abort", abortHandler); } } function waitForAbortableDelay(delayMs, abortSignal) { if (abortSignal?.aborted) return Promise.resolve(false); return new Promise((resolve) => { let settled = false; const finish = (value) => { if (settled) return; settled = true; if (timer) clearTimeout(timer); if (handleAbort) abortSignal?.removeEventListener("abort", handleAbort); resolve(value); }; const handleAbort = () => { finish(false); }; abortSignal?.addEventListener("abort", handleAbort, { once: true }); if (abortSignal?.aborted) { finish(false); return; } const timer = setTimeout(() => finish(true), resolveTimerTimeoutMs(delayMs, 1)); timer.unref?.(); }); } //#endregion //#region extensions/feishu/src/probe.ts /** Cache probe results to reduce repeated health-check calls. * Gateway health checks call probeFeishu() every minute; without caching this * burns ~43,200 calls/month, easily exceeding Feishu's free-tier quota. * Successful bot info is effectively static, while failures are cached briefly * to avoid hammering the API during transient outages. */ const probeCache = /* @__PURE__ */ new Map(); const PROBE_SUCCESS_TTL_MS = 600 * 1e3; const PROBE_ERROR_TTL_MS = 60 * 1e3; const MAX_PROBE_CACHE_SIZE = 64; const FEISHU_PROBE_REQUEST_TIMEOUT_MS = 1e4; function setCachedProbeResult(cacheKey, result, ttlMs) { const expiresAt = resolveExpiresAtMsFromDurationMs(ttlMs); if (expiresAt === void 0) { probeCache.delete(cacheKey); return result; } probeCache.set(cacheKey, { result, expiresAt }); if (probeCache.size > MAX_PROBE_CACHE_SIZE) { const oldest = probeCache.keys().next().value; if (oldest !== void 0) probeCache.delete(oldest); } return result; } async function probeFeishu(creds, options = {}) { if (!creds?.appId || !creds?.appSecret) return { ok: false, error: "missing credentials (appId, appSecret)" }; if (options.abortSignal?.aborted) return { ok: false, appId: creds.appId, error: "probe aborted" }; const timeoutMs = options.timeoutMs ?? 1e4; const cacheKey = creds.accountId ?? `${creds.appId}:${creds.appSecret.slice(0, 8)}`; const cached = probeCache.get(cacheKey); if (cached) { const now = asDateTimestampMs(Date.now()); const expiresAt = asDateTimestampMs(cached.expiresAt); if (now !== void 0 && expiresAt !== void 0 && expiresAt > now) return cached.result; probeCache.delete(cacheKey); } try { const responseResult = await raceWithTimeoutAndAbort(createFeishuClient(creds).request({ method: "POST", url: "/open-apis/bot/v1/openclaw_bot/ping", data: { needBotInfo: true }, timeout: timeoutMs }), { timeoutMs, abortSignal: options.abortSignal }); if (responseResult.status === "aborted") return { ok: false, appId: creds.appId, error: "probe aborted" }; if (responseResult.status === "timeout") return setCachedProbeResult(cacheKey, { ok: false, appId: creds.appId, error: `probe timed out after ${timeoutMs}ms` }, PROBE_ERROR_TTL_MS); const response = responseResult.value; if (options.abortSignal?.aborted) return { ok: false, appId: creds.appId, error: "probe aborted" }; if (response.code !== 0) return setCachedProbeResult(cacheKey, { ok: false, appId: creds.appId, error: `API error: ${response.msg || `code ${response.code}`}` }, PROBE_ERROR_TTL_MS); const botInfo = response.data?.pingBotInfo; return setCachedProbeResult(cacheKey, { ok: true, appId: creds.appId, botName: botInfo?.botName, botOpenId: botInfo?.botID }, PROBE_SUCCESS_TTL_MS); } catch (err) { return setCachedProbeResult(cacheKey, { ok: false, appId: creds.appId, error: formatErrorMessage(err) }, PROBE_ERROR_TTL_MS); } } //#endregion export { waitForAbortableDelay as i, probeFeishu as n, raceWithTimeoutAndAbort as r, FEISHU_PROBE_REQUEST_TIMEOUT_MS as t };