import { Logger } from '@nestjs/common'; import type { ApicoreRole } from '../runtime/role'; /** Key format issued by Anthropic: "sk-ant-" followed by URL-safe characters. */ export const ANTHROPIC_KEY_PATTERN = /^sk-ant-[A-Za-z0-9_-]{20,}$/; const PLACEHOLDER_PATTERN = /^change-me/i; const WORKER_ROLES: readonly ApicoreRole[] = ['worker', 'all']; function trimmed(value: string | undefined): string | null { const text = value?.trim(); return text ? text : null; } /** * The API key for voice extraction, or null if it is unset, a placeholder or * malformed - extraction then stays off (code EXTRACT_DISABLED). Only the * worker holds the key (Secret apicore-anthropic, optional). */ export function anthropicApiKey( env: NodeJS.ProcessEnv = process.env, ): string | null { const key = trimmed(env.ANTHROPIC_API_KEY); return key !== null && ANTHROPIC_KEY_PATTERN.test(key) ? key : null; } /** * Startup check, same rules as the Telegram token: optional, a bad value * never stops a pod. In the worker, a missing key is a warning and a * placeholder or malformed key an error, once, naming the variable and * never its value. API and job pods never hold the key and skip the check. */ export function assertAnthropicKey( role: ApicoreRole, env: NodeJS.ProcessEnv = process.env, logger: Pick = new Logger('AnthropicStartup'), ): void { if (!WORKER_ROLES.includes(role)) { return; } const raw = trimmed(env.ANTHROPIC_API_KEY); if (raw === null) { logger.warn( 'ANTHROPIC_API_KEY is not set: voice extraction stays off (EXTRACT_DISABLED).', ); return; } if (PLACEHOLDER_PATTERN.test(raw)) { logger.error( 'ANTHROPIC_API_KEY is still set to an example placeholder: voice extraction stays off - set the real key or remove it.', ); return; } if (!ANTHROPIC_KEY_PATTERN.test(raw)) { logger.error( 'ANTHROPIC_API_KEY is malformed (expected "sk-ant-..."): voice extraction stays off.', ); } }