type ChatCompletionResponse = { choices?: Array<{ message?: { content?: string; }; }>; }; type OpenClawAuthMode = 'bearer' | 'basic' | 'none'; type OpenClawConfig = { baseUrl?: string; apiKey?: string; model: string; authMode: OpenClawAuthMode; basicAuthUsername?: string; basicAuthPassword?: string; apiKeyHeader?: string; }; function envValue(name: string) { const value = process.env[name]; if (!value) { return undefined; } const trimmed = value.trim(); return trimmed.length > 0 ? trimmed : undefined; } const DEFAULT_MODEL = 'zeroclaw'; const DEFAULT_AUTH_MODE: OpenClawAuthMode = 'bearer'; function parseAuthMode(value: string | undefined): OpenClawAuthMode { const normalized = value?.trim().toLowerCase(); if (normalized === 'basic' || normalized === 'none') { return normalized; } return DEFAULT_AUTH_MODE; } function hasSupportedProtocol(url: string) { try { const parsed = new URL(url); return parsed.protocol === 'http:' || parsed.protocol === 'https:'; } catch { return false; } } function buildCompletionsUrl(baseUrl: string) { if (!hasSupportedProtocol(baseUrl)) { return undefined; } const withSlash = baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`; return new URL('v1/chat/completions', withSlash).toString(); } function hasRequiredAuth(config: OpenClawConfig) { if (config.authMode === 'none') { return true; } if (config.authMode === 'basic') { return Boolean(config.basicAuthUsername && config.basicAuthPassword); } return Boolean(config.apiKey); } function buildAuthHeaders(config: OpenClawConfig) { const headers: Record = { 'Content-Type': 'application/json' }; if (config.authMode === 'basic' && config.basicAuthUsername && config.basicAuthPassword) { const credentials = Buffer .from(`${config.basicAuthUsername}:${config.basicAuthPassword}`, 'utf8') .toString('base64'); headers.Authorization = `Basic ${credentials}`; } else if (config.authMode === 'bearer' && config.apiKey) { headers.Authorization = `Bearer ${config.apiKey}`; } if (config.apiKey && config.apiKeyHeader) { headers[config.apiKeyHeader] = config.apiKey; } return headers; } function fallbackResponse(prompt: string) { const clipped = prompt.split('\n').slice(0, 6).join(' ').slice(0, 260); return [ 'OpenClaw fallback mode is active (configuration is missing or invalid).', 'Thesis: Portfolio remains analyzable with local heuristics until live model access is configured.', 'Risk scan: Concentration and filing sentiment should be monitored after each sync cycle.', `Context digest: ${clipped}` ].join('\n\n'); } export function getOpenClawConfig() { return { baseUrl: envValue('OPENCLAW_BASE_URL'), apiKey: envValue('OPENCLAW_API_KEY'), model: envValue('OPENCLAW_MODEL') ?? DEFAULT_MODEL, authMode: parseAuthMode(envValue('OPENCLAW_AUTH_MODE')), basicAuthUsername: envValue('OPENCLAW_BASIC_AUTH_USERNAME'), basicAuthPassword: envValue('OPENCLAW_BASIC_AUTH_PASSWORD'), apiKeyHeader: envValue('OPENCLAW_API_KEY_HEADER') } satisfies OpenClawConfig; } export function isOpenClawConfigured() { const config = getOpenClawConfig(); return Boolean(config.baseUrl && hasSupportedProtocol(config.baseUrl) && hasRequiredAuth(config)); } export async function runOpenClawAnalysis(prompt: string, systemPrompt?: string) { const config = getOpenClawConfig(); const endpoint = config.baseUrl ? buildCompletionsUrl(config.baseUrl) : undefined; if (!endpoint || !hasRequiredAuth(config)) { return { provider: 'local-fallback', model: config.model, text: fallbackResponse(prompt) }; } const response = await fetch(endpoint, { method: 'POST', headers: buildAuthHeaders(config), body: JSON.stringify({ model: config.model, temperature: 0.2, messages: [ systemPrompt ? { role: 'system', content: systemPrompt } : null, { role: 'user', content: prompt } ].filter(Boolean) }), cache: 'no-store' }); if (!response.ok) { const body = await response.text(); throw new Error(`OpenClaw request failed (${response.status}): ${body.slice(0, 220)}`); } const payload = await response.json() as ChatCompletionResponse; const text = payload.choices?.[0]?.message?.content?.trim(); if (!text) { throw new Error('OpenClaw returned an empty response'); } return { provider: 'openclaw', model: config.model, text }; }