import { setUserSessionStatus } from './userSession'; import { ActionID, ActionPlan, ActionRegistryDetail, ActionRegistryResponse, ActionResult, ApprovalRequest, AiMemoryItem, AiRecommendation, App, AuditDetail, AuditEvent, Connector, ConnectorBinding, ConnectorCandidate, ConnectorSettings, ConnectorSettingsPayload, DeepHealth, HealthCheck, HomeAssistantExport, HomeAssistantVoiceReadiness, Incident, JobRun, NetworkExposure, PreflightPreview, RuntimeInfo, SetupCompleteResult, SetupConnectionTestResult, SetupReconcileResult, SetupStatus, Stack, StorageSummary, Container, ContainerLogsResponse, StatisticsSummary, SystemSummary, UnraidSummary, VoiceCommand, SourceType, PolicyRule, Playbook, PolicyDecision, RiskLevel, AiToolPermission, Status, SonarrAddSeriesPayload, SonarrHealth, SonarrQueueSummary, SonarrSearchResponse, SonarrWantedSummary, RadarrAddMoviePayload, RadarrHealth, RadarrQueueSummary, RadarrSearchResponse, RadarrWantedSummary, InventoryDiscoveryResult, InventoryDriftItem, InventoryMapExistingPayload, InventoryReviewUpdatePayload, InventoryRunSummary, DiscoveredContainer, AppContainerMapping, WidefrogActivity, WidefrogDownloadStatus, WidefrogFailedJobs, WidefrogHealth, WidefrogQueueSummary, SecretsResponse, SqliteBackupCreateResult, SqliteBackupsResponse, SqliteBackupVerifyResult, SqliteCheckpointResult, SqliteMaintenanceStatus, ProwlarrApplicationStatus, ProwlarrHealth, ProwlarrIndexerHealth, ProwlarrIndexerStatus, ProwlarrSearchTest, ProwlarrSyncStatus, DelugeDownloadSummary, DelugeHealth, DelugeSessionStatus, DelugeTorrentStatus, DelugeSpeedStatus, PlexHealth, PlexLibraryStatus, PlexRecentMedia, PlexServerStatus, PlexSessionStatus, PlexStreamSummary, TautulliActivity, TautulliHealth, TautulliHistory, TautulliLibraries, TautulliPlexInfo, TautulliRecentlyAdded, TautulliStatus, TautulliStreamSummary, TautulliUsers, TautulliWatchStats, NpmAccessLists, NpmCertificates, NpmExposureSummary, NpmHealth, NpmHostReachability, NpmProxyHosts, NpmSecurityWarnings, AuthentikApplication, AuthentikAuthCoverage, AuthentikCollection, AuthentikEvent, AuthentikFlowsSummary, AuthentikHealth, AuthentikOAuth2Provider, AuthentikOutpost, AuthentikOutpostHealth, AuthentikPoliciesSummary, AuthentikProvider, AuthentikProxyProvider, AuthentikSecurityWarnings, AuthentikSystemStatus, MediaPipelineActivity, MediaPipelineCheckResult, MediaPipelineDiagnosis, MediaPipelineDownloads, MediaPipelineFlow, MediaPipelineIssues, MediaPipelineLibrary, MediaPipelineRecommendations, MediaPipelineSummary, VoiceIntentTestResult } from '../types/appops'; import { AISummary, AIRecommendation, AIChatMessage, AIAutopilotRule, AITool } from '../types/ai'; import { toIsoString } from '@/lib/backendDateTime'; import type { AiSecurityFinding, AiSecurityRecommendation, AiSecurityReviewResult, AiSecurityScope, AiSecuritySummary, NotificationChannel, NotificationEvaluationResult, NotificationEvent, NotificationRule, NotificationSummary, RemediationApprovalProposal, RemediationDryRunResult, RemediationPlan, RemediationPlanCollection, SafetyDashboard, SafetyFinding, SafetyRecommendation, SafetyScoreBreakdown, SafetySection, SafetySummary, UnraidDisksResponse, UnraidDockerResponse, UnraidSharesResponse, UnraidVmCapabilitiesResponse, UnraidVmsResponse } from '../types/appops'; import type { GiteaActionsStatus, IntegrationStatus, GatewayUser, IdentityProvider, PermissionDefinition, UserAccessUpdate, UserAuthSession, RagQueryResult, RagStatus, RunnerFleetStatus } from '../types/appops'; import type { DeliveryStatus, DeploymentJob, Enrollment, EnrollmentCapabilities, EnrollmentStorageRootSelection, EnrollmentTrustStatus, WorkMeshPromotion } from '../types/appops'; import type { ExposurePreferences, ManagedAiConfig, ManagedAiConfigUpdate, ManagedAiStatus, ManagedAiInference } from '../types/appops'; type HealthStatus = { status: string; }; type NotificationEventPageOptions = { limit?: number; offset?: number; includeDismissed?: boolean; }; const notificationEventPath = (path: string, options: NotificationEventPageOptions = {}): string => { const params = new URLSearchParams(); if (options.limit !== undefined) params.set('limit', String(options.limit)); if (options.offset !== undefined) params.set('offset', String(options.offset)); if (options.includeDismissed !== undefined) params.set('include_dismissed', String(options.includeDismissed)); const query = params.toString(); return query ? `${path}?${query}` : path; }; export interface LiveClientConfig { baseUrl: string; requestTimeoutMs: number; } const defaultConfig: LiveClientConfig = { baseUrl: (import.meta.env.VITE_APPOPS_API_BASE_URL || '').replace(/\/$/, ''), requestTimeoutMs: 12000, }; const safetyConfig: LiveClientConfig = { ...defaultConfig, requestTimeoutMs: 30000, }; const aiConfig: LiveClientConfig = { ...defaultConfig, requestTimeoutMs: 65000 }; const containerListConfig: LiveClientConfig = { ...defaultConfig, requestTimeoutMs: 30000, }; const containerStatsConfig: LiveClientConfig = { ...defaultConfig, requestTimeoutMs: 45000, }; const inventoryConfig: LiveClientConfig = { ...defaultConfig, requestTimeoutMs: 30000, }; const mediaPipelineConfig: LiveClientConfig = { ...defaultConfig, requestTimeoutMs: 45000, }; const REDACT_PATTERNS: RegExp[] = [ /Bearer\s+\S+/gi, /\b(?:token|password|api[_-]?key)\s*[:=]\s*[^\s"'`]+/gi, ]; const redactText = (input: string): string => { let value = input; for (const pattern of REDACT_PATTERNS) { value = value.replace(pattern, (match) => `${match.split(':')[0].split('=')[0]} [REDACTED]`); } return value; }; const friendlyBackendError = (status: number, message: string): string => { if (status === 401) { return "Je aanmelding ontbreekt of is verlopen. Meld opnieuw aan via Authentik of Microsoft Entra."; } if (status === 403) { return "Je account heeft geen rechten voor deze actie."; } let detail = message || "Request failed"; try { const parsed = JSON.parse(detail) as { detail?: unknown }; if (typeof parsed.detail === 'string') detail = parsed.detail; else if (parsed.detail && typeof parsed.detail === 'object' && !Array.isArray(parsed.detail)) { const named = parsed.detail as { message?: string; code?: string; correlationId?: string }; if (typeof named.message === 'string') detail = `${named.message}${named.code ? ` (${named.code})` : ''}${named.correlationId ? ` ยท ${named.correlationId}` : ''}`; } } catch { /* Keep the existing redacted text for non-JSON backend errors. */ } return `Backend ${status}: ${redactText(detail)}`; }; const request = async ( path: string, config: LiveClientConfig, options: RequestInit = {}, ): Promise => { const url = `${config.baseUrl}${path.startsWith('/') ? path : `/${path}`}`; const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), config.requestTimeoutMs); const headers = new Headers(options.headers || {}); headers.set('Content-Type', 'application/json'); const method = (options.method || 'GET').toUpperCase(); if (!['GET', 'HEAD', 'OPTIONS'].includes(method)) { headers.set('X-AppOps-Session-CSRF', '1'); } try { const response = await fetch(url, { ...options, headers, credentials: 'include', signal: controller.signal, }); if (!response.ok) { if (response.status === 401) setUserSessionStatus({ authenticated: false, user: null, permissions: [] }); const message = await response.text(); throw new Error(friendlyBackendError(response.status, message || response.statusText)); } if (response.status === 204) { return null as T; } return (await response.json()) as T; } catch (error) { if (error instanceof Error) { throw new Error(redactText(error.message)); } throw new Error('Onbekende netwerkfout in backend aanvraag.'); } finally { clearTimeout(timeout); } }; const toRisk = (risk?: unknown): RiskLevel => ( risk === 'low' || risk === 'medium' || risk === 'high' || risk === 'critical' ? risk : 'low' ) as RiskLevel; const toPolicyDecision = (decision?: unknown): PolicyDecision => ( decision === 'allowed' || decision === 'approval_required' || decision === 'blocked' ? decision : 'blocked' ) as PolicyDecision; const toStringArray = (value: unknown): string[] => ( Array.isArray(value) ? value.filter((item): item is string => typeof item === 'string') : [] ); const toAuditStatus = (value: unknown): Status => { if (value === 'queued') { return 'running'; } if (value === 'succeeded') { return 'success'; } if (value === 'running' || value === 'failed' || value === 'success' || value === 'missing_config' || value === 'blocked' || value === 'acknowledged' || value === 'muted' || value === 'resolved' || value === 'warning' || value === 'healthy' || value === 'critical' || value === 'starting' || value === 'stopped' || value === 'unhealthy') { return value; } return 'failed'; }; const normalizePolicyRule = (raw: Record): PolicyRule => ({ id: String(raw.id ?? `${raw.action}_${raw.target}`), action: String(raw.action ?? ''), target: String(raw.target ?? ''), voice: toPolicyDecision(raw.voice), manual: toPolicyDecision(raw.manual), confirmation: Boolean(raw.confirmation), risk: toRisk(raw.risk), reason: typeof raw.reason === 'string' ? raw.reason : undefined, }); const normalizeConnector = (raw: Record): Connector => ({ id: String(raw.id ?? ''), name: String(raw.name ?? ''), status: (raw.status === 'connected' || raw.status === 'disconnected' || raw.status === 'error' || raw.status === 'missing_config') ? raw.status : 'disconnected', lastTest: typeof raw.last_test === 'string' ? raw.last_test : typeof raw.lastTest === 'string' ? raw.lastTest : 'never', capabilities: toStringArray(raw.capabilities), type: typeof raw.type === 'string' ? raw.type : 'Infrastructure', endpoint: typeof raw.endpoint === 'string' ? raw.endpoint : undefined, secretRef: typeof raw.secret_ref === 'string' ? raw.secret_ref : ( typeof raw.secretRef === 'string' ? raw.secretRef : undefined ), }); const normalizeApproval = (raw: Record): ApprovalRequest => { const target = String(raw.target ?? ''); const action = String(raw.action_id ?? raw.action ?? ''); const source = raw.source === 'voice' || raw.source === 'manual' || raw.source === 'automation' || raw.source === 'system' ? raw.source : 'manual'; const createdAt = raw.created_at ?? raw.timestamp; const expiresAt = raw.expires_at ?? raw.expiresAt; const reason = typeof raw.reason === 'string' ? raw.reason : ''; return { id: String(raw.id ?? ''), action, target, requester: typeof raw.requester === 'string' ? raw.requester : 'system', source, risk: toRisk(raw.risk), timestamp: toIsoString(createdAt), expiresAt: toIsoString(expiresAt), context: reason || `${action} op ${target}`, affectedApps: toStringArray(raw.affected_apps ?? raw.affectedApps).length ? toStringArray(raw.affected_apps ?? raw.affectedApps) : [target].filter(Boolean), reason, plan: toStringArray(raw.plan ?? raw.execution_plan), status: raw.status === 'approved' || raw.status === 'rejected' || raw.status === 'expired' || raw.status === 'executed' ? raw.status : 'pending', auditId: typeof raw.audit_event_id === 'string' ? raw.audit_event_id : ( typeof raw.audit_id === 'string' ? raw.audit_id : ( typeof raw.auditId === 'string' ? raw.auditId : undefined ) ), jobId: typeof raw.job_id === 'string' ? raw.job_id : ( typeof raw.jobId === 'string' ? raw.jobId : undefined ), executionSupported: raw.executionSupported === true || raw.execution_supported === true, executionReason: typeof raw.executionReason === 'string' ? raw.executionReason : ( typeof raw.execution_reason === 'string' ? raw.execution_reason : undefined ), }; }; const normalizeAudit = (raw: Record): AuditEvent => { const details: AuditDetail = { reason: typeof raw.reason === 'string' ? raw.reason : undefined, beforeState: raw.before_state ? JSON.stringify(raw.before_state) : undefined, afterState: raw.after_state ? JSON.stringify(raw.after_state) : undefined, metadata: raw.redacted_metadata && typeof raw.redacted_metadata === 'object' ? raw.redacted_metadata as Record : undefined, }; const executionPlan = raw.execution_plan; if (executionPlan) { if (Array.isArray(executionPlan)) { details.plan = executionPlan.filter((step): step is string => typeof step === 'string'); } else if (typeof executionPlan === 'object' && executionPlan !== null && Array.isArray((executionPlan as Record).plan)) { const nestedExecutionPlan = (executionPlan as Record).plan; details.plan = Array.isArray(nestedExecutionPlan) ? nestedExecutionPlan.filter((step): step is string => typeof step === 'string') : []; } else if (typeof executionPlan === 'string') { details.plan = [executionPlan]; } } if (Array.isArray(raw.post_checks)) { details.postChecks = raw.post_checks.filter((item): item is string => typeof item === 'string'); } else if (Array.isArray(raw.postChecks)) { details.postChecks = raw.postChecks.filter((item): item is string => typeof item === 'string'); } return { id: String(raw.id ?? ''), time: toIsoString(raw.timestamp), source: raw.source === 'manual' || raw.source === 'voice' || raw.source === 'automation' || raw.source === 'system' ? raw.source : 'manual', user: typeof raw.identity === 'string' ? raw.identity : 'system', action: String(raw.action_id ?? raw.action ?? ''), target: String(raw.target ?? ''), connector: typeof raw.target_type === 'string' ? raw.target_type : 'system', risk: toRisk(raw.risk), policy: toPolicyDecision(raw.policy_decision), result: toAuditStatus(raw.result), duration: typeof raw.duration_ms === 'number' ? `${raw.duration_ms}ms` : undefined, details, }; }; const normalizeJob = (raw: Record): JobRun => { const durationMs = typeof raw.duration_ms === 'number' ? raw.duration_ms : undefined; return { id: String(raw.id ?? ''), type: typeof raw.type === 'string' ? raw.type : 'job', status: (raw.status === 'queued' || raw.status === 'running' || raw.status === 'succeeded' || raw.status === 'failed' || raw.status === 'cancelled') ? raw.status : 'queued', source: raw.source === 'manual' || raw.source === 'voice' || raw.source === 'automation' || raw.source === 'system' ? raw.source : 'manual', actionId: String(raw.action_id ?? raw.actionId ?? ''), target: String(raw.target ?? ''), risk: toRisk(raw.risk), policyDecision: toPolicyDecision(raw.policy_decision), stepsJson: typeof raw.steps_json === 'string' ? raw.steps_json : JSON.stringify(raw.steps_json ?? []), progress: typeof raw.progress === 'number' ? raw.progress : 0, startedAt: typeof raw.started_at === 'string' || typeof raw.started_at === 'number' ? toIsoString(raw.started_at) : toIsoString(raw.startedAt), finishedAt: typeof raw.finished_at === 'string' || typeof raw.finished_at === 'number' ? toIsoString(raw.finished_at) : typeof raw.finishedAt === 'string' || typeof raw.finishedAt === 'number' ? toIsoString(raw.finishedAt) : undefined, durationMs, auditEventId: typeof raw.audit_event_id === 'string' ? raw.audit_event_id : ( typeof raw.auditEventId === 'string' ? raw.auditEventId : undefined ), resultSummary: typeof raw.result_summary === 'string' ? raw.result_summary : ( typeof raw.resultSummary === 'string' ? raw.resultSummary : undefined ), }; }; const post = (path: string, body: unknown, config: LiveClientConfig) => request(path, config, { method: 'POST', body: JSON.stringify(body) }); const patch = (path: string, body: unknown, config: LiveClientConfig) => request(path, config, { method: 'PATCH', body: JSON.stringify(body) }); const get = (path: string, config: LiveClientConfig) => request(path, config, { method: 'GET' }); export const createLiveClient = () => ({ async getApps(includeDiscovered = false): Promise { return get(`/api/apps${includeDiscovered ? '?include_discovered=true' : ''}`, defaultConfig); }, async getApp(id: string): Promise { return get(`/api/apps/${id}`, defaultConfig); }, async updateAppMetadata(id: string, payload: Pick): Promise { return request(`/api/apps/${encodeURIComponent(id)}/metadata`, defaultConfig, { method: 'PATCH', body: JSON.stringify(payload), }); }, async getContainers(includeStats = false, statsLimit = 250): Promise { const query = includeStats ? `?include_stats=true&stats_limit=${Math.max(1, Math.min(statsLimit, 500))}` : ''; return get(`/api/containers${query}`, includeStats ? containerStatsConfig : containerListConfig); }, async getContainerLogs(id: string): Promise { return get(`/api/containers/${encodeURIComponent(id)}/logs`, defaultConfig); }, async getStacks(): Promise { return get('/api/stacks', defaultConfig); }, async getDeploymentJobs(): Promise<{ items: DeploymentJob[] }> { return get<{ items: DeploymentJob[] }>('/api/deployments', defaultConfig); }, async getWorkMeshPromotions(limit = 20): Promise<{ items: WorkMeshPromotion[] }> { return get<{ items: WorkMeshPromotion[] }>(`/api/deployments/workmesh/promotions?limit=${Math.max(1, Math.min(limit, 50))}`, defaultConfig); }, async getWorkMeshPromotion(id: string): Promise { return get(`/api/deployments/workmesh/promotions/${encodeURIComponent(id)}`, defaultConfig); }, async requestWorkMeshPromotionPreflight(revision: string, idempotencyKey: string): Promise { return request('/api/deployments/workmesh/promotions/preflight', defaultConfig, { method: 'POST', headers: { 'Idempotency-Key': idempotencyKey }, body: JSON.stringify({ revision }), }); }, async getDeliveryStatus(): Promise { return get('/api/deployments/delivery-status', defaultConfig); }, async getEnrollmentCapabilities(): Promise { return get('/api/enrollments/capabilities', defaultConfig); }, async getEnrollmentTrustStatus(): Promise { return get('/api/enrollments/trust', defaultConfig); }, async updateEnrollmentStorageRoot(payload: EnrollmentStorageRootSelection): Promise { return patch('/api/enrollments/trust/storage-root', payload, defaultConfig); }, async getEnrollments(): Promise<{ items: Enrollment[] }> { return get<{ items: Enrollment[] }>('/api/enrollments', defaultConfig); }, async getSystemSummary(): Promise { return get('/api/system/summary', defaultConfig); }, async getHealth(): Promise { return get('/api/health', defaultConfig); }, async getRuntimeInfo(): Promise { return get('/api/runtime/info', defaultConfig); }, async getUserSession(): Promise { return get('/api/auth/session', defaultConfig); }, async getIdentityProviders(): Promise { return get('/api/auth/providers', defaultConfig); }, signIn(provider: IdentityProvider['id']): void { window.location.assign(`${defaultConfig.baseUrl}/api/auth/login/${encodeURIComponent(provider)}`); }, async logout(): Promise { return request('/api/auth/logout', defaultConfig, { method: 'POST' }); }, async getUsers(): Promise { return get('/api/auth/users', defaultConfig); }, async getPermissionDefinitions(): Promise { return get('/api/auth/permissions', defaultConfig); }, async updateUserAccess(id: string, payload: UserAccessUpdate): Promise { return request(`/api/auth/users/${encodeURIComponent(id)}`, defaultConfig, { method: 'PATCH', body: JSON.stringify(payload) }); }, async getDeepHealth(): Promise { return get('/api/health/deep', defaultConfig); }, async exportAdminData(kind: 'config' | 'policies' | 'inventory' | 'audit-summary'): Promise> { return get>(`/api/admin/export/${kind}`, defaultConfig); }, async listSqliteBackups(): Promise { return get('/api/admin/backups/sqlite', defaultConfig); }, async getSqliteMaintenanceStatus(): Promise { return get('/api/admin/database/status', defaultConfig); }, async checkpointSqlite(): Promise { return post('/api/admin/database/checkpoint', {}, defaultConfig); }, async createSqliteBackup(): Promise { return post('/api/admin/backups/sqlite', {}, defaultConfig); }, async verifySqliteBackup(name: string): Promise { return post(`/api/admin/backups/sqlite/${encodeURIComponent(name)}/verify`, {}, defaultConfig); }, async getUnraidSummary(): Promise { return get('/api/unraid/summary', defaultConfig); }, async getIncidents(): Promise { return get('/api/incidents', defaultConfig); }, async getAuditLogs(limit = 200, offset = 0): Promise { const response = await get>>(`/api/audit?limit=${Math.max(1, Math.min(limit, 500))}&offset=${Math.max(0, offset)}`, defaultConfig); return response.map(normalizeAudit); }, async getPolicies(): Promise { const response = await get>>('/api/policies', defaultConfig); return response.map(normalizePolicyRule); }, async updatePolicy(id: string, payload: Pick & { reason: string }): Promise { const response = await request>(`/api/policies/${encodeURIComponent(id)}`, defaultConfig, { method: 'PATCH', body: JSON.stringify(payload), }); return normalizePolicyRule(response); }, async getConnectors(): Promise { const response = await get>>('/api/connectors?refresh=true', defaultConfig); return response.map(normalizeConnector); }, async getSetupStatus(): Promise { return get('/api/setup/status', defaultConfig); }, async completeSetup(): Promise { return post('/api/setup/complete', {}, defaultConfig); }, async reconcileSetup(): Promise { return post('/api/setup/reconcile', {}, defaultConfig); }, async markSetupComplete(): Promise { return post('/api/setup/mark-complete', { confirm: 'MARK_SETUP_COMPLETE' }, defaultConfig); }, async markVoiceConfigured(): Promise { return post('/api/setup/voice/mark-configured', {}, defaultConfig); }, async getConnectorSettings(): Promise { return get('/api/settings/connectors', defaultConfig); }, async getSecrets(): Promise { return get('/api/settings/secrets', defaultConfig); }, async updateConnectorSettings(id: string, payload: ConnectorSettingsPayload): Promise { return request(`/api/settings/connectors/${encodeURIComponent(id)}`, defaultConfig, { method: 'PATCH', body: JSON.stringify(payload) }); }, async testSetupConnector(id: string): Promise { if (id === 'portainer' || id === 'unraid') { return post(`/api/setup/test/${id}`, {}, defaultConfig); } return post(`/api/setup/test/connector/${encodeURIComponent(id)}`, {}, defaultConfig); }, async getConnectorCandidates(id: string): Promise { return get(`/api/connectors/${encodeURIComponent(id)}/candidates`, defaultConfig); }, async getConnectorBindings(id: string): Promise { return get(`/api/connectors/${encodeURIComponent(id)}/bindings`, defaultConfig); }, async bindConnector(id: string, discoveredContainerId: string): Promise { return post(`/api/connectors/${encodeURIComponent(id)}/bind`, { discoveredContainerId }, defaultConfig); }, async unbindConnector(id: string, appId: string): Promise { return post(`/api/connectors/${encodeURIComponent(id)}/unbind`, { appId }, defaultConfig); }, async getPlaybooks(): Promise { return get('/api/playbooks', defaultConfig); }, async getApprovals(): Promise { const response = await get>>('/api/approvals', defaultConfig); return response.map(normalizeApproval); }, async cleanupExpiredApprovals(): Promise { return post('/api/approvals/cleanup', {}, defaultConfig); }, async getVoiceCommands(): Promise { return get('/api/operator/voice/commands', defaultConfig); }, async getHomeAssistantExport(): Promise { return get('/api/operator/voice/home-assistant/export', defaultConfig); }, async getHomeAssistantVoiceReadiness(): Promise { return get('/api/operator/voice/home-assistant/readiness', defaultConfig); }, async testVoiceIntent(input: string): Promise { return post('/api/operator/voice/test-intent', { input }, defaultConfig); }, async dryRunVoiceCommand(id: string): Promise { return post(`/api/operator/voice/commands/${encodeURIComponent(id)}/dry-run`, {}, defaultConfig); }, async getActionRegistry(): Promise { return get('/api/actions/registry', defaultConfig); }, async getActionRegistryDetail(id: string): Promise { return get(`/api/actions/registry/${encodeURIComponent(id)}`, defaultConfig); }, async getActionPlan(input: string, source: SourceType): Promise { const response = await post<{ intent?: string; target?: string; source?: SourceType; risk?: string; policy?: string; reason?: string; plan?: string[]; postChecks?: string[] }>( '/api/actions/plan', { input, source }, defaultConfig ); if (!response.intent || !response.target) { return null; } const risk = response.risk; const policy = response.policy; const mappedRisk = (risk === 'low' || risk === 'medium' || risk === 'high' || risk === 'critical') ? (risk as RiskLevel) : 'low'; const mappedPolicy = (policy === 'allowed' || policy === 'approval_required' || policy === 'blocked') ? (policy as PolicyDecision) : 'blocked'; return { intent: response.intent as ActionID, target: response.target, source: response.source || source, risk: mappedRisk, policy: mappedPolicy, reason: response.reason, plan: response.plan || [], postChecks: response.postChecks || [], }; }, async evaluatePolicy(action: string, target: string, source: string): Promise<{ decision: string; reason: string }> { return post<{ decision: string; reason: string }>('/api/policies/evaluate', { action, target, source }, defaultConfig); }, async executeAction(plan: ActionPlan): Promise { return post('/api/actions/execute', { plan }, defaultConfig); }, async restartContainer(appId: string): Promise { return post(`/api/actions/container/${encodeURIComponent(appId)}/restart`, {}, defaultConfig); }, async requestApproval(plan: ActionPlan): Promise { return post('/api/approvals', { action: plan.intent, target: plan.target, source: plan.source, risk: plan.risk, reason: plan.reason, plan: plan.plan }, defaultConfig); }, async updateApprovalStatus(id: string, status: 'approved' | 'rejected'): Promise { return patch(`/api/approvals/${encodeURIComponent(id)}`, { status }, defaultConfig); }, async executeApproval(id: string): Promise { return post(`/api/approvals/${encodeURIComponent(id)}/execute`, {}, defaultConfig); }, async updateIncidentStatus(id: string, status: string): Promise { return post(`/api/incidents/${id}/status`, { status }, defaultConfig); }, async exportAudit(format: string): Promise { return post('/api/audit/export', { format }, defaultConfig); }, async testConnector(id: string): Promise { return post(`/api/connectors/${id}/test`, {}, defaultConfig); }, async getExposurePreferences(): Promise { return get('/api/settings/exposure', defaultConfig); }, async updateExposurePreferences(payload: ExposurePreferences): Promise { return patch('/api/settings/exposure', payload, defaultConfig); }, async getManagedAiConfig(): Promise { return get('/api/settings/ai', defaultConfig); }, async getManagedAiStatus(): Promise { return get('/api/settings/ai/status', defaultConfig); }, async updateManagedAiConfig(payload: ManagedAiConfigUpdate): Promise { return patch('/api/settings/ai', payload, defaultConfig); }, async testManagedAiInference(): Promise { return post('/api/settings/ai/test', {}, aiConfig); }, async getAISummary(): Promise { return get('/api/ai/status', defaultConfig); }, async getAIRecommendations(): Promise { return get('/api/ai/recommendations', defaultConfig); }, async getAIChatHistory(): Promise { return get('/api/ai/history', defaultConfig); }, async getAIAutopilotRules(): Promise { return get('/api/ai/tools', defaultConfig); }, async getAIMemory(): Promise { return get('/api/ai/memory', defaultConfig); }, async getAITools(): Promise { return get('/api/ai/tools', defaultConfig); }, async sendAIChatMessage(content: string): Promise { return post('/api/ai/chat', { content }, aiConfig); }, async getIntegrationStatus(): Promise { return get('/api/integrations/status', defaultConfig); }, async getRagStatus(): Promise { return get('/api/ai/rag/status', defaultConfig); }, async rebuildRagIndex(): Promise { return post('/api/ai/rag/reindex', {}, defaultConfig); }, async queryRag(question: string, limit = 6): Promise { return post('/api/ai/rag/query', { question, limit }, defaultConfig); }, async getAiSecurityStatus(): Promise { return get('/api/ai/security/status', defaultConfig); }, async getAiSecuritySummary(): Promise { return get('/api/ai/security/summary', defaultConfig); }, async getAiSecurityFindings(scope: AiSecurityScope = 'all'): Promise { return get(`/api/ai/security/findings?scope=${encodeURIComponent(scope)}`, defaultConfig); }, async getAiSecurityFinding(id: string): Promise { return get(`/api/ai/security/findings/${encodeURIComponent(id)}`, defaultConfig); }, async getAiSecurityRecommendations(): Promise { return get('/api/ai/security/recommendations', defaultConfig); }, async runAiSecurityReview(scope: AiSecurityScope = 'all'): Promise { return post(`/api/ai/security/review/scope/${encodeURIComponent(scope)}`, {}, defaultConfig); }, async acknowledgeAiSecurityFinding(id: string): Promise<{ success: boolean; status: string; finding?: AiSecurityFinding | null }> { return post<{ success: boolean; status: string; finding?: AiSecurityFinding | null }>(`/api/ai/security/findings/${encodeURIComponent(id)}/acknowledge`, {}, defaultConfig); }, async dismissAiSecurityFinding(id: string): Promise<{ success: boolean; status: string; finding?: AiSecurityFinding | null }> { return post<{ success: boolean; status: string; finding?: AiSecurityFinding | null }>(`/api/ai/security/findings/${encodeURIComponent(id)}/dismiss`, {}, defaultConfig); }, async snoozeAiSecurityRecommendation(id: string): Promise<{ success: boolean; status: string; applied: boolean; recommendation?: AiSecurityRecommendation | null }> { return post<{ success: boolean; status: string; applied: boolean; recommendation?: AiSecurityRecommendation | null }>(`/api/ai/security/recommendations/${encodeURIComponent(id)}/snooze`, {}, defaultConfig); }, async dismissAiSecurityRecommendation(id: string): Promise<{ success: boolean; status: string; applied: boolean; recommendation?: AiSecurityRecommendation | null }> { return post<{ success: boolean; status: string; applied: boolean; recommendation?: AiSecurityRecommendation | null }>(`/api/ai/security/recommendations/${encodeURIComponent(id)}/dismiss`, {}, defaultConfig); }, async acceptAiSecurityRecommendation(id: string): Promise<{ success: boolean; status: string; applied: boolean; recommendation?: AiSecurityRecommendation | null }> { return post<{ success: boolean; status: string; applied: boolean; recommendation?: AiSecurityRecommendation | null }>(`/api/ai/security/recommendations/${encodeURIComponent(id)}/accept`, {}, defaultConfig); }, async getRemediationStatus(): Promise> { return get>('/api/ai/remediation/status', defaultConfig); }, async getRemediationPlans(): Promise { return get('/api/ai/remediation/plans', defaultConfig); }, async getRemediationPlan(id: string): Promise { return get(`/api/ai/remediation/plans/${encodeURIComponent(id)}`, defaultConfig); }, async generateRemediationPlans(): Promise { return post('/api/ai/remediation/plans/generate', {}, defaultConfig); }, async generateRemediationFromFinding(id: string): Promise { return post(`/api/ai/remediation/plans/from-finding/${encodeURIComponent(id)}`, {}, defaultConfig); }, async generateRemediationFromRecommendation(id: string): Promise { return post(`/api/ai/remediation/plans/from-recommendation/${encodeURIComponent(id)}`, {}, defaultConfig); }, async reviewRemediationPlan(id: string): Promise<{ success: boolean; status: string; plan?: RemediationPlan | null }> { return post<{ success: boolean; status: string; plan?: RemediationPlan | null }>(`/api/ai/remediation/plans/${encodeURIComponent(id)}/review`, {}, defaultConfig); }, async dismissRemediationPlan(id: string): Promise<{ success: boolean; status: string; plan?: RemediationPlan | null }> { return post<{ success: boolean; status: string; plan?: RemediationPlan | null }>(`/api/ai/remediation/plans/${encodeURIComponent(id)}/dismiss`, {}, defaultConfig); }, async createRemediationApprovalProposal(id: string): Promise { return post(`/api/ai/remediation/plans/${encodeURIComponent(id)}/create-approval-proposal`, {}, defaultConfig); }, async dryRunRemediationPlan(id: string): Promise { return post(`/api/ai/remediation/plans/${encodeURIComponent(id)}/dry-run`, {}, defaultConfig); }, async getStatisticsSummary(): Promise { return get('/api/statistics/summary', defaultConfig); }, async getStorageSummary(): Promise { return get('/api/storage/summary', defaultConfig); }, async getUnraidDisks(): Promise { return get('/api/unraid/disks', defaultConfig); }, async getUnraidVms(): Promise { return get('/api/unraid/vms', defaultConfig); }, async getUnraidVmCapabilities(): Promise { return get('/api/unraid/vms/capabilities', defaultConfig); }, async runUnraidVmAction(vmId: string, action: string): Promise { return post(`/api/unraid/vms/${encodeURIComponent(vmId)}/actions/${encodeURIComponent(action)}`, { source: 'manual', confirm: true }, defaultConfig); }, async getUnraidShares(): Promise { return get('/api/unraid/shares', defaultConfig); }, async getUnraidDocker(): Promise { return get('/api/unraid/docker', defaultConfig); }, async getNetworkExposure(refresh = false): Promise { return get(`/api/network/exposure${refresh ? '?refresh=true' : ''}`, defaultConfig); }, async getSafetyDashboard(refresh = false): Promise { return get(`/api/safety/dashboard${refresh ? '?refresh=true' : ''}`, safetyConfig); }, async getSafetySummary(): Promise { return get('/api/safety/summary', safetyConfig); }, async getSafetySections(): Promise { return get('/api/safety/sections', safetyConfig); }, async getSafetyFindings(): Promise { return get('/api/safety/findings', safetyConfig); }, async getSafetyRecommendations(): Promise { return get('/api/safety/recommendations', safetyConfig); }, async getSafetyScoreBreakdown(): Promise { return get('/api/safety/score-breakdown', safetyConfig); }, async refreshSafety(): Promise { return post('/api/safety/refresh', {}, safetyConfig); }, async getNotificationSummary(): Promise { return get('/api/notifications/summary', defaultConfig); }, async getNotificationRules(): Promise { return get('/api/notifications/rules', defaultConfig); }, async createNotificationRule(payload: Partial): Promise { return post('/api/notifications/rules', payload, defaultConfig); }, async updateNotificationRule(id: string, payload: Partial): Promise { return request(`/api/notifications/rules/${encodeURIComponent(id)}`, defaultConfig, { method: 'PATCH', body: JSON.stringify(payload) }); }, async deleteNotificationRule(id: string): Promise<{ success: boolean; status: string }> { return request<{ success: boolean; status: string }>(`/api/notifications/rules/${encodeURIComponent(id)}`, defaultConfig, { method: 'DELETE' }); }, async getNotificationEvents(options: NotificationEventPageOptions = {}): Promise { return get(notificationEventPath('/api/notifications/events', options), defaultConfig); }, async getUnreadNotificationEvents(options: Omit = {}): Promise { return get(notificationEventPath('/api/notifications/events/unread', options), defaultConfig); }, async dismissNotificationEvent(id: string): Promise { return post(`/api/notifications/events/${encodeURIComponent(id)}/dismiss`, {}, defaultConfig); }, async markNotificationRead(id: string): Promise { return post(`/api/notifications/events/${encodeURIComponent(id)}/mark-read`, {}, defaultConfig); }, async markAllNotificationsRead(): Promise<{ success: boolean; updated: number }> { return post<{ success: boolean; updated: number }>('/api/notifications/events/mark-all-read', {}, defaultConfig); }, async getNotificationChannels(): Promise { return get('/api/notifications/channels', defaultConfig); }, async createNotificationChannel(payload: Partial): Promise { return post('/api/notifications/channels', payload, defaultConfig); }, async updateNotificationChannel(id: string, payload: Partial): Promise { return request(`/api/notifications/channels/${encodeURIComponent(id)}`, defaultConfig, { method: 'PATCH', body: JSON.stringify(payload) }); }, async testNotificationChannel(id: string): Promise<{ success: boolean; status: string; channel?: NotificationChannel }> { return post<{ success: boolean; status: string; channel?: NotificationChannel }>(`/api/notifications/channels/${encodeURIComponent(id)}/test`, {}, defaultConfig); }, async testNotificationChannelConnection(id: string): Promise<{ success: boolean; status: string; channel?: NotificationChannel }> { return post<{ success: boolean; status: string; channel?: NotificationChannel }>(`/api/notifications/channels/${encodeURIComponent(id)}/test-connection`, {}, defaultConfig); }, async evaluateNotifications(source: 'all' | 'safety' | 'media' | 'connectors' = 'all'): Promise { return post(source === 'all' ? '/api/notifications/evaluate' : `/api/notifications/evaluate/${source}`, {}, defaultConfig); }, async acknowledgeSafetyFinding(id: string): Promise<{ success: boolean; status: string }> { return post<{ success: boolean; status: string }>(`/api/safety/findings/${encodeURIComponent(id)}/acknowledge`, {}, defaultConfig); }, async dismissSafetyFinding(id: string): Promise<{ success: boolean; status: string }> { return post<{ success: boolean; status: string }>(`/api/safety/findings/${encodeURIComponent(id)}/dismiss`, {}, defaultConfig); }, async dismissSafetyRecommendation(id: string): Promise<{ success: boolean; status: string }> { return post<{ success: boolean; status: string }>(`/api/safety/recommendations/${encodeURIComponent(id)}/dismiss`, {}, defaultConfig); }, async createSafetyRemediationPlan(id: string): Promise<{ success: boolean; status: string; href: string; executable: boolean; planId?: string | null }> { return post<{ success: boolean; status: string; href: string; executable: boolean; planId?: string | null }>(`/api/safety/recommendations/${encodeURIComponent(id)}/create-remediation-plan`, {}, defaultConfig); }, async getAiToolPermissions(): Promise { return get('/api/ai/tools', defaultConfig); }, async getHealthChecks(): Promise { return get('/api/healthchecks', defaultConfig); }, async getGiteaActionsStatus(): Promise { return get('/api/gitea/actions/status', defaultConfig); }, async getRunnerFleet(): Promise { return get('/api/gitea/actions/fleet', defaultConfig); }, async getJobs(): Promise { const response = await get>>('/api/jobs', defaultConfig); return response.map(normalizeJob); }, async cancelJob(id: string): Promise { return post(`/api/jobs/${encodeURIComponent(id)}/cancel`, {}, defaultConfig); }, async getOperationMap(): Promise<{ generatedAt?: string; sources?: Record; nodes: string[]; edges: Array<{ from: string; to: string }>}> { return get<{ generatedAt?: string; sources?: Record; nodes: string[]; edges: Array<{ from: string; to: string }> }>('/api/operations-map', defaultConfig); }, async previewAction(action: ActionID | string, target: string): Promise { return post('/api/actions/preflight', { action, target }, defaultConfig); }, async getSonarrHealth(): Promise { return get('/api/apps/sonarr/health', defaultConfig); }, async getSonarrQueue(): Promise { return get('/api/apps/sonarr/queue', defaultConfig); }, async getSonarrWanted(): Promise { return get('/api/apps/sonarr/wanted', defaultConfig); }, async searchSonarrSeries(query: string): Promise { return get(`/api/apps/sonarr/search?q=${encodeURIComponent(query)}`, defaultConfig); }, async addSonarrSeries(payload: SonarrAddSeriesPayload): Promise { return post('/api/apps/sonarr/series', payload, defaultConfig); }, async getRadarrHealth(): Promise { return get('/api/apps/radarr/health', defaultConfig); }, async getRadarrQueue(): Promise { return get('/api/apps/radarr/queue', defaultConfig); }, async getRadarrWanted(): Promise { return get('/api/apps/radarr/wanted', defaultConfig); }, async searchRadarrMovie(query: string): Promise { return get(`/api/apps/radarr/search?q=${encodeURIComponent(query)}`, defaultConfig); }, async addRadarrMovie(payload: RadarrAddMoviePayload): Promise { return post('/api/apps/radarr/movie', payload, defaultConfig); }, async getProwlarrHealth(): Promise { return get('/api/apps/prowlarr/health', defaultConfig); }, async getProwlarrIndexers(): Promise { return get('/api/apps/prowlarr/indexers', defaultConfig); }, async getProwlarrApplications(): Promise { return get('/api/apps/prowlarr/applications', defaultConfig); }, async getProwlarrIndexerHealth(): Promise { return get('/api/apps/prowlarr/indexer-health', defaultConfig); }, async getProwlarrSyncStatus(): Promise { return get('/api/apps/prowlarr/sync-status', defaultConfig); }, async searchProwlarr(query: string): Promise { return get(`/api/apps/prowlarr/search-test?q=${encodeURIComponent(query)}`, defaultConfig); }, async getDelugeHealth(): Promise { return get('/api/apps/deluge/health', defaultConfig); }, async getDelugeSession(): Promise { return get('/api/apps/deluge/session', defaultConfig); }, async getDelugeTorrents(): Promise { return get('/api/apps/deluge/torrents', defaultConfig); }, async getDelugeActive(): Promise { return get('/api/apps/deluge/active', defaultConfig); }, async getDelugeStalled(): Promise { return get('/api/apps/deluge/stalled', defaultConfig); }, async getDelugePaused(): Promise { return get('/api/apps/deluge/paused', defaultConfig); }, async getDelugeSpeeds(): Promise { return get('/api/apps/deluge/speeds', defaultConfig); }, async pauseDelugeTorrent(torrentId: string, payload: { source?: SourceType } = { source: 'manual' }): Promise { return post(`/api/apps/deluge/torrents/${encodeURIComponent(torrentId)}/pause`, payload, defaultConfig); }, async resumeDelugeTorrent(torrentId: string, payload: { source?: SourceType } = { source: 'manual' }): Promise { return post(`/api/apps/deluge/torrents/${encodeURIComponent(torrentId)}/resume`, payload, defaultConfig); }, async pauseAllDeluge(payload: { source?: SourceType } = { source: 'manual' }): Promise { return post('/api/apps/deluge/pause-all', payload, defaultConfig); }, async resumeAllDeluge(payload: { source?: SourceType } = { source: 'manual' }): Promise { return post('/api/apps/deluge/resume-all', payload, defaultConfig); }, async getPlexHealth(): Promise { return get('/api/apps/plex/health', defaultConfig); }, async getPlexServer(): Promise { return get('/api/apps/plex/server', defaultConfig); }, async getPlexLibraries(): Promise { return get('/api/apps/plex/libraries', defaultConfig); }, async getPlexSessions(): Promise { return get('/api/apps/plex/sessions', defaultConfig); }, async getPlexRecent(): Promise { return get('/api/apps/plex/recent', defaultConfig); }, async getPlexStreams(): Promise { return get('/api/apps/plex/streams', defaultConfig); }, async scanPlexLibrary(libraryId: string, payload: { source?: SourceType } = { source: 'manual' }): Promise { return post(`/api/apps/plex/libraries/${encodeURIComponent(libraryId)}/scan`, payload, defaultConfig); }, async scanAllPlex(payload: { source?: SourceType } = { source: 'manual' }): Promise { return post('/api/apps/plex/libraries/scan-all', payload, defaultConfig); }, async getTautulliHealth(): Promise { return get('/api/apps/tautulli/health', defaultConfig); }, async getTautulliStatus(): Promise { return get('/api/apps/tautulli/status', defaultConfig); }, async getTautulliActivity(): Promise { return get('/api/apps/tautulli/activity', defaultConfig); }, async getTautulliHistory(): Promise { return get('/api/apps/tautulli/history', defaultConfig); }, async getTautulliUsers(): Promise { return get('/api/apps/tautulli/users', defaultConfig); }, async getTautulliLibraries(): Promise { return get('/api/apps/tautulli/libraries', defaultConfig); }, async getTautulliRecentlyAdded(): Promise { return get('/api/apps/tautulli/recently-added', defaultConfig); }, async getTautulliWatchStats(): Promise { return get('/api/apps/tautulli/watch-stats', defaultConfig); }, async getTautulliStreams(): Promise { return get('/api/apps/tautulli/streams', defaultConfig); }, async getTautulliPlexInfo(): Promise { return get('/api/apps/tautulli/plex-info', defaultConfig); }, async getNpmHealth(): Promise { return get('/api/apps/nginx-proxy-manager/health', defaultConfig); }, async getNpmProxyHosts(): Promise { return get('/api/apps/nginx-proxy-manager/proxy-hosts', defaultConfig); }, async getNpmCertificates(): Promise { return get('/api/apps/nginx-proxy-manager/certificates', defaultConfig); }, async getNpmExpiringCertificates(days = 30): Promise { return get(`/api/apps/nginx-proxy-manager/certificates/expiring?days=${days}`, defaultConfig); }, async getNpmAccessLists(): Promise { return get('/api/apps/nginx-proxy-manager/access-lists', defaultConfig); }, async getNpmExposureSummary(): Promise { return get('/api/apps/nginx-proxy-manager/exposure-summary', defaultConfig); }, async getNpmHostReachability(): Promise { return get('/api/apps/nginx-proxy-manager/host-reachability', defaultConfig); }, async getNpmSecurityWarnings(): Promise { return get('/api/apps/nginx-proxy-manager/security-warnings', defaultConfig); }, async getAuthentikHealth(): Promise { return get('/api/apps/authentik/health', defaultConfig); }, async getAuthentikSystem(): Promise { return get('/api/apps/authentik/system', defaultConfig); }, async getAuthentikApplications(): Promise> { return get>('/api/apps/authentik/applications', defaultConfig); }, async getAuthentikProviders(): Promise> { return get>('/api/apps/authentik/providers', defaultConfig); }, async getAuthentikProxyProviders(): Promise> { return get>('/api/apps/authentik/providers/proxy', defaultConfig); }, async getAuthentikOAuth2Providers(): Promise> { return get>('/api/apps/authentik/providers/oauth2', defaultConfig); }, async getAuthentikOutposts(): Promise> { return get>('/api/apps/authentik/outposts', defaultConfig); }, async getAuthentikOutpostHealth(): Promise { return get('/api/apps/authentik/outposts/health', defaultConfig); }, async getAuthentikFlows(): Promise { return get('/api/apps/authentik/flows', defaultConfig); }, async getAuthentikPolicies(): Promise { return get('/api/apps/authentik/policies', defaultConfig); }, async getAuthentikEvents(): Promise> { return get>('/api/apps/authentik/events', defaultConfig); }, async getAuthentikAuthCoverage(): Promise { return get('/api/apps/authentik/auth-coverage', defaultConfig); }, async getAuthentikSecurityWarnings(): Promise { return get('/api/apps/authentik/security-warnings', defaultConfig); }, async getMediaPipelineSummary(refresh = false): Promise { return get(`/api/media-pipeline/summary${refresh ? '?refresh=true' : ''}`, mediaPipelineConfig); }, async getMediaPipelineHealth(): Promise { return get('/api/media-pipeline/health', mediaPipelineConfig); }, async getMediaPipelineFlow(): Promise { return get('/api/media-pipeline/flow', mediaPipelineConfig); }, async getMediaPipelineIssues(): Promise { return get('/api/media-pipeline/issues', mediaPipelineConfig); }, async getMediaPipelineDownloads(): Promise { return get('/api/media-pipeline/downloads', mediaPipelineConfig); }, async getMediaPipelineLibrary(): Promise { return get('/api/media-pipeline/library', mediaPipelineConfig); }, async getMediaPipelineActivity(): Promise { return get('/api/media-pipeline/activity', mediaPipelineConfig); }, async getMediaPipelineRecommendations(): Promise { return get('/api/media-pipeline/recommendations', mediaPipelineConfig); }, async checkMediaPipeline(): Promise { return post('/api/media-pipeline/check', {}, mediaPipelineConfig); }, async diagnoseMediaPipeline(payload: { query: string; target?: string; scope?: string }): Promise { return post('/api/media-pipeline/diagnose', payload, mediaPipelineConfig); }, async runInventoryDiscovery(): Promise { return post('/api/inventory/discover', {}, defaultConfig); }, async getInventoryRuns(limit = 100, offset = 0): Promise { return get(`/api/inventory/runs?limit=${Math.max(1, Math.min(limit, 500))}&offset=${Math.max(0, offset)}`, inventoryConfig); }, async getDiscoveredContainers(): Promise { return get('/api/inventory/discovered-containers', inventoryConfig); }, async getInventoryContainers(): Promise { return get('/api/inventory/containers', defaultConfig); }, async getInventoryApps(): Promise { return get('/api/inventory/apps?include_unmanaged=true', inventoryConfig); }, async getInventoryUnmapped(): Promise { return get('/api/inventory/unmapped', defaultConfig); }, async getInventoryUnknown(): Promise { return get('/api/inventory/unknown', defaultConfig); }, async getInventorySuggestions(): Promise { return get('/api/inventory/suggestions', defaultConfig); }, async getInventoryMappings(): Promise { return get('/api/inventory/mappings', inventoryConfig); }, async getInventoryDrift(): Promise { return get('/api/inventory/drift', inventoryConfig); }, async acknowledgeInventoryDrift(id: string): Promise { return post(`/api/inventory/drift/${encodeURIComponent(id)}/acknowledge`, {}, defaultConfig); }, async createInventoryDriftIncident(id: string): Promise { return post(`/api/inventory/drift/${encodeURIComponent(id)}/incident`, {}, defaultConfig); }, async importDiscoveredContainer(id: string): Promise { return post('/api/inventory/import', { discoveredContainerId: id }, defaultConfig); }, async ignoreDiscoveredContainer(id: string): Promise { return post('/api/inventory/ignore', { discoveredContainerId: id }, defaultConfig); }, async updateDiscoveredReview(payload: InventoryReviewUpdatePayload): Promise { return post('/api/inventory/review/update', payload, defaultConfig); }, async mapDiscoveredToExisting(payload: InventoryMapExistingPayload): Promise { return post('/api/inventory/map-existing', payload, defaultConfig); }, async completeInventoryReview(): Promise { return post('/api/inventory/review/complete', {}, defaultConfig); }, async updateInventoryBaseline(): Promise { return post('/api/inventory/baseline/update', {}, defaultConfig); }, async getWidefrogHealth(): Promise { return get('/api/apps/widefrog/health', defaultConfig); }, async getWidefrogQueue(): Promise { return get('/api/apps/widefrog/queue', defaultConfig); }, async getWidefrogFailedJobs(): Promise { return get('/api/apps/widefrog/failed', defaultConfig); }, async getWidefrogActivity(): Promise { return get('/api/apps/widefrog/activity', defaultConfig); }, async getWidefrogDownloadStatus(): Promise { return get('/api/apps/widefrog/download-status', defaultConfig); }, async retryWidefrogFailed(payload: Record = {}): Promise { return post('/api/apps/widefrog/retry-failed', payload, defaultConfig); }, async scanWidefrogLibrary(payload: Record = {}): Promise { return post('/api/apps/widefrog/scan-library', payload, defaultConfig); }, });