/** * Shared Configuration Module * @module @skillsmith/core/config * * SMI-1851: Shared Config Module * SMI-2714: CLI Login Device Flow - credential storage * * Provides cross-platform configuration loading from: * - Environment variables (highest precedence) * - ~/.skillsmith/config.json * - OS keyring (via @isaacs/keytar, optional) * * @example * ```typescript * import { loadConfig, getApiKey, saveConfig } from '@skillsmith/core/config' * * // Load full config * const config = loadConfig() * * // Get API key (env var takes precedence) * const apiKey = getApiKey() * * // Save config (creates file with 0600 permissions) * saveConfig({ apiKey: 'sk_live_...' }) * * // Store API key (tries keyring first, falls back to config file) * await storeApiKey('sk_live_...') * * // Get auth status * const status = await getAuthStatus() * ``` */ /** * Skillsmith configuration schema */ export interface SkillsmithConfig { /** API key for authenticated requests (sk_live_...) */ apiKey?: string; /** API base URL override */ apiBaseUrl?: string; /** Enable debug logging */ debug?: boolean; /** Telemetry settings */ telemetry?: { /** Enable telemetry (default: false, opt-in) */ enabled?: boolean; /** * Stable per-install identifier (SMI-5531): `sha256(randomUUID())`, * generated unconditionally, decoupled from `SKILLSMITH_TELEMETRY_ENABLED` * / `POSTHOG_API_KEY`. See `getOrCreateInstallId()` in `./device-identity.ts`. */ installId?: string; }; /** Sync settings */ sync?: { /** Enable background sync */ enabled?: boolean; /** Sync interval in milliseconds */ intervalMs?: number; }; /** Cross-harness inventory sync state (SMI-5391). */ inventory?: { /** Stable client-generated device UUID (v4). */ deviceId?: string; /** Optional user-facing device label. */ deviceLabel?: string; /** ISO timestamp of the last successful inventory push (auto-push throttle). */ lastPushAt?: string; }; /** Continuous-audit email digest state (SMI-5541). */ audit?: { /** ISO timestamp of the last background digest attempt (auto-notify throttle). */ lastNotifyAt?: string; /** * sha256 of the findings in the last digest we successfully emailed — * client-side dedup so an identical picture is not re-emailed every day. */ lastDigestHash?: string; }; defaultScope?: Record; } /** * Get the config directory path * Cross-platform: uses os.homedir() * * @returns Absolute path to ~/.skillsmith/ */ export declare function getConfigDir(): string; /** * Get the config file path * * @returns Absolute path to ~/.skillsmith/config.json */ export declare function getConfigPath(): string; /** * Ensure config directory exists with secure permissions * Creates ~/.skillsmith/ if it doesn't exist */ export declare function ensureConfigDir(): void; /** * Get the cache directory path (~/.skillsmith/cache). * * SMI-4577: First-class artifact directory for cached HNSW indexes, * model metadata, and similar machine-generated state. mkdir-on-first-call * mirrors the pattern used by `getConfigDir()`/`ensureConfigDir()`. * * The pathValidation allow-list (`packages/core/src/security/pathValidation.ts`) * already covers `~/.skillsmith` and therefore transitively allows this subtree. * * @returns Absolute path to ~/.skillsmith/cache/ */ export declare function getCacheDir(): string; /** * Load configuration from ~/.skillsmith/config.json * * @returns Parsed config or empty object if file doesn't exist */ export declare function loadConfig(): SkillsmithConfig; /** * Save configuration to ~/.skillsmith/config.json * Creates the file with 0600 permissions (owner read/write only) * * SMI-5531: serialized under a cross-process lock ({@link acquireConfigLock}) * and written atomically (temp-file + rename, {@link atomicWriteFile}) — * closes a lost-update race (two concurrent writers each dropping the * other's key) and a torn-write race under a bare `writeFileSync`. * `existingConfig` is (re-)read AFTER the lock is acquired, not before, or a * writer that read stale state while waiting would reintroduce the same * lost-update bug. * * @param config - Configuration to save (merged with existing) * @param options - Save options */ export declare function saveConfig(config: Partial, options?: { merge?: boolean; }): void; /** * Get API key with precedence: env var > config file * * Checks in order: * 1. SKILLSMITH_API_KEY environment variable * 2. ~/.skillsmith/config.json apiKey field * * @returns API key or undefined if not configured */ export declare function getApiKey(): string | undefined; /** * Get API base URL with precedence: env var > config file > default * * @param defaultUrl - Default URL if not configured * @returns API base URL */ export declare function getApiBaseUrl(defaultUrl?: string): string; /** * Check if debug mode is enabled * * @returns true if debug is enabled via env var or config */ export declare function isDebugEnabled(): boolean; /** * Check if telemetry is enabled (opt-in, default false) * * @returns true if telemetry is explicitly enabled */ export declare function isTelemetryEnabled(): boolean; /** * Validate API key format. * * Expected format: sk_live_ followed by 32-128 alphanumeric/dash/underscore chars. * The 200-char pre-check is a ReDoS guard per security standards. * * @param key - API key to validate * @returns true if key has valid format (sk_live_...) */ export declare function isValidApiKeyFormat(key: string): boolean; /** * Store an API key securely. * * @deprecated Use storeCredentials() from config/token-credentials.ts for the * new device-code flow (SMI-4402). This wrapper will be removed in 2 minor * releases (Wave 4 / SMI-4403). * * Attempts to use the OS keyring first (via @isaacs/keytar). * Falls back to saving in ~/.skillsmith/config.json when keyring is unavailable. * * @param apiKey - The API key to store (must pass isValidApiKeyFormat) */ export declare function storeApiKey(apiKey: string): Promise; /** * Clear the stored API key from all storage locations. * * Attempts to delete from the OS keyring AND removes apiKey from the config file. * Returns explicit success/failure info so callers can report partial failures. * * @returns Result indicating which storage locations were cleared and any errors */ export declare function clearApiKey(): Promise<{ success: boolean; source: string; error?: string; }>; /** * Get current authentication status. * * Checks in precedence order: * 1. SKILLSMITH_API_KEY environment variable * 2. OS keyring (via @isaacs/keytar) * 3. ~/.skillsmith/config.json apiKey field * * @returns Authentication status with masked key prefix and storage source */ export declare function getAuthStatus(): Promise<{ authenticated: boolean; keyPrefix: string | null; source: 'keyring' | 'config' | 'env' | 'none'; }>; //# sourceMappingURL=index.d.ts.map