export const OPENCLAW_CHAT_CONFIG_KEY = "openclaw_chat_config"; export const DEFAULT_OPENCLAW_CHAT_BASE_URL = "http://192.168.42.154:18790"; export type OpenClawChatConfig = { enabled: boolean; baseUrl: string; token: string; timeoutMs: number; }; export type PublicOpenClawChatConfig = { enabled: boolean; baseUrl: string; }; export type AdminOpenClawChatConfig = Omit & { token: ""; hasToken: boolean; tokenPreview: string; }; export const DEFAULT_OPENCLAW_TASK_TIMEOUT_MS = 30 * 60 * 1000; export const MIN_OPENCLAW_TASK_TIMEOUT_MS = 5 * 60 * 1000; // Complex OpenClaw skills can legitimately run for up to thirty minutes. // Keep this aligned with the route maxDuration and the integration contract. export const MAX_OPENCLAW_TASK_TIMEOUT_MS = 30 * 60 * 1000; const LEGACY_CONNECTION_TIMEOUT_MAX_MS = 5 * 60 * 1000; function readRecord(value: unknown): Record { return value && typeof value === "object" && !Array.isArray(value) ? (value as Record) : {}; } function normalizeBaseUrl(value: unknown) { return ( String(value || "").trim().replace(/\/+$/, "") || DEFAULT_OPENCLAW_CHAT_BASE_URL ); } function normalizeTimeoutMs(value: unknown) { const numeric = Number(value); if (!Number.isFinite(numeric)) return DEFAULT_OPENCLAW_TASK_TIMEOUT_MS; // Older settings described one SSE connection and were commonly saved as // 180-300 seconds. Durable tasks use a separate business-task lifetime. if (numeric <= LEGACY_CONNECTION_TIMEOUT_MAX_MS) { return DEFAULT_OPENCLAW_TASK_TIMEOUT_MS; } return Math.min( MAX_OPENCLAW_TASK_TIMEOUT_MS, Math.max(MIN_OPENCLAW_TASK_TIMEOUT_MS, Math.round(numeric)) ); } export function normalizeOpenClawChatConfig( value: unknown, fallbackToken = "" ): OpenClawChatConfig { const raw = readRecord(value); const token = String(raw.token || "").trim() || fallbackToken.trim(); return { enabled: raw.enabled === true, baseUrl: normalizeBaseUrl(raw.baseUrl), token, timeoutMs: normalizeTimeoutMs(raw.timeoutMs), }; } export function normalizePublicOpenClawChatConfig( value: unknown ): PublicOpenClawChatConfig { const config = normalizeOpenClawChatConfig(value); return { enabled: config.enabled && Boolean(config.baseUrl) && Boolean(config.token), baseUrl: config.baseUrl, }; } export function maskOpenClawToken(token: string) { const normalized = token.trim(); if (!normalized) return ""; if (normalized.length <= 8) return "********"; return `${normalized.slice(0, 4)}****${normalized.slice(-4)}`; } export function toAdminOpenClawChatConfig( value: unknown ): AdminOpenClawChatConfig { const config = normalizeOpenClawChatConfig(value); return { enabled: config.enabled, baseUrl: config.baseUrl, token: "", hasToken: Boolean(config.token), tokenPreview: maskOpenClawToken(config.token), timeoutMs: config.timeoutMs, }; }