From 3e3cc904f76b213d07b74edb9700e120d676b0d5 Mon Sep 17 00:00:00 2001 From: Oliver Kieselbach Date: Tue, 10 Feb 2026 02:37:31 +0100 Subject: [PATCH] Agent re-architectured - new IME log parser - IME log replay option - revised timeline and enrollment tracker UI - removed flight tracker - fixed download tracker - nice progress portal page --- .gitignore | 2 +- .vscode/extensions.json | 6 + .vscode/launch.json | 11 + .vscode/settings.json | 8 + .vscode/tasks.json | 81 ++ .../Configuration/AgentConfiguration.cs | 20 + .../Configuration/RemoteConfigService.cs | 5 +- .../EventCollection/AutopilotSimulator.cs | 522 -------- .../CertValidationCollector.cs | 235 ---- .../DownloadProgressCollector.cs | 212 ---- .../EventCollection/EspUiStateCollector.cs | 254 ---- .../EventCollection/EventLogWatcher.cs | 185 --- .../EventCollection/PhaseDetector.cs | 238 ---- .../EventCollection/RegistryMonitor.cs | 154 --- .../Collectors}/GatherRuleExecutor.cs | 187 ++- .../Collectors}/HelloDetector.cs | 6 +- .../Collectors}/PerformanceCollector.cs | 3 +- .../Core}/EventSpool.cs | 31 +- .../{ => Core}/MonitoringService.cs | 191 +-- .../Core}/SessionPersistence.cs | 2 +- .../Network}/BackendApiClient.cs | 2 +- .../Network/GeoLocationService.cs | 2 +- .../Simulation/AutopilotSimulator.cs | 202 +++ .../Monitoring/Tracking/AppPackageState.cs | 314 +++++ .../Tracking/AppPackageStateList.cs | 406 ++++++ .../Monitoring/Tracking/CmTraceLogParser.cs | 123 ++ .../Monitoring/Tracking/EnrollmentTracker.cs | 699 +++++++++++ .../Monitoring/Tracking/ImeLogTracker.cs | 602 +++++++++ .../Tracking/LogFilePositionTracker.cs | 75 ++ .../AutopilotMonitorService.cs | 3 +- src/Agent/AutopilotMonitor.Agent/Program.cs | 49 +- .../Functions/GetAgentConfigFunction.cs | 13 +- .../Functions/IngestEventsFunction.cs | 2 +- .../Services/BuiltInGatherRules.cs | 234 +--- .../Services/BuiltInImeLogPatterns.cs | 457 +++++++ .../Services/TableStorageService.cs | 62 +- .../Services/TenantConfigurationService.cs | 14 +- .../AutopilotMonitor.Shared/Constants.cs | 2 +- .../Models/AgentConfigResponse.cs | 57 +- .../Models/EnrollmentEvent.cs | 16 +- .../Models/EnrollmentPhase.cs | 32 +- .../Models/GatherRule.cs | 4 +- .../Models/ImeLogPattern.cs | 67 + .../Models/TenantConfiguration.cs | 39 +- .../app/flight-tracker/[sessionId]/page.tsx | 1108 ----------------- .../app/progress/page.tsx | 120 +- .../app/sessions/[sessionId]/page.tsx | 442 ++++++- .../app/settings/page.tsx | 106 +- .../components/DownloadProgress.tsx | 57 +- .../tsconfig.tsbuildinfo | 2 +- 50 files changed, 4061 insertions(+), 3603 deletions(-) create mode 100644 .vscode/extensions.json create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 .vscode/tasks.json delete mode 100644 src/Agent/AutopilotMonitor.Agent.Core/EventCollection/AutopilotSimulator.cs delete mode 100644 src/Agent/AutopilotMonitor.Agent.Core/EventCollection/CertValidationCollector.cs delete mode 100644 src/Agent/AutopilotMonitor.Agent.Core/EventCollection/DownloadProgressCollector.cs delete mode 100644 src/Agent/AutopilotMonitor.Agent.Core/EventCollection/EspUiStateCollector.cs delete mode 100644 src/Agent/AutopilotMonitor.Agent.Core/EventCollection/EventLogWatcher.cs delete mode 100644 src/Agent/AutopilotMonitor.Agent.Core/EventCollection/PhaseDetector.cs delete mode 100644 src/Agent/AutopilotMonitor.Agent.Core/EventCollection/RegistryMonitor.cs rename src/Agent/AutopilotMonitor.Agent.Core/{EventCollection => Monitoring/Collectors}/GatherRuleExecutor.cs (73%) rename src/Agent/AutopilotMonitor.Agent.Core/{EventCollection => Monitoring/Collectors}/HelloDetector.cs (98%) rename src/Agent/AutopilotMonitor.Agent.Core/{EventCollection => Monitoring/Collectors}/PerformanceCollector.cs (98%) rename src/Agent/AutopilotMonitor.Agent.Core/{Storage => Monitoring/Core}/EventSpool.cs (83%) rename src/Agent/AutopilotMonitor.Agent.Core/Monitoring/{ => Core}/MonitoringService.cs (86%) rename src/Agent/AutopilotMonitor.Agent.Core/{Storage => Monitoring/Core}/SessionPersistence.cs (98%) rename src/Agent/AutopilotMonitor.Agent.Core/{Api => Monitoring/Network}/BackendApiClient.cs (99%) rename src/Agent/AutopilotMonitor.Agent.Core/{ => Monitoring}/Network/GeoLocationService.cs (98%) create mode 100644 src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Simulation/AutopilotSimulator.cs create mode 100644 src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Tracking/AppPackageState.cs create mode 100644 src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Tracking/AppPackageStateList.cs create mode 100644 src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Tracking/CmTraceLogParser.cs create mode 100644 src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Tracking/EnrollmentTracker.cs create mode 100644 src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Tracking/ImeLogTracker.cs create mode 100644 src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Tracking/LogFilePositionTracker.cs create mode 100644 src/Backend/AutopilotMonitor.Functions/Services/BuiltInImeLogPatterns.cs create mode 100644 src/Shared/AutopilotMonitor.Shared/Models/ImeLogPattern.cs delete mode 100644 src/Web/autopilot-monitor-web/app/flight-tracker/[sessionId]/page.tsx diff --git a/.gitignore b/.gitignore index f03b735c..266805d0 100644 --- a/.gitignore +++ b/.gitignore @@ -16,7 +16,7 @@ bld/ # Visual Studio cache/options .vs/ -.vscode/ + *.rsuser *.suo *.user diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..bb763007 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,6 @@ +{ + "recommendations": [ + "ms-azuretools.vscode-azurefunctions", + "ms-dotnettools.csharp" + ] +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..894cbe6a --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,11 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Attach to .NET Functions", + "type": "coreclr", + "request": "attach", + "processId": "${command:azureFunctions.pickProcess}" + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..fb7f4c1c --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "azureFunctions.projectSubpath": "src\\Backend\\AutopilotMonitor.Functions", + "azureFunctions.deploySubpath": "src\\Backend\\AutopilotMonitor.Functions/bin/Release/net8.0/publish", + "azureFunctions.projectLanguage": "C#", + "azureFunctions.projectRuntime": "~4", + "debug.internalConsoleOptions": "neverOpen", + "azureFunctions.preDeployTask": "publish (functions)" +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 00000000..3d38290c --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,81 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "clean (functions)", + "command": "dotnet", + "args": [ + "clean", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "type": "process", + "problemMatcher": "$msCompile", + "options": { + "cwd": "${workspaceFolder}/src\\Backend\\AutopilotMonitor.Functions" + } + }, + { + "label": "build (functions)", + "command": "dotnet", + "args": [ + "build", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "type": "process", + "dependsOn": "clean (functions)", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": "$msCompile", + "options": { + "cwd": "${workspaceFolder}/src\\Backend\\AutopilotMonitor.Functions" + } + }, + { + "label": "clean release (functions)", + "command": "dotnet", + "args": [ + "clean", + "--configuration", + "Release", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "type": "process", + "problemMatcher": "$msCompile", + "options": { + "cwd": "${workspaceFolder}/src\\Backend\\AutopilotMonitor.Functions" + } + }, + { + "label": "publish (functions)", + "command": "dotnet", + "args": [ + "publish", + "--configuration", + "Release", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "type": "process", + "dependsOn": "clean release (functions)", + "problemMatcher": "$msCompile", + "options": { + "cwd": "${workspaceFolder}/src\\Backend\\AutopilotMonitor.Functions" + } + }, + { + "type": "func", + "dependsOn": "build (functions)", + "options": { + "cwd": "${workspaceFolder}/src\\Backend\\AutopilotMonitor.Functions/bin/Debug/net8.0" + }, + "command": "host start", + "isBackground": true, + "problemMatcher": "$func-dotnet-watch" + } + ] +} \ No newline at end of file diff --git a/src/Agent/AutopilotMonitor.Agent.Core/Configuration/AgentConfiguration.cs b/src/Agent/AutopilotMonitor.Agent.Core/Configuration/AgentConfiguration.cs index b02cb2c3..a2cac1cb 100644 --- a/src/Agent/AutopilotMonitor.Agent.Core/Configuration/AgentConfiguration.cs +++ b/src/Agent/AutopilotMonitor.Agent.Core/Configuration/AgentConfiguration.cs @@ -97,6 +97,26 @@ namespace AutopilotMonitor.Agent.Core.Configuration /// public bool EnableGeoLocation { get; set; } = true; + /// + /// Optional custom path to IME logs directory for testing. + /// If set, overrides the default %ProgramData%\Microsoft\IntuneManagementExtension\Logs path. + /// + public string ImeLogPathOverride { get; set; } + + /// + /// Path to directory with real IME log files for simulation replay. + /// When set (with EnableSimulator), the simulator replays these logs with time compression + /// instead of generating artificial events. + /// + public string SimulationLogDirectory { get; set; } + + /// + /// Time compression factor for simulation mode. + /// Higher values = faster replay. Default: 50 (e.g., 50 minutes of real logs = 1 minute of replay). + /// Targeting 3-5 minute total enrollment replay. + /// + public double SimulationSpeedFactor { get; set; } = 50; + /// /// Validates the configuration /// diff --git a/src/Agent/AutopilotMonitor.Agent.Core/Configuration/RemoteConfigService.cs b/src/Agent/AutopilotMonitor.Agent.Core/Configuration/RemoteConfigService.cs index 174344b6..9bd8caa0 100644 --- a/src/Agent/AutopilotMonitor.Agent.Core/Configuration/RemoteConfigService.cs +++ b/src/Agent/AutopilotMonitor.Agent.Core/Configuration/RemoteConfigService.cs @@ -2,7 +2,7 @@ using System; using System.IO; using System.Threading; using System.Threading.Tasks; -using AutopilotMonitor.Agent.Core.Api; +using AutopilotMonitor.Agent.Core.Monitoring.Network; using AutopilotMonitor.Agent.Core.Logging; using AutopilotMonitor.Shared.Models; using Newtonsoft.Json; @@ -175,9 +175,6 @@ namespace AutopilotMonitor.Agent.Core.Configuration _logger.Info(" Collector settings:"); _logger.Info($" Performance: {(collectors.EnablePerformanceCollector ? "ON" : "OFF")} (interval: {collectors.PerformanceIntervalSeconds}s)"); - _logger.Info($" Download Progress: {(collectors.EnableDownloadProgressCollector ? "ON" : "OFF")} (interval: {collectors.DownloadProgressIntervalSeconds}s)"); - _logger.Info($" Cert Validation: {(collectors.EnableCertValidationCollector ? "ON" : "OFF")}"); - _logger.Info($" ESP UI State: {(collectors.EnableEspUiStateCollector ? "ON" : "OFF")} (interval: {collectors.EspUiStateIntervalSeconds}s)"); } private void CacheConfig(AgentConfigResponse config) diff --git a/src/Agent/AutopilotMonitor.Agent.Core/EventCollection/AutopilotSimulator.cs b/src/Agent/AutopilotMonitor.Agent.Core/EventCollection/AutopilotSimulator.cs deleted file mode 100644 index e70d6755..00000000 --- a/src/Agent/AutopilotMonitor.Agent.Core/EventCollection/AutopilotSimulator.cs +++ /dev/null @@ -1,522 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using AutopilotMonitor.Agent.Core.Logging; -using AutopilotMonitor.Shared.Models; - -namespace AutopilotMonitor.Agent.Core.EventCollection -{ - /// - /// Simulates a realistic Autopilot enrollment flow for testing and demo purposes - /// - public class AutopilotSimulator : IDisposable - { - private readonly AgentLogger _logger; - private readonly string _sessionId; - private readonly string _tenantId; - private readonly Action _onEventCollected; - private readonly bool _simulateFailure; - private readonly Random _random = new Random(); - private CancellationTokenSource _cancellationTokenSource; - private CancellationTokenSource _perfCancellationTokenSource; - private Task _simulationTask; - private EnrollmentPhase _currentPhase = EnrollmentPhase.PreFlight; - private bool _disposed = false; - private double _simulatedDiskFreeGb; - private readonly double _simulatedDiskTotalGb; - - public AutopilotSimulator( - string sessionId, - string tenantId, - Action onEventCollected, - AgentLogger logger, - bool simulateFailure = false) - { - _sessionId = sessionId ?? throw new ArgumentNullException(nameof(sessionId)); - _tenantId = tenantId ?? throw new ArgumentNullException(nameof(tenantId)); - _onEventCollected = onEventCollected ?? throw new ArgumentNullException(nameof(onEventCollected)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _simulateFailure = simulateFailure; - _simulatedDiskTotalGb = 237.0 + _random.Next(0, 20); // ~237-256 GB typical SSD - _simulatedDiskFreeGb = _simulatedDiskTotalGb - _random.Next(40, 60); // Start with 40-60 GB used - } - - /// - /// Starts the Autopilot simulation - /// - public void Start() - { - _logger.Info("Starting Autopilot Simulator"); - _cancellationTokenSource = new CancellationTokenSource(); - _simulationTask = Task.Run(() => RunSimulation(_cancellationTokenSource.Token)); - } - - /// - /// Stops the simulation - /// - public void Stop() - { - if (_cancellationTokenSource == null || _cancellationTokenSource.IsCancellationRequested) - return; - - _logger.Info("Stopping Autopilot Simulator"); - - try - { - _cancellationTokenSource?.Cancel(); - _simulationTask?.Wait(TimeSpan.FromSeconds(5)); - } - catch (ObjectDisposedException) - { - // Already disposed, ignore - } - } - - private async Task RunSimulation(CancellationToken cancellationToken) - { - try - { - _logger.Info("Starting simulated Autopilot enrollment flow"); - - // Start background performance snapshot emitter (with its own token so we can stop it on completion) - _perfCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); - var perfTask = EmitPerformanceSnapshotsAsync(_perfCancellationTokenSource.Token); - - // Phase 1: Network - await SimulatePhase(EnrollmentPhase.Network, cancellationToken); - await EmitPhaseEvents(EnrollmentPhase.Network, new[] - { - ("network_connected", "Network connection established", EventSeverity.Info), - ("wifi_connected", "Connected to WiFi network", EventSeverity.Info), - ("internet_check", "Internet connectivity verified", EventSeverity.Info) - }, cancellationToken); - - // Phase 2: Identity - await SimulatePhase(EnrollmentPhase.Identity, cancellationToken); - await EmitPhaseEvents(EnrollmentPhase.Identity, new[] - { - ("aad_join_start", "Starting Azure AD join", EventSeverity.Info), - ("device_registration", "Registering device with Azure AD", EventSeverity.Info), - ("certificate_request", "Requesting device certificate", EventSeverity.Info), - ("aad_join_complete", "Azure AD join completed successfully", EventSeverity.Info) - }, cancellationToken); - - // Phase 3: MDM Enrollment - await SimulatePhase(EnrollmentPhase.MdmEnrollment, cancellationToken); - await EmitPhaseEvents(EnrollmentPhase.MdmEnrollment, new[] - { - ("mdm_discovery", "Discovering MDM endpoints", EventSeverity.Info), - ("intune_enrollment_start", "Starting Intune enrollment", EventSeverity.Info), - ("policy_sync", "Syncing initial policies", EventSeverity.Info), - ("profile_download", "Downloaded Autopilot profile", EventSeverity.Info), - ("mdm_enrollment_complete", "MDM enrollment completed", EventSeverity.Info) - }, cancellationToken); - - // Phase 4: ESP Device Setup - await SimulatePhase(EnrollmentPhase.EspDeviceSetup, cancellationToken); - - if (_simulateFailure && _random.Next(0, 100) < 30) - { - // Simulate a failure during device setup (30% chance if failure mode enabled) - await EmitPhaseEvents(EnrollmentPhase.EspDeviceSetup, new[] - { - ("esp_device_start", "Starting ESP Device Setup", EventSeverity.Info), - ("device_compliance_check", "Checking device compliance", EventSeverity.Info), - ("security_baseline_apply", "Applying security baseline", EventSeverity.Warning), - ("bitlocker_encryption", "BitLocker encryption failed", EventSeverity.Error), - }, cancellationToken); - - await SimulatePhase(EnrollmentPhase.Failed, cancellationToken); - EmitEvent(new EnrollmentEvent - { - SessionId = _sessionId, - TenantId = _tenantId, - Timestamp = DateTime.UtcNow, - EventType = "enrollment_failed", - Severity = EventSeverity.Critical, - Source = "Simulator", - Phase = EnrollmentPhase.Failed, - Message = "Enrollment failed: BitLocker encryption error", - Data = new Dictionary - { - { "errorCode", "0x80070002" }, - { "failedPhase", "EspDeviceSetup" } - } - }); - - _logger.Warning("Simulated enrollment failure"); - StopPerformanceSnapshots(); - return; - } - - await EmitPhaseEvents(EnrollmentPhase.EspDeviceSetup, new[] - { - ("esp_device_start", "Starting ESP Device Setup", EventSeverity.Info), - ("device_compliance_check", "Checking device compliance", EventSeverity.Info), - ("security_baseline_apply", "Applying security baseline", EventSeverity.Info), - ("bitlocker_encryption", "Enabling BitLocker encryption", EventSeverity.Info), - ("device_config_apply", "Applying device configurations", EventSeverity.Info), - ("esp_device_complete", "ESP Device Setup completed", EventSeverity.Info) - }, cancellationToken); - - // Phase 5: App Installation - await SimulatePhase(EnrollmentPhase.AppInstallation, cancellationToken); - EmitEvent(new EnrollmentEvent - { - SessionId = _sessionId, - TenantId = _tenantId, - Timestamp = DateTime.UtcNow, - EventType = "app_install_start", - Severity = EventSeverity.Info, - Source = "Simulator", - Phase = EnrollmentPhase.AppInstallation, - Message = "Starting application installation", - Data = new Dictionary { { "simulated", true } } - }); - await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken); - - // Simulate realistic download progress for multiple apps - await SimulateAppDownloads(cancellationToken); - - EmitEvent(new EnrollmentEvent - { - SessionId = _sessionId, - TenantId = _tenantId, - Timestamp = DateTime.UtcNow, - EventType = "app_install_complete", - Severity = EventSeverity.Info, - Source = "Simulator", - Phase = EnrollmentPhase.AppInstallation, - Message = "All required apps installed", - Data = new Dictionary { { "simulated", true }, { "appsInstalled", 5 } } - }); - - // Phase 6: ESP User Setup - await SimulatePhase(EnrollmentPhase.EspUserSetup, cancellationToken); - await EmitPhaseEvents(EnrollmentPhase.EspUserSetup, new[] - { - ("esp_user_start", "Starting ESP User Setup", EventSeverity.Info), - ("user_policy_sync", "Syncing user policies", EventSeverity.Info), - ("user_profile_setup", "Setting up user profile", EventSeverity.Info), - ("user_certs_install", "Installing user certificates", EventSeverity.Info), - ("esp_user_complete", "ESP User Setup completed", EventSeverity.Info) - }, cancellationToken); - - // Phase 7: Complete - await SimulatePhase(EnrollmentPhase.Complete, cancellationToken); - EmitEvent(new EnrollmentEvent - { - SessionId = _sessionId, - TenantId = _tenantId, - Timestamp = DateTime.UtcNow, - EventType = "enrollment_complete", - Severity = EventSeverity.Info, - Source = "Simulator", - Phase = EnrollmentPhase.Complete, - Message = "Autopilot enrollment completed successfully", - Data = new Dictionary - { - { "totalDurationSeconds", 420 }, // ~7 minutes - { "appsInstalled", 3 }, - { "policiesApplied", 15 } - } - }); - - _logger.Info("Simulated Autopilot enrollment completed successfully"); - StopPerformanceSnapshots(); - } - catch (OperationCanceledException) - { - _logger.Info("Autopilot simulation cancelled"); - } - catch (Exception ex) - { - _logger.Error("Error in Autopilot simulation", ex); - } - } - - /// - /// Emits periodic performance_snapshot events throughout the simulation. - /// Disk free GB decreases gradually as apps are installed. - /// - private async Task EmitPerformanceSnapshotsAsync(CancellationToken cancellationToken) - { - try - { - while (!cancellationToken.IsCancellationRequested) - { - // CPU: fluctuates between 5-95%, higher during app install phase - var baseCpu = _currentPhase == EnrollmentPhase.AppInstallation ? 40.0 : 15.0; - var cpuPercent = Math.Min(100, baseCpu + _random.Next(-10, 40)); - - // Memory: 8-16 GB total, usage increases over time - var memTotalMb = 16384.0; // 16 GB - var memUsedPercent = 35.0 + _random.Next(0, 25); - var memAvailMb = memTotalMb * (1.0 - memUsedPercent / 100.0); - - // Disk queue: higher during installs - var diskQueue = _currentPhase == EnrollmentPhase.AppInstallation - ? 1.0 + _random.NextDouble() * 6.0 - : _random.NextDouble() * 2.0; - - // Disk free: smooth gradual decline throughout enrollment - // Small consistent decrements to produce a clean downward slope - if (_currentPhase == EnrollmentPhase.AppInstallation) - { - _simulatedDiskFreeGb -= (0.4 + _random.NextDouble() * 0.2); // 0.4-0.6 GB per snapshot (main install) - } - else if (_currentPhase == EnrollmentPhase.EspDeviceSetup) - { - _simulatedDiskFreeGb -= (0.15 + _random.NextDouble() * 0.1); // 0.15-0.25 GB (policies/configs) - } - else if (_currentPhase >= EnrollmentPhase.Network && _currentPhase <= EnrollmentPhase.MdmEnrollment) - { - _simulatedDiskFreeGb -= (0.05 + _random.NextDouble() * 0.05); // 0.05-0.1 GB (small overhead) - } - - EmitEvent(new EnrollmentEvent - { - SessionId = _sessionId, - TenantId = _tenantId, - Timestamp = DateTime.UtcNow, - EventType = "performance_snapshot", - Severity = EventSeverity.Debug, - Source = "Simulator", - Phase = _currentPhase, - Message = $"CPU: {cpuPercent:F1}%, Memory: {memUsedPercent:F1}%, Disk Free: {_simulatedDiskFreeGb:F1} GB", - Data = new Dictionary - { - { "cpu_percent", Math.Round(cpuPercent, 1) }, - { "memory_available_mb", Math.Round(memAvailMb, 0) }, - { "memory_total_mb", memTotalMb }, - { "memory_used_percent", Math.Round(memUsedPercent, 1) }, - { "disk_queue_length", Math.Round(diskQueue, 1) }, - { "disk_free_gb", Math.Round(_simulatedDiskFreeGb, 1) }, - { "disk_total_gb", Math.Round(_simulatedDiskTotalGb, 1) }, - { "simulated", true } - } - }); - - await Task.Delay(TimeSpan.FromSeconds(_random.Next(8, 15)), cancellationToken); - } - } - catch (OperationCanceledException) - { - // Expected when simulation ends - } - } - - /// - /// Simulates realistic download progress for multiple apps with staggered starts, - /// incremental progress updates, and proper completion events - /// - private async Task SimulateAppDownloads(CancellationToken cancellationToken) - { - var apps = new[] - { - new { Name = "Microsoft 365 Apps for Enterprise", SizeBytes = (long)(2.1 * 1024 * 1024 * 1024) }, - new { Name = "Microsoft Teams", SizeBytes = (long)(180 * 1024 * 1024) }, - new { Name = "Microsoft Edge", SizeBytes = (long)(160 * 1024 * 1024) }, - new { Name = "Company Portal", SizeBytes = (long)(45 * 1024 * 1024) }, - new { Name = "Global Protect VPN", SizeBytes = (long)(95 * 1024 * 1024) }, - }; - - // Track progress per app - var progress = new long[apps.Length]; - var completed = new bool[apps.Length]; - var started = new bool[apps.Length]; - // Stagger start: app 0 starts immediately, others start after delays - var startAtTick = new int[apps.Length]; - startAtTick[0] = 0; - for (int i = 1; i < apps.Length; i++) - { - startAtTick[i] = _random.Next(2, 5) + startAtTick[i - 1]; - } - - int tick = 0; - while (!AllCompleted(completed)) - { - cancellationToken.ThrowIfCancellationRequested(); - - for (int i = 0; i < apps.Length; i++) - { - if (completed[i]) continue; - if (tick < startAtTick[i]) continue; - - if (!started[i]) - { - started[i] = true; - // Emit start event - EmitDownloadProgressEvent(apps[i].Name, 0, apps[i].SizeBytes, 0, "downloading"); - continue; - } - - // Simulate download chunk: variable speed between 15-80 MB/s - var speedMbps = _random.Next(15, 80); - var chunkBytes = (long)(speedMbps * 1024 * 1024 * (2.0 + _random.NextDouble())); // ~2-3 seconds worth - progress[i] = Math.Min(progress[i] + chunkBytes, apps[i].SizeBytes); - var rateBps = speedMbps * 1024.0 * 1024.0; - - if (progress[i] >= apps[i].SizeBytes) - { - // Completed - completed[i] = true; - EmitDownloadProgressEvent(apps[i].Name, apps[i].SizeBytes, apps[i].SizeBytes, 0, "completed"); - - // Also emit an install event - EmitEvent(new EnrollmentEvent - { - SessionId = _sessionId, - TenantId = _tenantId, - Timestamp = DateTime.UtcNow, - EventType = $"app_install_{apps[i].Name.ToLower().Replace(" ", "_")}", - Severity = EventSeverity.Info, - Source = "Simulator", - Phase = EnrollmentPhase.AppInstallation, - Message = $"Installed {apps[i].Name}", - Data = new Dictionary - { - { "appName", apps[i].Name }, - { "simulated", true } - } - }); - } - else - { - EmitDownloadProgressEvent(apps[i].Name, progress[i], apps[i].SizeBytes, rateBps, "downloading"); - } - } - - tick++; - await Task.Delay(TimeSpan.FromSeconds(_random.Next(2, 4)), cancellationToken); - } - } - - private static bool AllCompleted(bool[] completed) - { - for (int i = 0; i < completed.Length; i++) - if (!completed[i]) return false; - return true; - } - - private void EmitDownloadProgressEvent(string appName, long bytesDownloaded, long bytesTotal, double rateBps, string status) - { - EmitEvent(new EnrollmentEvent - { - SessionId = _sessionId, - TenantId = _tenantId, - Timestamp = DateTime.UtcNow, - EventType = "download_progress", - Severity = EventSeverity.Debug, - Source = "Simulator", - Phase = EnrollmentPhase.AppInstallation, - Message = status == "completed" - ? $"Download complete: {appName}" - : $"Downloading {appName}: {(bytesTotal > 0 ? (bytesDownloaded * 100.0 / bytesTotal).ToString("F0") : "?")}%", - Data = new Dictionary - { - { "app_name", appName }, - { "bytes_downloaded", bytesDownloaded }, - { "bytes_total", bytesTotal }, - { "download_rate_bps", Math.Round(rateBps, 0) }, - { "status", status }, - { "simulated", true } - } - }); - } - - private async Task SimulatePhase(EnrollmentPhase phase, CancellationToken cancellationToken) - { - var previousPhase = _currentPhase; - _currentPhase = phase; - - // Emit phase change event - EmitEvent(new EnrollmentEvent - { - SessionId = _sessionId, - TenantId = _tenantId, - Timestamp = DateTime.UtcNow, - EventType = "phase_changed", - Severity = EventSeverity.Info, - Source = "Simulator", - Phase = phase, - Message = $"Enrollment phase changed: {previousPhase} -> {phase}", - Data = new Dictionary - { - { "previousPhase", previousPhase.ToString() }, - { "currentPhase", phase.ToString() }, - { "phaseNumber", (int)phase } - } - }); - - // Small delay between phases (2-5 seconds) - await Task.Delay(TimeSpan.FromSeconds(_random.Next(2, 6)), cancellationToken); - } - - private async Task EmitPhaseEvents( - EnrollmentPhase phase, - (string eventType, string message, EventSeverity severity)[] events, - CancellationToken cancellationToken) - { - foreach (var (eventType, message, severity) in events) - { - EmitEvent(new EnrollmentEvent - { - SessionId = _sessionId, - TenantId = _tenantId, - Timestamp = DateTime.UtcNow, - EventType = eventType, - Severity = severity, - Source = "Simulator", - Phase = phase, - Message = message, - Data = new Dictionary - { - { "simulated", true }, - { "phase", phase.ToString() } - } - }); - - // Random delay between events (1-3 seconds) - await Task.Delay(TimeSpan.FromSeconds(_random.Next(1, 4)), cancellationToken); - } - } - - private void EmitEvent(EnrollmentEvent evt) - { - try - { - _onEventCollected(evt); - _logger.Debug($"Simulator event: {evt.EventType} - {evt.Message}"); - } - catch (Exception ex) - { - _logger.Error($"Error emitting simulator event: {evt.EventType}", ex); - } - } - - private void StopPerformanceSnapshots() - { - try - { - _perfCancellationTokenSource?.Cancel(); - } - catch (ObjectDisposedException) - { - // Already disposed - } - } - - public void Dispose() - { - if (_disposed) - return; - - Stop(); - _perfCancellationTokenSource?.Dispose(); - _cancellationTokenSource?.Dispose(); - _disposed = true; - } - } -} diff --git a/src/Agent/AutopilotMonitor.Agent.Core/EventCollection/CertValidationCollector.cs b/src/Agent/AutopilotMonitor.Agent.Core/EventCollection/CertValidationCollector.cs deleted file mode 100644 index ca099c15..00000000 --- a/src/Agent/AutopilotMonitor.Agent.Core/EventCollection/CertValidationCollector.cs +++ /dev/null @@ -1,235 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Net.Security; -using System.Security.Cryptography.X509Certificates; -using System.Threading; -using AutopilotMonitor.Agent.Core.Logging; -using AutopilotMonitor.Shared.Models; - -namespace AutopilotMonitor.Agent.Core.EventCollection -{ - /// - /// Validates certificate chains for critical enrollment endpoints - /// Detects SSL inspection, untrusted CAs, and certificate issues - /// Optional collector - toggled on/off via remote config - /// - public class CertValidationCollector : IDisposable - { - private readonly AgentLogger _logger; - private readonly string _sessionId; - private readonly string _tenantId; - private readonly Action _onEventCollected; - private bool _hasRun; - - // Critical enrollment endpoints to validate - private static readonly EndpointCheck[] Endpoints = new[] - { - new EndpointCheck("enterpriseregistration.windows.net", 443, new[] { "Microsoft", "DigiCert" }), - new EndpointCheck("login.microsoftonline.com", 443, new[] { "Microsoft", "DigiCert" }), - new EndpointCheck("device.login.microsoftonline.com", 443, new[] { "Microsoft", "DigiCert" }), - new EndpointCheck("enrollment.manage.microsoft.com", 443, new[] { "Microsoft", "DigiCert", "Baltimore" }), - new EndpointCheck("portal.manage.microsoft.com", 443, new[] { "Microsoft", "DigiCert", "Baltimore" }) - }; - - public CertValidationCollector(string sessionId, string tenantId, Action onEventCollected, - AgentLogger logger) - { - _sessionId = sessionId ?? throw new ArgumentNullException(nameof(sessionId)); - _tenantId = tenantId ?? throw new ArgumentNullException(nameof(tenantId)); - _onEventCollected = onEventCollected ?? throw new ArgumentNullException(nameof(onEventCollected)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - } - - /// - /// Starts the collector - runs validation once at startup - /// Call RunValidation() again for subsequent checks (e.g., on network phase completion) - /// - public void Start() - { - _logger.Info("Starting Certificate Validation collector"); - ThreadPool.QueueUserWorkItem(_ => RunValidation()); - } - - public void Stop() - { - _logger.Info("Stopping Certificate Validation collector"); - } - - /// - /// Runs certificate validation for all endpoints - /// Can be called multiple times (e.g., at startup + after network phase) - /// - public void RunValidation() - { - _logger.Info("Running certificate validation checks..."); - - foreach (var endpoint in Endpoints) - { - try - { - ValidateEndpoint(endpoint); - } - catch (Exception ex) - { - _logger.Debug($"Cert validation for {endpoint.Hostname} failed: {ex.Message}"); - - _onEventCollected(new EnrollmentEvent - { - SessionId = _sessionId, - TenantId = _tenantId, - Timestamp = DateTime.UtcNow, - EventType = "cert_validation", - Severity = EventSeverity.Warning, - Source = "CertValidationCollector", - Message = $"Certificate validation failed for {endpoint.Hostname}: {ex.Message}", - Data = new Dictionary - { - { "endpoint", endpoint.Hostname }, - { "port", endpoint.Port }, - { "chain_valid", false }, - { "error", ex.Message }, - { "ssl_inspection_detected", false } - } - }); - } - } - - _hasRun = true; - _logger.Info("Certificate validation checks complete"); - } - - private void ValidateEndpoint(EndpointCheck endpoint) - { - X509Certificate2 serverCert = null; - X509Chain chain = null; - var chainDetails = new List>(); - bool chainValid = false; - bool sslInspectionDetected = false; - - try - { - var request = (HttpWebRequest)WebRequest.Create($"https://{endpoint.Hostname}:{endpoint.Port}"); - request.Timeout = 10000; - request.AllowAutoRedirect = false; - - // Custom validation callback to capture certificate details - request.ServerCertificateValidationCallback = (sender, certificate, certChain, sslPolicyErrors) => - { - if (certificate != null) - { - serverCert = new X509Certificate2(certificate); - } - chain = certChain; - chainValid = sslPolicyErrors == SslPolicyErrors.None; - - // Capture chain details - if (certChain != null) - { - foreach (var element in certChain.ChainElements) - { - chainDetails.Add(new Dictionary - { - { "subject", element.Certificate.Subject }, - { "issuer", element.Certificate.Issuer }, - { "thumbprint", element.Certificate.Thumbprint }, - { "notBefore", element.Certificate.NotBefore.ToString("o") }, - { "notAfter", element.Certificate.NotAfter.ToString("o") } - }); - } - } - - return true; // Accept all certs for inspection purposes - }; - - using (var response = (HttpWebResponse)request.GetResponse()) - { - // We just needed the certificate - response content doesn't matter - } - } - catch (WebException ex) - { - // Connection failures or HTTP errors are OK - we still got the certificate - if (serverCert == null) - { - throw new Exception($"Could not connect to {endpoint.Hostname}: {ex.Message}"); - } - } - - if (serverCert == null) - return; - - // Check for SSL inspection by examining the issuer - var issuer = serverCert.Issuer; - sslInspectionDetected = !endpoint.ExpectedIssuers.Any( - expected => issuer.IndexOf(expected, StringComparison.OrdinalIgnoreCase) >= 0 - ); - - var severity = EventSeverity.Info; - var message = $"Certificate valid for {endpoint.Hostname}"; - - if (sslInspectionDetected) - { - severity = EventSeverity.Warning; - message = $"SSL inspection detected for {endpoint.Hostname} (Issuer: {issuer})"; - _logger.Warning(message); - } - else if (!chainValid) - { - severity = EventSeverity.Warning; - message = $"Certificate chain invalid for {endpoint.Hostname}"; - _logger.Warning(message); - } - - var data = new Dictionary - { - { "endpoint", endpoint.Hostname }, - { "port", endpoint.Port }, - { "chain_valid", chainValid }, - { "ssl_inspection_detected", sslInspectionDetected }, - { "subject", serverCert.Subject }, - { "issuer", serverCert.Issuer }, - { "thumbprint", serverCert.Thumbprint }, - { "not_before", serverCert.NotBefore.ToString("o") }, - { "not_after", serverCert.NotAfter.ToString("o") } - }; - - if (chainDetails.Count > 0) - { - data["chain_length"] = chainDetails.Count; - } - - _onEventCollected(new EnrollmentEvent - { - SessionId = _sessionId, - TenantId = _tenantId, - Timestamp = DateTime.UtcNow, - EventType = "cert_validation", - Severity = severity, - Source = "CertValidationCollector", - Message = message, - Data = data - }); - } - - public void Dispose() - { - Stop(); - } - - private class EndpointCheck - { - public string Hostname { get; } - public int Port { get; } - public string[] ExpectedIssuers { get; } - - public EndpointCheck(string hostname, int port, string[] expectedIssuers) - { - Hostname = hostname; - Port = port; - ExpectedIssuers = expectedIssuers; - } - } - } -} diff --git a/src/Agent/AutopilotMonitor.Agent.Core/EventCollection/DownloadProgressCollector.cs b/src/Agent/AutopilotMonitor.Agent.Core/EventCollection/DownloadProgressCollector.cs deleted file mode 100644 index ed6e0370..00000000 --- a/src/Agent/AutopilotMonitor.Agent.Core/EventCollection/DownloadProgressCollector.cs +++ /dev/null @@ -1,212 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading; -using AutopilotMonitor.Agent.Core.Logging; -using AutopilotMonitor.Shared.Models; - -namespace AutopilotMonitor.Agent.Core.EventCollection -{ - /// - /// Monitors IME content download directories to track app download progress - /// Optional collector - toggled on/off via remote config - /// - public class DownloadProgressCollector : IDisposable - { - private readonly AgentLogger _logger; - private readonly string _sessionId; - private readonly string _tenantId; - private readonly Action _onEventCollected; - private readonly int _intervalSeconds; - private Timer _pollTimer; - - // Track file sizes for download rate calculation - private readonly Dictionary _previousFileSizes = new Dictionary(); - private DateTime _lastCheck = DateTime.MinValue; - - // IME content directories - private static readonly string[] MonitoredPaths = new[] - { - @"C:\Windows\IMECache", - @"C:\Program Files (x86)\Microsoft Intune Management Extension\Content" - }; - - public DownloadProgressCollector(string sessionId, string tenantId, Action onEventCollected, - AgentLogger logger, int intervalSeconds = 15) - { - _sessionId = sessionId ?? throw new ArgumentNullException(nameof(sessionId)); - _tenantId = tenantId ?? throw new ArgumentNullException(nameof(tenantId)); - _onEventCollected = onEventCollected ?? throw new ArgumentNullException(nameof(onEventCollected)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _intervalSeconds = intervalSeconds; - } - - public void Start() - { - _logger.Info($"Starting Download Progress collector (interval: {_intervalSeconds}s)"); - - _pollTimer = new Timer( - _ => CheckDownloads(), - null, - TimeSpan.FromSeconds(5), - TimeSpan.FromSeconds(_intervalSeconds) - ); - } - - public void Stop() - { - _logger.Info("Stopping Download Progress collector"); - _pollTimer?.Dispose(); - _pollTimer = null; - } - - private void CheckDownloads() - { - try - { - var now = DateTime.UtcNow; - var elapsedSeconds = _lastCheck == DateTime.MinValue ? _intervalSeconds : (now - _lastCheck).TotalSeconds; - var currentFiles = new Dictionary(); - var hasActiveDownloads = false; - - foreach (var basePath in MonitoredPaths) - { - if (!Directory.Exists(basePath)) - continue; - - try - { - var files = Directory.GetFiles(basePath, "*", SearchOption.AllDirectories); - foreach (var filePath in files) - { - try - { - var info = new FileInfo(filePath); - var size = info.Length; - currentFiles[filePath] = size; - - // Check if this file is actively being downloaded (size changed) - long previousSize; - if (_previousFileSizes.TryGetValue(filePath, out previousSize)) - { - if (size > previousSize) - { - hasActiveDownloads = true; - var bytesDownloaded = size - previousSize; - var downloadRateBps = elapsedSeconds > 0 ? bytesDownloaded / elapsedSeconds : 0; - - var data = new Dictionary - { - { "file_name", Path.GetFileName(filePath) }, - { "directory", Path.GetDirectoryName(filePath) }, - { "bytes_downloaded", size }, - { "bytes_delta", bytesDownloaded }, - { "download_rate_bps", Math.Round(downloadRateBps, 0) }, - { "download_rate_mbps", Math.Round(downloadRateBps / (1024 * 1024), 2) } - }; - - _onEventCollected(new EnrollmentEvent - { - SessionId = _sessionId, - TenantId = _tenantId, - Timestamp = DateTime.UtcNow, - EventType = "download_progress", - Severity = EventSeverity.Debug, - Source = "DownloadProgressCollector", - Message = $"Download: {Path.GetFileName(filePath)} - {FormatBytes(size)} ({FormatBytes((long)downloadRateBps)}/s)", - Data = data - }); - } - } - else if (size > 0) - { - // New file detected - hasActiveDownloads = true; - - _onEventCollected(new EnrollmentEvent - { - SessionId = _sessionId, - TenantId = _tenantId, - Timestamp = DateTime.UtcNow, - EventType = "download_progress", - Severity = EventSeverity.Debug, - Source = "DownloadProgressCollector", - Message = $"Download started: {Path.GetFileName(filePath)} ({FormatBytes(size)})", - Data = new Dictionary - { - { "file_name", Path.GetFileName(filePath) }, - { "directory", Path.GetDirectoryName(filePath) }, - { "bytes_downloaded", size }, - { "status", "started" } - } - }); - } - } - catch (Exception) - { - // File may be locked or in use - skip - } - } - } - catch (Exception ex) - { - _logger.Debug($"Error scanning download directory {basePath}: {ex.Message}"); - } - } - - // Detect completed downloads (files that existed before but are no longer present) - foreach (var kvp in _previousFileSizes) - { - if (!currentFiles.ContainsKey(kvp.Key)) - { - _onEventCollected(new EnrollmentEvent - { - SessionId = _sessionId, - TenantId = _tenantId, - Timestamp = DateTime.UtcNow, - EventType = "download_progress", - Severity = EventSeverity.Info, - Source = "DownloadProgressCollector", - Message = $"Download completed/removed: {Path.GetFileName(kvp.Key)} ({FormatBytes(kvp.Value)})", - Data = new Dictionary - { - { "file_name", Path.GetFileName(kvp.Key) }, - { "final_size", kvp.Value }, - { "status", "completed" } - } - }); - } - } - - // Update tracked files - _previousFileSizes.Clear(); - foreach (var kvp in currentFiles) - { - _previousFileSizes[kvp.Key] = kvp.Value; - } - _lastCheck = now; - } - catch (Exception ex) - { - _logger.Warning($"Download progress check failed: {ex.Message}"); - } - } - - private string FormatBytes(long bytes) - { - if (bytes >= 1073741824) - return $"{bytes / 1073741824.0:F1} GB"; - if (bytes >= 1048576) - return $"{bytes / 1048576.0:F1} MB"; - if (bytes >= 1024) - return $"{bytes / 1024.0:F1} KB"; - return $"{bytes} B"; - } - - public void Dispose() - { - Stop(); - } - } -} diff --git a/src/Agent/AutopilotMonitor.Agent.Core/EventCollection/EspUiStateCollector.cs b/src/Agent/AutopilotMonitor.Agent.Core/EventCollection/EspUiStateCollector.cs deleted file mode 100644 index 536828a5..00000000 --- a/src/Agent/AutopilotMonitor.Agent.Core/EventCollection/EspUiStateCollector.cs +++ /dev/null @@ -1,254 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using AutopilotMonitor.Agent.Core.Logging; -using AutopilotMonitor.Shared.Models; -using Microsoft.Win32; - -namespace AutopilotMonitor.Agent.Core.EventCollection -{ - /// - /// Reads ESP (Enrollment Status Page) status from the registry - /// Tracks blocking apps, install progress, and status text - /// Optional collector - toggled on/off via remote config - /// - public class EspUiStateCollector : IDisposable - { - private readonly AgentLogger _logger; - private readonly string _sessionId; - private readonly string _tenantId; - private readonly Action _onEventCollected; - private readonly int _intervalSeconds; - private Timer _pollTimer; - - // Track last known state to avoid duplicate events - private string _lastStateHash; - - private const string EnrollmentsKeyPath = @"SOFTWARE\Microsoft\Enrollments"; - - public EspUiStateCollector(string sessionId, string tenantId, Action onEventCollected, - AgentLogger logger, int intervalSeconds = 30) - { - _sessionId = sessionId ?? throw new ArgumentNullException(nameof(sessionId)); - _tenantId = tenantId ?? throw new ArgumentNullException(nameof(tenantId)); - _onEventCollected = onEventCollected ?? throw new ArgumentNullException(nameof(onEventCollected)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _intervalSeconds = intervalSeconds; - } - - public void Start() - { - _logger.Info($"Starting ESP UI State collector (interval: {_intervalSeconds}s)"); - - _pollTimer = new Timer( - _ => CollectEspState(), - null, - TimeSpan.FromSeconds(5), - TimeSpan.FromSeconds(_intervalSeconds) - ); - } - - public void Stop() - { - _logger.Info("Stopping ESP UI State collector"); - _pollTimer?.Dispose(); - _pollTimer = null; - } - - private void CollectEspState() - { - try - { - using (var enrollmentsKey = Registry.LocalMachine.OpenSubKey(EnrollmentsKeyPath, false)) - { - if (enrollmentsKey == null) - return; - - var subKeyNames = enrollmentsKey.GetSubKeyNames(); - - foreach (var subKeyName in subKeyNames) - { - // Skip non-GUID keys - Guid guid; - if (!Guid.TryParse(subKeyName, out guid)) - continue; - - try - { - CollectEnrollmentEspState(enrollmentsKey, subKeyName); - } - catch (Exception ex) - { - _logger.Debug($"Failed to read ESP state for {subKeyName}: {ex.Message}"); - } - } - } - } - catch (Exception ex) - { - _logger.Debug($"ESP state collection failed: {ex.Message}"); - } - } - - private void CollectEnrollmentEspState(RegistryKey enrollmentsKey, string enrollmentGuid) - { - using (var enrollmentKey = enrollmentsKey.OpenSubKey(enrollmentGuid, false)) - { - if (enrollmentKey == null) - return; - - // Check for FirstSync tracking (ESP tracking policies) - using (var firstSyncKey = enrollmentKey.OpenSubKey("FirstSync", false)) - { - if (firstSyncKey == null) - return; - - var data = new Dictionary(); - - // Read ESP status values - var deviceSetupStatus = ReadRegistryValue(firstSyncKey, "DeviceSetupStatus"); - var accountSetupStatus = ReadRegistryValue(firstSyncKey, "AccountSetupStatus"); - var espComplete = ReadRegistryValue(firstSyncKey, "IsComplete"); - - // Determine current ESP phase - string espPhase = "unknown"; - if (accountSetupStatus != null) - espPhase = "account_setup"; - else if (deviceSetupStatus != null) - espPhase = "device_setup"; - - data["phase"] = espPhase; - data["enrollment_guid"] = enrollmentGuid; - - if (deviceSetupStatus != null) - data["device_setup_status"] = deviceSetupStatus; - if (accountSetupStatus != null) - data["account_setup_status"] = accountSetupStatus; - if (espComplete != null) - data["esp_complete"] = espComplete; - - // Read blocking apps tracking - CollectBlockingApps(firstSyncKey, "Apps", data); - - // Read tracking policies - CollectTrackingPolicies(firstSyncKey, data); - - // Create state hash to avoid duplicate events - var stateHash = string.Join("|", data.OrderBy(k => k.Key).Select(k => $"{k.Key}={k.Value}")); - if (stateHash == _lastStateHash) - return; // No change - - _lastStateHash = stateHash; - - var blockingTotal = data.ContainsKey("blocking_apps_total") ? data["blocking_apps_total"] : 0; - var blockingCompleted = data.ContainsKey("blocking_apps_completed") ? data["blocking_apps_completed"] : 0; - - _onEventCollected(new EnrollmentEvent - { - SessionId = _sessionId, - TenantId = _tenantId, - Timestamp = DateTime.UtcNow, - EventType = "esp_ui_state", - Severity = EventSeverity.Debug, - Source = "EspUiStateCollector", - Message = $"ESP State: {espPhase} - Apps: {blockingCompleted}/{blockingTotal}", - Data = data - }); - } - } - } - - private void CollectBlockingApps(RegistryKey firstSyncKey, string subKeyName, Dictionary data) - { - try - { - using (var appsKey = firstSyncKey.OpenSubKey(subKeyName, false)) - { - if (appsKey == null) - return; - - var appSubKeys = appsKey.GetSubKeyNames(); - int total = appSubKeys.Length; - int completed = 0; - int failed = 0; - var appStatuses = new List>(); - - foreach (var appId in appSubKeys) - { - using (var appKey = appsKey.OpenSubKey(appId, false)) - { - if (appKey == null) continue; - - var status = ReadRegistryValue(appKey, "Status"); - var statusInt = 0; - if (status != null && int.TryParse(status, out statusInt)) - { - if (statusInt == 1 || statusInt == 2) // Completed states - completed++; - else if (statusInt == 3) // Failed - failed++; - } - - appStatuses.Add(new Dictionary - { - { "app_id", appId }, - { "status", status ?? "unknown" } - }); - } - } - - data["blocking_apps_total"] = total; - data["blocking_apps_completed"] = completed; - data["blocking_apps_failed"] = failed; - - if (total > 0) - { - data["progress_percent"] = Math.Round(completed * 100.0 / total, 0); - } - } - } - catch (Exception ex) - { - _logger.Debug($"Failed to collect blocking apps: {ex.Message}"); - } - } - - private void CollectTrackingPolicies(RegistryKey firstSyncKey, Dictionary data) - { - try - { - using (var policiesKey = firstSyncKey.OpenSubKey("TrackingPoliciesCreated", false)) - { - if (policiesKey == null) - return; - - var policyCount = policiesKey.GetSubKeyNames().Length; - data["tracking_policies_count"] = policyCount; - } - } - catch (Exception ex) - { - _logger.Debug($"Failed to collect tracking policies: {ex.Message}"); - } - } - - private string ReadRegistryValue(RegistryKey key, string valueName) - { - try - { - var value = key.GetValue(valueName); - return value?.ToString(); - } - catch - { - return null; - } - } - - public void Dispose() - { - Stop(); - } - } -} diff --git a/src/Agent/AutopilotMonitor.Agent.Core/EventCollection/EventLogWatcher.cs b/src/Agent/AutopilotMonitor.Agent.Core/EventCollection/EventLogWatcher.cs deleted file mode 100644 index 2856ac2b..00000000 --- a/src/Agent/AutopilotMonitor.Agent.Core/EventCollection/EventLogWatcher.cs +++ /dev/null @@ -1,185 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.Eventing.Reader; -using AutopilotMonitor.Agent.Core.Logging; -using AutopilotMonitor.Shared.Models; - -namespace AutopilotMonitor.Agent.Core.EventCollection -{ - /// - /// Watches Windows Event Logs for Autopilot-related events - /// - public class EventLogWatcher : IDisposable - { - private readonly AgentLogger _logger; - private readonly string _sessionId; - private readonly string _tenantId; - private readonly Action _onEventCollected; - private readonly List _queries = new List(); - private readonly List _watchers = new List(); - - // Autopilot Event Log channels - private static readonly string[] AutopilotLogChannels = new[] - { - "Microsoft-Windows-DeviceManagement-Enterprise-Diagnostics-Provider/Admin", - "Microsoft-Windows-Provisioning-Diagnostics-Provider/Admin", - "Microsoft-Windows-ModernDeployment-Diagnostics-Provider/Autopilot" - }; - - public EventLogWatcher(string sessionId, string tenantId, Action onEventCollected, AgentLogger logger) - { - _sessionId = sessionId ?? throw new ArgumentNullException(nameof(sessionId)); - _tenantId = tenantId ?? throw new ArgumentNullException(nameof(tenantId)); - _onEventCollected = onEventCollected ?? throw new ArgumentNullException(nameof(onEventCollected)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - } - - /// - /// Starts watching event logs - /// - public void Start() - { - _logger.Info("Starting EventLog watchers"); - - foreach (var channel in AutopilotLogChannels) - { - try - { - // Create query for new events (from now onwards) - var query = new EventLogQuery(channel, PathType.LogName, "*[System[TimeCreated[timediff(@SystemTime) <= 1000]]]"); - _queries.Add(query); - - // Create watcher - var watcher = new System.Diagnostics.Eventing.Reader.EventLogWatcher(query); - watcher.EventRecordWritten += OnEventRecordWritten; - watcher.Enabled = true; - - _watchers.Add(watcher); - _logger.Info($"Started watching: {channel}"); - } - catch (EventLogNotFoundException) - { - _logger.Warning($"Event log not found: {channel} (This is normal if not on a real Autopilot device)"); - } - catch (Exception ex) - { - _logger.Error($"Failed to start watcher for {channel}", ex); - } - } - - if (_watchers.Count == 0) - { - _logger.Warning("No event log watchers started. Running in simulation mode."); - } - } - - /// - /// Stops watching event logs - /// - public void Stop() - { - _logger.Info("Stopping EventLog watchers"); - - foreach (var watcher in _watchers) - { - try - { - watcher.Enabled = false; - watcher.EventRecordWritten -= OnEventRecordWritten; - watcher.Dispose(); - } - catch (Exception ex) - { - _logger.Error("Error stopping watcher", ex); - } - } - - _watchers.Clear(); - _queries.Clear(); - } - - private void OnEventRecordWritten(object sender, EventRecordWrittenEventArgs e) - { - if (e.EventRecord == null) - return; - - try - { - var record = e.EventRecord; - - // Convert Windows Event to EnrollmentEvent - var evt = new EnrollmentEvent - { - SessionId = _sessionId, - TenantId = _tenantId, - Timestamp = record.TimeCreated ?? DateTime.UtcNow, - EventType = $"eventlog_{record.Id}", - Severity = MapEventLevel(record.Level), - Source = record.ProviderName ?? "EventLog", - Phase = DetectPhaseFromEvent(record), - Message = record.FormatDescription() ?? $"Event ID {record.Id}", - Data = new Dictionary - { - { "eventId", record.Id }, - { "level", record.Level ?? 0 }, - { "logName", record.LogName }, - { "providerName", record.ProviderName } - } - }; - - _onEventCollected(evt); - } - catch (Exception ex) - { - _logger.Error("Error processing event record", ex); - } - } - - private EventSeverity MapEventLevel(byte? level) - { - if (!level.HasValue) - return EventSeverity.Info; - - switch (level.Value) - { - case 1: // Critical - return EventSeverity.Critical; - case 2: // Error - return EventSeverity.Error; - case 3: // Warning - return EventSeverity.Warning; - case 4: // Information - default: - return EventSeverity.Info; - } - } - - private EnrollmentPhase DetectPhaseFromEvent(EventRecord record) - { - // Simple phase detection based on event content - var description = record.FormatDescription()?.ToLowerInvariant() ?? ""; - - if (description.Contains("device preparation") || description.Contains("preparing device")) - return EnrollmentPhase.MdmEnrollment; - if (description.Contains("device setup") || description.Contains("setting up device")) - return EnrollmentPhase.EspDeviceSetup; - if (description.Contains("account setup") || description.Contains("setting up account") || description.Contains("user setup")) - return EnrollmentPhase.EspUserSetup; - if (description.Contains("esp") || description.Contains("enrollment status")) - return EnrollmentPhase.EspDeviceSetup; - if (description.Contains("identity") || description.Contains("azure ad") || description.Contains("entra")) - return EnrollmentPhase.Identity; - if (description.Contains("app") || description.Contains("application")) - return EnrollmentPhase.AppInstallation; - if (description.Contains("complete") || description.Contains("finished")) - return EnrollmentPhase.Complete; - - return EnrollmentPhase.PreFlight; - } - - public void Dispose() - { - Stop(); - } - } -} diff --git a/src/Agent/AutopilotMonitor.Agent.Core/EventCollection/PhaseDetector.cs b/src/Agent/AutopilotMonitor.Agent.Core/EventCollection/PhaseDetector.cs deleted file mode 100644 index 13bbae35..00000000 --- a/src/Agent/AutopilotMonitor.Agent.Core/EventCollection/PhaseDetector.cs +++ /dev/null @@ -1,238 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using AutopilotMonitor.Agent.Core.Logging; -using AutopilotMonitor.Shared.Models; -using Microsoft.Win32; - -namespace AutopilotMonitor.Agent.Core.EventCollection -{ - /// - /// Detects and tracks Autopilot enrollment phases - /// - public class PhaseDetector : IDisposable - { - private readonly AgentLogger _logger; - private readonly string _sessionId; - private readonly string _tenantId; - private readonly Action _onEventCollected; - private Timer _pollTimer; - private EnrollmentPhase _currentPhase = EnrollmentPhase.PreFlight; - - // ESP (Enrollment Status Page) registry paths - private const string EspKeyPath = @"SOFTWARE\Microsoft\Enrollments"; - private const string AutopilotKeyPath = @"SOFTWARE\Microsoft\Provisioning\Diagnostics\Autopilot"; - - public EnrollmentPhase CurrentPhase => _currentPhase; - - public PhaseDetector(string sessionId, string tenantId, Action onEventCollected, AgentLogger logger) - { - _sessionId = sessionId ?? throw new ArgumentNullException(nameof(sessionId)); - _tenantId = tenantId ?? throw new ArgumentNullException(nameof(tenantId)); - _onEventCollected = onEventCollected ?? throw new ArgumentNullException(nameof(onEventCollected)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - } - - /// - /// Starts phase detection - /// - public void Start() - { - _logger.Info("Starting Phase detector"); - - // Initial phase check - CheckPhase(); - - // Poll every 10 seconds - _pollTimer = new Timer( - _ => CheckPhase(), - null, - TimeSpan.FromSeconds(10), - TimeSpan.FromSeconds(10) - ); - } - - /// - /// Stops phase detection - /// - public void Stop() - { - _logger.Info("Stopping Phase detector"); - _pollTimer?.Dispose(); - } - - private void CheckPhase() - { - try - { - var detectedPhase = DetectCurrentPhase(); - - if (detectedPhase != _currentPhase) - { - var previousPhase = _currentPhase; - _currentPhase = detectedPhase; - - // Emit phase change event - var evt = new EnrollmentEvent - { - SessionId = _sessionId, - TenantId = _tenantId, - Timestamp = DateTime.UtcNow, - EventType = "phase_changed", - Severity = EventSeverity.Info, - Source = "PhaseDetector", - Phase = _currentPhase, - Message = $"Enrollment phase changed: {previousPhase} -> {_currentPhase}", - Data = new Dictionary - { - { "previousPhase", previousPhase.ToString() }, - { "currentPhase", _currentPhase.ToString() }, - { "phaseNumber", (int)_currentPhase } - } - }; - - _onEventCollected(evt); - _logger.Info($"Phase changed: {previousPhase} -> {_currentPhase}"); - - // Check if enrollment completed or failed - if (_currentPhase == EnrollmentPhase.Complete) - { - _logger.Info("Enrollment completed successfully - emitting completion event"); - var completionEvent = new EnrollmentEvent - { - SessionId = _sessionId, - TenantId = _tenantId, - Timestamp = DateTime.UtcNow, - EventType = "enrollment_complete", - Severity = EventSeverity.Info, - Source = "PhaseDetector", - Phase = EnrollmentPhase.Complete, - Message = "Autopilot enrollment completed successfully" - }; - _onEventCollected(completionEvent); - } - else if (_currentPhase == EnrollmentPhase.Failed) - { - _logger.Warning("Enrollment failed - emitting failure event"); - var failureEvent = new EnrollmentEvent - { - SessionId = _sessionId, - TenantId = _tenantId, - Timestamp = DateTime.UtcNow, - EventType = "enrollment_failed", - Severity = EventSeverity.Critical, - Source = "PhaseDetector", - Phase = EnrollmentPhase.Failed, - Message = "Autopilot enrollment failed" - }; - _onEventCollected(failureEvent); - } - } - } - catch (Exception ex) - { - _logger.Error("Error detecting phase", ex); - } - } - - private EnrollmentPhase DetectCurrentPhase() - { - // Try to detect phase from various indicators - - // 1. Check ESP completion status first (highest priority) - try - { - using (var enrollmentsKey = Registry.LocalMachine.OpenSubKey(EspKeyPath, false)) - { - if (enrollmentsKey != null) - { - var subKeyNames = enrollmentsKey.GetSubKeyNames(); - foreach (var subKeyName in subKeyNames) - { - using (var enrollmentKey = enrollmentsKey.OpenSubKey(subKeyName, false)) - { - if (enrollmentKey != null) - { - // Check for ESP tracking policies - using (var statusKey = enrollmentKey.OpenSubKey("Status", false)) - { - if (statusKey != null) - { - // Check for completion indicators - var espComplete = statusKey.GetValue("EnrollmentStatusPageComplete"); - var deviceSetupStatus = statusKey.GetValue("DeviceSetupStatus"); - var accountSetupStatus = statusKey.GetValue("AccountSetupStatus"); - - // Check for failure - var hasError = statusKey.GetValue("HasError"); - if (hasError != null && Convert.ToInt32(hasError) > 0) - { - return EnrollmentPhase.Failed; - } - - // Check for completion (ESP finished successfully) - if (espComplete != null && Convert.ToInt32(espComplete) > 0) - { - return EnrollmentPhase.Complete; - } - - // Check phase progression - if (accountSetupStatus != null) - { - var accountStatus = Convert.ToInt32(accountSetupStatus); - // 2 = Completed, check if this is final phase - if (accountStatus == 2) - { - return EnrollmentPhase.Complete; - } - return EnrollmentPhase.EspUserSetup; - } - - if (deviceSetupStatus != null) - { - var deviceStatus = Convert.ToInt32(deviceSetupStatus); - // Check for failure during device setup - if (deviceStatus == 3) // 3 = Failed - { - return EnrollmentPhase.Failed; - } - return EnrollmentPhase.EspDeviceSetup; - } - } - } - } - } - } - } - } - } - catch { } - - // 2. Check Autopilot registry for deployment profile - try - { - using (var key = Registry.LocalMachine.OpenSubKey(AutopilotKeyPath, false)) - { - if (key != null) - { - var profileName = key.GetValue("DeploymentProfileName") as string; - if (!string.IsNullOrEmpty(profileName)) - { - // Profile downloaded - at least in MDM Enrollment - return EnrollmentPhase.MdmEnrollment; - } - } - } - } - catch { } - - // 3. If we have an Autopilot session running, we're at least in PreFlight - return EnrollmentPhase.PreFlight; - } - - public void Dispose() - { - Stop(); - } - } -} diff --git a/src/Agent/AutopilotMonitor.Agent.Core/EventCollection/RegistryMonitor.cs b/src/Agent/AutopilotMonitor.Agent.Core/EventCollection/RegistryMonitor.cs deleted file mode 100644 index e36662ea..00000000 --- a/src/Agent/AutopilotMonitor.Agent.Core/EventCollection/RegistryMonitor.cs +++ /dev/null @@ -1,154 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using AutopilotMonitor.Agent.Core.Logging; -using AutopilotMonitor.Shared.Models; -using Microsoft.Win32; - -namespace AutopilotMonitor.Agent.Core.EventCollection -{ - /// - /// Monitors Autopilot registry keys for status changes - /// - public class RegistryMonitor : IDisposable - { - private readonly AgentLogger _logger; - private readonly string _sessionId; - private readonly string _tenantId; - private readonly Action _onEventCollected; - private Timer _pollTimer; - private readonly Dictionary _lastValues = new Dictionary(); - - // Autopilot registry paths - private const string AutopilotKeyPath = @"SOFTWARE\Microsoft\Provisioning\Diagnostics\Autopilot"; - private const string AutopilotDiagnosticsPath = @"SOFTWARE\Microsoft\Provisioning\Diagnostics"; - - // Registry keys to monitor - private static readonly string[] MonitoredValues = new[] - { - "CloudAssignedTenantId", - "CloudAssignedOobeConfig", - "AutopilotServiceCorrelationId", - "DeploymentProfileName", - "IsAutopilotDisabled", - "CloudAssignedTenantDomain" - }; - - public RegistryMonitor(string sessionId, string tenantId, Action onEventCollected, AgentLogger logger) - { - _sessionId = sessionId ?? throw new ArgumentNullException(nameof(sessionId)); - _tenantId = tenantId ?? throw new ArgumentNullException(nameof(tenantId)); - _onEventCollected = onEventCollected ?? throw new ArgumentNullException(nameof(onEventCollected)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - } - - /// - /// Starts monitoring registry keys - /// - public void Start() - { - _logger.Info("Starting Registry monitor"); - - // Initial read - CheckRegistryValues(); - - // Poll every 5 seconds - _pollTimer = new Timer( - _ => CheckRegistryValues(), - null, - TimeSpan.FromSeconds(5), - TimeSpan.FromSeconds(5) - ); - } - - /// - /// Stops monitoring registry keys - /// - public void Stop() - { - _logger.Info("Stopping Registry monitor"); - _pollTimer?.Dispose(); - } - - private void CheckRegistryValues() - { - try - { - using (var key = Registry.LocalMachine.OpenSubKey(AutopilotKeyPath, false)) - { - if (key == null) - { - // Autopilot key doesn't exist - not an Autopilot device or not started yet - return; - } - - foreach (var valueName in MonitoredValues) - { - try - { - var currentValue = key.GetValue(valueName); - - if (currentValue != null) - { - var valueString = currentValue.ToString(); - - // Check if value changed - if (!_lastValues.ContainsKey(valueName) || !_lastValues[valueName].Equals(currentValue)) - { - _lastValues[valueName] = currentValue; - - // Emit event for registry change - var evt = new EnrollmentEvent - { - SessionId = _sessionId, - TenantId = _tenantId, - Timestamp = DateTime.UtcNow, - EventType = $"registry_changed", - Severity = EventSeverity.Info, - Source = "RegistryMonitor", - Phase = DetectPhaseFromKey(valueName), - Message = $"Registry value changed: {valueName} = {valueString}", - Data = new Dictionary - { - { "registryKey", AutopilotKeyPath }, - { "valueName", valueName }, - { "value", valueString } - } - }; - - _onEventCollected(evt); - _logger.Debug($"Registry change detected: {valueName} = {valueString}"); - } - } - } - catch (Exception ex) - { - _logger.Error($"Error reading registry value {valueName}", ex); - } - } - } - } - catch (Exception ex) - { - _logger.Error("Error checking registry values", ex); - } - } - - private EnrollmentPhase DetectPhaseFromKey(string keyName) - { - // Map registry keys to phases - if (keyName.Contains("CloudAssigned") || keyName.Contains("Tenant")) - return EnrollmentPhase.Identity; - if (keyName.Contains("Profile") || keyName.Contains("Config")) - return EnrollmentPhase.MdmEnrollment; - - return EnrollmentPhase.PreFlight; - } - - public void Dispose() - { - Stop(); - } - } -} diff --git a/src/Agent/AutopilotMonitor.Agent.Core/EventCollection/GatherRuleExecutor.cs b/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Collectors/GatherRuleExecutor.cs similarity index 73% rename from src/Agent/AutopilotMonitor.Agent.Core/EventCollection/GatherRuleExecutor.cs rename to src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Collectors/GatherRuleExecutor.cs index 49f3d927..bda5aeb5 100644 --- a/src/Agent/AutopilotMonitor.Agent.Core/EventCollection/GatherRuleExecutor.cs +++ b/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Collectors/GatherRuleExecutor.cs @@ -4,16 +4,19 @@ using System.Diagnostics; using System.IO; using System.Linq; using System.Management; +using System.Text; +using System.Text.RegularExpressions; using System.Threading; using AutopilotMonitor.Agent.Core.Logging; +using AutopilotMonitor.Agent.Core.Monitoring.Tracking; using AutopilotMonitor.Shared.Models; using Microsoft.Win32; -namespace AutopilotMonitor.Agent.Core.EventCollection +namespace AutopilotMonitor.Agent.Core.Monitoring.Collectors { /// /// Executes gather rules received from the backend API - /// Supports registry, eventlog, wmi, file, and command (allowlisted) collector types + /// Supports registry, eventlog, wmi, file, command (allowlisted), and logparser collector types /// public class GatherRuleExecutor : IDisposable { @@ -21,10 +24,12 @@ namespace AutopilotMonitor.Agent.Core.EventCollection private readonly string _sessionId; private readonly string _tenantId; private readonly Action _onEventCollected; + private readonly string _imeLogPathOverride; private List _activeRules = new List(); private readonly Dictionary _intervalTimers = new Dictionary(); private readonly HashSet _startupRulesExecuted = new HashSet(); + private readonly LogFilePositionTracker _filePositionTracker = new LogFilePositionTracker(); /// /// Strict command allowlist - only these commands can be executed via gather rules @@ -59,12 +64,13 @@ namespace AutopilotMonitor.Agent.Core.EventCollection }; public GatherRuleExecutor(string sessionId, string tenantId, Action onEventCollected, - AgentLogger logger) + AgentLogger logger, string imeLogPathOverride = null) { _sessionId = sessionId ?? throw new ArgumentNullException(nameof(sessionId)); _tenantId = tenantId ?? throw new ArgumentNullException(nameof(tenantId)); _onEventCollected = onEventCollected ?? throw new ArgumentNullException(nameof(onEventCollected)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _imeLogPathOverride = imeLogPathOverride; } /// @@ -173,6 +179,10 @@ namespace AutopilotMonitor.Agent.Core.EventCollection result = ExecuteEventLogRule(rule); break; + case "logparser": + ExecuteLogParserRule(rule); + return; // Return early - logparser emits events directly + default: _logger.Warning($"Unknown collector type: {rule.CollectorType} for rule {rule.RuleId}"); return; @@ -538,6 +548,177 @@ namespace AutopilotMonitor.Agent.Core.EventCollection return data; } + private void ExecuteLogParserRule(GatherRule rule) + { + var filePath = rule.Target; + if (string.IsNullOrEmpty(filePath)) + return; + + filePath = Environment.ExpandEnvironmentVariables(filePath); + + // Apply IME log path override if set + if (!string.IsNullOrEmpty(_imeLogPathOverride)) + { + var fileName = Path.GetFileName(filePath); + filePath = Path.Combine(_imeLogPathOverride, fileName); + } + + // Get parameters + string patternStr; + if (rule.Parameters == null || !rule.Parameters.TryGetValue("pattern", out patternStr) || + string.IsNullOrEmpty(patternStr)) + { + _logger.Warning($"LogParser rule {rule.RuleId} has no 'pattern' parameter"); + return; + } + + string trackPositionStr; + bool trackPosition = true; + if (rule.Parameters.TryGetValue("trackPosition", out trackPositionStr)) + bool.TryParse(trackPositionStr, out trackPosition); + + string maxLinesStr; + int maxLines = 1000; + if (rule.Parameters.TryGetValue("maxLines", out maxLinesStr)) + int.TryParse(maxLinesStr, out maxLines); + + Regex pattern; + try + { + pattern = new Regex(patternStr, RegexOptions.Compiled); + } + catch (Exception ex) + { + _logger.Warning($"LogParser rule {rule.RuleId} has invalid regex: {ex.Message}"); + return; + } + + if (!File.Exists(filePath)) + { + _logger.Debug($"LogParser rule {rule.RuleId}: file not found: {filePath}"); + return; + } + + try + { + var fileInfo = new FileInfo(filePath); + long startPosition = trackPosition + ? _filePositionTracker.GetSafePosition(filePath, fileInfo.Length) + : 0; + + // Nothing new to read + if (startPosition >= fileInfo.Length) + return; + + int matchCount = 0; + int linesRead = 0; + long endPosition = startPosition; + + using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + stream.Seek(startPosition, SeekOrigin.Begin); + + using (var reader = new StreamReader(stream, Encoding.UTF8)) + { + string line; + while ((line = reader.ReadLine()) != null && linesRead < maxLines) + { + linesRead++; + + CmTraceLogEntry entry; + if (!CmTraceLogParser.TryParseLine(line, out entry)) + continue; + + var match = pattern.Match(entry.Message); + if (!match.Success) + continue; + + // Build data dictionary from named capture groups + var data = new Dictionary(); + foreach (var groupName in pattern.GetGroupNames()) + { + if (groupName == "0") continue; // Skip full match group + var group = match.Groups[groupName]; + if (group.Success) + data[groupName] = group.Value; + } + + // Add CMTrace metadata + data["logTimestamp"] = entry.Timestamp.ToString("o"); + data["logComponent"] = entry.Component; + data["logType"] = entry.Type; + data["logMessage"] = TruncateMessage(entry.Message, 500); + data["ruleId"] = rule.RuleId; + data["ruleTitle"] = rule.Title; + + var eventType = !string.IsNullOrEmpty(rule.OutputEventType) + ? rule.OutputEventType + : "logparser_match"; + var severity = MapCmTraceTypeToSeverity(entry.Type, rule.OutputSeverity); + + _onEventCollected(new EnrollmentEvent + { + SessionId = _sessionId, + TenantId = _tenantId, + Timestamp = entry.Timestamp, + EventType = eventType, + Severity = severity, + Source = "GatherRuleExecutor", + Message = $"Gather: {rule.Title}", + Data = data + }); + + matchCount++; + } + + // Capture the final stream position after reading + endPosition = stream.Position; + } + } + + if (trackPosition) + _filePositionTracker.SetPosition(filePath, endPosition); + + if (matchCount > 0) + _logger.Debug($"LogParser rule {rule.RuleId}: {matchCount} matches from {linesRead} lines in {Path.GetFileName(filePath)}"); + } + catch (Exception ex) + { + _logger.Warning($"LogParser rule {rule.RuleId} failed reading {filePath}: {ex.Message}"); + } + } + + private static EventSeverity MapCmTraceTypeToSeverity(int cmTraceType, string ruleOverride) + { + // If the rule specifies a severity, use it + if (!string.IsNullOrEmpty(ruleOverride)) + { + switch (ruleOverride.ToLower()) + { + case "debug": return EventSeverity.Debug; + case "info": return EventSeverity.Info; + case "warning": return EventSeverity.Warning; + case "error": return EventSeverity.Error; + case "critical": return EventSeverity.Critical; + } + } + + // Otherwise derive from CMTrace log type + switch (cmTraceType) + { + case 2: return EventSeverity.Warning; + case 3: return EventSeverity.Error; + default: return EventSeverity.Info; + } + } + + private static string TruncateMessage(string message, int maxLength) + { + if (string.IsNullOrEmpty(message) || message.Length <= maxLength) + return message; + return message.Substring(0, maxLength) + "..."; + } + /// /// Checks if a command is on the allowlist /// Uses exact match comparison for security diff --git a/src/Agent/AutopilotMonitor.Agent.Core/EventCollection/HelloDetector.cs b/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Collectors/HelloDetector.cs similarity index 98% rename from src/Agent/AutopilotMonitor.Agent.Core/EventCollection/HelloDetector.cs rename to src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Collectors/HelloDetector.cs index 6349a9b3..2e8ad02e 100644 --- a/src/Agent/AutopilotMonitor.Agent.Core/EventCollection/HelloDetector.cs +++ b/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Collectors/HelloDetector.cs @@ -5,7 +5,7 @@ using AutopilotMonitor.Agent.Core.Logging; using AutopilotMonitor.Shared.Models; using Microsoft.Win32; -namespace AutopilotMonitor.Agent.Core.EventCollection +namespace AutopilotMonitor.Agent.Core.Monitoring.Collectors { /// /// Detects and tracks Windows Hello for Business provisioning during Autopilot enrollment. @@ -107,7 +107,7 @@ namespace AutopilotMonitor.Agent.Core.EventCollection EventType = "hello_policy_detected", Severity = EventSeverity.Info, Source = "HelloDetector", - Phase = EnrollmentPhase.EspDeviceSetup, + Phase = EnrollmentPhase.Unknown, Message = $"Windows Hello for Business policy detected: {status} (via {source})", Data = new Dictionary { @@ -273,7 +273,7 @@ namespace AutopilotMonitor.Agent.Core.EventCollection EventType = eventType, Severity = severity, Source = "HelloDetector", - Phase = EnrollmentPhase.EspDeviceSetup, + Phase = EnrollmentPhase.Unknown, Message = message, Data = new Dictionary { diff --git a/src/Agent/AutopilotMonitor.Agent.Core/EventCollection/PerformanceCollector.cs b/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Collectors/PerformanceCollector.cs similarity index 98% rename from src/Agent/AutopilotMonitor.Agent.Core/EventCollection/PerformanceCollector.cs rename to src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Collectors/PerformanceCollector.cs index e456d064..1957245a 100644 --- a/src/Agent/AutopilotMonitor.Agent.Core/EventCollection/PerformanceCollector.cs +++ b/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Collectors/PerformanceCollector.cs @@ -7,7 +7,7 @@ using System.Threading; using AutopilotMonitor.Agent.Core.Logging; using AutopilotMonitor.Shared.Models; -namespace AutopilotMonitor.Agent.Core.EventCollection +namespace AutopilotMonitor.Agent.Core.Monitoring.Collectors { /// /// Collects system performance metrics (CPU, memory, disk) on a configurable interval @@ -139,6 +139,7 @@ namespace AutopilotMonitor.Agent.Core.EventCollection EventType = "performance_snapshot", Severity = EventSeverity.Debug, Source = "PerformanceCollector", + Phase = EnrollmentPhase.Unknown, Message = $"CPU: {(data.ContainsKey("cpu_percent") ? data["cpu_percent"] : "?")}%, " + $"Memory: {(data.ContainsKey("memory_used_percent") ? data["memory_used_percent"] : "?")}%, " + $"Disk Free: {(data.ContainsKey("disk_free_gb") ? data["disk_free_gb"] : "?")} GB", diff --git a/src/Agent/AutopilotMonitor.Agent.Core/Storage/EventSpool.cs b/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Core/EventSpool.cs similarity index 83% rename from src/Agent/AutopilotMonitor.Agent.Core/Storage/EventSpool.cs rename to src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Core/EventSpool.cs index 87d8eadc..93d11f36 100644 --- a/src/Agent/AutopilotMonitor.Agent.Core/Storage/EventSpool.cs +++ b/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Core/EventSpool.cs @@ -5,7 +5,7 @@ using System.Linq; using AutopilotMonitor.Shared.Models; using Newtonsoft.Json; -namespace AutopilotMonitor.Agent.Core.Storage +namespace AutopilotMonitor.Agent.Core.Monitoring.Core { /// /// Manages offline event storage (spool) for resilient uploads @@ -79,7 +79,7 @@ namespace AutopilotMonitor.Agent.Core.Storage } /// - /// Gets a batch of events from the spool (oldest first) + /// Gets a batch of events from the spool (oldest first, sorted by sequence) /// public List GetBatch(int maxBatchSize) { @@ -87,8 +87,10 @@ namespace AutopilotMonitor.Agent.Core.Storage { var events = new List(); var files = Directory.GetFiles(_spoolDirectory, "event_*.json") - .OrderBy(f => f) + .Select(f => new { Path = f, Sequence = ExtractSequenceFromFilename(f) }) + .OrderBy(x => x.Sequence) .Take(maxBatchSize) + .Select(x => x.Path) .ToList(); foreach (var file in files) @@ -112,6 +114,29 @@ namespace AutopilotMonitor.Agent.Core.Storage } } + /// + /// Extracts the sequence number from event filename + /// Filename format: event_{timestamp}_{sequence}.json + /// + private long ExtractSequenceFromFilename(string filePath) + { + try + { + var fileName = Path.GetFileNameWithoutExtension(filePath); + var parts = fileName.Split('_'); + if (parts.Length >= 3) + { + if (long.TryParse(parts[2], out var sequence)) + return sequence; + } + } + catch + { + // Fallback to 0 if parsing fails + } + return 0; + } + /// /// Removes events from the spool after successful upload /// diff --git a/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/MonitoringService.cs b/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Core/MonitoringService.cs similarity index 86% rename from src/Agent/AutopilotMonitor.Agent.Core/Monitoring/MonitoringService.cs rename to src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Core/MonitoringService.cs index 1adf9ea1..4cacc874 100644 --- a/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/MonitoringService.cs +++ b/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Core/MonitoringService.cs @@ -1,17 +1,18 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Threading; using System.Threading.Tasks; -using AutopilotMonitor.Agent.Core.Api; using AutopilotMonitor.Agent.Core.Configuration; -using AutopilotMonitor.Agent.Core.EventCollection; using AutopilotMonitor.Agent.Core.Logging; -using AutopilotMonitor.Agent.Core.Network; -using AutopilotMonitor.Agent.Core.Storage; +using AutopilotMonitor.Agent.Core.Monitoring.Collectors; +using AutopilotMonitor.Agent.Core.Monitoring.Network; +using AutopilotMonitor.Agent.Core.Monitoring.Simulation; +using AutopilotMonitor.Agent.Core.Monitoring.Tracking; using AutopilotMonitor.Shared.Models; -namespace AutopilotMonitor.Agent.Core.Monitoring +namespace AutopilotMonitor.Agent.Core.Monitoring.Core { /// /// Main monitoring service that collects and uploads telemetry @@ -29,17 +30,14 @@ namespace AutopilotMonitor.Agent.Core.Monitoring private EnrollmentPhase? _lastPhase = null; // Core event collectors (always on) - private EventLogWatcher _eventLogWatcher; - private RegistryMonitor _registryMonitor; - private PhaseDetector _phaseDetector; private HelloDetector _helloDetector; private AutopilotSimulator _simulator; // Optional collectors (toggled via remote config) private PerformanceCollector _performanceCollector; - private DownloadProgressCollector _downloadProgressCollector; - private CertValidationCollector _certValidationCollector; - private EspUiStateCollector _espUiStateCollector; + + // Smart enrollment tracking (replaces DownloadProgressCollector + EspUiStateCollector) + private EnrollmentTracker _enrollmentTracker; // Remote config and gather rules private RemoteConfigService _remoteConfigService; @@ -96,7 +94,7 @@ namespace AutopilotMonitor.Agent.Core.Monitoring _spool.StartWatching(); _logger.Info("FileSystemWatcher started for efficient event upload"); - // Emit initial event + // Emit agent_started event EmitEvent(new EnrollmentEvent { SessionId = _configuration.SessionId, @@ -104,8 +102,12 @@ namespace AutopilotMonitor.Agent.Core.Monitoring EventType = "agent_started", Severity = EventSeverity.Info, Source = "Agent", - Phase = EnrollmentPhase.PreFlight, - Message = "Autopilot Monitor Agent started" + Phase = EnrollmentPhase.Start, + Message = "Autopilot Monitor Agent started", + Data = new Dictionary + { + { "agentVersion", "1.0.0" } + } }); // Collect and emit device geo-location (if enabled) @@ -114,6 +116,9 @@ namespace AutopilotMonitor.Agent.Core.Monitoring EmitGeoLocationEvent(); } + // Note: Device info (network, DNS, proxy, Autopilot profile, AAD join, etc.) + // is collected by EnrollmentTracker when it starts + // Fetch remote config (collector toggles + gather rules) from backend FetchRemoteConfig(); @@ -146,7 +151,7 @@ namespace AutopilotMonitor.Agent.Core.Monitoring EventType = "device_location", Severity = EventSeverity.Info, Source = "Network", - Phase = EnrollmentPhase.PreFlight, + Phase = EnrollmentPhase.Unknown, Message = $"Device location: {location.City}, {location.Region}, {location.Country} (via {location.Source})", Data = location.ToDictionary() }); @@ -167,33 +172,6 @@ namespace AutopilotMonitor.Agent.Core.Monitoring try { - // Start EventLog watcher - _eventLogWatcher = new EventLogWatcher( - _configuration.SessionId, - _configuration.TenantId, - EmitEvent, - _logger - ); - _eventLogWatcher.Start(); - - // Start Registry monitor - _registryMonitor = new RegistryMonitor( - _configuration.SessionId, - _configuration.TenantId, - EmitEvent, - _logger - ); - _registryMonitor.Start(); - - // Start Phase detector - _phaseDetector = new PhaseDetector( - _configuration.SessionId, - _configuration.TenantId, - EmitEvent, - _logger - ); - _phaseDetector.Start(); - // Start Hello detector (WHfB provisioning tracking) _helloDetector = new HelloDetector( _configuration.SessionId, @@ -207,12 +185,16 @@ namespace AutopilotMonitor.Agent.Core.Monitoring if (_configuration.EnableSimulator) { _logger.Info("Simulator mode enabled - starting Autopilot simulator"); + var imeLogPatterns = _remoteConfigService?.CurrentConfig?.ImeLogPatterns; _simulator = new AutopilotSimulator( _configuration.SessionId, _configuration.TenantId, EmitEvent, _logger, - _configuration.SimulateFailure + _configuration.SimulateFailure, + _configuration.SimulationLogDirectory, + _configuration.SimulationSpeedFactor, + imeLogPatterns ); _simulator.Start(); _logger.Info("Autopilot simulator started"); @@ -251,61 +233,35 @@ namespace AutopilotMonitor.Agent.Core.Monitoring var config = _remoteConfigService?.CurrentConfig; var collectors = config?.Collectors; - if (collectors == null) - { - _logger.Info("No remote config available - optional collectors disabled"); - return; - } - - _logger.Info("Starting optional collectors based on remote config"); + _logger.Info("Starting collectors based on remote config"); try { - if (collectors.EnablePerformanceCollector) + // PerformanceCollector is always on (feeds UI chart) + var perfInterval = collectors?.PerformanceIntervalSeconds ?? 60; + _performanceCollector = new PerformanceCollector( + _configuration.SessionId, + _configuration.TenantId, + EmitEvent, + _logger, + perfInterval + ); + _performanceCollector.Start(); + + // EnrollmentTracker: smart enrollment tracking with IME log parsing + // (replaces DownloadProgressCollector + EspUiStateCollector) + if (!_configuration.EnableSimulator) { - _performanceCollector = new PerformanceCollector( + var imeLogPatterns = config?.ImeLogPatterns; + _enrollmentTracker = new EnrollmentTracker( _configuration.SessionId, _configuration.TenantId, EmitEvent, _logger, - collectors.PerformanceIntervalSeconds + imeLogPatterns, + _configuration.ImeLogPathOverride ); - _performanceCollector.Start(); - } - - if (collectors.EnableDownloadProgressCollector) - { - _downloadProgressCollector = new DownloadProgressCollector( - _configuration.SessionId, - _configuration.TenantId, - EmitEvent, - _logger, - collectors.DownloadProgressIntervalSeconds - ); - _downloadProgressCollector.Start(); - } - - if (collectors.EnableCertValidationCollector) - { - _certValidationCollector = new CertValidationCollector( - _configuration.SessionId, - _configuration.TenantId, - EmitEvent, - _logger - ); - _certValidationCollector.Start(); - } - - if (collectors.EnableEspUiStateCollector) - { - _espUiStateCollector = new EspUiStateCollector( - _configuration.SessionId, - _configuration.TenantId, - EmitEvent, - _logger, - collectors.EspUiStateIntervalSeconds - ); - _espUiStateCollector.Start(); + _enrollmentTracker.Start(); } } catch (Exception ex) @@ -323,17 +279,9 @@ namespace AutopilotMonitor.Agent.Core.Monitoring _performanceCollector?.Dispose(); _performanceCollector = null; - _downloadProgressCollector?.Stop(); - _downloadProgressCollector?.Dispose(); - _downloadProgressCollector = null; - - _certValidationCollector?.Stop(); - _certValidationCollector?.Dispose(); - _certValidationCollector = null; - - _espUiStateCollector?.Stop(); - _espUiStateCollector?.Dispose(); - _espUiStateCollector = null; + _enrollmentTracker?.Stop(); + _enrollmentTracker?.Dispose(); + _enrollmentTracker = null; } /// @@ -354,7 +302,8 @@ namespace AutopilotMonitor.Agent.Core.Monitoring _configuration.SessionId, _configuration.TenantId, EmitEvent, - _logger + _logger, + _configuration.ImeLogPathOverride ); _gatherRuleExecutor.UpdateRules(config.GatherRules); } @@ -374,6 +323,12 @@ namespace AutopilotMonitor.Agent.Core.Monitoring try { + // Hot-reload IME log patterns without restart + if (_enrollmentTracker != null && newConfig.ImeLogPatterns != null) + { + _enrollmentTracker.UpdatePatterns(newConfig.ImeLogPatterns); + } + // Stop and restart optional collectors with new settings StopOptionalCollectors(); StartOptionalCollectors(); @@ -415,7 +370,7 @@ namespace AutopilotMonitor.Agent.Core.Monitoring OsEdition = GetOsEdition(), OsLanguage = System.Globalization.CultureInfo.CurrentCulture.Name, StartedAt = DateTime.UtcNow, - AgentVersion = "1.0.0-phase1" + AgentVersion = "1.0.0" }; var response = await _apiClient.RegisterSessionAsync(registration); @@ -532,7 +487,7 @@ namespace AutopilotMonitor.Agent.Core.Monitoring EventType = "agent_stopped", Severity = EventSeverity.Info, Source = "Agent", - Phase = _phaseDetector?.CurrentPhase ?? EnrollmentPhase.PreFlight, + Phase = _lastPhase ?? EnrollmentPhase.Unknown, Message = "Autopilot Monitor Agent stopped" }); @@ -551,15 +506,6 @@ namespace AutopilotMonitor.Agent.Core.Monitoring try { - _eventLogWatcher?.Stop(); - _eventLogWatcher?.Dispose(); - - _registryMonitor?.Stop(); - _registryMonitor?.Dispose(); - - _phaseDetector?.Stop(); - _phaseDetector?.Dispose(); - _helloDetector?.Stop(); _helloDetector?.Dispose(); @@ -603,12 +549,6 @@ namespace AutopilotMonitor.Agent.Core.Monitoring // Notify gather rule executor of phase change try { _gatherRuleExecutor?.OnPhaseChanged(evt.Phase); } catch { } - - // Re-run cert validation on network phase completion - if (evt.Phase == EnrollmentPhase.Identity && _certValidationCollector != null) - { - try { _certValidationCollector.RunValidation(); } catch { } - } } // Notify gather rule executor of event type (for on_event triggers) @@ -659,11 +599,15 @@ namespace AutopilotMonitor.Agent.Core.Monitoring // 1. Critical events (errors) - for troubleshooting // 2. Phase transitions (start/end) - for real-time phase tracking in UI // 3. Events with "phase" in EventType - explicit phase-related events + // 4. App download/install events - for real-time download progress UI updates + var isAppEvent = evt.EventType?.StartsWith("app_", StringComparison.OrdinalIgnoreCase) == true; + if (evt.Severity >= EventSeverity.Error || isPhaseTransition || - evt.EventType?.Contains("phase", StringComparison.OrdinalIgnoreCase) == true) + evt.EventType?.Contains("phase", StringComparison.OrdinalIgnoreCase) == true || + isAppEvent) { - _logger.Debug("Critical/Phase event detected, triggering immediate upload (bypassing debounce)"); + _logger.Debug($"Critical/Phase/App event detected ({evt.EventType}), triggering immediate upload (bypassing debounce)"); Task.Run(() => UploadEventsAsync()); } } @@ -956,15 +900,10 @@ Restart-Computer -Force _debounceTimer?.Dispose(); _apiClient?.Dispose(); _spool?.Dispose(); - _eventLogWatcher?.Dispose(); - _registryMonitor?.Dispose(); - _phaseDetector?.Dispose(); _helloDetector?.Dispose(); _simulator?.Dispose(); _performanceCollector?.Dispose(); - _downloadProgressCollector?.Dispose(); - _certValidationCollector?.Dispose(); - _espUiStateCollector?.Dispose(); + _enrollmentTracker?.Dispose(); _gatherRuleExecutor?.Dispose(); _remoteConfigService?.Dispose(); } diff --git a/src/Agent/AutopilotMonitor.Agent.Core/Storage/SessionPersistence.cs b/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Core/SessionPersistence.cs similarity index 98% rename from src/Agent/AutopilotMonitor.Agent.Core/Storage/SessionPersistence.cs rename to src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Core/SessionPersistence.cs index 4f4361bc..564fd03f 100644 --- a/src/Agent/AutopilotMonitor.Agent.Core/Storage/SessionPersistence.cs +++ b/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Core/SessionPersistence.cs @@ -1,7 +1,7 @@ using System; using System.IO; -namespace AutopilotMonitor.Agent.Core.Storage +namespace AutopilotMonitor.Agent.Core.Monitoring.Core { /// /// Manages persistent session ID storage to maintain tracking across agent restarts diff --git a/src/Agent/AutopilotMonitor.Agent.Core/Api/BackendApiClient.cs b/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Network/BackendApiClient.cs similarity index 99% rename from src/Agent/AutopilotMonitor.Agent.Core/Api/BackendApiClient.cs rename to src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Network/BackendApiClient.cs index f30a9311..90e38ccc 100644 --- a/src/Agent/AutopilotMonitor.Agent.Core/Api/BackendApiClient.cs +++ b/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Network/BackendApiClient.cs @@ -12,7 +12,7 @@ using AutopilotMonitor.Shared; using AutopilotMonitor.Shared.Models; using Newtonsoft.Json; -namespace AutopilotMonitor.Agent.Core.Api +namespace AutopilotMonitor.Agent.Core.Monitoring.Network { /// /// Client for communicating with the backend API diff --git a/src/Agent/AutopilotMonitor.Agent.Core/Network/GeoLocationService.cs b/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Network/GeoLocationService.cs similarity index 98% rename from src/Agent/AutopilotMonitor.Agent.Core/Network/GeoLocationService.cs rename to src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Network/GeoLocationService.cs index bda51f86..2284eb52 100644 --- a/src/Agent/AutopilotMonitor.Agent.Core/Network/GeoLocationService.cs +++ b/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Network/GeoLocationService.cs @@ -5,7 +5,7 @@ using System.Threading.Tasks; using AutopilotMonitor.Agent.Core.Logging; using Newtonsoft.Json.Linq; -namespace AutopilotMonitor.Agent.Core.Network +namespace AutopilotMonitor.Agent.Core.Monitoring.Network { public class GeoLocationResult { diff --git a/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Simulation/AutopilotSimulator.cs b/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Simulation/AutopilotSimulator.cs new file mode 100644 index 00000000..fbc3d7e9 --- /dev/null +++ b/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Simulation/AutopilotSimulator.cs @@ -0,0 +1,202 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using AutopilotMonitor.Agent.Core.Logging; +using AutopilotMonitor.Agent.Core.Monitoring.Tracking; +using AutopilotMonitor.Shared.Models; + +namespace AutopilotMonitor.Agent.Core.Monitoring.Simulation +{ + /// + /// Simulates Autopilot enrollment using real IME log replay and real device data. + /// + /// Key principle: Real data, no mocks. + /// - Device info: collected from actual WMI/Registry on the machine running the simulator + /// - App tracking: replayed from real IME log files with time-compressed delays + /// - Performance data: handled by PerformanceCollector (always on, real machine data) + /// + public class AutopilotSimulator : IDisposable + { + private readonly AgentLogger _logger; + private readonly string _sessionId; + private readonly string _tenantId; + private readonly Action _onEventCollected; + private readonly bool _simulateFailure; + private readonly string _simulationLogDirectory; + private readonly double _speedFactor; + private readonly List _imeLogPatterns; + + private CancellationTokenSource _cancellationTokenSource; + private Task _simulationTask; + private EnrollmentTracker _enrollmentTracker; + private bool _disposed = false; + + public AutopilotSimulator( + string sessionId, + string tenantId, + Action onEventCollected, + AgentLogger logger, + bool simulateFailure = false, + string simulationLogDirectory = null, + double speedFactor = 50, + List imeLogPatterns = null) + { + _sessionId = sessionId ?? throw new ArgumentNullException(nameof(sessionId)); + _tenantId = tenantId ?? throw new ArgumentNullException(nameof(tenantId)); + _onEventCollected = onEventCollected ?? throw new ArgumentNullException(nameof(onEventCollected)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _simulateFailure = simulateFailure; + _simulationLogDirectory = simulationLogDirectory; + _speedFactor = speedFactor; + _imeLogPatterns = imeLogPatterns ?? new List(); + } + + /// + /// Starts the Autopilot simulation + /// + public void Start() + { + _logger.Info("Starting Autopilot Simulator (log replay mode)"); + + if (string.IsNullOrEmpty(_simulationLogDirectory) || !Directory.Exists(_simulationLogDirectory)) + { + _logger.Warning($"Simulation log directory not found or not specified: {_simulationLogDirectory}"); + _logger.Info("Simulator will only emit device info events (no log replay)"); + } + + _cancellationTokenSource = new CancellationTokenSource(); + _simulationTask = Task.Run(() => RunSimulation(_cancellationTokenSource.Token)); + } + + /// + /// Stops the simulation + /// + public void Stop() + { + if (_cancellationTokenSource == null || _cancellationTokenSource.IsCancellationRequested) + return; + + _logger.Info("Stopping Autopilot Simulator"); + + try + { + _cancellationTokenSource?.Cancel(); + _simulationTask?.Wait(TimeSpan.FromSeconds(5)); + } + catch (ObjectDisposedException) { } + catch (AggregateException) { } + } + + private async Task RunSimulation(CancellationToken cancellationToken) + { + try + { + _logger.Info("Starting simulated Autopilot enrollment (real device data + log replay)"); + + // Create EnrollmentTracker in simulation mode + // This collects REAL device info from this machine and replays IME logs + // The EnrollmentTracker will emit all real events from log replay + _enrollmentTracker = new EnrollmentTracker( + _sessionId, + _tenantId, + _onEventCollected, + _logger, + _imeLogPatterns, + _simulationLogDirectory, + simulationMode: true, + speedFactor: _speedFactor + ); + + _enrollmentTracker.Start(); + _logger.Info($"EnrollmentTracker started in simulation mode (speed factor: {_speedFactor}x)"); + + // Wait for log replay to complete or be cancelled + // The ImeLogTracker will parse all log files and then the polling loop will idle + // We monitor the app tracking state to know when to complete + var checkInterval = TimeSpan.FromSeconds(5); + var maxWaitTime = TimeSpan.FromMinutes(10); // Safety timeout + var elapsed = TimeSpan.Zero; + var allAppsCompleted = false; + + while (!cancellationToken.IsCancellationRequested && elapsed < maxWaitTime) + { + await Task.Delay(checkInterval, cancellationToken); + elapsed += checkInterval; + + // Check if all apps have been tracked and completed + var packageStates = _enrollmentTracker.ImeTracker?.PackageStates; + if (packageStates != null && packageStates.CountAll > 0 && packageStates.IsAllCompleted()) + { + allAppsCompleted = true; + _logger.Info($"Simulator: all {packageStates.CountAll} apps completed after {elapsed.TotalSeconds:F0}s"); + break; + } + } + + if (cancellationToken.IsCancellationRequested) return; + + // Note: enrollment_complete event is now emitted by EnrollmentTracker + // when it detects "Completed user session" in the IME log. + // We only emit failure events here if simulation fails. + if (_simulateFailure && !allAppsCompleted) + { + EmitEvent("enrollment_failed", "Enrollment failed: app installation timeout", + EventSeverity.Critical, EnrollmentPhase.Failed, + new Dictionary + { + { "failedPhase", "AppInstallation" }, + { "reason", "App installation did not complete within timeout" } + }); + } + + _logger.Info("Simulated Autopilot enrollment replay completed (enrollment_complete will be emitted when IME log shows user session completion)"); + } + catch (OperationCanceledException) + { + _logger.Info("Autopilot simulation cancelled"); + } + catch (Exception ex) + { + _logger.Error("Error in Autopilot simulation", ex); + } + } + + private void EmitEvent(string eventType, string message, EventSeverity severity, + EnrollmentPhase phase, Dictionary data = null) + { + try + { + _onEventCollected(new EnrollmentEvent + { + SessionId = _sessionId, + TenantId = _tenantId, + Timestamp = DateTime.UtcNow, + EventType = eventType, + Severity = severity, + Source = "Simulator", + Phase = phase, + Message = message, + Data = data ?? new Dictionary() + }); + _logger.Debug($"Simulator event: {eventType} - {message}"); + } + catch (Exception ex) + { + _logger.Error($"Error emitting simulator event: {eventType}", ex); + } + } + + public void Dispose() + { + if (_disposed) + return; + + Stop(); + _enrollmentTracker?.Dispose(); + _cancellationTokenSource?.Dispose(); + _disposed = true; + } + } +} diff --git a/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Tracking/AppPackageState.cs b/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Tracking/AppPackageState.cs new file mode 100644 index 00000000..92deb826 --- /dev/null +++ b/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Tracking/AppPackageState.cs @@ -0,0 +1,314 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace AutopilotMonitor.Agent.Core.Monitoring.Tracking +{ + /// + /// Installation state of an app package during enrollment. + /// Order matters: states are compared numerically for upgrade-only transitions. + /// + public enum AppInstallationState + { + Unknown = 0, + NotInstalled = 1, + InProgress = 2, + Downloading = 3, + Installing = 4, + Installed = 5, + Skipped = 6, + Postponed = 7, + Error = 8 + } + + /// + /// How the app should run + /// + public enum AppRunAs + { + Unknown = -1, + User = 0, + System = 1 + } + + /// + /// App install intent from Intune policy + /// + public enum AppIntent + { + Unknown = -1, + NotTargeted = 0, + Available = 1, + Install = 3, + Uninstall = 4 + } + + /// + /// How the app is targeted + /// + [Flags] + public enum AppTargeted + { + Unknown = 128, + Dependency = 0, + User = 1, + Device = 2 + } + + /// + /// Win32 app state values from IME + /// + public enum Win32AppState + { + Unknown = 0, + NotInstalled = 1, + InProgress = 2, + Completed = 3, + Error = 4 + } + + /// + /// Represents the state of a single app package during enrollment. + /// Adapted from EspOverlay PackageState with state transition protection. + /// + public class AppPackageState : IComparable + { + public static bool SortErrorsToTop = false; + + public string Id { get; } + public int ListPos { get; } + public string Name { get; private set; } + public AppRunAs RunAs { get; private set; } = AppRunAs.Unknown; + public AppIntent Intent { get; private set; } = AppIntent.Unknown; + public AppTargeted Targeted { get; private set; } = AppTargeted.Unknown; + public HashSet DependsOn { get; private set; } = new HashSet(); + public AppInstallationState InstallationState { get; private set; } = AppInstallationState.Unknown; + public long InstallationStateLastChangedTicks { get; private set; } = 0; + public bool DownloadingOrInstallingSeen { get; private set; } = false; + public int? ProgressPercent { get; private set; } = null; + public long BytesDownloaded { get; private set; } = 0; + public long BytesTotal { get; private set; } = 0; + + private static readonly Dictionary Win32StateMap = + new Dictionary + { + { Win32AppState.Unknown, AppInstallationState.Unknown }, + { Win32AppState.InProgress, AppInstallationState.InProgress }, + { Win32AppState.Completed, AppInstallationState.Installed }, + { Win32AppState.Error, AppInstallationState.Error } + }; + + private static readonly Dictionary SortOrderOverrides = + new Dictionary + { + { AppInstallationState.Skipped, (int)AppInstallationState.Installed }, + { AppInstallationState.Postponed, (int)AppInstallationState.Installed }, + { AppInstallationState.Error, (int)AppInstallationState.Installed } + }; + + public AppPackageState(string id, int listPos) + { + Id = id; + ListPos = listPos; + } + + /// + /// Updates the app name. Only updates if the new name is not a truncated version of the current name. + /// + public bool UpdateName(string newName) + { + if (string.IsNullOrEmpty(newName) || string.Equals(Name, newName)) + return false; + + // Avoid overwriting with a truncated version + if (Name != null && newName.Length > 0 && Name.StartsWith(newName)) + return false; + + Name = newName; + return true; + } + + /// + /// Updates RunAs property + /// + public bool UpdateRunAs(AppRunAs newRunAs) + { + if (RunAs == newRunAs) return false; + RunAs = newRunAs; + return true; + } + + /// + /// Updates Intent property + /// + public bool UpdateIntent(AppIntent newIntent) + { + if (Intent == newIntent) return false; + Intent = newIntent; + return true; + } + + /// + /// Updates Targeted property + /// + public bool UpdateTargeted(AppTargeted newTargeted) + { + if (Targeted == newTargeted) return false; + Targeted = newTargeted; + return true; + } + + /// + /// Updates DependsOn set + /// + public bool UpdateDependsOn(HashSet newDependsOn) + { + if (newDependsOn == null) newDependsOn = new HashSet(); + if (DependsOn.SetEquals(newDependsOn)) return false; + DependsOn = newDependsOn; + return true; + } + + /// + /// Updates the installation state with transition protection. + /// Returns true if the state actually changed. + /// + public bool UpdateState(AppInstallationState newState, int? newProgressPercent = null, bool upgradeOnly = false, long bytesDownloaded = 0, long bytesTotal = 0) + { + // upgradeOnly: only allow "higher" states (used by Win32AppState mapping) + if (upgradeOnly && newState < InstallationState) + return false; + + // Cannot downgrade from Installed to Skipped or Postponed + if (InstallationState == AppInstallationState.Installed && + (newState == AppInstallationState.Skipped || newState == AppInstallationState.Postponed)) + return false; + + // Postponed cannot go back to Downloading or lower active states + if (InstallationState == AppInstallationState.Postponed && newState <= AppInstallationState.Downloading) + return false; + + // If "Installed" without ever seeing download/install -> auto-downgrade to Skipped + // This handles apps with "inverse" detection rules (e.g., uninstall packages marked as + // installed when old software is NOT detected) + if (newState == AppInstallationState.Installed && !DownloadingOrInstallingSeen) + newState = AppInstallationState.Skipped; + + // Skip if no actual change + if (newState == InstallationState && newProgressPercent == ProgressPercent && bytesDownloaded == BytesDownloaded && bytesTotal == BytesTotal) + return false; + + var oldState = InstallationState; + InstallationState = newState; + InstallationStateLastChangedTicks = Stopwatch.GetTimestamp(); + DownloadingOrInstallingSeen |= (InstallationState >= AppInstallationState.Downloading && + InstallationState <= AppInstallationState.Installing); + ProgressPercent = newProgressPercent; + BytesDownloaded = bytesDownloaded; + BytesTotal = bytesTotal; + + return true; + } + + /// + /// Updates state from a Win32AppState string or integer string (e.g., "2" or "InProgress"). + /// Uses upgrade-only mode to prevent "InProgress" from destroying more detailed states. + /// + public bool UpdateStateFromWin32AppState(string win32AppStateStringOrIntString) + { + Win32AppState win32State; + if (Enum.TryParse(win32AppStateStringOrIntString, true, out win32State)) + return UpdateStateFromWin32AppState(win32State); + return false; + } + + /// + /// Updates state from Win32AppState enum value. + /// + public bool UpdateStateFromWin32AppState(Win32AppState win32State) + { + AppInstallationState newState; + if (Win32StateMap.TryGetValue(win32State, out newState)) + return UpdateState(newState, upgradeOnly: true); + return false; + } + + /// + /// Whether this app requires installation (Install or Uninstall intent) + /// + public bool IsRequired => Intent == AppIntent.Install || Intent == AppIntent.Uninstall; + + /// + /// Whether the app is currently being processed (InProgress, Downloading, or Installing) + /// + public bool IsActive => InstallationState >= AppInstallationState.InProgress && + InstallationState <= AppInstallationState.Installing; + + /// + /// Whether the app has reached a terminal state (Installed, Skipped, Postponed, Error) + /// + public bool IsCompleted => InstallationState >= AppInstallationState.Installed; + + /// + /// Whether the app is in an error state + /// + public bool IsError => InstallationState == AppInstallationState.Error; + + /// + /// Comparison for sorting: active apps first, then by state change time, then by list position. + /// + public int CompareTo(AppPackageState other) + { + // First: descending on sort order (active apps at top) + var result = -(SortOrder.CompareTo(other.SortOrder)); + if (result == 0) + { + // Then: by installation state last changed + result = InstallationStateLastChangedTicks.CompareTo(other.InstallationStateLastChangedTicks); + } + if (result == 0) + { + // Then: by original position in policy list + result = ListPos.CompareTo(other.ListPos); + } + return result; + } + + private int SortOrder + { + get + { + if (SortErrorsToTop && InstallationState == AppInstallationState.Error) + return -1; + + int sortOrder; + if (SortOrderOverrides.TryGetValue(InstallationState, out sortOrder)) + return sortOrder; + + return (int)InstallationState; + } + } + + /// + /// Returns a summary dictionary for event data + /// + public Dictionary ToEventData() + { + return new Dictionary + { + { "appId", Id }, + { "name", Name ?? Id }, + { "appName", Name ?? Id }, + { "state", InstallationState.ToString() }, + { "intent", Intent.ToString() }, + { "targeted", Targeted.ToString() }, + { "runAs", RunAs.ToString() }, + { "progressPercent", ProgressPercent ?? 0 }, + { "bytesDownloaded", BytesDownloaded }, + { "bytesTotal", BytesTotal }, + { "isError", IsError }, + { "isCompleted", IsCompleted } + }; + } + } +} diff --git a/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Tracking/AppPackageStateList.cs b/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Tracking/AppPackageStateList.cs new file mode 100644 index 00000000..5f8249eb --- /dev/null +++ b/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Tracking/AppPackageStateList.cs @@ -0,0 +1,406 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using AutopilotMonitor.Agent.Core.Logging; + +namespace AutopilotMonitor.Agent.Core.Monitoring.Tracking +{ + /// + /// Manages a collection of app package states during enrollment. + /// Tracks current app, ignore list, and dependency cascading. + /// Adapted from EspOverlay PackageStatesList. + /// + public class AppPackageStateList : List + { + /// + /// The ID of the app currently being processed by IME + /// + public string CurrentPackageId { get; set; } + + /// + /// Apps to ignore (e.g., user-targeted apps during device phase) + /// + public List IgnoreList { get; } = new List(); + + private readonly AgentLogger _logger; + + // States that should also cascade to dependents + private static readonly HashSet StatesToCascade = + new HashSet + { + AppInstallationState.Postponed, + AppInstallationState.Error + }; + + public AppPackageStateList(AgentLogger logger) + { + _logger = logger; + } + + /// + /// Gets a package by ID, optionally creating it if not found. + /// Returns null if the ID is in the ignore list. + /// + public AppPackageState GetPackage(string id, bool createIfNotFound = false) + { + if (string.IsNullOrEmpty(id) || IgnoreList.Contains(id)) + return null; + + var result = this.SingleOrDefault(x => string.Equals(x.Id, id, StringComparison.OrdinalIgnoreCase)); + if (result == null && createIfNotFound) + { + result = new AppPackageState(id, Count); + Add(result); + } + return result; + } + + /// + /// Adds/updates packages from a JSON policy list (from IME "Get policies" log line). + /// Returns true if any package was updated. + /// + public bool AddUpdateFromJsonPolicies(List> jsonPolicies, bool ignoreUserTargeted) + { + if (jsonPolicies == null) return false; + + var updated = false; + foreach (var policy in jsonPolicies) + { + string id; + object idObj; + if (!policy.TryGetValue("Id", out idObj) || (id = idObj?.ToString()) == null) + continue; + + // Check RunAs - ignore user-targeted during device phase + if (ignoreUserTargeted) + { + var runAs = GetRunAsFromPolicy(policy); + if (runAs == AppRunAs.User) + { + updated |= AddToIgnoreList(id); + continue; + } + } + + var pkg = GetPackage(id, createIfNotFound: true); + if (pkg == null) continue; + + // Update name + object nameObj; + if (policy.TryGetValue("Name", out nameObj)) + updated |= pkg.UpdateName(nameObj?.ToString()); + + // Update intent + object intentObj; + if (policy.TryGetValue("Intent", out intentObj)) + { + AppIntent intent; + if (Enum.TryParse(intentObj.ToString(), true, out intent)) + updated |= pkg.UpdateIntent(intent); + } + + // Update targeted + object targetObj; + if (policy.TryGetValue("TargetType", out targetObj)) + { + AppTargeted targeted; + if (Enum.TryParse(targetObj.ToString(), true, out targeted)) + updated |= pkg.UpdateTargeted(targeted); + } + + // Update RunAs from InstallEx + object installExObj; + if (policy.TryGetValue("InstallEx", out installExObj)) + { + var runAs = ExtractRunAsFromInstallEx(installExObj); + if (runAs != AppRunAs.Unknown) + updated |= pkg.UpdateRunAs(runAs); + } + + // Update dependencies from FlatDependencies + object flatDepsObj; + if (policy.TryGetValue("FlatDependencies", out flatDepsObj)) + { + var deps = ExtractDependencies(flatDepsObj, id); + if (deps != null) + updated |= pkg.UpdateDependsOn(deps); + } + } + + if (updated) + { + Sort(); + LogStates(); + } + + return updated; + } + + /// + /// Updates the name of a specific package + /// + public bool UpdateName(string id, string newName) + { + var pkg = GetPackage(id); + if (pkg == null) return false; + var updated = pkg.UpdateName(newName); + if (updated) LogStates(); + return updated; + } + + /// + /// Updates the installation state of a specific package. + /// Returns true if state actually changed. + /// Optionally cascades to dependent packages for error/postpone states. + /// + public bool UpdateState(string id, AppInstallationState newState, int? progressPercent = null) + { + var packageIds = new HashSet { id }; + + // Cascade error/postpone to dependents + if (StatesToCascade.Contains(newState)) + packageIds.UnionWith(GetDependentIdsDeep(id)); + + var packages = packageIds.Select(x => GetPackage(x)).Where(x => x != null).ToList(); + + var result = false; + foreach (var pkg in packages) + result |= pkg.UpdateState(newState, progressPercent); + + if (result) + { + AppPackageState.SortErrorsToTop = IsAllCompleted(); + Sort(); + } + + return result; + } + + /// + /// Updates state to Downloading with optional progress tracking + /// + public bool UpdateStateToDownloading(string id, string bytesDownloadedStr, string totalBytesStr) + { + int? progressPercent = null; + long bytesDownloaded = 0; + long bytesTotal = 0; + + try + { + bytesDownloaded = Convert.ToInt64(bytesDownloadedStr); + bytesTotal = Convert.ToInt64(totalBytesStr); + if (bytesTotal != 0) + progressPercent = (int)Math.Round((double)bytesDownloaded / bytesTotal * 100); + } + catch { } + + var pkg = GetPackage(id); + if (pkg != null && + pkg.InstallationState != AppInstallationState.Skipped && + pkg.InstallationState != AppInstallationState.Installed) + { + var result = pkg.UpdateState(AppInstallationState.Downloading, progressPercent, false, bytesDownloaded, bytesTotal); + if (result) + { + AppPackageState.SortErrorsToTop = IsAllCompleted(); + Sort(); + } + return result; + } + + return false; + } + + /// + /// Updates state from a Win32AppState string + /// + public bool UpdateStateFromWin32AppState(string id, string win32AppStateString) + { + var pkg = GetPackage(id); + if (pkg == null) return false; + var updated = pkg.UpdateStateFromWin32AppState(win32AppStateString); + if (updated) + { + AppPackageState.SortErrorsToTop = IsAllCompleted(); + Sort(); + } + return updated; + } + + /// + /// Sets the current package being processed + /// + public void SetCurrent(string id) + { + CurrentPackageId = id; + } + + /// + /// Adds an ID to the ignore list + /// + public bool AddToIgnoreList(string id) + { + if (string.IsNullOrEmpty(id) || IgnoreList.Contains(id)) + return false; + + IgnoreList.Add(id); + return true; + } + + /// + /// Checks if all required apps are completed + /// + public bool IsAllCompleted() + { + if (!this.Any()) return false; + + var allRequiredComplete = this.Where(x => x.IsRequired).All(x => x.IsCompleted); + if (allRequiredComplete) + { + // Mark dependency-only packages that IME never touched as Skipped + foreach (var pkg in this.Where(x => !x.IsRequired && x.InstallationState == AppInstallationState.Unknown)) + pkg.UpdateState(AppInstallationState.Skipped); + } + return allRequiredComplete; + } + + /// + /// Gets the first non-completed package (for display) + /// + public AppPackageState ActivePackage => this.FirstOrDefault(x => !x.IsCompleted); + + public int CountAll => Count; + public int CountCompleted => this.Count(x => x.IsCompleted); + public bool HasError => this.Any(x => x.IsError); + public int ErrorCount => this.Count(x => x.IsError); + + /// + /// Gets summary data for event emission + /// + public Dictionary GetSummaryData() + { + return new Dictionary + { + { "totalApps", CountAll }, + { "completedApps", CountCompleted }, + { "errorCount", ErrorCount }, + { "hasErrors", HasError }, + { "isAllCompleted", IsAllCompleted() }, + { "ignoredCount", IgnoreList.Count }, + { "apps", this.Select(x => x.ToEventData()).ToList() } + }; + } + + /// + /// Recursively gets all dependent package IDs (packages that depend on the given parent) + /// + private HashSet GetDependentIdsDeep(string parentId, int level = 0) + { + var result = new HashSet(); + try + { + level++; + if (level > 10) + { + _logger?.Warning("Dependency tree deeper than 10 levels - possible circular dependency"); + return result; + } + + foreach (var dependent in this.Where(x => x.DependsOn.Contains(parentId)).Select(x => x.Id)) + { + result.Add(dependent); + result.UnionWith(GetDependentIdsDeep(dependent, level)); + } + } + catch (Exception ex) + { + _logger?.Warning($"Error resolving dependencies for {parentId}: {ex.Message}"); + } + + return result; + } + + private static AppRunAs GetRunAsFromPolicy(Dictionary policy) + { + try + { + object installExObj; + if (policy.TryGetValue("InstallEx", out installExObj)) + return ExtractRunAsFromInstallEx(installExObj); + } + catch { } + return AppRunAs.Unknown; + } + + private static AppRunAs ExtractRunAsFromInstallEx(object installExObj) + { + try + { + if (installExObj is Dictionary installEx) + { + object runAsObj; + if (installEx.TryGetValue("RunAs", out runAsObj)) + return (AppRunAs)Convert.ToInt32(runAsObj); + } + // Handle as JSON string + var str = installExObj?.ToString(); + if (!string.IsNullOrEmpty(str)) + { + var json = System.Text.Json.JsonSerializer.Deserialize>(str); + if (json != null) + { + object runAsObj; + if (json.TryGetValue("RunAs", out runAsObj)) + return (AppRunAs)Convert.ToInt32(runAsObj.ToString()); + } + } + } + catch { } + return AppRunAs.Unknown; + } + + private static HashSet ExtractDependencies(object flatDepsObj, string appId) + { + try + { + if (flatDepsObj == null) return new HashSet(); + + var deps = flatDepsObj as List; + if (deps == null) return new HashSet(); + + var result = new HashSet(); + foreach (var dep in deps) + { + var depDict = dep as Dictionary; + if (depDict == null) continue; + + object actionObj, appIdObj, childIdObj, typeObj, levelObj; + if (depDict.TryGetValue("Action", out actionObj) && + depDict.TryGetValue("AppId", out appIdObj) && + depDict.TryGetValue("ChildId", out childIdObj) && + depDict.TryGetValue("Type", out typeObj) && + depDict.TryGetValue("Level", out levelObj)) + { + if (Convert.ToInt32(actionObj) == 10 && + string.Equals(appIdObj?.ToString(), appId, StringComparison.OrdinalIgnoreCase) && + Convert.ToInt32(typeObj) == 0 && + Convert.ToInt32(levelObj) == 0) + { + result.Add(childIdObj.ToString()); + } + } + } + return result; + } + catch { } + return new HashSet(); + } + + private void LogStates() + { + if (_logger == null) return; + var stateList = string.Join(", ", this.Select(x => $"{x.Name ?? x.Id}:{x.InstallationState}")); + _logger.Debug($"App states [{Count}]: {stateList}"); + } + } +} diff --git a/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Tracking/CmTraceLogParser.cs b/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Tracking/CmTraceLogParser.cs new file mode 100644 index 00000000..65e48bd4 --- /dev/null +++ b/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Tracking/CmTraceLogParser.cs @@ -0,0 +1,123 @@ +using System; +using System.Globalization; +using System.Text.RegularExpressions; + +namespace AutopilotMonitor.Agent.Core.Monitoring.Tracking +{ + /// + /// Represents a single parsed line from a CMTrace-format log file + /// + public class CmTraceLogEntry + { + public DateTime Timestamp { get; set; } + public string Message { get; set; } + public string Component { get; set; } + public int Type { get; set; } // 1=Info, 2=Warning, 3=Error + public string Thread { get; set; } + } + + /// + /// Parses CMTrace/SCCM log format used by IME and other Microsoft components. + /// Format: <![LOG[{message}]LOG]!><time="{time}" date="{date}" component="{comp}" context="" type="{type}" thread="{thread}" file=""> + /// + public static class CmTraceLogParser + { + // Pre-compiled regex for the CMTrace format + private static readonly Regex CmTraceRegex = new Regex( + @".*)\]LOG\]!>[\d:.]+)""\s+date=""(?[\d-]+)""\s+component=""(?[^""]*)""\s+context=""[^""]*""\s+type=""(?\d+)""\s+thread=""(?\d+)""\s+file=""[^""]*"">", + RegexOptions.Compiled | RegexOptions.Singleline + ); + + /// + /// Attempts to parse a single line of CMTrace-formatted log. + /// Returns true if parsing succeeded, false if the line doesn't match the format. + /// + public static bool TryParseLine(string line, out CmTraceLogEntry entry) + { + entry = null; + + if (string.IsNullOrEmpty(line) || !line.StartsWith(" 7) + fraction = fraction.Substring(0, 7); + normalizedTime = timeParts[0] + "." + fraction; + } + else + { + normalizedTime = timeStr; + } + + var combined = dateStr + " " + normalizedTime; + + // Try multiple date formats to handle varying date styles + string[] formats = new[] + { + "M-d-yyyy H:mm:ss.fffffff", + "M-d-yyyy H:mm:ss.ffffff", + "M-d-yyyy H:mm:ss.fffff", + "M-d-yyyy H:mm:ss.ffff", + "M-d-yyyy H:mm:ss.fff", + "M-d-yyyy H:mm:ss.ff", + "M-d-yyyy H:mm:ss.f", + "M-d-yyyy H:mm:ss", + "M-d-yyyy HH:mm:ss.fffffff", + "M-d-yyyy HH:mm:ss.ffffff", + "M-d-yyyy HH:mm:ss.fffff", + "M-d-yyyy HH:mm:ss.ffff", + "M-d-yyyy HH:mm:ss.fff", + "M-d-yyyy HH:mm:ss.ff", + "M-d-yyyy HH:mm:ss.f", + "M-d-yyyy HH:mm:ss" + }; + + return DateTime.TryParseExact(combined, formats, CultureInfo.InvariantCulture, + DateTimeStyles.None, out result); + } + } +} diff --git a/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Tracking/EnrollmentTracker.cs b/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Tracking/EnrollmentTracker.cs new file mode 100644 index 00000000..6e43c53d --- /dev/null +++ b/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Tracking/EnrollmentTracker.cs @@ -0,0 +1,699 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Management; +using System.Threading; +using AutopilotMonitor.Agent.Core.Logging; +using AutopilotMonitor.Shared.Models; +using Microsoft.Win32; + +namespace AutopilotMonitor.Agent.Core.Monitoring.Tracking +{ + /// + /// Central enrollment tracking orchestrator. + /// Collects consolidated device info events at startup and manages ImeLogTracker + /// for smart app installation tracking with strategic event emission. + /// + public class EnrollmentTracker : IDisposable + { + private readonly string _sessionId; + private readonly string _tenantId; + private readonly Action _emitEvent; + private readonly AgentLogger _logger; + private readonly string _imeLogFolder; + private readonly List _imeLogPatterns; + + private ImeLogTracker _imeLogTracker; + private Timer _summaryTimer; + private bool _summaryTimerActive; + private string _lastEspPhase; // Track last ESP phase to prevent duplicate events + private bool _hasAutoSwitchedToAppsPhase; // Track if we've already auto-switched to apps phase for current ESP phase + + // Default IME log folder + private const string DefaultImeLogFolder = @"%ProgramData%\Microsoft\IntuneManagementExtension\Logs"; + + public EnrollmentTracker( + string sessionId, + string tenantId, + Action emitEvent, + AgentLogger logger, + List imeLogPatterns, + string imeLogFolderOverride = null, + bool simulationMode = false, + double speedFactor = 50) + { + _sessionId = sessionId; + _tenantId = tenantId; + _emitEvent = emitEvent; + _logger = logger; + _imeLogPatterns = imeLogPatterns ?? new List(); + _imeLogFolder = imeLogFolderOverride ?? DefaultImeLogFolder; + + // Create ImeLogTracker + _imeLogTracker = new ImeLogTracker(_imeLogFolder, _imeLogPatterns, _logger); + _imeLogTracker.SimulationMode = simulationMode; + _imeLogTracker.SpeedFactor = speedFactor; + + // Wire up callbacks + _imeLogTracker.OnEspPhaseChanged = HandleEspPhaseChanged; + _imeLogTracker.OnImeAgentVersion = HandleImeAgentVersion; + _imeLogTracker.OnImeStarted = HandleImeStarted; + _imeLogTracker.OnAppStateChanged = HandleAppStateChanged; + _imeLogTracker.OnPoliciesDiscovered = HandlePoliciesDiscovered; + _imeLogTracker.OnAllAppsCompleted = HandleAllAppsCompleted; + _imeLogTracker.OnUserSessionCompleted = HandleUserSessionCompleted; + } + + /// + /// Starts the enrollment tracker: collects device info and starts IME log tracking. + /// + public void Start() + { + _logger.Info("EnrollmentTracker: starting"); + + // Collect and emit consolidated device info events + CollectDeviceInfo(); + + // Start IME log tracking + _imeLogTracker.Start(); + + // Start periodic summary timer (30s, starts when app tracking begins) + _summaryTimer = new Timer(SummaryTimerCallback, null, Timeout.Infinite, Timeout.Infinite); + + _logger.Info("EnrollmentTracker: started"); + } + + /// + /// Stops the enrollment tracker + /// + public void Stop() + { + _logger.Info("EnrollmentTracker: stopping"); + _summaryTimer?.Change(Timeout.Infinite, Timeout.Infinite); + _imeLogTracker?.Stop(); + _logger.Info("EnrollmentTracker: stopped"); + } + + /// + /// Updates IME log patterns (hot-reload from config change) + /// + public void UpdatePatterns(List newPatterns) + { + if (newPatterns != null) + { + _logger.Info("EnrollmentTracker: updating IME log patterns (hot-reload)"); + _imeLogTracker?.CompilePatterns(newPatterns); + } + } + + /// + /// Access to the ImeLogTracker (for simulator to reference package states) + /// + public ImeLogTracker ImeTracker => _imeLogTracker; + + // ===== Device Info Collection ===== + + private void CollectDeviceInfo() + { + _logger.Info("EnrollmentTracker: collecting device info"); + + CollectNetworkAdapters(); + CollectDnsConfiguration(); + CollectProxyConfiguration(); + CollectAutopilotProfile(); + CollectSecureBootStatus(); + CollectBitLockerStatus(); + CollectAadJoinStatus(); + } + + private void CollectNetworkAdapters() + { + try + { + var adapters = new List>(); + using (var searcher = new ManagementObjectSearcher( + "SELECT Description, IPAddress, IPSubnet, DefaultIPGateway, MACAddress, DHCPEnabled, DHCPServer, DNSServerSearchOrder " + + "FROM Win32_NetworkAdapterConfiguration WHERE IPEnabled = True")) + { + foreach (ManagementObject obj in searcher.Get()) + { + var adapter = new Dictionary + { + { "description", obj["Description"]?.ToString() }, + { "macAddress", obj["MACAddress"]?.ToString() }, + { "dhcpEnabled", obj["DHCPEnabled"]?.ToString() }, + { "dhcpServer", obj["DHCPServer"]?.ToString() } + }; + + var ipAddresses = obj["IPAddress"] as string[]; + if (ipAddresses != null) + adapter["ipAddresses"] = string.Join(", ", ipAddresses); + + var subnets = obj["IPSubnet"] as string[]; + if (subnets != null) + adapter["subnets"] = string.Join(", ", subnets); + + var gateways = obj["DefaultIPGateway"] as string[]; + if (gateways != null) + adapter["gateways"] = string.Join(", ", gateways); + + adapters.Add(adapter); + } + } + + EmitDeviceInfoEvent("network_adapters", "Network adapters configuration", + new Dictionary + { + { "adapterCount", adapters.Count }, + { "adapters", adapters } + }); + } + catch (Exception ex) + { + _logger.Warning($"EnrollmentTracker: failed to collect network adapters: {ex.Message}"); + } + } + + private void CollectDnsConfiguration() + { + try + { + var dnsServers = new List>(); + using (var searcher = new ManagementObjectSearcher( + "SELECT Description, DNSServerSearchOrder FROM Win32_NetworkAdapterConfiguration WHERE IPEnabled = True")) + { + foreach (ManagementObject obj in searcher.Get()) + { + var servers = obj["DNSServerSearchOrder"] as string[]; + if (servers != null && servers.Length > 0) + { + dnsServers.Add(new Dictionary + { + { "adapter", obj["Description"]?.ToString() }, + { "servers", string.Join(", ", servers) } + }); + } + } + } + + EmitDeviceInfoEvent("dns_configuration", "DNS server configuration", + new Dictionary + { + { "dnsEntries", dnsServers } + }); + } + catch (Exception ex) + { + _logger.Warning($"EnrollmentTracker: failed to collect DNS config: {ex.Message}"); + } + } + + private void CollectProxyConfiguration() + { + try + { + var data = new Dictionary(); + + using (var key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Internet Settings")) + { + if (key != null) + { + data["proxyEnabled"] = key.GetValue("ProxyEnable")?.ToString() == "1"; + data["proxyServer"] = key.GetValue("ProxyServer")?.ToString(); + data["proxyOverride"] = key.GetValue("ProxyOverride")?.ToString(); + data["autoConfigUrl"] = key.GetValue("AutoConfigURL")?.ToString(); + } + } + + // Also check WinHTTP proxy + using (var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\Connections")) + { + data["winHttpProxyConfigured"] = key?.GetValue("WinHttpSettings") != null; + } + + var proxyType = "Direct"; + if (data.ContainsKey("proxyEnabled") && (bool)data["proxyEnabled"]) + proxyType = "Proxy"; + else if (data.ContainsKey("autoConfigUrl") && data["autoConfigUrl"] != null) + proxyType = "PAC"; + + data["proxyType"] = proxyType; + + EmitDeviceInfoEvent("proxy_configuration", $"Proxy configuration: {proxyType}", data); + } + catch (Exception ex) + { + _logger.Warning($"EnrollmentTracker: failed to collect proxy config: {ex.Message}"); + } + } + + private void CollectAutopilotProfile() + { + try + { + var data = new Dictionary(); + + // Read CloudAssigned* values from AutopilotSettings + using (var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Provisioning\AutopilotSettings")) + { + if (key != null) + { + data["cloudAssignedTenantId"] = key.GetValue("CloudAssignedTenantId")?.ToString(); + data["cloudAssignedTenantDomain"] = key.GetValue("CloudAssignedTenantDomain")?.ToString(); + data["cloudAssignedOobeConfig"] = key.GetValue("CloudAssignedOobeConfig")?.ToString(); + } + } + + // Read deployment profile name + using (var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Provisioning\Diagnostics\AutoPilot")) + { + if (key != null) + { + data["deploymentProfileName"] = key.GetValue("DeploymentProfileName")?.ToString(); + } + } + + EmitDeviceInfoEvent("autopilot_profile", "Autopilot profile configuration", data); + } + catch (Exception ex) + { + _logger.Warning($"EnrollmentTracker: failed to collect autopilot profile: {ex.Message}"); + } + } + + private void CollectSecureBootStatus() + { + try + { + var data = new Dictionary(); + + using (var key = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\SecureBoot\State")) + { + if (key != null) + { + var value = key.GetValue("UEFISecureBootEnabled"); + data["uefiSecureBootEnabled"] = value != null && Convert.ToInt32(value) == 1; + } + else + { + data["uefiSecureBootEnabled"] = false; + data["note"] = "SecureBoot registry key not found"; + } + } + + EmitDeviceInfoEvent("secureboot_status", $"SecureBoot: {data["uefiSecureBootEnabled"]}", data); + } + catch (Exception ex) + { + _logger.Warning($"EnrollmentTracker: failed to collect SecureBoot status: {ex.Message}"); + } + } + + private void CollectBitLockerStatus() + { + try + { + var data = new Dictionary(); + var volumes = new List>(); + + using (var searcher = new ManagementObjectSearcher( + new ManagementScope(@"\\.\root\CIMV2\Security\MicrosoftVolumeEncryption"), + new ObjectQuery("SELECT DriveLetter, ProtectionStatus, ConversionStatus, EncryptionMethod FROM Win32_EncryptableVolume"))) + { + foreach (ManagementObject obj in searcher.Get()) + { + volumes.Add(new Dictionary + { + { "driveLetter", obj["DriveLetter"]?.ToString() }, + { "protectionStatus", obj["ProtectionStatus"]?.ToString() }, + { "conversionStatus", obj["ConversionStatus"]?.ToString() }, + { "encryptionMethod", obj["EncryptionMethod"]?.ToString() } + }); + } + } + + data["volumes"] = volumes; + data["systemDriveProtected"] = volumes.Any(v => + v["driveLetter"]?.ToString() == "C:" && v["protectionStatus"]?.ToString() == "1"); + + EmitDeviceInfoEvent("bitlocker_status", "BitLocker encryption status", data); + } + catch (Exception ex) + { + _logger.Warning($"EnrollmentTracker: failed to collect BitLocker status: {ex.Message}"); + } + } + + private void CollectAadJoinStatus() + { + try + { + var data = new Dictionary(); + + using (var joinInfoKey = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\CloudDomainJoin\JoinInfo")) + { + if (joinInfoKey != null) + { + var subKeyNames = joinInfoKey.GetSubKeyNames(); + if (subKeyNames.Length > 0) + { + using (var subKey = joinInfoKey.OpenSubKey(subKeyNames[0])) + { + if (subKey != null) + { + data["tenantId"] = subKey.GetValue("TenantId")?.ToString(); + data["userEmail"] = subKey.GetValue("UserEmail")?.ToString(); + data["joinType"] = "Azure AD Joined"; + data["thumbprint"] = subKeyNames[0]; // Certificate thumbprint + } + } + } + else + { + data["joinType"] = "Not Joined"; + } + } + else + { + data["joinType"] = "Not Joined"; + } + } + + object joinTypeValue; + var joinType = data.TryGetValue("joinType", out joinTypeValue) ? joinTypeValue?.ToString() ?? "Unknown" : "Unknown"; + EmitDeviceInfoEvent("aad_join_status", $"AAD join: {joinType}", data); + } + catch (Exception ex) + { + _logger.Warning($"EnrollmentTracker: failed to collect AAD join status: {ex.Message}"); + } + } + + private void EmitDeviceInfoEvent(string eventType, string message, Dictionary data) + { + _emitEvent(new EnrollmentEvent + { + SessionId = _sessionId, + TenantId = _tenantId, + EventType = eventType, + Severity = EventSeverity.Info, + Source = "EnrollmentTracker", + Phase = EnrollmentPhase.Unknown, + Message = message, + Data = data + }); + } + + // ===== ImeLogTracker Callbacks -> Strategic Events ===== + + private void HandleEspPhaseChanged(string phase) + { + // Only emit event if the phase has actually changed + if (string.Equals(phase, _lastEspPhase, StringComparison.OrdinalIgnoreCase)) + { + _logger.Debug($"EnrollmentTracker: ESP phase unchanged ({phase}), skipping event"); + return; + } + + _logger.Info($"EnrollmentTracker: ESP phase changed from '{_lastEspPhase ?? "null"}' to '{phase}'"); + _lastEspPhase = phase; + _hasAutoSwitchedToAppsPhase = false; // Reset when ESP phase changes + + // Map ESP phase to EnrollmentPhase (phase change events) + var enrollmentPhase = EnrollmentPhase.DeviceSetup; + if (string.Equals(phase, "AccountSetup", StringComparison.OrdinalIgnoreCase)) + enrollmentPhase = EnrollmentPhase.AccountSetup; + + _emitEvent(new EnrollmentEvent + { + SessionId = _sessionId, + TenantId = _tenantId, + EventType = "esp_phase_changed", + Severity = EventSeverity.Info, + Source = "ImeLogTracker", + Phase = enrollmentPhase, + Message = $"ESP phase: {phase}", + Data = new Dictionary { { "espPhase", phase } } + }); + + // Start summary timer when we detect ESP phase + if (!_summaryTimerActive) + { + _summaryTimerActive = true; + _summaryTimer?.Change(TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(30)); + } + } + + private void HandleImeAgentVersion(string version) + { + _emitEvent(new EnrollmentEvent + { + SessionId = _sessionId, + TenantId = _tenantId, + EventType = "ime_agent_version", + Severity = EventSeverity.Info, + Source = "ImeLogTracker", + Phase = EnrollmentPhase.Unknown, + Message = $"IME Agent version: {version}", + Data = new Dictionary { { "agentVersion", version } } + }); + } + + private void HandleImeStarted() + { + _logger.Info("EnrollmentTracker: IME started event"); + } + + private void HandleAppStateChanged(AppPackageState app, AppInstallationState oldState, AppInstallationState newState) + { + // Auto-switch to app installation phase when first app activity detected + // If we're in DeviceSetup and an app starts downloading/installing, switch to AppsDevice + // If we're in AccountSetup and an app starts downloading/installing, switch to AppsUser + if (!_hasAutoSwitchedToAppsPhase && + (newState == AppInstallationState.Downloading || newState == AppInstallationState.Installing) && + oldState < AppInstallationState.Downloading) + { + if (_lastEspPhase != null) + { + if (string.Equals(_lastEspPhase, "DeviceSetup", StringComparison.OrdinalIgnoreCase)) + { + // Switch from DeviceSetup to AppsDevice + _logger.Info($"EnrollmentTracker: First app activity detected during DeviceSetup, switching to AppsDevice phase"); + _hasAutoSwitchedToAppsPhase = true; + _emitEvent(new EnrollmentEvent + { + SessionId = _sessionId, + TenantId = _tenantId, + EventType = "esp_phase_changed", + Severity = EventSeverity.Info, + Source = "EnrollmentTracker", + Phase = EnrollmentPhase.AppsDevice, + Message = "ESP phase: AppsDevice (auto-detected from app activity)", + Data = new Dictionary { { "espPhase", "AppsDevice" }, { "autoDetected", true } } + }); + } + else if (string.Equals(_lastEspPhase, "AccountSetup", StringComparison.OrdinalIgnoreCase)) + { + // Switch from AccountSetup to AppsUser + _logger.Info($"EnrollmentTracker: First app activity detected during AccountSetup, switching to AppsUser phase"); + _hasAutoSwitchedToAppsPhase = true; + _emitEvent(new EnrollmentEvent + { + SessionId = _sessionId, + TenantId = _tenantId, + EventType = "esp_phase_changed", + Severity = EventSeverity.Info, + Source = "EnrollmentTracker", + Phase = EnrollmentPhase.AppsUser, + Message = "ESP phase: AppsUser (auto-detected from app activity)", + Data = new Dictionary { { "espPhase", "AppsUser" }, { "autoDetected", true } } + }); + } + } + } + + // Only emit strategic events for significant state transitions + string eventType; + var severity = EventSeverity.Info; + var phase = EnrollmentPhase.Unknown; // Apps set to Unknown, will be sorted chronologically into active phase + + switch (newState) + { + case AppInstallationState.Downloading: + // Emit strategic event once when download starts + if (oldState < AppInstallationState.Downloading) + { + eventType = "app_download_started"; + } + else + { + // Emit debug event for download progress updates + // Skip if no real download data (bytesTotal too small or zero) + if (app.BytesTotal > 1024) // At least 1 KB to be a real download + { + _emitEvent(new EnrollmentEvent + { + SessionId = _sessionId, + TenantId = _tenantId, + EventType = "download_progress", + Severity = EventSeverity.Debug, + Source = "ImeLogTracker", + Phase = phase, + Message = $"{app.Name ?? app.Id}: {app.ProgressPercent}%", + Data = app.ToEventData() + }); + } + return; // Skip main event emission below + } + break; + + case AppInstallationState.Installing: + eventType = "app_install_started"; + break; + + case AppInstallationState.Installed: + eventType = "app_install_completed"; + // Emit download_progress event for download manager (shows as completed) + _emitEvent(new EnrollmentEvent + { + SessionId = _sessionId, + TenantId = _tenantId, + EventType = "download_progress", + Severity = EventSeverity.Debug, + Source = "ImeLogTracker", + Phase = phase, + Message = $"{app.Name ?? app.Id}: completed", + Data = new Dictionary(app.ToEventData()) + { + ["status"] = "completed" + } + }); + break; + + case AppInstallationState.Skipped: + eventType = "app_install_skipped"; + break; + + case AppInstallationState.Error: + eventType = "app_install_failed"; + severity = EventSeverity.Error; + // Emit download_progress event for download manager (shows as failed) + _emitEvent(new EnrollmentEvent + { + SessionId = _sessionId, + TenantId = _tenantId, + EventType = "download_progress", + Severity = EventSeverity.Debug, + Source = "ImeLogTracker", + Phase = phase, + Message = $"{app.Name ?? app.Id}: failed", + Data = new Dictionary(app.ToEventData()) + { + ["status"] = "failed" + } + }); + break; + + case AppInstallationState.Postponed: + eventType = "app_install_failed"; + severity = EventSeverity.Warning; + break; + + default: + return; // Don't emit for Unknown, NotInstalled, InProgress + } + + _emitEvent(new EnrollmentEvent + { + SessionId = _sessionId, + TenantId = _tenantId, + EventType = eventType, + Severity = severity, + Source = "ImeLogTracker", + Phase = phase, + Message = $"{app.Name ?? app.Id}: {newState}", + Data = app.ToEventData() + }); + } + + private void HandlePoliciesDiscovered(string policiesJson) + { + _logger.Info($"EnrollmentTracker: policies discovered, tracking {_imeLogTracker.PackageStates.Count} apps"); + } + + private void HandleAllAppsCompleted() + { + _logger.Info("EnrollmentTracker: all apps completed"); + + // Stop summary timer + _summaryTimer?.Change(Timeout.Infinite, Timeout.Infinite); + _summaryTimerActive = false; + + // Emit final summary + EmitAppTrackingSummary(); + + // If we're in AppsDevice phase, we might transition back to DeviceSetup or to AccountSetup + // If we're in AppsUser phase, enrollment is likely about to complete + // For now, just log - the IME log will show the next phase change + if (_lastEspPhase != null) + { + _logger.Info($"EnrollmentTracker: All apps completed while in phase '{_lastEspPhase}'"); + } + } + + private void HandleUserSessionCompleted() + { + _logger.Info("EnrollmentTracker: User session completed (detected from IME log)"); + + // Stop summary timer if running + _summaryTimer?.Change(Timeout.Infinite, Timeout.Infinite); + _summaryTimerActive = false; + + // Emit enrollment completed event + _emitEvent(new EnrollmentEvent + { + SessionId = _sessionId, + TenantId = _tenantId, + EventType = "enrollment_complete", + Severity = EventSeverity.Info, + Source = "ImeLogTracker", + Phase = EnrollmentPhase.Complete, + Message = "Autopilot enrollment completed successfully (user session completed)" + }); + } + + private void SummaryTimerCallback(object state) + { + if (_imeLogTracker?.PackageStates?.CountAll > 0) + { + EmitAppTrackingSummary(); + } + } + + private void EmitAppTrackingSummary() + { + var states = _imeLogTracker?.PackageStates; + if (states == null || states.CountAll == 0) return; + + _emitEvent(new EnrollmentEvent + { + SessionId = _sessionId, + TenantId = _tenantId, + EventType = "app_tracking_summary", + Severity = states.HasError ? EventSeverity.Warning : EventSeverity.Info, + Source = "EnrollmentTracker", + Phase = EnrollmentPhase.Unknown, + Message = $"App tracking: {states.CountCompleted}/{states.CountAll} completed" + + (states.HasError ? $" ({states.ErrorCount} errors)" : ""), + Data = states.GetSummaryData() + }); + } + + public void Dispose() + { + Stop(); + _summaryTimer?.Dispose(); + _imeLogTracker?.Dispose(); + } + } +} diff --git a/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Tracking/ImeLogTracker.cs b/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Tracking/ImeLogTracker.cs new file mode 100644 index 00000000..0466379d --- /dev/null +++ b/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Tracking/ImeLogTracker.cs @@ -0,0 +1,602 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.Json; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; +using AutopilotMonitor.Agent.Core.Monitoring.Collectors; +using AutopilotMonitor.Agent.Core.Monitoring.Tracking; +using AutopilotMonitor.Agent.Core.Monitoring.Simulation; +using AutopilotMonitor.Agent.Core.Logging; +using AutopilotMonitor.Shared.Models; + +namespace AutopilotMonitor.Agent.Core.Monitoring.Tracking +{ + /// + /// Parses IME (Intune Management Extension) log files using regex patterns from the backend. + /// Tracks app installation state transitions and emits strategic events. + /// Adapted from EspOverlay's ImeLogFileParser + RjLogFileParser. + /// + /// Key design: Regex patterns are NOT hardcoded - they come from backend config via ImeLogPattern list. + /// This allows updating patterns without agent rebuild when Microsoft changes IME log output. + /// + public class ImeLogTracker : IDisposable + { + private readonly string _logFolder; + private readonly AgentLogger _logger; + private readonly LogFilePositionTracker _positionTracker = new LogFilePositionTracker(); + private readonly AppPackageStateList _packageStates; + private readonly int _pollingIntervalMs; + + // Compiled pattern matchers grouped by category + private List _patternsAlways = new List(); + private List _patternsCurrentPhase = new List(); + private List _patternsOtherPhases = new List(); + + // Active matchers (changes based on ESP phase) + private List _activePatterns = new List(); + private bool _logPhaseIsCurrentPhase = false; + + // Simulation mode + public bool SimulationMode { get; set; } + public double SpeedFactor { get; set; } = 50; + private DateTime _lastLogTimestamp = DateTime.MinValue; + + // Background task + private Task _pollingTask; + private CancellationTokenSource _cts; + + // Standard GUID capture pattern used as {GUID} placeholder in patterns + private const string GuidPattern = @"(?[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12})"; + + // Log files to monitor + private static readonly string[] LogFilePatterns = new[] + { + "IntuneManagementExtension.log", + "_IntuneManagementExtension.log", + "IntuneManagementExtension-????????-??????.log", + "AppWorkload.log", + "AppWorkload-????????-??????.log" + }; + + // Callbacks to EnrollmentTracker + public Action OnEspPhaseChanged { get; set; } + public Action OnImeAgentVersion { get; set; } + public Action OnImeStarted { get; set; } + public Action OnAppStateChanged { get; set; } + public Action OnPoliciesDiscovered { get; set; } + public Action OnAllAppsCompleted { get; set; } + public Action OnUserSessionCompleted { get; set; } + + /// + /// Access to the tracked package states + /// + public AppPackageStateList PackageStates => _packageStates; + + public ImeLogTracker(string logFolder, List patterns, AgentLogger logger, int pollingIntervalMs = 100) + { + _logFolder = Environment.ExpandEnvironmentVariables(logFolder); + _logger = logger; + _pollingIntervalMs = pollingIntervalMs; + _packageStates = new AppPackageStateList(logger); + + CompilePatterns(patterns); + ActivatePatterns(logPhaseIsCurrentPhase: false, force: true); + } + + /// + /// Compiles ImeLogPattern list from backend into Regex objects grouped by category. + /// + public void CompilePatterns(List patterns) + { + _patternsAlways = new List(); + _patternsCurrentPhase = new List(); + _patternsOtherPhases = new List(); + + if (patterns == null) return; + + foreach (var pattern in patterns.Where(p => p.Enabled)) + { + try + { + // Replace {GUID} placeholder with actual GUID capture regex + var regexStr = pattern.Pattern.Replace("{GUID}", GuidPattern); + var regex = new Regex(regexStr, RegexOptions.IgnoreCase | RegexOptions.Compiled); + + var compiled = new CompiledPattern + { + PatternId = pattern.PatternId, + Regex = regex, + Action = pattern.Action, + Parameters = pattern.Parameters ?? new Dictionary() + }; + + switch (pattern.Category?.ToLower()) + { + case "always": + _patternsAlways.Add(compiled); + break; + case "currentphase": + _patternsCurrentPhase.Add(compiled); + break; + case "otherphases": + _patternsOtherPhases.Add(compiled); + break; + default: + _patternsAlways.Add(compiled); // Default to always + break; + } + } + catch (Exception ex) + { + _logger.Warning($"Failed to compile IME pattern {pattern.PatternId}: {ex.Message}"); + } + } + + _logger.Info($"ImeLogTracker: compiled {_patternsAlways.Count} always, {_patternsCurrentPhase.Count} currentPhase, {_patternsOtherPhases.Count} otherPhases patterns"); + + // Re-activate with current phase state + ActivatePatterns(_logPhaseIsCurrentPhase, force: true); + } + + /// + /// Starts the background polling task + /// + public void Start() + { + if (_pollingTask != null) return; + + _cts = new CancellationTokenSource(); + var token = _cts.Token; + + _pollingTask = Task.Run(async () => + { + _logger.Info("ImeLogTracker: polling started"); + + while (!token.IsCancellationRequested) + { + try + { + await CheckLogFilesAsync(token); + } + catch (OperationCanceledException) { break; } + catch (Exception ex) + { + _logger.Warning($"ImeLogTracker: error during log check: {ex.Message}"); + try { await Task.Delay(1000, token); } catch (OperationCanceledException) { break; } + } + + try { await Task.Delay(_pollingIntervalMs, token); } catch (OperationCanceledException) { break; } + } + + _logger.Info("ImeLogTracker: polling stopped"); + }, token); + } + + /// + /// Stops the background polling task + /// + public void Stop() + { + _cts?.Cancel(); + try { _pollingTask?.Wait(TimeSpan.FromSeconds(5)); } catch { } + _pollingTask = null; + } + + private async Task CheckLogFilesAsync(CancellationToken token) + { + if (!Directory.Exists(_logFolder)) + return; + + // Get all matching log files, sorted by name (archived files come before current) + var files = new List(); + foreach (var pattern in LogFilePatterns) + { + try + { + files.AddRange(Directory.GetFiles(_logFolder, pattern)); + } + catch (DirectoryNotFoundException) { } + } + + files.Sort(StringComparer.OrdinalIgnoreCase); + + foreach (var filePath in files) + { + if (token.IsCancellationRequested) break; + + try + { + var fileInfo = new FileInfo(filePath); + if (!fileInfo.Exists) continue; + + var startPos = _positionTracker.GetSafePosition(filePath, fileInfo.Length); + if (startPos >= fileInfo.Length) continue; + + using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + stream.Seek(startPos, SeekOrigin.Begin); + + using (var reader = new StreamReader(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks: true, bufferSize: 1024, leaveOpen: true)) + { + string line; + while ((line = await reader.ReadLineAsync()) != null) + { + if (token.IsCancellationRequested) break; + + // Parse CMTrace format to get the message content + CmTraceLogEntry entry; + string messageToMatch; + if (CmTraceLogParser.TryParseLine(line, out entry)) + { + messageToMatch = entry.Message; + } + else + { + // Non-CMTrace line - match raw + messageToMatch = line; + entry = null; + } + + if (string.IsNullOrEmpty(messageToMatch)) continue; + + // Simulation mode delay + if (SimulationMode && entry != null) + { + await ApplySimulationDelay(entry.Timestamp, token); + } + + // Match against active patterns + foreach (var pattern in _activePatterns) + { + var match = pattern.Regex.Match(messageToMatch); + if (match.Success) + { + HandlePatternMatch(pattern, match, messageToMatch); + } + } + } + } + + _positionTracker.SetPosition(filePath, stream.Position); + } + } + catch (FileNotFoundException) { } + catch (IOException ex) + { + _logger.Debug($"ImeLogTracker: IO error reading {Path.GetFileName(filePath)}: {ex.Message}"); + } + } + } + + private void HandlePatternMatch(CompiledPattern pattern, Match match, string message) + { + try + { + var id = match.Groups["id"]?.Value; + var useCurrentApp = pattern.Parameters.ContainsKey("useCurrentApp") && + pattern.Parameters["useCurrentApp"] == "true"; + + if (useCurrentApp && string.IsNullOrEmpty(id)) + id = _packageStates.CurrentPackageId; + + switch (pattern.Action?.ToLower()) + { + case "imestarted": + HandleImeStarted(); + break; + + case "imesessionchange": + _logger.Debug($"IME session change: {match.Groups["change"]?.Value}"); + break; + + case "espphasedetected": + var phase = match.Groups["espPhase"]?.Value; + if (string.IsNullOrEmpty(phase) && pattern.Parameters.ContainsKey("phase")) + phase = pattern.Parameters["phase"]; + if (!string.IsNullOrEmpty(phase)) + HandleEspPhaseDetected(phase); + break; + + case "setcurrentapp": + if (!string.IsNullOrEmpty(id)) + _packageStates.SetCurrent(id); + break; + + case "imeagentversion": + var version = match.Groups["agentVersion"]?.Value; + if (!string.IsNullOrEmpty(version)) + OnImeAgentVersion?.Invoke(version); + break; + + case "imeimpersonation": + _logger.Debug($"IME impersonation: {match.Groups["user"]?.Value}"); + break; + + case "enrollmentcompleted": + _logger.Info("ImeLogTracker: User session completed detected"); + OnUserSessionCompleted?.Invoke(); + break; + + case "updatestateinstalled": + if (!string.IsNullOrEmpty(id)) + UpdateStateWithCallback(id, AppInstallationState.Installed); + break; + + case "updatestatedownloading": + if (!string.IsNullOrEmpty(id)) + { + var bytes = match.Groups["bytes"]?.Value; + var ofbytes = match.Groups["ofbytes"]?.Value; + if (!string.IsNullOrEmpty(bytes) && !string.IsNullOrEmpty(ofbytes)) + UpdateDownloadingWithCallback(id, bytes, ofbytes); + else + UpdateStateWithCallback(id, AppInstallationState.Downloading); + } + break; + + case "updatestateinstalling": + if (!string.IsNullOrEmpty(id)) + UpdateStateWithCallback(id, AppInstallationState.Installing); + break; + + case "updatestateskipped": + if (!string.IsNullOrEmpty(id)) + UpdateStateWithCallback(id, AppInstallationState.Skipped); + break; + + case "updatestateerror": + if (pattern.Parameters.ContainsKey("checkTo") && pattern.Parameters["checkTo"] == "true") + { + // Only apply error if the "to" value is "Error" + var toValue = match.Groups["to"]?.Value; + if (string.Equals(toValue, "Error", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrEmpty(id)) + UpdateStateWithCallback(id, AppInstallationState.Error); + } + else if (!string.IsNullOrEmpty(id)) + { + UpdateStateWithCallback(id, AppInstallationState.Error); + } + break; + + case "updatestatepostponed": + if (!string.IsNullOrEmpty(id)) + { + // Only postpone if not already in a terminal state + var pkg = _packageStates.GetPackage(id); + if (pkg != null && pkg.InstallationState != AppInstallationState.Installed && + pkg.InstallationState != AppInstallationState.Error) + { + UpdateStateWithCallback(id, AppInstallationState.Postponed); + } + } + break; + + case "esptrackstatus": + HandleEspTrackStatus(match); + break; + + case "policiesdiscovered": + var policiesJson = match.Groups["policies"]?.Value; + if (!string.IsNullOrEmpty(policiesJson)) + HandlePoliciesDiscovered(policiesJson); + break; + + case "ignorecompletedapp": + _packageStates.AddToIgnoreList(_packageStates.CurrentPackageId); + break; + + case "updatename": + var name = match.Groups["name"]?.Value; + if (!string.IsNullOrEmpty(id) && !string.IsNullOrEmpty(name)) + _packageStates.UpdateName(id, name); + break; + + case "updatewin32appstate": + var state = match.Groups["state"]?.Value; + if (!string.IsNullOrEmpty(id) && !string.IsNullOrEmpty(state)) + _packageStates.UpdateStateFromWin32AppState(id, state); + break; + + case "cancelstuckandsetcurrent": + HandleCancelStuckAndSetCurrent(id); + break; + + default: + _logger.Debug($"ImeLogTracker: unhandled action '{pattern.Action}' for pattern {pattern.PatternId}"); + break; + } + } + catch (Exception ex) + { + _logger.Warning($"ImeLogTracker: error handling match for {pattern.PatternId}: {ex.Message}"); + } + } + + private void HandleImeStarted() + { + _logger.Info("ImeLogTracker: IME Agent Started detected"); + + // Mark any currently active app as installed (it was running before restart) + if (!string.IsNullOrEmpty(_packageStates.CurrentPackageId)) + { + var currentPkg = _packageStates.GetPackage(_packageStates.CurrentPackageId); + if (currentPkg?.IsActive == true) + UpdateStateWithCallback(_packageStates.CurrentPackageId, AppInstallationState.Installed); + } + + OnImeStarted?.Invoke(); + } + + private void HandleEspPhaseDetected(string espPhaseString) + { + bool logPhaseIsCurrentPhase; + + if (string.Equals(espPhaseString, "DeviceSetup", StringComparison.OrdinalIgnoreCase)) + logPhaseIsCurrentPhase = true; // We treat DeviceSetup as current phase + else if (string.Equals(espPhaseString, "AccountSetup", StringComparison.OrdinalIgnoreCase)) + logPhaseIsCurrentPhase = true; // AccountSetup is also current phase + else + return; // Not a valid/relevant phase + + _logger.Info($"ImeLogTracker: ESP phase detected: {espPhaseString}"); + ActivatePatterns(logPhaseIsCurrentPhase); + + OnEspPhaseChanged?.Invoke(espPhaseString); + } + + private void HandleEspTrackStatus(Match match) + { + var from = match.Groups["from"]?.Value; + var to = match.Groups["to"]?.Value; + var id = match.Groups["id"]?.Value; + + if (string.IsNullOrEmpty(id) || string.IsNullOrEmpty(to)) return; + + if (string.Equals(to, "InProgress", StringComparison.OrdinalIgnoreCase)) + { + _packageStates.SetCurrent(id); + UpdateStateWithCallback(id, AppInstallationState.Installing); + } + else if (string.Equals(to, "Completed", StringComparison.OrdinalIgnoreCase)) + { + UpdateStateWithCallback(id, AppInstallationState.Installed); + } + else if (string.Equals(to, "Error", StringComparison.OrdinalIgnoreCase)) + { + UpdateStateWithCallback(id, AppInstallationState.Error); + } + } + + private void HandlePoliciesDiscovered(string policiesJson) + { + try + { + var policies = JsonSerializer.Deserialize>>(policiesJson); + if (policies != null) + { + // Ignore user-targeted packages during device setup (we only track device phase in general) + _packageStates.AddUpdateFromJsonPolicies(policies, ignoreUserTargeted: false); + _logger.Info($"ImeLogTracker: discovered {policies.Count} policies, tracking {_packageStates.Count} packages"); + OnPoliciesDiscovered?.Invoke(policiesJson); + } + } + catch (Exception ex) + { + _logger.Warning($"ImeLogTracker: failed to parse policies JSON: {ex.Message}"); + } + } + + private void HandleCancelStuckAndSetCurrent(string newId) + { + if (string.IsNullOrEmpty(newId)) return; + + // If current app is stuck in an active state, mark it as skipped + var currentPkg = _packageStates.GetPackage(_packageStates.CurrentPackageId); + if (currentPkg != null && (currentPkg.InstallationState == AppInstallationState.Installing || + currentPkg.InstallationState == AppInstallationState.InProgress || + currentPkg.InstallationState == AppInstallationState.Downloading)) + { + UpdateStateWithCallback(_packageStates.CurrentPackageId, AppInstallationState.Skipped); + } + + _packageStates.SetCurrent(newId); + } + + private void UpdateStateWithCallback(string id, AppInstallationState newState, int? progressPercent = null) + { + var pkg = _packageStates.GetPackage(id); + if (pkg == null) return; + + var oldState = pkg.InstallationState; + var changed = _packageStates.UpdateState(id, newState, progressPercent); + + if (changed) + { + _logger.Debug($"ImeLogTracker: {pkg.Name ?? id} state: {oldState} -> {newState}"); + OnAppStateChanged?.Invoke(pkg, oldState, newState); + + // Check if all apps are now completed + if (_packageStates.CountAll > 0 && _packageStates.IsAllCompleted()) + { + OnAllAppsCompleted?.Invoke(); + } + } + } + + private void UpdateDownloadingWithCallback(string id, string bytes, string ofbytes) + { + var pkg = _packageStates.GetPackage(id); + if (pkg == null) return; + + var oldState = pkg.InstallationState; + var changed = _packageStates.UpdateStateToDownloading(id, bytes, ofbytes); + + if (changed) + { + _logger.Debug($"ImeLogTracker: {pkg.Name ?? id} downloading: {bytes}/{ofbytes}"); + OnAppStateChanged?.Invoke(pkg, oldState, AppInstallationState.Downloading); + } + } + + private void ActivatePatterns(bool logPhaseIsCurrentPhase, bool force = false) + { + if (!force && _logPhaseIsCurrentPhase == logPhaseIsCurrentPhase) + return; + + var patterns = new List(_patternsAlways); + if (logPhaseIsCurrentPhase) + { + patterns.AddRange(_patternsCurrentPhase); + _lastLogTimestamp = DateTime.MinValue; // Reset simulation delay + } + else + { + patterns.AddRange(_patternsOtherPhases); + } + + _activePatterns = patterns; + _packageStates.SetCurrent(""); // Reset current package + _logPhaseIsCurrentPhase = logPhaseIsCurrentPhase; + } + + private async Task ApplySimulationDelay(DateTime logTimestamp, CancellationToken token) + { + if (!SimulationMode || logTimestamp == DateTime.MinValue) + return; + + if (_lastLogTimestamp != DateTime.MinValue) + { + var timeSpan = logTimestamp - _lastLogTimestamp; + if (timeSpan.TotalMilliseconds > 0) + { + var delayMs = (int)(timeSpan.TotalMilliseconds / SpeedFactor); + delayMs = Math.Max(0, Math.Min(delayMs, 5000)); // Cap at 5 seconds + if (delayMs > 0) + await Task.Delay(delayMs, token); + } + } + _lastLogTimestamp = logTimestamp; + } + + public void Dispose() + { + Stop(); + _cts?.Dispose(); + } + + /// + /// A compiled regex pattern with its action and parameters + /// + private class CompiledPattern + { + public string PatternId { get; set; } + public Regex Regex { get; set; } + public string Action { get; set; } + public Dictionary Parameters { get; set; } + } + } +} diff --git a/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Tracking/LogFilePositionTracker.cs b/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Tracking/LogFilePositionTracker.cs new file mode 100644 index 00000000..5f7dc0ed --- /dev/null +++ b/src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Tracking/LogFilePositionTracker.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; + +namespace AutopilotMonitor.Agent.Core.Monitoring.Tracking +{ + /// + /// Tracks file read positions for incremental log file reading. + /// In-memory only - resets on agent restart, which is acceptable since + /// we start fresh per enrollment session. + /// + public class LogFilePositionTracker + { + private readonly Dictionary _positions + = new Dictionary(StringComparer.OrdinalIgnoreCase); + + /// + /// Gets the safe read position for a file, detecting rollover/truncation. + /// If the current file size is smaller than the stored position, the file + /// has been rotated - resets to 0 to read from beginning. + /// Returns 0 if no position has been recorded yet. + /// + public long GetSafePosition(string filePath, long currentFileSize) + { + FilePositionState state; + if (!_positions.TryGetValue(filePath, out state)) + return 0; + + // Detect rollover: file is smaller than our stored position + if (currentFileSize < state.Position) + { + state.Position = 0; + state.LastKnownSize = currentFileSize; + return 0; + } + + return state.Position; + } + + /// + /// Stores the current read position for a file after successful reading. + /// + public void SetPosition(string filePath, long position) + { + FilePositionState state; + if (!_positions.TryGetValue(filePath, out state)) + { + state = new FilePositionState(); + _positions[filePath] = state; + } + + state.Position = position; + state.LastKnownSize = position; // Position is always <= file size + state.LastReadTime = DateTime.UtcNow; + } + + /// + /// Gets the stored position for a file, or 0 if not tracked. + /// Does not perform rollover detection. + /// + public long GetPosition(string filePath) + { + FilePositionState state; + if (_positions.TryGetValue(filePath, out state)) + return state.Position; + return 0; + } + } + + internal class FilePositionState + { + public long Position { get; set; } + public long LastKnownSize { get; set; } + public DateTime LastReadTime { get; set; } + } +} diff --git a/src/Agent/AutopilotMonitor.Agent/AutopilotMonitorService.cs b/src/Agent/AutopilotMonitor.Agent/AutopilotMonitorService.cs index 0fbc1f0e..1062e045 100644 --- a/src/Agent/AutopilotMonitor.Agent/AutopilotMonitorService.cs +++ b/src/Agent/AutopilotMonitor.Agent/AutopilotMonitorService.cs @@ -2,8 +2,7 @@ using System; using System.ServiceProcess; using AutopilotMonitor.Agent.Core.Configuration; using AutopilotMonitor.Agent.Core.Logging; -using AutopilotMonitor.Agent.Core.Monitoring; -using AutopilotMonitor.Agent.Core.Storage; +using AutopilotMonitor.Agent.Core.Monitoring.Core; namespace AutopilotMonitor.Agent { diff --git a/src/Agent/AutopilotMonitor.Agent/Program.cs b/src/Agent/AutopilotMonitor.Agent/Program.cs index f64c9b4e..0a08e082 100644 --- a/src/Agent/AutopilotMonitor.Agent/Program.cs +++ b/src/Agent/AutopilotMonitor.Agent/Program.cs @@ -5,8 +5,7 @@ using System.Linq; using System.ServiceProcess; using AutopilotMonitor.Agent.Core.Configuration; using AutopilotMonitor.Agent.Core.Logging; -using AutopilotMonitor.Agent.Core.Monitoring; -using AutopilotMonitor.Agent.Core.Storage; +using AutopilotMonitor.Agent.Core.Monitoring.Core; using Microsoft.Win32; using Newtonsoft.Json; @@ -56,6 +55,11 @@ namespace AutopilotMonitor.Agent { Console.WriteLine($"Simulator Mode: ENABLED"); Console.WriteLine($"Simulate Failure: {(config.SimulateFailure ? "YES" : "NO")}"); + if (!string.IsNullOrEmpty(config.SimulationLogDirectory)) + { + Console.WriteLine($"Simulation Log Dir: {config.SimulationLogDirectory}"); + Console.WriteLine($"Simulation Speed: {config.SimulationSpeedFactor}x"); + } } if (config.RebootOnComplete) @@ -282,10 +286,14 @@ namespace AutopilotMonitor.Agent var noCleanup = args?.Contains("--no-cleanup") ?? false; var rebootOnComplete = args?.Contains("--reboot-on-complete") ?? false; var disableGeoLocation = args?.Contains("--disable-geolocation") ?? false; + var newSession = args?.Contains("--new-session") ?? false; // Parse certificate thumbprint if provided string certThumbprint = null; string tenantIdOverride = null; + string imeLogPathOverride = null; + string simulationLogDirectory = null; + double simulationSpeedFactor = 50; if (args != null) { var thumbprintIndex = Array.IndexOf(args, "--cert-thumbprint"); @@ -299,6 +307,30 @@ namespace AutopilotMonitor.Agent { tenantIdOverride = args[tenantIdIndex + 1]; } + + var imeLogPathIndex = Array.IndexOf(args, "--ime-log-path"); + if (imeLogPathIndex >= 0 && imeLogPathIndex + 1 < args.Length) + { + imeLogPathOverride = args[imeLogPathIndex + 1]; + Console.WriteLine($"Using custom IME log path: {imeLogPathOverride}"); + } + + var simLogDirIndex = Array.IndexOf(args, "--simulation-log-dir"); + if (simLogDirIndex >= 0 && simLogDirIndex + 1 < args.Length) + { + simulationLogDirectory = args[simLogDirIndex + 1]; + Console.WriteLine($"Using simulation log directory: {simulationLogDirectory}"); + } + + var simSpeedIndex = Array.IndexOf(args, "--simulation-speed-factor"); + if (simSpeedIndex >= 0 && simSpeedIndex + 1 < args.Length) + { + if (double.TryParse(args[simSpeedIndex + 1], out var speed)) + { + simulationSpeedFactor = speed; + Console.WriteLine($"Using simulation speed factor: {simulationSpeedFactor}x"); + } + } } // Defaults (fallback values if no config file exists) @@ -391,6 +423,14 @@ namespace AutopilotMonitor.Agent // Load or create persisted session ID var dataDirectory = Environment.ExpandEnvironmentVariables(@"%ProgramData%\AutopilotMonitor"); var sessionPersistence = new SessionPersistence(dataDirectory); + + // Force new session if --new-session flag is set + if (newSession) + { + Console.WriteLine("--new-session flag detected, deleting existing session..."); + sessionPersistence.DeleteSession(); + } + var sessionId = sessionPersistence.LoadOrCreateSessionId(); var sessionStatus = sessionPersistence.SessionExists() && File.GetCreationTime(Path.Combine(dataDirectory, "session.id")) < DateTime.Now.AddMinutes(-1) @@ -415,7 +455,10 @@ namespace AutopilotMonitor.Agent CleanupOnExit = cleanupOnExit, SelfDestructOnComplete = selfDestructOnComplete, RebootOnComplete = rebootOnComplete || rebootOnCompleteConfig, - EnableGeoLocation = !disableGeoLocation && enableGeoLocationConfig + EnableGeoLocation = !disableGeoLocation && enableGeoLocationConfig, + ImeLogPathOverride = imeLogPathOverride, + SimulationLogDirectory = simulationLogDirectory, + SimulationSpeedFactor = simulationSpeedFactor }; } } diff --git a/src/Backend/AutopilotMonitor.Functions/Functions/GetAgentConfigFunction.cs b/src/Backend/AutopilotMonitor.Functions/Functions/GetAgentConfigFunction.cs index 1c72ae1d..ef13caee 100644 --- a/src/Backend/AutopilotMonitor.Functions/Functions/GetAgentConfigFunction.cs +++ b/src/Backend/AutopilotMonitor.Functions/Functions/GetAgentConfigFunction.cs @@ -75,17 +75,15 @@ namespace AutopilotMonitor.Functions.Functions var collectors = new CollectorConfiguration { EnablePerformanceCollector = tenantConfig.EnablePerformanceCollector, - PerformanceIntervalSeconds = tenantConfig.PerformanceCollectorIntervalSeconds, - EnableDownloadProgressCollector = tenantConfig.EnableDownloadProgressCollector, - DownloadProgressIntervalSeconds = tenantConfig.DownloadProgressCollectorIntervalSeconds, - EnableCertValidationCollector = tenantConfig.EnableCertValidationCollector, - EnableEspUiStateCollector = tenantConfig.EnableEspUiStateCollector, - EspUiStateIntervalSeconds = tenantConfig.EspUiStateCollectorIntervalSeconds + PerformanceIntervalSeconds = tenantConfig.PerformanceCollectorIntervalSeconds }; - // Get active gather rules for this tenant + // Get active gather rules for this tenant (user-defined ad-hoc only) var gatherRules = await _gatherRuleService.GetActiveRulesForTenantAsync(tenantId); + // Get built-in IME log patterns for smart enrollment tracking + var imeLogPatterns = BuiltInImeLogPatterns.GetAll(); + var response = req.CreateResponse(HttpStatusCode.OK); await response.WriteAsJsonAsync(new AgentConfigResponse { @@ -93,6 +91,7 @@ namespace AutopilotMonitor.Functions.Functions Message = "Configuration retrieved successfully", Collectors = collectors, GatherRules = gatherRules, + ImeLogPatterns = imeLogPatterns, ConfigVersion = 1, RefreshIntervalSeconds = 300 }); diff --git a/src/Backend/AutopilotMonitor.Functions/Functions/IngestEventsFunction.cs b/src/Backend/AutopilotMonitor.Functions/Functions/IngestEventsFunction.cs index aae0aa38..6bc447d9 100644 --- a/src/Backend/AutopilotMonitor.Functions/Functions/IngestEventsFunction.cs +++ b/src/Backend/AutopilotMonitor.Functions/Functions/IngestEventsFunction.cs @@ -123,7 +123,7 @@ namespace AutopilotMonitor.Functions.Functions foreach (var evt in storedEvents) { // Track special events for session status updates - if (evt.EventType == "phase_changed") + if (evt.EventType == "phase_changed" || evt.EventType == "esp_phase_changed") { lastPhaseChangeEvent = evt; } diff --git a/src/Backend/AutopilotMonitor.Functions/Services/BuiltInGatherRules.cs b/src/Backend/AutopilotMonitor.Functions/Services/BuiltInGatherRules.cs index df7c69ed..1a7bfa4e 100644 --- a/src/Backend/AutopilotMonitor.Functions/Services/BuiltInGatherRules.cs +++ b/src/Backend/AutopilotMonitor.Functions/Services/BuiltInGatherRules.cs @@ -3,230 +3,26 @@ using AutopilotMonitor.Shared.Models; namespace AutopilotMonitor.Functions.Services { /// - /// Built-in gather rules shipped with the system - /// These define what data the agent collects beyond the core collectors + /// Built-in gather rules shipped with the system. + /// + /// NOTE: All network, identity, device, ESP, and IME log parsing rules have been moved + /// to the EnrollmentTracker (direct WMI/Registry collection) and ImeLogTracker (IME log + /// patterns via BuiltInImeLogPatterns). GatherRules now serve only as a mechanism for + /// user-defined ad-hoc collection rules. /// public static class BuiltInGatherRules { public static List GetAll() { - return new List - { - // ===== NETWORK RULES ===== - new GatherRule - { - RuleId = "GATHER-NET-001", - Title = "Collect Proxy Settings", - Description = "Reads WinHTTP and Internet Explorer proxy configuration from the registry. Helps diagnose proxy-related enrollment failures.", - Category = "network", - CollectorType = "registry", - Target = @"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings", - Parameters = new Dictionary - { - { "values", "ProxyServer,ProxyEnable,ProxyOverride,AutoConfigURL" } - }, - Trigger = "startup", - OutputEventType = "gather_proxy_settings", - Tags = new[] { "network", "proxy", "common" }, - Author = "Autopilot Monitor" - }, - new GatherRule - { - RuleId = "GATHER-NET-002", - Title = "Collect DNS Configuration", - Description = "Queries DNS client server addresses to verify DNS resolution capability.", - Category = "network", - CollectorType = "command", - Target = "Get-DnsClientServerAddress | Select-Object InterfaceAlias, ServerAddresses", - Trigger = "startup", - OutputEventType = "gather_dns_config", - Tags = new[] { "network", "dns" }, - Author = "Autopilot Monitor" - }, - new GatherRule - { - RuleId = "GATHER-NET-003", - Title = "Collect Network Adapters", - Description = "Lists all network adapters and their status via WMI.", - Category = "network", - CollectorType = "wmi", - Target = "SELECT Name, NetConnectionStatus, Speed, MACAddress FROM Win32_NetworkAdapter WHERE NetConnectionStatus IS NOT NULL", - Trigger = "startup", - OutputEventType = "gather_network_adapters", - Tags = new[] { "network", "adapters" }, - Author = "Autopilot Monitor" - }, - - // ===== IDENTITY RULES ===== - new GatherRule - { - RuleId = "GATHER-ID-001", - Title = "Collect AAD Join Status", - Description = "Reads Azure AD join state from the registry to verify device identity.", - Category = "identity", - CollectorType = "registry", - Target = @"HKLM:\SYSTEM\CurrentControlSet\Control\CloudDomainJoin\JoinInfo", - Parameters = new Dictionary - { - { "recurse", "true" } - }, - Trigger = "phase_change", - TriggerPhase = "Identity", - OutputEventType = "gather_aad_join_status", - Tags = new[] { "identity", "aad", "entra" }, - Author = "Autopilot Monitor" - }, - new GatherRule - { - RuleId = "GATHER-ID-002", - Title = "Collect Device Certificates", - Description = "Lists certificates in the machine personal store to verify MDM and device certificates.", - Category = "identity", - CollectorType = "command", - Target = "certutil -store My", - Trigger = "phase_change", - TriggerPhase = "Identity", - OutputEventType = "gather_device_certificates", - Tags = new[] { "identity", "certificates" }, - Author = "Autopilot Monitor" - }, - - // ===== MDM ENROLLMENT RULES ===== - new GatherRule - { - RuleId = "GATHER-MDM-001", - Title = "Collect MDM Enrollment Status", - Description = "Reads MDM enrollment registry keys to verify Intune enrollment state.", - Category = "enrollment", - CollectorType = "registry", - Target = @"HKLM:\SOFTWARE\Microsoft\Enrollments", - Parameters = new Dictionary - { - { "recurse", "true" }, - { "maxDepth", "2" } - }, - Trigger = "phase_change", - TriggerPhase = "MdmEnrollment", - OutputEventType = "gather_mdm_enrollment_status", - Tags = new[] { "enrollment", "mdm", "intune" }, - Author = "Autopilot Monitor" - }, - - // ===== APP INSTALLATION RULES ===== - new GatherRule - { - RuleId = "GATHER-APP-001", - Title = "Collect IME App Inventory", - Description = "Reads the Intune Management Extension app inventory from registry.", - Category = "apps", - CollectorType = "registry", - Target = @"HKLM:\SOFTWARE\Microsoft\IntuneManagementExtension\Win32Apps", - Parameters = new Dictionary - { - { "recurse", "true" }, - { "maxDepth", "2" } - }, - Trigger = "phase_change", - TriggerPhase = "AppInstallation", - OutputEventType = "gather_ime_app_inventory", - Tags = new[] { "apps", "ime", "win32" }, - Author = "Autopilot Monitor" - }, - new GatherRule - { - RuleId = "GATHER-APP-002", - Title = "Collect Win32App Detection Results", - Description = "Periodically checks Win32 app detection script results during app installation phase.", - Category = "apps", - CollectorType = "registry", - Target = @"HKLM:\SOFTWARE\Microsoft\IntuneManagementExtension\Win32Apps", - Parameters = new Dictionary - { - { "recurse", "true" }, - { "filter", "DetectionState" } - }, - Trigger = "interval", - IntervalSeconds = 60, - OutputEventType = "gather_win32app_detection", - Tags = new[] { "apps", "detection", "win32" }, - Author = "Autopilot Monitor" - }, - - // ===== ESP RULES ===== - new GatherRule - { - RuleId = "GATHER-ESP-001", - Title = "Collect ESP Tracking Policies", - Description = "Reads ESP tracking policy list from registry to identify blocking apps and policies.", - Category = "esp", - CollectorType = "registry", - Target = @"HKLM:\SOFTWARE\Microsoft\Enrollments", - Parameters = new Dictionary - { - { "recurse", "true" }, - { "filter", "FirstSync" } - }, - Trigger = "phase_change", - TriggerPhase = "EspDeviceSetup", - OutputEventType = "gather_esp_tracking_policies", - Tags = new[] { "esp", "tracking", "blocking" }, - Author = "Autopilot Monitor" - }, - - // ===== DEVICE RULES ===== - new GatherRule - { - RuleId = "GATHER-DEV-001", - Title = "Collect TPM Status", - Description = "Queries TPM status via WMI to verify TPM readiness for Autopilot.", - Category = "device", - CollectorType = "wmi", - Target = "SELECT * FROM Win32_Tpm", - Parameters = new Dictionary - { - { "namespace", @"root\CIMV2\Security\MicrosoftTpm" } - }, - Trigger = "startup", - OutputEventType = "gather_tpm_status", - Tags = new[] { "device", "tpm", "security" }, - Author = "Autopilot Monitor" - }, - new GatherRule - { - RuleId = "GATHER-DEV-002", - Title = "Collect BitLocker Status", - Description = "Queries BitLocker volume encryption status.", - Category = "device", - CollectorType = "wmi", - Target = "SELECT * FROM Win32_EncryptableVolume", - Parameters = new Dictionary - { - { "namespace", @"root\CIMV2\Security\MicrosoftVolumeEncryption" } - }, - Trigger = "phase_change", - TriggerPhase = "EspDeviceSetup", - OutputEventType = "gather_bitlocker_status", - Tags = new[] { "device", "bitlocker", "encryption" }, - Author = "Autopilot Monitor" - }, - new GatherRule - { - RuleId = "GATHER-DEV-003", - Title = "Collect SecureBoot Status", - Description = "Reads Secure Boot state from the registry.", - Category = "device", - CollectorType = "registry", - Target = @"HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot\State", - Parameters = new Dictionary - { - { "values", "UEFISecureBootEnabled" } - }, - Trigger = "startup", - OutputEventType = "gather_secureboot_status", - Tags = new[] { "device", "secureboot", "security" }, - Author = "Autopilot Monitor" - } - }; + // No built-in rules - all data collection is now handled by: + // - EnrollmentTracker: device info (network, DNS, proxy, autopilot profile, SecureBoot, BitLocker, AAD join) + // - ImeLogTracker: IME log parsing with patterns from BuiltInImeLogPatterns + // - PerformanceCollector: CPU/memory/disk (always on) + // - CertValidationCollector: certificate validation (optional toggle) + // - EventLogWatcher, RegistryMonitor, PhaseDetector, HelloDetector: core collectors + // + // GatherRules remain as a mechanism for user-defined ad-hoc collection only. + return new List(); } } } diff --git a/src/Backend/AutopilotMonitor.Functions/Services/BuiltInImeLogPatterns.cs b/src/Backend/AutopilotMonitor.Functions/Services/BuiltInImeLogPatterns.cs new file mode 100644 index 00000000..0bb98178 --- /dev/null +++ b/src/Backend/AutopilotMonitor.Functions/Services/BuiltInImeLogPatterns.cs @@ -0,0 +1,457 @@ +using System.Collections.Generic; +using AutopilotMonitor.Shared.Models; + +namespace AutopilotMonitor.Functions.Services +{ + /// + /// Built-in IME log regex patterns for smart enrollment tracking. + /// These patterns are adapted from the EspOverlay project's proven IME log parsing logic. + /// Delivered to the agent via the config API so patterns can be updated without agent rebuild. + /// + /// Pattern uses {GUID} as placeholder which the agent expands to the standard GUID capture regex. + /// Named capture groups (e.g., (?<id>...)) are passed to the action handler. + /// + public static class BuiltInImeLogPatterns + { + public static List GetAll() + { + return new List + { + // ============================================================ + // CATEGORY: always (active regardless of ESP phase) + // ============================================================ + + new ImeLogPattern + { + PatternId = "IME-STARTED", + Category = "always", + Pattern = @"EMS Agent Started", + Action = "imeStarted" + }, + new ImeLogPattern + { + PatternId = "IME-SESSION-CHANGE", + Category = "always", + Pattern = @"OnSessionChange:? (?.*?)$", + Action = "imeSessionChange" + }, + new ImeLogPattern + { + PatternId = "IME-ESP-PHASE", + Category = "always", + Pattern = @"\[Win32App\] (?:In|The) EspPhase: (?\w+)", + Action = "espPhaseDetected" + }, + new ImeLogPattern + { + PatternId = "IME-TOKEN-SUCCESS", + Category = "always", + Pattern = @"Successfully get the token", + Action = "espPhaseDetected", + Parameters = new Dictionary { { "phase", "AccountSetup" } } + }, + new ImeLogPattern + { + PatternId = "IME-SET-CURRENT-1", + Category = "always", + Pattern = @"\[Win32App\] ProcessAppWithDependencies starts for {GUID} with name", + Action = "setCurrentApp" + }, + new ImeLogPattern + { + PatternId = "IME-SET-CURRENT-2", + Category = "always", + Pattern = @"\[Win32App\] ExecManager: processing targeted app.*?\bid\W+{GUID}", + Action = "setCurrentApp" + }, + new ImeLogPattern + { + PatternId = "IME-SET-CURRENT-3", + Category = "always", + Pattern = @"\[Win32App\] ===Step=== Start to Present app {GUID}", + Action = "setCurrentApp" + }, + new ImeLogPattern + { + PatternId = "IME-SET-CURRENT-4", + Category = "always", + Pattern = @"\[Win32App\] SetCurrentDirectory:.*?\\{GUID}_\d+", + Action = "setCurrentApp" + }, + new ImeLogPattern + { + PatternId = "IME-AGENT-VERSION", + Category = "always", + Pattern = @"Agent version is: (?[\d.]+)", + Action = "imeAgentVersion" + }, + new ImeLogPattern + { + PatternId = "IME-IMPERSONATION", + Category = "always", + Pattern = @"After impersonation: (?.*?)$", + Action = "imeImpersonation" + }, + new ImeLogPattern + { + PatternId = "IME-USER-SESSION-COMPLETED", + Category = "always", + Pattern = @"\[Win32App\] \.+ Completed user session \d+, userId: (?[a-f0-9\-]+), userSID: (?S-[\d\-]+) \.+", + Action = "enrollmentCompleted" + }, + + // ============================================================ + // CATEGORY: currentPhase (active during current ESP phase) + // ============================================================ + + // --- Shutdown / Postpone --- + new ImeLogPattern + { + PatternId = "IME-SHUTDOWN", + Category = "currentPhase", + Pattern = @"EMS Agent received shutdown signal", + Action = "updateStatePostponed", + Parameters = new Dictionary { { "useCurrentApp", "true" } } + }, + + // --- ESP Track Status (new logs) --- + new ImeLogPattern + { + PatternId = "IME-ESP-TRACK-STATUS", + Category = "currentPhase", + Pattern = @"\[Win32App\]\[EspManager\] Updating ESP tracked install status from (?\w+) to (?\w+) for application {GUID}\.", + Action = "espTrackStatus" + }, + + // --- Skipped / Not Applicable --- + new ImeLogPattern + { + PatternId = "IME-NO-ACTION", + Category = "currentPhase", + Pattern = @"\[Win32App\]\[ActionProcessor\] No action required for app with id: {GUID}", + Action = "updateStateSkipped" + }, + new ImeLogPattern + { + PatternId = "IME-NOT-APPLICABLE-1", + Category = "currentPhase", + Pattern = @"\[Win32App\]\[ApplicabilityActionHandler\] Applicability check for policy with id: {GUID} resulted in action status: Success and applicability state: NotApplicable", + Action = "updateStateSkipped" + }, + new ImeLogPattern + { + PatternId = "IME-NOT-APPLICABLE-2", + Category = "currentPhase", + Pattern = @"\[Win32App\]\[ReportingManager\] Applicability state for app with id: {GUID} has been updated\. Report delta: \{""ApplicabilityState"":\{""OldValue"":null,""NewValue"":""ScriptRequirementRuleNotMet""", + Action = "updateStateSkipped" + }, + new ImeLogPattern + { + PatternId = "IME-NOT-APPLICABLE-3", + Category = "currentPhase", + Pattern = @"\[Win32App\]\[ReportingManager\] Desired state for app with id: {GUID} has been updated\. Report delta: \{""DesiredState"":\{""OldValue"":null,""NewValue"":""None""", + Action = "updateStateSkipped" + }, + new ImeLogPattern + { + PatternId = "IME-NOT-APPLICABLE-4", + Category = "currentPhase", + Pattern = @"\[Win32App\]\[ReportingManager\] Desired state for app with id: {GUID} has been updated\. Report delta: \{""DesiredState"":\{""OldValue"":null,""NewValue"":""NotPresent""", + Action = "updateStateSkipped" + }, + new ImeLogPattern + { + PatternId = "IME-NOT-TARGETED-1", + Category = "currentPhase", + Pattern = @"\[Win32App\]\[ActionProcessor\] App with id: {GUID}, targeted intent: RequiredInstall, and enforceability: Enforceable has projected enforcement classification: NotApplicableOrNotTargeted with desired state: None\.", + Action = "updateStateSkipped" + }, + new ImeLogPattern + { + PatternId = "IME-NOT-TARGETED-2", + Category = "currentPhase", + Pattern = @"\[Win32App\]\[ActionProcessor\] App with id: {GUID}, effective intent: RequiredInstall, and enforceability: Enforceable has projected enforcement classification: NotApplicableOrNotTargeted with desired state: None\.", + Action = "updateStateSkipped" + }, + new ImeLogPattern + { + PatternId = "IME-SKIP-FILTERS", + Category = "currentPhase", + Pattern = @"\[Win32App\]\[V3Processor\] All apps in the subgraph are not applicable due to assignment filters\. Skipping processing\.", + Action = "updateStateSkipped", + Parameters = new Dictionary { { "useCurrentApp", "true" } } + }, + + // --- Old log format: Skipped patterns --- + new ImeLogPattern + { + PatternId = "IME-SKIP-UNINSTALL-NOTDETECTED", + Category = "currentPhase", + Pattern = @"\[Win32App\] Not detected app {GUID} and intent is to un-install, skip applicability check", + Action = "updateStateSkipped" + }, + new ImeLogPattern + { + PatternId = "IME-SKIP-INSTALL-DETECTED", + Category = "currentPhase", + Pattern = @"\[Win32App\] Detected app {GUID} and intent is to install, skip applicability check", + Action = "updateStateSkipped" + }, + new ImeLogPattern + { + PatternId = "IME-SKIP-APPLICABILITY-NOTMET", + Category = "currentPhase", + Pattern = @"\[Win32App\] (?=.*?\bapplicability\b)(?=.*?NotMet\b).*?\bapp\W+{GUID}\b", + Action = "updateStateSkipped" + }, + new ImeLogPattern + { + PatternId = "IME-SKIP-UNINSTALL-NOTDETECTED-2", + Category = "currentPhase", + Pattern = @"\[Win32App\] NOT detected app {GUID}.*?skip un-installation", + Action = "updateStateSkipped" + }, + new ImeLogPattern + { + PatternId = "IME-SKIP-DEPENDENCY-DETECT", + Category = "currentPhase", + Pattern = @"\[Win32App\] app\W+{GUID}\b.*?\bis a dependency\b.*?\bDetect Only mode\b.*?\bfinished processing", + Action = "updateStateSkipped" + }, + new ImeLogPattern + { + PatternId = "IME-SKIP-DETECTED", + Category = "currentPhase", + Pattern = @"\[Win32App\] Detected app {GUID}.*?skip next step", + Action = "updateStateSkipped" + }, + + // --- Installed / Detected --- + new ImeLogPattern + { + PatternId = "IME-DETECTED-INSTALLED-1", + Category = "currentPhase", + Pattern = @"\[Win32App\]\[ReportingManager\] Detection state for app with id: {GUID} has been updated\. Report delta: \{""DetectionState"":\{""OldValue"":null,""NewValue"":""Installed""", + Action = "updateStateInstalled" + }, + new ImeLogPattern + { + PatternId = "IME-DETECTED-INSTALLED-2", + Category = "currentPhase", + Pattern = @"\[Win32App\]\[DetectionActionHandler\] Detection for policy with id: {GUID} resulted in action status: Success and detection state: Detected", + Action = "updateStateInstalled" + }, + new ImeLogPattern + { + PatternId = "IME-DETECTED-OLD", + Category = "currentPhase", + Pattern = @"\[Win32App\] Detected app {GUID} ", + Action = "updateStateInstalled" + }, + new ImeLogPattern + { + PatternId = "IME-ENFORCEMENT-SUCCESS", + Category = "currentPhase", + Pattern = @"\[Win32App\]\[ReportingManager\] Execution state for app with id: {GUID} has been updated\. Report delta: \{""EnforcementState"":\{""OldValue"":""InProgressDownloadCompleted"",""NewValue"":""Success""", + Action = "updateStateInstalled" + }, + new ImeLogPattern + { + PatternId = "IME-ENFORCEMENT-SUCCESS-OLD", + Category = "currentPhase", + Pattern = @"\[Win32App\] Setting enforcementState as\W+Success\b", + Action = "updateStateInstalled", + Parameters = new Dictionary { { "useCurrentApp", "true" } } + }, + + // --- Installing --- + new ImeLogPattern + { + PatternId = "IME-INSTALL-HANDLER", + Category = "currentPhase", + Pattern = @"\[Win32App\]\[ExecutionActionHandler\] Handler invoked with execution type: Install for policy with id: {GUID}", + Action = "updateStateInstalling" + }, + new ImeLogPattern + { + PatternId = "IME-LAUNCH-INSTALLER", + Category = "currentPhase", + Pattern = @"\[Win32App\] Launch Win32AppInstaller in \w+? session", + Action = "updateStateInstalling", + Parameters = new Dictionary { { "useCurrentApp", "true" } } + }, + new ImeLogPattern + { + PatternId = "IME-WINGET-INSTALLING-1", + Category = "currentPhase", + Pattern = @"\[WinGetMessageProcessor\] Processing Progress for app = {GUID} and user = .*?- Operation Phase = Installing", + Action = "updateStateInstalling" + }, + new ImeLogPattern + { + PatternId = "IME-WINGET-INSTALLING-2", + Category = "currentPhase", + Pattern = @"\[WinGetMessageProcessor\] Processing Progress for app = {GUID}.*?- Operation Phase = Installing", + Action = "updateStateInstalling" + }, + + // --- Downloading --- + new ImeLogPattern + { + PatternId = "IME-DOWNLOADING", + Category = "currentPhase", + Pattern = @"\[StatusService\] Downloading app \(id = {GUID}.*?\) via (?\w+), bytes (?\w+)/(?\w+) for user", + Action = "updateStateDownloading" + }, + + // --- Policies discovered --- + new ImeLogPattern + { + PatternId = "IME-POLICIES", + Category = "currentPhase", + Pattern = @"Get policies =\s*(?.*)", + Action = "policiesDiscovered" + }, + + // --- Update name (old log format) --- + new ImeLogPattern + { + PatternId = "IME-UPDATE-NAME-1", + Category = "currentPhase", + Pattern = @"\[Win32App\] ExecManager: processing targeted app.*?name\W+(?.*?)\W+id\W+{GUID}", + Action = "updateName" + }, + new ImeLogPattern + { + PatternId = "IME-UPDATE-NAME-2", + Category = "currentPhase", + Pattern = @"\[Win32App\] ProcessAppWithDependencies starts for {GUID} with name (?.*?)$", + Action = "updateName" + }, + new ImeLogPattern + { + PatternId = "IME-UPDATE-NAME-3", + Category = "currentPhase", + Pattern = @"\[Win32App\] Processing app \(id={GUID}, name = (?.*?)\)", + Action = "updateName" + }, + + // --- Win32AppState (old log format) --- + new ImeLogPattern + { + PatternId = "IME-WIN32-STATE-1", + Category = "currentPhase", + Pattern = @"\[Win32App\] Got InstanceID: Win32App_{GUID}_\d+, installationStateString: (?\d)", + Action = "updateWin32AppState" + }, + new ImeLogPattern + { + PatternId = "IME-WIN32-STATE-2", + Category = "currentPhase", + Pattern = @"\[Win32App\] (?:Got|Set) (?:ESP )?win32App state (?\w+) for {GUID}", + Action = "updateWin32AppState" + }, + + // --- Error states --- + new ImeLogPattern + { + PatternId = "IME-DECRYPT-FAILED", + Category = "currentPhase", + Pattern = @"Decryption is failed for appId {GUID}", + Action = "updateStateError" + }, + new ImeLogPattern + { + PatternId = "IME-ERROR-DOWNLOAD", + Category = "currentPhase", + Pattern = @"\[Win32App\]\[ReportingManager\] Download state for app with id: {GUID} has been updated\. Report delta: \{""EnforcementState"":\{""OldValue"":""InProgress"",""NewValue"":""ErrorDownloadingContent""", + Action = "updateStateError" + }, + new ImeLogPattern + { + PatternId = "IME-ERROR-UNMAPPED-EXIT", + Category = "currentPhase", + Pattern = @"\[Win32App\] Admin did NOT set mapping for lpExitCode\W+\d+ of app\W+{GUID}", + Action = "updateStateError" + }, + new ImeLogPattern + { + PatternId = "IME-ERROR-ENFORCEMENT", + Category = "currentPhase", + Pattern = @"\[Win32App\] Setting enforcementState as\W+Error\b", + Action = "updateStateError", + Parameters = new Dictionary { { "useCurrentApp", "true" } } + }, + new ImeLogPattern + { + PatternId = "IME-ERROR-TIMEOUT", + Category = "currentPhase", + Pattern = @"\[Win32App\] Installation is timeout", + Action = "updateStateError", + Parameters = new Dictionary { { "useCurrentApp", "true" } } + }, + new ImeLogPattern + { + PatternId = "IME-ERROR-NO-CONTENT", + Category = "currentPhase", + Pattern = @"\[Win32App\] Didn't get content info from service", + Action = "updateStateError", + Parameters = new Dictionary { { "useCurrentApp", "true" } } + }, + new ImeLogPattern + { + PatternId = "IME-ERROR-REPORT", + Category = "currentPhase", + Pattern = @"\[Win32App\]\[ReportingManager\] Execution state for app with id: {GUID} has been updated\. Report delta: \{""EnforcementState"":\{""OldValue"":""(?\w+)"",""NewValue"":""(?\w+)""", + Action = "updateStateError", + Parameters = new Dictionary { { "checkTo", "true" } } + }, + + // --- DO Timeout / Postpone --- + new ImeLogPattern + { + PatternId = "IME-DO-TIMEOUT-1", + Category = "currentPhase", + Pattern = @"\[Win32App DO\] DO download(?=.*?\bnot finished\b)(?=.*?\btimeout\b).*?intunewin-bin_{GUID}", + Action = "updateStatePostponed" + }, + new ImeLogPattern + { + PatternId = "IME-DO-TIMEOUT-2", + Category = "currentPhase", + Pattern = @"\[Win32App DO\] DO download(?=.*?\bnot finished\b)(?=.*?\btimeout\b)", + Action = "updateStatePostponed", + Parameters = new Dictionary { { "useCurrentApp", "true" } } + }, + + // --- Subgraph processing (cancel stuck + set current) --- + new ImeLogPattern + { + PatternId = "IME-SUBGRAPH", + Category = "currentPhase", + Pattern = @"\[Win32App\]\[V3Processor\] Processing subgraph with app ids: {GUID}", + Action = "cancelStuckAndSetCurrent" + }, + + // ============================================================ + // CATEGORY: otherPhases (for apps completed in previous phases) + // ============================================================ + + new ImeLogPattern + { + PatternId = "IME-ALREADY-DETECTED-1", + Category = "otherPhases", + Pattern = @"\[Win32App\] Completed detectionManager SideCar\w+?DetectionManager, applicationDetectedByCurrentRule: True", + Action = "ignoreCompletedApp" + }, + new ImeLogPattern + { + PatternId = "IME-ALREADY-DETECTED-2", + Category = "otherPhases", + Pattern = @"\[Win32App\] Setting enforcementState as\W+Success\b", + Action = "ignoreCompletedApp" + } + }; + } + } +} diff --git a/src/Backend/AutopilotMonitor.Functions/Services/TableStorageService.cs b/src/Backend/AutopilotMonitor.Functions/Services/TableStorageService.cs index a4820ea4..7caa2464 100644 --- a/src/Backend/AutopilotMonitor.Functions/Services/TableStorageService.cs +++ b/src/Backend/AutopilotMonitor.Functions/Services/TableStorageService.cs @@ -6,6 +6,7 @@ using AutopilotMonitor.Shared.Models; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Newtonsoft.Json; +using Newtonsoft.Json.Linq; namespace AutopilotMonitor.Functions.Services { @@ -105,7 +106,7 @@ namespace AutopilotMonitor.Functions.Services ["IsPreProvisioned"] = registration.IsPreProvisioned, ["StartedAt"] = registration.StartedAt, ["AgentVersion"] = registration.AgentVersion ?? string.Empty, - ["CurrentPhase"] = (int)EnrollmentPhase.PreFlight, + ["CurrentPhase"] = (int)EnrollmentPhase.Start, ["Status"] = SessionStatus.InProgress.ToString(), ["EventCount"] = 0 }; @@ -515,9 +516,7 @@ namespace AutopilotMonitor.Functions.Services Phase = (EnrollmentPhase)(entity.GetInt32("Phase") ?? 0), Message = entity.GetString("Message") ?? string.Empty, Sequence = entity.GetInt64("Sequence") ?? 0, - Data = entity.ContainsKey("DataJson") && entity.GetString("DataJson") != null - ? JsonConvert.DeserializeObject>(entity.GetString("DataJson")) - : new Dictionary() + Data = DeserializeEventData(entity.GetString("DataJson")) }; } @@ -1770,6 +1769,61 @@ namespace AutopilotMonitor.Functions.Services return Array.Empty(); } } + + /// + /// Deserializes event data JSON and converts JToken objects to native .NET types + /// + private Dictionary DeserializeEventData(string? dataJson) + { + if (string.IsNullOrEmpty(dataJson)) + return new Dictionary(); + + try + { + var deserialized = JsonConvert.DeserializeObject>(dataJson); + if (deserialized == null) + return new Dictionary(); + + // Convert all JToken values to native types + var result = new Dictionary(); + foreach (var kvp in deserialized) + { + result[kvp.Key] = ConvertJTokenToNative(kvp.Value); + } + return result; + } + catch + { + return new Dictionary(); + } + } + + /// + /// Converts JToken objects (JArray, JObject) to native .NET types + /// This fixes the issue where Newtonsoft.Json deserialization creates JToken objects + /// that get serialized incorrectly as nested empty arrays + /// + private object ConvertJTokenToNative(object value) + { + if (value is JArray jArray) + { + return jArray.Select(item => ConvertJTokenToNative(item)).ToList(); + } + else if (value is JObject jObject) + { + var dict = new Dictionary(); + foreach (var prop in jObject.Properties()) + { + dict[prop.Name] = ConvertJTokenToNative(prop.Value); + } + return dict; + } + else if (value is JValue jValue) + { + return jValue.Value ?? string.Empty; + } + return value; + } } public class AuditLogEntry diff --git a/src/Backend/AutopilotMonitor.Functions/Services/TenantConfigurationService.cs b/src/Backend/AutopilotMonitor.Functions/Services/TenantConfigurationService.cs index 02f22579..f1b0b0e8 100644 --- a/src/Backend/AutopilotMonitor.Functions/Services/TenantConfigurationService.cs +++ b/src/Backend/AutopilotMonitor.Functions/Services/TenantConfigurationService.cs @@ -189,12 +189,7 @@ namespace AutopilotMonitor.Functions.Services { "MaxNdjsonPayloadSizeMB", config.MaxNdjsonPayloadSizeMB }, { "CustomSettings", config.CustomSettings }, { "EnablePerformanceCollector", config.EnablePerformanceCollector }, - { "PerformanceCollectorIntervalSeconds", config.PerformanceCollectorIntervalSeconds }, - { "EnableDownloadProgressCollector", config.EnableDownloadProgressCollector }, - { "DownloadProgressCollectorIntervalSeconds", config.DownloadProgressCollectorIntervalSeconds }, - { "EnableCertValidationCollector", config.EnableCertValidationCollector }, - { "EnableEspUiStateCollector", config.EnableEspUiStateCollector }, - { "EspUiStateCollectorIntervalSeconds", config.EspUiStateCollectorIntervalSeconds } + { "PerformanceCollectorIntervalSeconds", config.PerformanceCollectorIntervalSeconds } }; return entity; @@ -221,12 +216,7 @@ namespace AutopilotMonitor.Functions.Services MaxNdjsonPayloadSizeMB = entity.GetInt32("MaxNdjsonPayloadSizeMB") ?? 5, CustomSettings = entity.GetString("CustomSettings"), EnablePerformanceCollector = entity.GetBoolean("EnablePerformanceCollector") ?? false, - PerformanceCollectorIntervalSeconds = entity.GetInt32("PerformanceCollectorIntervalSeconds") ?? 30, - EnableDownloadProgressCollector = entity.GetBoolean("EnableDownloadProgressCollector") ?? false, - DownloadProgressCollectorIntervalSeconds = entity.GetInt32("DownloadProgressCollectorIntervalSeconds") ?? 10, - EnableCertValidationCollector = entity.GetBoolean("EnableCertValidationCollector") ?? false, - EnableEspUiStateCollector = entity.GetBoolean("EnableEspUiStateCollector") ?? false, - EspUiStateCollectorIntervalSeconds = entity.GetInt32("EspUiStateCollectorIntervalSeconds") ?? 15 + PerformanceCollectorIntervalSeconds = entity.GetInt32("PerformanceCollectorIntervalSeconds") ?? 30 }; } diff --git a/src/Shared/AutopilotMonitor.Shared/Constants.cs b/src/Shared/AutopilotMonitor.Shared/Constants.cs index cfaa04bf..401eafa7 100644 --- a/src/Shared/AutopilotMonitor.Shared/Constants.cs +++ b/src/Shared/AutopilotMonitor.Shared/Constants.cs @@ -8,7 +8,7 @@ namespace AutopilotMonitor.Shared /// /// Agent version /// - public const string AgentVersion = "1.0.0-phase1"; + public const string AgentVersion = "1.0.0"; /// /// Registry path for agent configuration diff --git a/src/Shared/AutopilotMonitor.Shared/Models/AgentConfigResponse.cs b/src/Shared/AutopilotMonitor.Shared/Models/AgentConfigResponse.cs index de7b885f..475400b1 100644 --- a/src/Shared/AutopilotMonitor.Shared/Models/AgentConfigResponse.cs +++ b/src/Shared/AutopilotMonitor.Shared/Models/AgentConfigResponse.cs @@ -11,7 +11,18 @@ namespace AutopilotMonitor.Shared.Models public bool Success { get; set; } public string Message { get; set; } public CollectorConfiguration Collectors { get; set; } + + /// + /// User-defined ad-hoc gather rules (minimal set, not for IME log parsing) + /// public List GatherRules { get; set; } = new List(); + + /// + /// IME log regex patterns for smart enrollment tracking. + /// Delivered from backend so patterns can be updated without agent rebuild. + /// + public List ImeLogPatterns { get; set; } = new List(); + public int ConfigVersion { get; set; } /// @@ -23,17 +34,14 @@ namespace AutopilotMonitor.Shared.Models /// /// Configuration for optional agent collectors - /// Core collectors (EventLogWatcher, RegistryMonitor, PhaseDetector, HelloDetector) are always on /// public class CollectorConfiguration { - // ===== PERFORMANCE COLLECTOR ===== - /// - /// Enable CPU, memory, disk, network performance monitoring + /// Enable CPU, memory, disk, network performance monitoring (always on for UI chart) /// Generates traffic: ~1 event per interval /// - public bool EnablePerformanceCollector { get; set; } = false; + public bool EnablePerformanceCollector { get; set; } = true; /// /// Interval in seconds for performance snapshots @@ -41,45 +49,8 @@ namespace AutopilotMonitor.Shared.Models /// public int PerformanceIntervalSeconds { get; set; } = 60; - // ===== DOWNLOAD PROGRESS COLLECTOR ===== - /// - /// Enable IME/Intune app download progress tracking - /// Generates traffic: ~1 event per interval per active download - /// - public bool EnableDownloadProgressCollector { get; set; } = false; - - /// - /// Interval in seconds for download progress checks - /// Default: 15 seconds - /// - public int DownloadProgressIntervalSeconds { get; set; } = 15; - - // ===== CERTIFICATE VALIDATION COLLECTOR ===== - - /// - /// Enable certificate chain validation for enrollment endpoints - /// Generates traffic: low (runs at startup + on network phase) - /// - public bool EnableCertValidationCollector { get; set; } = false; - - // ===== ESP UI STATE COLLECTOR ===== - - /// - /// Enable ESP (Enrollment Status Page) UI state tracking - /// Captures blocking apps, progress, status text - /// Generates traffic: ~1 event per interval during ESP phases - /// - public bool EnableEspUiStateCollector { get; set; } = false; - - /// - /// Interval in seconds for ESP UI state checks - /// Default: 30 seconds - /// - public int EspUiStateIntervalSeconds { get; set; } = 30; - - /// - /// Creates default collector configuration (all optional collectors off) + /// Creates default collector configuration /// public static CollectorConfiguration CreateDefault() { diff --git a/src/Shared/AutopilotMonitor.Shared/Models/EnrollmentEvent.cs b/src/Shared/AutopilotMonitor.Shared/Models/EnrollmentEvent.cs index 4728ea62..ab4a27ac 100644 --- a/src/Shared/AutopilotMonitor.Shared/Models/EnrollmentEvent.cs +++ b/src/Shared/AutopilotMonitor.Shared/Models/EnrollmentEvent.cs @@ -53,9 +53,10 @@ namespace AutopilotMonitor.Shared.Models /// /// Phase during which this event occurred (internal property, not serialized) + /// Defaults to Unknown (chronologically sorted into active phase) /// [JsonIgnore] - public EnrollmentPhase Phase { get; set; } + public EnrollmentPhase Phase { get; set; } = EnrollmentPhase.Unknown; /// /// Phase as number for JSON serialization (frontend expects number) @@ -73,13 +74,12 @@ namespace AutopilotMonitor.Shared.Models { return phase switch { - EnrollmentPhase.PreFlight => "PreFlight", - EnrollmentPhase.Network => "Network", - EnrollmentPhase.Identity => "Identity", - EnrollmentPhase.MdmEnrollment => "MDM Enrollment", - EnrollmentPhase.EspDeviceSetup => "ESP Device Setup", - EnrollmentPhase.AppInstallation => "App Installation", - EnrollmentPhase.EspUserSetup => "ESP User Setup", + EnrollmentPhase.Start => "Start", + EnrollmentPhase.DevicePreparation => "Device Preparation", + EnrollmentPhase.DeviceSetup => "Device Setup", + EnrollmentPhase.AppsDevice => "Apps (Device)", + EnrollmentPhase.AccountSetup => "Account Setup", + EnrollmentPhase.AppsUser => "Apps (User)", EnrollmentPhase.Complete => "Complete", EnrollmentPhase.Failed => "Failed", _ => "Unknown" diff --git a/src/Shared/AutopilotMonitor.Shared/Models/EnrollmentPhase.cs b/src/Shared/AutopilotMonitor.Shared/Models/EnrollmentPhase.cs index f3436ce6..21505237 100644 --- a/src/Shared/AutopilotMonitor.Shared/Models/EnrollmentPhase.cs +++ b/src/Shared/AutopilotMonitor.Shared/Models/EnrollmentPhase.cs @@ -6,44 +6,44 @@ namespace AutopilotMonitor.Shared.Models public enum EnrollmentPhase { /// - /// Pre-flight: Agent deployed, session registered + /// Unknown: Events without explicit phase assignment (sorted chronologically into active phase) /// - PreFlight = 0, + Unknown = -1, /// - /// OOBE/Network: Network connection established + /// Start: Initial boot, OOBE begins /// - Network = 1, + Start = 0, /// - /// Identity: Azure AD/Entra joined, device certificate issued + /// Device Preparation: OOBE, network, identity, Autopilot profile applied /// - Identity = 2, + DevicePreparation = 1, /// - /// MDM Enrollment: Intune enrolled, policies starting + /// Device Setup: ESP Device phase, agent deployed/bootstrapped, policies applied /// - MdmEnrollment = 3, + DeviceSetup = 2, /// - /// ESP Device Setup: Device policies applied + /// Apps (Device): Required apps installing during device phase /// - EspDeviceSetup = 4, + AppsDevice = 3, /// - /// App Installation: Required apps installing + /// Account Setup: ESP User/Account phase, user sign-in /// - AppInstallation = 5, + AccountSetup = 4, /// - /// ESP User Setup: User policies and apps + /// Apps (User): User-targeted apps installing /// - EspUserSetup = 6, + AppsUser = 5, /// - /// Complete: Desktop reached + /// Complete: Desktop reached, enrollment succeeded /// - Complete = 7, + Complete = 6, /// /// Failed: Enrollment failed diff --git a/src/Shared/AutopilotMonitor.Shared/Models/GatherRule.cs b/src/Shared/AutopilotMonitor.Shared/Models/GatherRule.cs index fe24cbec..c5ab1aaa 100644 --- a/src/Shared/AutopilotMonitor.Shared/Models/GatherRule.cs +++ b/src/Shared/AutopilotMonitor.Shared/Models/GatherRule.cs @@ -59,7 +59,7 @@ namespace AutopilotMonitor.Shared.Models // ===== WHAT TO COLLECT ===== /// - /// Type of data collection: "registry", "eventlog", "wmi", "file", "command" + /// Type of data collection: "registry", "eventlog", "wmi", "file", "command", "logparser" /// public string CollectorType { get; set; } @@ -70,6 +70,7 @@ namespace AutopilotMonitor.Shared.Models /// - wmi: WMI query (e.g., "SELECT * FROM Win32_TPM") /// - file: File path (e.g., "C:\Windows\INF\setupapi.dev.log") /// - command: Command name from allowlist (e.g., "Get-TpmStatus") + /// - logparser: Log file path with env vars (e.g., "%ProgramData%\Microsoft\IntuneManagementExtension\Logs\AppWorkload.log") /// public string Target { get; set; } @@ -80,6 +81,7 @@ namespace AutopilotMonitor.Shared.Models /// - wmi: { "namespace": "root\\CIMV2\\Security\\MicrosoftTpm" } /// - file: { "maxLines": "100", "pattern": "error|fail" } /// - command: { "arguments": "-Detailed" } + /// - logparser: { "pattern": "regex with (?<named>groups)", "logFormat": "cmtrace", "trackPosition": "true", "maxLines": "1000" } /// public Dictionary Parameters { get; set; } = new Dictionary(); diff --git a/src/Shared/AutopilotMonitor.Shared/Models/ImeLogPattern.cs b/src/Shared/AutopilotMonitor.Shared/Models/ImeLogPattern.cs new file mode 100644 index 00000000..a447edfb --- /dev/null +++ b/src/Shared/AutopilotMonitor.Shared/Models/ImeLogPattern.cs @@ -0,0 +1,67 @@ +using System.Collections.Generic; + +namespace AutopilotMonitor.Shared.Models +{ + /// + /// Defines a regex pattern for IME log parsing. + /// Delivered from backend via agent config endpoint. + /// Allows updating patterns without agent rebuild when Microsoft changes IME log formats. + /// + public class ImeLogPattern + { + /// + /// Unique pattern identifier (e.g., "IME-ESP-PHASE") + /// + public string PatternId { get; set; } + + /// + /// Pattern category controlling when the pattern is active: + /// - "always": Always active regardless of ESP phase + /// - "currentPhase": Only active during the current ESP phase + /// - "otherPhases": Only active during non-current ESP phases (for history/completed apps) + /// + public string Category { get; set; } + + /// + /// Regex pattern string to match against IME log message content. + /// Supports named capture groups (e.g., (?<id>...)) which are passed to the action handler. + /// Uses {GUID} as placeholder for the standard GUID capture pattern. + /// + public string Pattern { get; set; } + + /// + /// Action to perform when the pattern matches: + /// - "setCurrentApp": Set the current app being processed (uses 'id' capture group) + /// - "updateStateInstalled": Mark app as installed + /// - "updateStateDownloading": Mark app as downloading (uses 'bytes'/'ofbytes' for progress) + /// - "updateStateInstalling": Mark app as installing + /// - "updateStateSkipped": Mark app as skipped/not applicable + /// - "updateStateError": Mark app as errored + /// - "updateStatePostponed": Mark app as postponed (e.g., timeout) + /// - "espPhaseDetected": ESP phase transition detected (uses 'espPhase' capture group) + /// - "imeStarted": IME agent started + /// - "policiesDiscovered": App policies JSON discovered (uses 'policies' capture group) + /// - "ignoreCompletedApp": Add current app to ignore list (already completed in prior phase) + /// - "imeAgentVersion": IME version detected (uses 'agentVersion' capture group) + /// - "espTrackStatus": ESP tracked install status update (uses 'from'/'to'/'id' capture groups) + /// - "updateName": Update app name (uses 'id'/'name' capture groups) + /// - "updateWin32AppState": Update from Win32 app state (uses 'id'/'state' capture groups) + /// - "cancelStuckAndSetCurrent": Cancel stuck app and set new current (uses 'id' capture group) + /// + public string Action { get; set; } + + /// + /// Optional extra parameters for the action handler. + /// Examples: + /// - { "phase": "AccountSetup" } for espPhaseDetected to force a specific phase + /// - { "useCurrentApp": "true" } to use CurrentPackageId instead of captured 'id' + /// - { "checkTo": "true" } to check the 'to' capture group value before applying state + /// + public Dictionary Parameters { get; set; } + + /// + /// Whether this pattern is enabled. Allows disabling patterns without removing them. + /// + public bool Enabled { get; set; } = true; + } +} diff --git a/src/Shared/AutopilotMonitor.Shared/Models/TenantConfiguration.cs b/src/Shared/AutopilotMonitor.Shared/Models/TenantConfiguration.cs index f3622668..88b02f86 100644 --- a/src/Shared/AutopilotMonitor.Shared/Models/TenantConfiguration.cs +++ b/src/Shared/AutopilotMonitor.Shared/Models/TenantConfiguration.cs @@ -131,38 +131,6 @@ namespace AutopilotMonitor.Shared.Models /// public int PerformanceCollectorIntervalSeconds { get; set; } = 60; - /// - /// Enable Download Progress Collector (IME/Intune app download tracking) - /// Generates ~1 event per interval per active download - /// Default: false (opt-in) - /// - public bool EnableDownloadProgressCollector { get; set; } = false; - - /// - /// Download progress collector interval in seconds - /// Default: 15 seconds - /// - public int DownloadProgressCollectorIntervalSeconds { get; set; } = 15; - - /// - /// Enable Certificate Validation Collector (validates cert chains for enrollment endpoints) - /// Low traffic: runs at startup + on network phase change - /// Default: false (opt-in) - /// - public bool EnableCertValidationCollector { get; set; } = false; - - /// - /// Enable ESP UI State Collector (captures ESP blocking apps, progress, status) - /// Generates ~1 event per interval during ESP phases only - /// Default: false (opt-in) - /// - public bool EnableEspUiStateCollector { get; set; } = false; - - /// - /// ESP UI State collector interval in seconds - /// Default: 30 seconds - /// - public int EspUiStateCollectorIntervalSeconds { get; set; } = 30; // ===== FUTURE SETTINGS (Extensible) ===== @@ -234,12 +202,7 @@ namespace AutopilotMonitor.Shared.Models SessionTimeoutHours = 5, MaxNdjsonPayloadSizeMB = 5, EnablePerformanceCollector = false, - PerformanceCollectorIntervalSeconds = 60, - EnableDownloadProgressCollector = false, - DownloadProgressCollectorIntervalSeconds = 15, - EnableCertValidationCollector = false, - EnableEspUiStateCollector = false, - EspUiStateCollectorIntervalSeconds = 30 + PerformanceCollectorIntervalSeconds = 60 }; } } diff --git a/src/Web/autopilot-monitor-web/app/flight-tracker/[sessionId]/page.tsx b/src/Web/autopilot-monitor-web/app/flight-tracker/[sessionId]/page.tsx deleted file mode 100644 index bb0f3f11..00000000 --- a/src/Web/autopilot-monitor-web/app/flight-tracker/[sessionId]/page.tsx +++ /dev/null @@ -1,1108 +0,0 @@ -"use client"; - -import { useEffect, useState, useRef, useMemo } from "react"; -import { useParams, useRouter } from "next/navigation"; -import { useSignalR } from "../../../contexts/SignalRContext"; -import { useTenant } from "../../../contexts/TenantContext"; -import { useAuth } from "../../../contexts/AuthContext"; -import { ProtectedRoute } from "../../../components/ProtectedRoute"; -import { API_BASE_URL } from "@/lib/config"; - -interface EnrollmentEvent { - eventId: string; - sessionId: string; - timestamp: string; - eventType: string; - severity: string; - source: string; - phase: number; - phaseName?: string; - message: string; - sequence: number; - data?: Record; -} - -interface RuleResult { - resultId: string; - ruleId: string; - ruleTitle: string; - severity: string; - category: string; - confidenceScore: number; - explanation: string; - remediation: { title: string; steps: string[] }[]; - relatedDocs: { title: string; url: string }[]; - matchedConditions: Record; - detectedAt: string; -} - -interface Session { - sessionId: string; - tenantId: string; - serialNumber: string; - deviceName: string; - manufacturer: string; - model: string; - startedAt: string; - status: string; - currentPhase: number; - eventCount: number; - durationSeconds: number; - failureReason?: string; -} - -const phaseNames: Record = { - 0: "PreFlight", - 1: "Network", - 2: "Identity", - 3: "MDM Enrollment", - 4: "ESP Device Setup", - 5: "App Installation", - 6: "ESP User Setup", - 7: "Complete", - 99: "Failed", -}; - -const phaseShortNames: Record = { - 0: "OOBE", - 1: "Join", - 2: "Identity", - 3: "MDM", - 4: "ESP Device", - 5: "Apps", - 6: "ESP User", - 7: "Complete", -}; - -function formatBytes(bytes: number): string { - if (bytes === 0) return "0 B"; - const k = 1024; - const sizes = ["B", "KB", "MB", "GB"]; - const i = Math.floor(Math.log(bytes) / Math.log(k)); - return `${(bytes / Math.pow(k, i)).toFixed(1)} ${sizes[i]}`; -} - -function formatSpeed(bps: number): string { - if (bps === 0) return "0 B/s"; - const k = 1024; - const sizes = ["B/s", "KB/s", "MB/s", "GB/s"]; - const i = Math.floor(Math.log(bps) / Math.log(k)); - return `${(bps / Math.pow(k, i)).toFixed(1)} ${sizes[i]}`; -} - -export default function FlightTrackerPage() { - const params = useParams(); - const router = useRouter(); - const sessionId = params?.sessionId as string; - const [events, setEvents] = useState([]); - const [session, setSession] = useState(null); - const [sessionTenantId, setSessionTenantId] = useState(null); - const [loading, setLoading] = useState(true); - const [analysisResults, setAnalysisResults] = useState([]); - - const refreshTimeoutRef = useRef(null); - const sessionIdRef = useRef(sessionId); - const hasInitialFetch = useRef(false); - const lastFetchedSessionId = useRef(null); - const hasJoinedGroups = useRef(false); - - const { on, off, isConnected, joinGroup, leaveGroup } = useSignalR(); - const { tenantId } = useTenant(); - const { getAccessToken } = useAuth(); - - const [galacticAdminMode] = useState(() => { - if (typeof window !== "undefined") { - return localStorage.getItem("galacticAdminMode") === "true"; - } - return false; - }); - - // Initial data fetch - useEffect(() => { - if (!sessionId) return; - sessionIdRef.current = sessionId; - if (lastFetchedSessionId.current !== sessionId) { - hasInitialFetch.current = false; - lastFetchedSessionId.current = sessionId; - setSessionTenantId(null); - } - if (hasInitialFetch.current) return; - hasInitialFetch.current = true; - fetchSessionDetails(); - }, [sessionId]); - - useEffect(() => { - if (sessionTenantId && sessionId) { - fetchEvents(); - fetchAnalysisResults(); - } - }, [sessionTenantId, sessionId]); - - // SignalR groups - subscribe-then-fetch pattern - useEffect(() => { - const effectiveTenantId = sessionTenantId || tenantId; - if (!sessionId || !isConnected || !effectiveTenantId) return; - if (!hasJoinedGroups.current) { - const joinAndCatchUp = async () => { - await joinGroup(`tenant-${effectiveTenantId}`); - await joinGroup(`session-${effectiveTenantId}-${sessionId}`); - hasJoinedGroups.current = true; - // Re-fetch events after group join to catch any missed during join - fetchEvents(); - }; - joinAndCatchUp(); - } - return () => { - if (hasJoinedGroups.current && effectiveTenantId) { - leaveGroup(`tenant-${effectiveTenantId}`); - leaveGroup(`session-${effectiveTenantId}-${sessionId}`); - hasJoinedGroups.current = false; - } - }; - }, [sessionId, isConnected, sessionTenantId, tenantId]); - - // SignalR listeners - useEffect(() => { - const handleEventStream = (data: { - sessionId: string; - tenantId: string; - events: EnrollmentEvent[]; - session: Session; - newRuleResults?: RuleResult[]; - }) => { - if (data.sessionId === sessionIdRef.current && data.events?.length > 0) { - const eventsWithPhaseNames = data.events.map((e) => ({ - ...e, - phaseName: phaseNames[e.phase] || "Unknown", - })); - setEvents((prev) => { - const existingIds = new Set(prev.map((e) => e.eventId)); - const newEvents = eventsWithPhaseNames.filter( - (e) => !existingIds.has(e.eventId) - ); - return [...prev, ...newEvents].sort( - (a, b) => a.sequence - b.sequence - ); - }); - if (data.session) setSession(data.session); - if (data.newRuleResults?.length) { - setAnalysisResults((prev) => { - const existingIds = new Set(prev.map((r) => r.ruleId)); - const newResults = data.newRuleResults!.filter( - (r) => !existingIds.has(r.ruleId) - ); - return [...prev, ...newResults].sort( - (a, b) => b.confidenceScore - a.confidenceScore - ); - }); - } - } - }; - - const handleNewEvents = (data: { - sessionId: string; - tenantId: string; - eventCount: number; - session: Session; - }) => { - if (data.sessionId === sessionIdRef.current && data.session) { - setSession(data.session); - } - }; - - on("eventstream", handleEventStream); - on("newevents", handleNewEvents); - return () => { - off("eventstream", handleEventStream); - off("newevents", handleNewEvents); - if (refreshTimeoutRef.current) clearTimeout(refreshTimeoutRef.current); - }; - }, [on, off]); - - const fetchSessionDetails = async () => { - try { - const endpoint = galacticAdminMode - ? `${API_BASE_URL}/api/galactic/sessions` - : `${API_BASE_URL}/api/sessions?tenantId=${tenantId}`; - const token = await getAccessToken(); - if (!token) return; - const response = await fetch(endpoint, { - headers: { Authorization: `Bearer ${token}` }, - }); - if (response.ok) { - const data = await response.json(); - const found = data.sessions?.find( - (s: Session) => s.sessionId === sessionId - ); - if (found) { - setSession(found); - setSessionTenantId(found.tenantId); - } - } - } catch (error) { - console.error("Failed to fetch session details:", error); - } - }; - - const fetchEvents = async () => { - const effectiveTenantId = sessionTenantId || tenantId; - try { - const token = await getAccessToken(); - if (!token) { - setLoading(false); - return; - } - const response = await fetch( - `${API_BASE_URL}/api/sessions/${sessionId}/events?tenantId=${effectiveTenantId}`, - { headers: { Authorization: `Bearer ${token}` } } - ); - if (response.ok) { - const data = await response.json(); - const eventsWithPhaseNames = data.events.map((e: EnrollmentEvent) => ({ - ...e, - phaseName: phaseNames[e.phase] || "Unknown", - })); - // Merge with existing events (from SignalR) instead of replacing - setEvents(prev => { - if (prev.length === 0) return eventsWithPhaseNames; - const existingIds = new Set(prev.map(e => e.eventId)); - const newEvents = eventsWithPhaseNames.filter((e: EnrollmentEvent) => !existingIds.has(e.eventId)); - if (newEvents.length === 0) return prev; - return [...prev, ...newEvents].sort((a: EnrollmentEvent, b: EnrollmentEvent) => a.sequence - b.sequence); - }); - } - } catch (error) { - console.error("Failed to fetch events:", error); - } finally { - setLoading(false); - } - }; - - const fetchAnalysisResults = async () => { - const effectiveTenantId = sessionTenantId || tenantId; - try { - const token = await getAccessToken(); - if (!token) return; - const response = await fetch( - `${API_BASE_URL}/api/sessions/${sessionId}/analysis?tenantId=${effectiveTenantId}`, - { headers: { Authorization: `Bearer ${token}` } } - ); - if (response.ok) { - const data = await response.json(); - if (data.results) { - setAnalysisResults( - data.results.sort( - (a: RuleResult, b: RuleResult) => - b.confidenceScore - a.confidenceScore - ) - ); - } - } - } catch (error) { - console.error("Failed to fetch analysis results:", error); - } - }; - - // Derived data - const currentDownload = useMemo(() => { - const downloadEvents = events.filter( - (e) => e.eventType === "download_progress" - ); - if (downloadEvents.length === 0) return null; - - const latest = downloadEvents[downloadEvents.length - 1]; - const d = latest.data; - if (!d) return null; - - const appName = d.app_name ?? d.appName ?? "Unknown App"; - const bytesDownloaded = parseInt( - d.bytes_downloaded ?? d.bytesDownloaded ?? "0", - 10 - ); - const bytesTotal = parseInt(d.bytes_total ?? d.bytesTotal ?? "0", 10); - const downloadRateBps = parseFloat( - d.download_rate_bps ?? d.downloadRateBps ?? "0" - ); - const status = d.status ?? ""; - const isComplete = - status === "completed" || (bytesTotal > 0 && bytesDownloaded >= bytesTotal); - - if (isComplete) return null; - - const progressPercent = - bytesTotal > 0 ? Math.min(100, (bytesDownloaded / bytesTotal) * 100) : 0; - const bytesRemaining = bytesTotal > 0 ? bytesTotal - bytesDownloaded : 0; - const etaSeconds = - downloadRateBps > 0 && bytesRemaining > 0 - ? bytesRemaining / downloadRateBps - : 0; - - return { - appName, - bytesDownloaded, - bytesTotal, - downloadRateBps, - progressPercent, - etaSeconds, - }; - }, [events]); - - const appProgress = useMemo(() => { - const espEvents = events.filter((e) => e.eventType === "esp_ui_state"); - if (espEvents.length === 0) return null; - const latest = espEvents[espEvents.length - 1]; - const d = latest.data; - if (!d) return null; - - const completed = parseInt( - d.blocking_apps_completed ?? d.blockingAppsCompleted ?? "0", - 10 - ); - const total = parseInt( - d.blocking_apps_total ?? d.blockingAppsTotal ?? "0", - 10 - ); - const currentItem = - d.current_item ?? d.currentItem ?? d.status_text ?? d.statusText ?? null; - - return { completed, total, currentItem }; - }, [events]); - - const recentEvents = useMemo(() => { - return [...events] - .sort((a, b) => b.sequence - a.sequence) - .slice(0, 8) - .filter((e) => e.eventType !== "performance_snapshot" && e.eventType !== "download_progress"); - }, [events]); - - // Derive the highest real phase (0-7) seen in events, excluding phase 99 (Failed) - const maxEventPhase = useMemo(() => { - const realPhases = events - .filter((e) => e.phase >= 0 && e.phase <= 7) - .map((e) => e.phase); - if (realPhases.length === 0) return -1; - return Math.max(...realPhases); - }, [events]); - - const estimatedCompletion = useMemo(() => { - if (!session || session.status !== "InProgress") return null; - // Simple estimation based on phase progress and duration so far - const totalPhases = 7; - // Use the higher of backend phase and events phase for more accurate ETA - const currentPhase = Math.min( - Math.max(session.currentPhase, maxEventPhase >= 0 ? maxEventPhase : 0), - 7 - ); - if (currentPhase === 0) return null; - - const elapsed = session.durationSeconds; - const ratePerPhase = elapsed / currentPhase; - const remainingPhases = totalPhases - currentPhase; - const estimatedRemainingSeconds = remainingPhases * ratePerPhase; - - const completionTime = new Date( - new Date(session.startedAt).getTime() + - (elapsed + estimatedRemainingSeconds) * 1000 - ); - - return { - time: completionTime, - remainingMinutes: Math.round(estimatedRemainingSeconds / 60), - }; - }, [session, maxEventPhase]); - - // Determine failure phase - const failurePhase = useMemo(() => { - if (!session || session.status !== "Failed" || session.currentPhase !== 99) - return null; - return maxEventPhase >= 0 ? maxEventPhase : 0; - }, [session, maxEventPhase]); - - // Effective current phase: use the max of backend currentPhase and what events show. - // This prevents the tracker from lagging behind when events from a new phase arrive - // via SignalR before the backend updates session.currentPhase. - const effectiveCurrentPhase = useMemo(() => { - if (!session) return 0; - if (session.status === "Succeeded") return session.currentPhase; - if (session.status === "Failed") return failurePhase !== null ? failurePhase : session.currentPhase; - if (maxEventPhase < 0) return session.currentPhase; - return Math.max(session.currentPhase, maxEventPhase); - }, [session, failurePhase, maxEventPhase]); - - const getPhaseStatus = (phaseId: number) => { - if (!session) return "pending"; - if (session.status === "Succeeded" && phaseId <= 7) return "completed"; - if (session.status === "Failed" && failurePhase !== null) { - if (phaseId === failurePhase) return "failed"; - if (phaseId < failurePhase) return "completed"; - return "pending"; - } - if (phaseId === effectiveCurrentPhase) return "current"; - if (phaseId < effectiveCurrentPhase) return "completed"; - return "pending"; - }; - - if (loading) { - return ( -
-
Loading flight tracker...
-
- ); - } - - if (!session) { - return ( -
-
-
Session not found
- -
-
- ); - } - - const sessionShortId = session.sessionId.split("-")[0]?.toUpperCase() || session.sessionId.substring(0, 8).toUpperCase(); - - return ( - -
- {/* Top Bar */} -
-
-
-
- -
- - {session.status === "Succeeded" - ? "\u2705" - : session.status === "Failed" - ? "\u274C" - : "\uD83D\uDEEB"} - -

- ENROLLMENT FLIGHT TRACKER -

-
-
-
- - {session.status === "Failed" && analysisResults.length > 0 && ( - - )} - - Session: {sessionShortId} - -
-
-
-
- - {/* Device Info Bar */} -
-
-
-
-
- - - - - {session.deviceName || session.serialNumber} - -
- | - - {session.manufacturer} {session.model} - - | - {session.serialNumber} - | - - Started:{" "} - {new Date(session.startedAt).toLocaleTimeString([], { - hour: "2-digit", - minute: "2-digit", - })} - -
-
- -
-
-
-
- -
- {/* Phase Progress Track */} -
-
- {/* Progress Line */} -
-
-
- - {/* Phase Dots */} -
- {[0, 1, 2, 3, 4, 5, 6, 7].map((phaseId) => { - const status = getPhaseStatus(phaseId); - return ( -
-
- {status === "completed" ? ( - - - - ) : status === "failed" ? ( - - - - ) : status === "current" ? ( -
- ) : ( - {phaseId + 1} - )} -
-
-
- {phaseShortNames[phaseId]} -
- {appProgress && - phaseId === 5 && - status === "current" && ( -
- ({appProgress.completed}/{appProgress.total}) -
- )} -
-
- ); - })} -
-
-
- - {/* Currently Installing + Recent Events side by side */} -
- {/* Currently Installing */} -
-
- - - -

Current Activity

-
- - {currentDownload ? ( -
-
- {currentDownload.appName} -
- {/* Progress bar */} -
-
-
-
- - Download:{" "} - {formatBytes(currentDownload.bytesDownloaded)} /{" "} - {formatBytes(currentDownload.bytesTotal)} - - - {currentDownload.progressPercent.toFixed(0)}% - -
-
- - Speed: {formatSpeed(currentDownload.downloadRateBps)} - - - ETA: ~ - {currentDownload.etaSeconds < 60 - ? `${Math.round(currentDownload.etaSeconds)} seconds` - : `${Math.round(currentDownload.etaSeconds / 60)} minutes`} - -
-
- ) : appProgress && appProgress.currentItem ? ( -
-
- {appProgress.currentItem} -
-
-
0 - ? (appProgress.completed / appProgress.total) * 100 - : 0 - }%`, - }} - /> -
-
- Apps: {appProgress.completed} / {appProgress.total}{" "} - completed -
-
- ) : session.status === "InProgress" ? ( -
-
- - {phaseNames[effectiveCurrentPhase] || "Processing"}... - -
- ) : session.status === "Succeeded" ? ( -
- - - -
-
- Enrollment Complete -
-
- Duration: {Math.round(session.durationSeconds / 60)} min -
-
-
- ) : ( -
- - - -
-
- Enrollment Failed -
- {session.failureReason && ( -
- {session.failureReason} -
- )} -
-
- )} -
- - {/* Recent Events */} -
-
-
- - - -

Recent Events

-
- - {events.length} total - -
- -
- {recentEvents.length === 0 ? ( -
- Waiting for events... -
- ) : ( - recentEvents.map((event) => ( -
- - {new Date(event.timestamp).toLocaleTimeString([], { - hour: "2-digit", - minute: "2-digit", - second: "2-digit", - })} - - - {event.severity === "Error" || - event.severity === "Critical" ? ( - ✖ - ) : event.severity === "Warning" ? ( - ⚡ - ) : event.message - .toLowerCase() - .includes("success") || - event.message - .toLowerCase() - .includes("completed") ? ( - ✔ - ) : ( - ● - )} - - - {event.message} - -
- )) - )} -
-
-
- - {/* Bottom: Estimated Completion + Quick Diagnosis if failed */} -
- {/* Estimated Completion / Duration */} -
- {session.status === "InProgress" && estimatedCompletion ? ( -
-
- - - -
-
-
- Estimated completion -
-
- {estimatedCompletion.time.toLocaleTimeString([], { - hour: "2-digit", - minute: "2-digit", - })} -
-
- ~{estimatedCompletion.remainingMinutes} minutes remaining -
-
-
- ) : session.status === "Succeeded" ? ( -
-
- - - -
-
-
- Completed successfully -
-
- {Math.round(session.durationSeconds / 60)} min total -
-
- {new Date(session.startedAt).toLocaleTimeString([], { - hour: "2-digit", - minute: "2-digit", - })}{" "} - -{" "} - {new Date( - new Date(session.startedAt).getTime() + - session.durationSeconds * 1000 - ).toLocaleTimeString([], { - hour: "2-digit", - minute: "2-digit", - })} -
-
-
- ) : ( -
-
- - - -
-
-
- Failed after {Math.round(session.durationSeconds / 60)}{" "} - minutes -
-
- {session.failureReason || "Enrollment failed"} -
-
-
- )} -
- - {/* Quick Analysis Summary */} - {analysisResults.length > 0 && ( -
-
-
- - - -

Analysis

-
- -
-
- {analysisResults.slice(0, 3).map((result) => ( -
- - - {result.ruleTitle} - - - {result.confidenceScore}% - -
- ))} -
-
- )} -
-
-
- - ); -} - -function StatusPill({ status }: { status: string }) { - const config = { - InProgress: { - bg: "bg-blue-500/20", - text: "text-blue-400", - label: "In Progress", - dot: "bg-blue-400 animate-pulse", - }, - Succeeded: { - bg: "bg-green-500/20", - text: "text-green-400", - label: "Succeeded", - dot: "bg-green-400", - }, - Failed: { - bg: "bg-red-500/20", - text: "text-red-400", - label: "Failed", - dot: "bg-red-400", - }, - }; - - const c = config[status as keyof typeof config] || { - bg: "bg-gray-500/20", - text: "text-gray-400", - label: status, - dot: "bg-gray-400", - }; - - return ( - - - {c.label} - - ); -} diff --git a/src/Web/autopilot-monitor-web/app/progress/page.tsx b/src/Web/autopilot-monitor-web/app/progress/page.tsx index d1e139eb..4245fa68 100644 --- a/src/Web/autopilot-monitor-web/app/progress/page.tsx +++ b/src/Web/autopilot-monitor-web/app/progress/page.tsx @@ -6,6 +6,7 @@ import { useTenant } from "../../contexts/TenantContext"; import { useAuth } from "../../contexts/AuthContext"; import { useSignalR } from "../../contexts/SignalRContext"; import { ProtectedRoute } from "../../components/ProtectedRoute"; +import DownloadProgress from "../../components/DownloadProgress"; interface Session { sessionId: string; @@ -34,14 +35,13 @@ interface EnrollmentEvent { } const phaseSteps = [ - { id: 0, label: "Device registered", shortLabel: "Registration" }, - { id: 1, label: "Network configured", shortLabel: "Network" }, - { id: 2, label: "Identity verified", shortLabel: "Identity" }, - { id: 3, label: "Management enrolled", shortLabel: "Enrollment" }, - { id: 4, label: "Security policies applied", shortLabel: "Policies" }, - { id: 5, label: "Installing apps", shortLabel: "Apps" }, - { id: 6, label: "User setup", shortLabel: "User Setup" }, - { id: 7, label: "Finalizing setup", shortLabel: "Complete" }, + { id: 0, label: "Setup start", shortLabel: "Start" }, + { id: 1, label: "Device preparation", shortLabel: "Preparation" }, + { id: 2, label: "Device setup", shortLabel: "Device" }, + { id: 3, label: "Installing apps (device)", shortLabel: "Apps (D)" }, + { id: 4, label: "Account setup", shortLabel: "Account" }, + { id: 5, label: "Installing apps (user)", shortLabel: "Apps (U)" }, + { id: 6, label: "Finalizing setup", shortLabel: "Complete" }, ]; export default function ProgressPortalPage() { @@ -58,7 +58,8 @@ export default function ProgressPortalPage() { const { getAccessToken } = useAuth(); const { on, off, isConnected, joinGroup, leaveGroup } = useSignalR(); - const hasJoinedGroup = useRef(false); + const hasJoinedTenantGroup = useRef(false); + const hasJoinedSessionGroup = useRef(false); const sessionRef = useRef(null); // Keep ref in sync @@ -68,34 +69,95 @@ export default function ProgressPortalPage() { // Join tenant group for real-time updates useEffect(() => { - if (isConnected && !hasJoinedGroup.current) { + if (isConnected && !hasJoinedTenantGroup.current) { joinGroup(`tenant-${tenantId}`); - hasJoinedGroup.current = true; + hasJoinedTenantGroup.current = true; } return () => { - if (hasJoinedGroup.current) { + if (hasJoinedTenantGroup.current) { leaveGroup(`tenant-${tenantId}`); - hasJoinedGroup.current = false; + hasJoinedTenantGroup.current = false; } }; }, [isConnected, tenantId]); + // Join session-specific group when session is found + useEffect(() => { + if (!isConnected || !session) { + return; + } + + const sessionGroup = `session-${session.tenantId}-${session.sessionId}`; + console.log('[Progress] Joining session group:', sessionGroup); + joinGroup(sessionGroup); + hasJoinedSessionGroup.current = true; + + return () => { + console.log('[Progress] Leaving session group:', sessionGroup); + leaveGroup(sessionGroup); + hasJoinedSessionGroup.current = false; + }; + }, [isConnected, session?.sessionId, session?.tenantId, joinGroup, leaveGroup]); + // Listen for real-time session updates useEffect(() => { - const handleNewEvents = (data: { session: Session }) => { + const handleNewEvents = (data: { session: Session; events?: EnrollmentEvent[] }) => { + console.log('[Progress] Received newevents:', data); if ( data.session && sessionRef.current && data.session.sessionId === sessionRef.current.sessionId ) { + console.log('[Progress] Updating session from newevents'); setSession(data.session); + + // Add new events to the events list + if (data.events && data.events.length > 0) { + console.log('[Progress] Adding', data.events.length, 'new events from newevents'); + setEvents((prev) => { + const existingIds = new Set(prev.map((e) => e.eventId)); + const newEvents = data.events!.filter((e) => !existingIds.has(e.eventId)); + if (newEvents.length === 0) return prev; + return [...prev, ...newEvents].sort((a, b) => a.sequence - b.sequence); + }); + } } }; + + const handleEventStream = (data: { session: Session; events?: EnrollmentEvent[] }) => { + console.log('[Progress] Received eventStream:', data); + if ( + data.session && + sessionRef.current && + data.session.sessionId === sessionRef.current.sessionId + ) { + console.log('[Progress] Updating session from eventStream'); + console.log('[Progress] Session currentPhase:', data.session.currentPhase, 'status:', data.session.status); + setSession(data.session); + + // Add new events to the events list + if (data.events && data.events.length > 0) { + console.log('[Progress] Adding', data.events.length, 'new events from eventStream'); + console.log('[Progress] Event types:', data.events.map(e => e.eventType).join(', ')); + setEvents((prev) => { + const existingIds = new Set(prev.map((e) => e.eventId)); + const newEvents = data.events!.filter((e) => !existingIds.has(e.eventId)); + if (newEvents.length === 0) return prev; + console.log('[Progress] Actually adding', newEvents.length, 'new events'); + console.log('[Progress] Total events after update:', prev.length + newEvents.length); + return [...prev, ...newEvents].sort((a, b) => a.sequence - b.sequence); + }); + } + } + }; + on("newevents", handleNewEvents); on("newSession", handleNewEvents); + on("eventStream", handleEventStream); return () => { off("newevents", handleNewEvents); off("newSession", handleNewEvents); + off("eventStream", handleEventStream); }; }, [on, off]); @@ -248,20 +310,20 @@ export default function ProgressPortalPage() { ((session.currentPhase === 99 ? 3 : session.currentPhase) / - 7) * + 6) * 100 ) - : Math.min(100, (session.currentPhase / 7) * 100) + : Math.min(100, (session.currentPhase / 6) * 100) : 0; const estimatedRemaining = session ? (() => { if (session.status !== "InProgress") return null; - const currentPhase = Math.min(session.currentPhase, 7); + const currentPhase = Math.min(session.currentPhase, 6); if (currentPhase === 0) return null; const elapsed = session.durationSeconds; const rate = elapsed / currentPhase; - const remaining = (7 - currentPhase) * rate; + const remaining = (6 - currentPhase) * rate; return Math.round(remaining / 60); })() : null; @@ -483,7 +545,7 @@ export default function ProgressPortalPage() { ? 3 : session.currentPhase; const isCompleted = - session.status === "Succeeded" || + (session.status === "Succeeded" && step.id <= 6) || step.id < effectivePhase; const isCurrent = step.id === effectivePhase && @@ -555,14 +617,14 @@ export default function ProgressPortalPage() { > {step.label} {isCurrent && - step.id === 5 && + (step.id === 3 || step.id === 5) && appProgress && appProgress.total > 0 && appProgress.completed > 0 && ` (${appProgress.completed}/${appProgress.total})`} - {/* App install detail below the "Installing apps" step */} - {isCurrent && step.id === 5 && ( + {/* App install detail below the "Installing apps" steps */} + {isCurrent && (step.id === 3 || step.id === 5) && (
@@ -579,6 +641,20 @@ export default function ProgressPortalPage() { })}
+ {/* Download Progress - Show during app installation phases */} + {session.status === "InProgress" && + (session.currentPhase === 3 || session.currentPhase === 5) && + events.filter(e => e.eventType === "download_progress").length > 0 && ( +
+ e.eventType === "download_progress").map(e => ({ + timestamp: e.timestamp, + data: e.data + }))} + /> +
+ )} + {/* Estimated Time / Status */} {session.status === "InProgress" && estimatedRemaining != null && estimatedRemaining > 0 && (
diff --git a/src/Web/autopilot-monitor-web/app/sessions/[sessionId]/page.tsx b/src/Web/autopilot-monitor-web/app/sessions/[sessionId]/page.tsx index 1613bc6c..a37a4142 100644 --- a/src/Web/autopilot-monitor-web/app/sessions/[sessionId]/page.tsx +++ b/src/Web/autopilot-monitor-web/app/sessions/[sessionId]/page.tsx @@ -1,6 +1,6 @@ "use client"; -import { useEffect, useState, useRef } from "react"; +import { useEffect, useState, useRef, useMemo } from "react"; import { useParams, useRouter } from "next/navigation"; import { useSignalR } from "../../../contexts/SignalRContext"; import { useTenant } from "../../../contexts/TenantContext"; @@ -54,14 +54,14 @@ interface Session { } const phaseNames: Record = { - 0: "PreFlight", - 1: "Network", - 2: "Identity", - 3: "MDM Enrollment", - 4: "ESP Device Setup", - 5: "App Installation", - 6: "ESP User Setup", - 7: "Complete", + [-1]: "Unknown", + 0: "Start", + 1: "Device Preparation", + 2: "Device Setup", + 3: "Apps (Device)", + 4: "Account Setup", + 5: "Apps (User)", + 6: "Complete", 99: "Failed" }; @@ -73,8 +73,7 @@ export default function SessionDetailPage() { const [session, setSession] = useState(null); const [sessionTenantId, setSessionTenantId] = useState(null); const [loading, setLoading] = useState(true); - const [expandedPhases, setExpandedPhases] = useState>(new Set()); - const [severityFilters, setSeverityFilters] = useState>(new Set(["Info", "Warning", "Error", "Critical", "Debug"])); + const [severityFilters, setSeverityFilters] = useState>(new Set(["Info", "Warning", "Error", "Critical"])); const [showMarkFailedConfirm, setShowMarkFailedConfirm] = useState(false); const [analysisResults, setAnalysisResults] = useState([]); const [loadingAnalysis, setLoadingAnalysis] = useState(false); @@ -105,7 +104,7 @@ export default function SessionDetailPage() { // Use global contexts const { on, off, isConnected, joinGroup, leaveGroup } = useSignalR(); const { tenantId } = useTenant(); - const { getAccessToken } = useAuth(); + const { getAccessToken, user } = useAuth(); // Initial data fetch useEffect(() => { @@ -393,27 +392,63 @@ export default function SessionDetailPage() { }; // Filter events by severity for the timeline - const filteredEvents = events.filter(e => severityFilters.has(e.severity)); + const filteredEvents = useMemo(() => + events.filter(e => severityFilters.has(e.severity)), + [events, severityFilters] + ); - // Group filtered events by phase - const eventsByPhase = filteredEvents.reduce((acc, event) => { - const phaseName = event.phaseName || "Unknown"; - if (!acc[phaseName]) { - acc[phaseName] = []; + // Group events by phase with memoization + const { eventsByPhase, orderedPhases } = useMemo(() => { + // Sort events by sequence (chronological order) + const sortedEvents = [...filteredEvents].sort((a, b) => a.sequence - b.sequence); + + // Group events by phase, inserting "Unknown" events into their chronological position + const eventsByPhase = {} as Record; + let currentActivePhaseName = "Start"; // Default to Start phase + + for (let i = 0; i < sortedEvents.length; i++) { + const event = sortedEvents[i]; + let targetPhase = event.phaseName || "Unknown"; + + // If event has explicit phase (not Unknown), update current active phase + if (targetPhase !== "Unknown") { + currentActivePhaseName = targetPhase; + } else { + // Unknown events go into the current active phase + targetPhase = currentActivePhaseName; + } + + if (!eventsByPhase[targetPhase]) { + eventsByPhase[targetPhase] = []; + } + eventsByPhase[targetPhase].push(event); } - acc[phaseName].push(event); - return acc; - }, {} as Record); - const phaseOrder = ["PreFlight", "Network", "Identity", "MDM Enrollment", "ESP Device Setup", "App Installation", "ESP User Setup", "Complete", "Failed"]; - const orderedPhases = phaseOrder.filter(phase => eventsByPhase[phase]); + const phaseOrder = ["Start", "Device Preparation", "Device Setup", "Apps (Device)", "Account Setup", "Apps (User)", "Complete", "Failed"]; + const orderedPhases = phaseOrder.filter(phase => eventsByPhase[phase] && eventsByPhase[phase].length > 0); - // Initialize all phases as expanded on first load + return { eventsByPhase, orderedPhases }; + }, [filteredEvents, events.length]); + + const [expandedPhases, setExpandedPhases] = useState>(new Set()); + + // Auto-expand new phases as they appear (keeps existing expanded/collapsed state) useEffect(() => { - if (orderedPhases.length > 0 && expandedPhases.size === 0) { - setExpandedPhases(new Set(orderedPhases)); - } - }, [orderedPhases.length]); + setExpandedPhases(prev => { + // Add any new phases that aren't already tracked + const newExpanded = new Set(prev); + let hasChanges = false; + + for (const phase of orderedPhases) { + if (!prev.has(phase)) { + newExpanded.add(phase); + hasChanges = true; + } + } + + return hasChanges ? newExpanded : prev; + }); + }, [orderedPhases]); const expandAll = () => { setExpandedPhases(new Set(orderedPhases)); @@ -424,13 +459,15 @@ export default function SessionDetailPage() { }; const togglePhase = (phaseName: string) => { - const newExpanded = new Set(expandedPhases); - if (newExpanded.has(phaseName)) { - newExpanded.delete(phaseName); - } else { - newExpanded.add(phaseName); - } - setExpandedPhases(newExpanded); + setExpandedPhases(prev => { + const newExpanded = new Set(prev); + if (newExpanded.has(phaseName)) { + newExpanded.delete(phaseName); + } else { + newExpanded.add(phaseName); + } + return newExpanded; + }); }; if (loading) { @@ -459,15 +496,6 @@ export default function SessionDetailPage() {
- {session?.status === 'Failed' && (
)} + {/* Device Details Card (from enrollment tracker events) */} + + {/* Phase Timeline */} {session && (

Enrollment Progress

@@ -611,9 +642,9 @@ export default function SessionDetailPage() { events={events.filter(e => e.eventType === "performance_snapshot")} /> - {/* Download Progress (from download_progress events) */} + {/* Download Progress (from download_progress or app_tracking_summary events) */} e.eventType === "download_progress")} + events={events.filter(e => e.eventType === "download_progress" || e.eventType === "app_tracking_summary")} /> {/* Timeline */} @@ -681,6 +712,7 @@ export default function SessionDetailPage() { events={eventsByPhase[phaseName]} isExpanded={expandedPhases.has(phaseName)} onToggle={() => togglePhase(phaseName)} + isGalacticAdmin={user?.isGalacticAdmin} /> ))}
@@ -745,12 +777,14 @@ function PhaseSection({ phaseName, events, isExpanded, - onToggle + onToggle, + isGalacticAdmin }: { phaseName: string; events: EnrollmentEvent[]; isExpanded: boolean; onToggle: () => void; + isGalacticAdmin?: boolean; }) { return (
@@ -767,7 +801,11 @@ function PhaseSection({ {isExpanded && (
{events.map((event, index) => ( - + ))}
)} @@ -775,8 +813,19 @@ function PhaseSection({ ); } -function EventRow({ event }: { event: EnrollmentEvent }) { +function EventRow({ event, isGalacticAdmin }: { event: EnrollmentEvent; isGalacticAdmin?: boolean }) { const [showDetails, setShowDetails] = useState(false); + const [copied, setCopied] = useState(false); + + const copyEventId = async () => { + try { + await navigator.clipboard.writeText(event.eventId); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + } catch (err) { + console.error('Failed to copy EventID:', err); + } + }; return (
@@ -793,6 +842,15 @@ function EventRow({ event }: { event: EnrollmentEvent }) {
Source: {event.source} Seq: {event.sequence} + {isGalacticAdmin && ( + + )}
{event.data && Object.keys(event.data).length > 0 && ( @@ -821,14 +879,13 @@ function PhaseTimeline({ currentPhase, completedPhases, events = [], sessionStat sessionStatus?: string; }) { const phases = [ - { id: 0, name: "PreFlight", shortName: "Pre-Flight" }, - { id: 1, name: "Network", shortName: "Network" }, - { id: 2, name: "Identity", shortName: "Identity" }, - { id: 3, name: "MDM Enrollment", shortName: "MDM" }, - { id: 4, name: "ESP Device Setup", shortName: "Device" }, - { id: 5, name: "App Installation", shortName: "Apps" }, - { id: 6, name: "ESP User Setup", shortName: "User" }, - { id: 7, name: "Complete", shortName: "Complete" }, + { id: 0, name: "Start", shortName: "Start" }, + { id: 1, name: "Device Preparation", shortName: "Device Preparation" }, + { id: 2, name: "Device Setup", shortName: "Device Setup" }, + { id: 3, name: "Apps (Device)", shortName: "Apps (Device)" }, + { id: 4, name: "Account Setup", shortName: "Account Setup" }, + { id: 5, name: "Apps (User)", shortName: "Apps (User)" }, + { id: 6, name: "Complete", shortName: "Complete" }, ]; // Derive current activity for the active phase from events @@ -850,7 +907,18 @@ function PhaseTimeline({ currentPhase, completedPhases, events = [], sessionStat if (phaseEvents.length === 0) return null; - // Check for esp_ui_state events to show app install progress + // Check for app_tracking_summary events (new strategic events) + const trackingSummary = phaseEvents.find(e => e.eventType === "app_tracking_summary"); + if (trackingSummary?.data) { + const d = trackingSummary.data; + const completed = parseInt(d.appsCompleted ?? "0", 10); + const total = parseInt(d.totalApps ?? "0", 10); + if (total > 0) { + return `Installing apps (${completed}/${total})`; + } + } + + // Check for esp_ui_state events (legacy) to show app install progress const espState = phaseEvents.find(e => e.eventType === "esp_ui_state"); if (espState?.data) { const d = espState.data; @@ -865,7 +933,17 @@ function PhaseTimeline({ currentPhase, completedPhases, events = [], sessionStat } } - // Check for download_progress to show active download + // Check for app install events (new strategic events) + const appInstallEvt = phaseEvents.find(e => + e.eventType === "app_download_started" || e.eventType === "app_install_started" + ); + if (appInstallEvt?.data) { + const appName = appInstallEvt.data.appName ?? appInstallEvt.data.appId ?? "app"; + if (appInstallEvt.eventType === "app_download_started") return `Downloading ${appName}`; + return `Installing ${appName}`; + } + + // Check for download_progress to show active download (legacy) const downloadEvt = phaseEvents.find(e => e.eventType === "download_progress"); if (downloadEvt?.data) { const d = downloadEvt.data; @@ -932,6 +1010,10 @@ function PhaseTimeline({ currentPhase, completedPhases, events = [], sessionStat })(); const getPhaseStatus = (phaseId: number) => { + // Agent starts at MDM phase (3) - Pre-Flight(0)/Network(1)/Identity(2) are inferred as completed + // since the machine reached MDM enrollment + if (phaseId >= 0 && phaseId <= 2) return 'completed'; // Pre-Flight(0), Network(1), Identity(2) + // If phase is completed, show as completed (green) if (completedPhases.includes(phaseId)) return 'completed'; @@ -1164,6 +1246,246 @@ function AnalysisResultCard({ result }: { result: RuleResult }) { ); } +function DeviceDetailsCard({ events }: { events: EnrollmentEvent[] }) { + const [expanded, setExpanded] = useState(false); + const [showIpv6, setShowIpv6] = useState>({}); + + // Extract device detail events + const getEventData = (eventType: string): Record | null => { + const evt = events.find(e => e.eventType === eventType); + return evt?.data ?? null; + }; + + // Helper to check if IP is IPv6 + const isIpv6 = (ip: string): boolean => { + // IPv6 contains colons and has multiple segments + // IPv4 is like: 192.168.1.1 + // IPv6 is like: fe80::1234:5678:abcd:ef01 or 2001:db8::1 + if (!ip || typeof ip !== 'string') return false; + + // If it contains a colon and doesn't look like IPv4, it's IPv6 + // Simple check: IPv6 has at least 2 colons (even compressed form) + const colonCount = (ip.match(/:/g) || []).length; + return colonCount >= 2; + }; + + // Helper to split IPs into IPv4 and IPv6 + const splitIpAddresses = (ipAddresses: string | string[]): { ipv4: string[]; ipv6: string[] } => { + // Handle both array and comma-separated string + let ips: string[]; + if (Array.isArray(ipAddresses)) { + ips = ipAddresses; + } else if (typeof ipAddresses === 'string') { + // Split by comma if it's a comma-separated string + ips = ipAddresses.split(',').map(ip => ip.trim()).filter(ip => ip.length > 0); + } else { + ips = []; + } + + const ipv4: string[] = []; + const ipv6: string[] = []; + + for (const ip of ips) { + if (typeof ip === 'string' && ip.trim()) { + if (isIpv6(ip)) { + ipv6.push(ip); + } else { + ipv4.push(ip); + } + } + } + + return { ipv4, ipv6 }; + }; + + const agentStarted = getEventData("agent_started"); + const networkAdapters = getEventData("network_adapters"); + const dnsConfig = getEventData("dns_configuration"); + const proxyConfig = getEventData("proxy_configuration"); + const autopilotProfile = getEventData("autopilot_profile"); + const aadJoinStatus = getEventData("aad_join_status"); + const imeVersion = getEventData("ime_agent_version"); + const bitLockerStatus = getEventData("bitlocker_status"); + const secureBootStatus = getEventData("secureboot_status"); + + // Check if we have any device detail events at all + const hasData = agentStarted || networkAdapters || dnsConfig || proxyConfig || autopilotProfile || + aadJoinStatus || imeVersion || bitLockerStatus || secureBootStatus; + + if (!hasData) return null; + + return ( +
+ + + {expanded && ( +
+ {/* Boot Time */} + {agentStarted?.bootTime && ( + + + {agentStarted.agentVersion && } + + )} + + {/* Network */} + {networkAdapters && ( + + {networkAdapters.adapters ? ( + (networkAdapters.adapters as any[]).map((adapter: any, i: number) => { + const { ipv4, ipv6 } = adapter.ipAddresses ? splitIpAddresses(adapter.ipAddresses) : { ipv4: [], ipv6: [] }; + const hasIpv6 = ipv6.length > 0; + const isIpv6Shown = showIpv6[i] ?? false; + + return ( +
+
{adapter.description || adapter.name || `Adapter ${i + 1}`}
+ + {/* DHCP - first */} + {adapter.dhcpEnabled !== undefined && } + + {/* MAC - second */} + {adapter.macAddress && } + + {/* IPv4 - third */} + {ipv4.length > 0 && } + + {/* IPv6 - fourth (collapsible) */} + {hasIpv6 && ( +
+ + {isIpv6Shown && ( +
+ {ipv6.map((ip, idx) => ( +
{ip}
+ ))} +
+ )} +
+ )} +
+ ); + }) + ) : ( + + )} +
+ )} + + {/* DNS */} + {dnsConfig && ( + + {dnsConfig.dnsEntries && Array.isArray(dnsConfig.dnsEntries) && dnsConfig.dnsEntries.length > 0 ? ( + (dnsConfig.dnsEntries as any[]).map((entry: any, i: number) => ( +
+ +
+ )) + ) : ( +
No DNS data
+ )} +
+ )} + + {/* Proxy */} + {proxyConfig && ( + + + {proxyConfig.proxyServer && } + {proxyConfig.autoConfigUrl && } + {proxyConfig.winHttpProxy && } + + )} + + {/* Autopilot Profile */} + {autopilotProfile && ( + + {autopilotProfile.tenantDomain && } + {autopilotProfile.deploymentProfileName && } + {autopilotProfile.cloudAssignedTenantId && } + {autopilotProfile.oobeConfig && } + + )} + + {/* Identity */} + {aadJoinStatus && ( + + + {aadJoinStatus.tenantId && } + + )} + + {/* IME */} + {imeVersion && ( + + + + )} + + {/* Security */} + {(bitLockerStatus || secureBootStatus) && ( + + {secureBootStatus && ( + + )} + {bitLockerStatus && ( + <> + + {bitLockerStatus.volumes && Array.isArray(bitLockerStatus.volumes) && bitLockerStatus.volumes.length > 0 && ( +
+ {(bitLockerStatus.volumes as any[]).map((vol: any, i: number) => ( +
+ {vol.driveLetter}: {vol.protectionStatus === "1" ? "Protected" : "Not Protected"} + {vol.encryptionMethod && ` (Method: ${vol.encryptionMethod})`} +
+ ))} +
+ )} + + )} +
+ )} +
+ )} +
+ ); +} + +function DetailSection({ title, children }: { title: string; children: React.ReactNode }) { + return ( +
+

{title}

+
{children}
+
+ ); +} + +function DetailRow({ label, value }: { label: string; value: string }) { + return ( +
+ {label} + {value} +
+ ); +} + function StatusBadge({ status, failureReason }: { status: string; failureReason?: string }) { const statusConfig = { InProgress: { color: "bg-blue-100 text-blue-800", text: "In Progress" }, diff --git a/src/Web/autopilot-monitor-web/app/settings/page.tsx b/src/Web/autopilot-monitor-web/app/settings/page.tsx index e3665a04..44394bd4 100644 --- a/src/Web/autopilot-monitor-web/app/settings/page.tsx +++ b/src/Web/autopilot-monitor-web/app/settings/page.tsx @@ -20,11 +20,6 @@ interface TenantConfiguration { // Agent collector settings enablePerformanceCollector: boolean; performanceCollectorIntervalSeconds: number; - enableDownloadProgressCollector: boolean; - downloadProgressCollectorIntervalSeconds: number; - enableCertValidationCollector: boolean; - enableEspUiStateCollector: boolean; - espUiStateCollectorIntervalSeconds: number; } interface TenantAdmin { @@ -63,11 +58,6 @@ export default function SettingsPage() { // Collector settings state const [enablePerformanceCollector, setEnablePerformanceCollector] = useState(false); const [performanceCollectorInterval, setPerformanceCollectorInterval] = useState(60); - const [enableDownloadProgressCollector, setEnableDownloadProgressCollector] = useState(false); - const [downloadProgressCollectorInterval, setDownloadProgressCollectorInterval] = useState(15); - const [enableCertValidationCollector, setEnableCertValidationCollector] = useState(false); - const [enableEspUiStateCollector, setEnableEspUiStateCollector] = useState(false); - const [espUiStateCollectorInterval, setEspUiStateCollectorInterval] = useState(30); // Fetch configuration @@ -106,11 +96,6 @@ export default function SettingsPage() { setSessionTimeoutHours(data.sessionTimeoutHours ?? 5); setEnablePerformanceCollector(data.enablePerformanceCollector ?? false); setPerformanceCollectorInterval(data.performanceCollectorIntervalSeconds ?? 60); - setEnableDownloadProgressCollector(data.enableDownloadProgressCollector ?? false); - setDownloadProgressCollectorInterval(data.downloadProgressCollectorIntervalSeconds ?? 15); - setEnableCertValidationCollector(data.enableCertValidationCollector ?? false); - setEnableEspUiStateCollector(data.enableEspUiStateCollector ?? false); - setEspUiStateCollectorInterval(data.espUiStateCollectorIntervalSeconds ?? 30); } catch (err) { console.error("Error fetching configuration:", err); setError(err instanceof Error ? err.message : "Failed to load configuration"); @@ -176,11 +161,6 @@ export default function SettingsPage() { sessionTimeoutHours, enablePerformanceCollector, performanceCollectorIntervalSeconds: performanceCollectorInterval, - enableDownloadProgressCollector, - downloadProgressCollectorIntervalSeconds: downloadProgressCollectorInterval, - enableCertValidationCollector, - enableEspUiStateCollector, - espUiStateCollectorIntervalSeconds: espUiStateCollectorInterval, }; const token = await getAccessToken(); @@ -225,11 +205,6 @@ export default function SettingsPage() { setSessionTimeoutHours(config.sessionTimeoutHours ?? 5); setEnablePerformanceCollector(config.enablePerformanceCollector ?? false); setPerformanceCollectorInterval(config.performanceCollectorIntervalSeconds ?? 60); - setEnableDownloadProgressCollector(config.enableDownloadProgressCollector ?? false); - setDownloadProgressCollectorInterval(config.downloadProgressCollectorIntervalSeconds ?? 15); - setEnableCertValidationCollector(config.enableCertValidationCollector ?? false); - setEnableEspUiStateCollector(config.enableEspUiStateCollector ?? false); - setEspUiStateCollectorInterval(config.espUiStateCollectorIntervalSeconds ?? 30); setSuccessMessage(null); setError(null); }; @@ -770,92 +745,13 @@ export default function SettingsPage() {
- {/* Download Progress Collector */} -
-
-
-

Download Progress Collector

- High Traffic -
-

Tracks IME content download progress and speed

- {enableDownloadProgressCollector && ( -
- Interval: - setDownloadProgressCollectorInterval(parseInt(e.target.value) || 15)} - className="w-20 px-2 py-1 border border-gray-300 rounded text-sm text-gray-900 focus:ring-2 focus:ring-emerald-500 focus:border-emerald-500" - /> - seconds -
- )} -
- -
- - {/* Certificate Validation Collector */} -
-
-
-

Certificate Validation Collector

- Low Traffic -
-

Validates enrollment endpoint certificates, detects SSL inspection

-
- -
- - {/* ESP UI State Collector */} -
-
-
-

ESP UI State Collector

- Medium Traffic -
-

Tracks ESP blocking apps progress and status text

- {enableEspUiStateCollector && ( -
- Interval: - setEspUiStateCollectorInterval(parseInt(e.target.value) || 30)} - className="w-20 px-2 py-1 border border-gray-300 rounded text-sm text-gray-900 focus:ring-2 focus:ring-emerald-500 focus:border-emerald-500" - /> - seconds -
- )} -
- -
-

- Changes take effect on the next agent config refresh (default: every 5 minutes). Core collectors (EventLog, Registry, Phase Detector, Hello Detector) are always active. + Changes take effect on the next agent config refresh (default: every 5 minutes). Core collectors (Hello Detector) and enrollment tracking are always active.

diff --git a/src/Web/autopilot-monitor-web/components/DownloadProgress.tsx b/src/Web/autopilot-monitor-web/components/DownloadProgress.tsx index c1211180..08bc2e88 100644 --- a/src/Web/autopilot-monitor-web/components/DownloadProgress.tsx +++ b/src/Web/autopilot-monitor-web/components/DownloadProgress.tsx @@ -18,6 +18,7 @@ interface DownloadItem { downloadRateBps: number; lastUpdated: string; isComplete: boolean; + eventData?: Record; } function formatBytes(bytes: number): string { @@ -55,25 +56,45 @@ export default function DownloadProgress({ events }: DownloadProgressProps) { if (!d) continue; const appName = d.app_name ?? d.appName ?? d.file_name ?? d.fileName ?? "Unknown App"; + + // Skip unknown apps + if (appName === "Unknown App") continue; + const bytesDownloaded = parseInt(d.bytes_downloaded ?? d.bytesDownloaded ?? "0", 10); const bytesTotal = parseInt(d.bytes_total ?? d.bytesTotal ?? "0", 10); const downloadRateBps = parseFloat(d.download_rate_bps ?? d.downloadRateBps ?? "0"); const status = d.status ?? ""; + // Determine if complete: explicit status or bytes comparison + const isComplete = status === "completed" || status === "failed" || (bytesTotal > 0 && bytesDownloaded >= bytesTotal); + + // Skip if bytesTotal is too small (< 1 KB) AND not explicitly completed/failed + if (bytesTotal > 0 && bytesTotal < 1024 && status !== "completed" && status !== "failed") { + continue; + } + + // Skip if no download activity yet (0 bytes downloaded, not completed/failed) + // This makes downloads appear dynamically as they start + if (bytesDownloaded === 0 && bytesTotal === 0 && status !== "completed" && status !== "failed") { + continue; + } + downloadMap.set(appName, { appName, bytesDownloaded: isNaN(bytesDownloaded) ? 0 : bytesDownloaded, bytesTotal: isNaN(bytesTotal) ? 0 : bytesTotal, downloadRateBps: isNaN(downloadRateBps) ? 0 : downloadRateBps, lastUpdated: evt.timestamp, - isComplete: status === "completed" || (bytesTotal > 0 && bytesDownloaded >= bytesTotal), + isComplete, + eventData: d, }); } return Array.from(downloadMap.values()).sort((a, b) => { // In-progress first, then completed if (a.isComplete !== b.isComplete) return a.isComplete ? 1 : -1; - return new Date(b.lastUpdated).getTime() - new Date(a.lastUpdated).getTime(); + // Chronological order: oldest first (first download on top, newest at bottom) + return new Date(a.lastUpdated).getTime() - new Date(b.lastUpdated).getTime(); }); }, [events]); @@ -119,9 +140,18 @@ export default function DownloadProgress({ events }: DownloadProgressProps) { : 0; const bytesRemaining = dl.bytesTotal > 0 ? dl.bytesTotal - dl.bytesDownloaded : 0; - return ( + return ; + })} +
} +
+ ); +} + +function DownloadItem({ download: dl, progressPercent, bytesRemaining }: { download: DownloadItem; progressPercent: number; bytesRemaining: number }) { + const [showDetails, setShowDetails] = useState(false); + + return (
@@ -144,6 +174,14 @@ export default function DownloadProgress({ events }: DownloadProgressProps) { {!dl.isComplete && dl.bytesTotal > 0 && ( ETA: {formatEta(bytesRemaining, dl.downloadRateBps)} )} + {dl.eventData && Object.keys(dl.eventData).length > 0 && ( + + )}
@@ -172,10 +210,13 @@ export default function DownloadProgress({ events }: DownloadProgressProps) { {dl.downloadRateBps > 0 && ` at ${formatSpeed(dl.downloadRateBps)}`}
)} + + {/* Event details (expandable) */} + {showDetails && dl.eventData && ( +
+
{JSON.stringify(dl.eventData, null, 2)}
+
+ )}
- ); - })} -
} -
); } diff --git a/src/Web/autopilot-monitor-web/tsconfig.tsbuildinfo b/src/Web/autopilot-monitor-web/tsconfig.tsbuildinfo index f4488ee9..204b874d 100644 --- a/src/Web/autopilot-monitor-web/tsconfig.tsbuildinfo +++ b/src/Web/autopilot-monitor-web/tsconfig.tsbuildinfo @@ -1 +1 @@ -{"fileNames":["./node_modules/typescript/lib/lib.es5.d.ts","./node_modules/typescript/lib/lib.es2015.d.ts","./node_modules/typescript/lib/lib.es2016.d.ts","./node_modules/typescript/lib/lib.es2017.d.ts","./node_modules/typescript/lib/lib.es2018.d.ts","./node_modules/typescript/lib/lib.es2019.d.ts","./node_modules/typescript/lib/lib.es2020.d.ts","./node_modules/typescript/lib/lib.es2021.d.ts","./node_modules/typescript/lib/lib.es2022.d.ts","./node_modules/typescript/lib/lib.es2023.d.ts","./node_modules/typescript/lib/lib.es2024.d.ts","./node_modules/typescript/lib/lib.esnext.d.ts","./node_modules/typescript/lib/lib.dom.d.ts","./node_modules/typescript/lib/lib.dom.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.core.d.ts","./node_modules/typescript/lib/lib.es2015.collection.d.ts","./node_modules/typescript/lib/lib.es2015.generator.d.ts","./node_modules/typescript/lib/lib.es2015.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.promise.d.ts","./node_modules/typescript/lib/lib.es2015.proxy.d.ts","./node_modules/typescript/lib/lib.es2015.reflect.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2016.array.include.d.ts","./node_modules/typescript/lib/lib.es2016.intl.d.ts","./node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","./node_modules/typescript/lib/lib.es2017.date.d.ts","./node_modules/typescript/lib/lib.es2017.object.d.ts","./node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2017.string.d.ts","./node_modules/typescript/lib/lib.es2017.intl.d.ts","./node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","./node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","./node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","./node_modules/typescript/lib/lib.es2018.intl.d.ts","./node_modules/typescript/lib/lib.es2018.promise.d.ts","./node_modules/typescript/lib/lib.es2018.regexp.d.ts","./node_modules/typescript/lib/lib.es2019.array.d.ts","./node_modules/typescript/lib/lib.es2019.object.d.ts","./node_modules/typescript/lib/lib.es2019.string.d.ts","./node_modules/typescript/lib/lib.es2019.symbol.d.ts","./node_modules/typescript/lib/lib.es2019.intl.d.ts","./node_modules/typescript/lib/lib.es2020.bigint.d.ts","./node_modules/typescript/lib/lib.es2020.date.d.ts","./node_modules/typescript/lib/lib.es2020.promise.d.ts","./node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2020.string.d.ts","./node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2020.intl.d.ts","./node_modules/typescript/lib/lib.es2020.number.d.ts","./node_modules/typescript/lib/lib.es2021.promise.d.ts","./node_modules/typescript/lib/lib.es2021.string.d.ts","./node_modules/typescript/lib/lib.es2021.weakref.d.ts","./node_modules/typescript/lib/lib.es2021.intl.d.ts","./node_modules/typescript/lib/lib.es2022.array.d.ts","./node_modules/typescript/lib/lib.es2022.error.d.ts","./node_modules/typescript/lib/lib.es2022.intl.d.ts","./node_modules/typescript/lib/lib.es2022.object.d.ts","./node_modules/typescript/lib/lib.es2022.string.d.ts","./node_modules/typescript/lib/lib.es2022.regexp.d.ts","./node_modules/typescript/lib/lib.es2023.array.d.ts","./node_modules/typescript/lib/lib.es2023.collection.d.ts","./node_modules/typescript/lib/lib.es2023.intl.d.ts","./node_modules/typescript/lib/lib.es2024.arraybuffer.d.ts","./node_modules/typescript/lib/lib.es2024.collection.d.ts","./node_modules/typescript/lib/lib.es2024.object.d.ts","./node_modules/typescript/lib/lib.es2024.promise.d.ts","./node_modules/typescript/lib/lib.es2024.regexp.d.ts","./node_modules/typescript/lib/lib.es2024.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2024.string.d.ts","./node_modules/typescript/lib/lib.esnext.array.d.ts","./node_modules/typescript/lib/lib.esnext.collection.d.ts","./node_modules/typescript/lib/lib.esnext.intl.d.ts","./node_modules/typescript/lib/lib.esnext.disposable.d.ts","./node_modules/typescript/lib/lib.esnext.promise.d.ts","./node_modules/typescript/lib/lib.esnext.decorators.d.ts","./node_modules/typescript/lib/lib.esnext.iterator.d.ts","./node_modules/typescript/lib/lib.esnext.float16.d.ts","./node_modules/typescript/lib/lib.esnext.error.d.ts","./node_modules/typescript/lib/lib.esnext.sharedmemory.d.ts","./node_modules/typescript/lib/lib.decorators.d.ts","./node_modules/typescript/lib/lib.decorators.legacy.d.ts","./.next/types/routes.d.ts","./node_modules/@types/react/global.d.ts","./node_modules/csstype/index.d.ts","./node_modules/@types/react/index.d.ts","./node_modules/next/dist/styled-jsx/types/css.d.ts","./node_modules/next/dist/styled-jsx/types/macro.d.ts","./node_modules/next/dist/styled-jsx/types/style.d.ts","./node_modules/next/dist/styled-jsx/types/global.d.ts","./node_modules/next/dist/styled-jsx/types/index.d.ts","./node_modules/next/dist/shared/lib/amp.d.ts","./node_modules/next/amp.d.ts","./node_modules/next/dist/server/get-page-files.d.ts","./node_modules/@types/node/compatibility/disposable.d.ts","./node_modules/@types/node/compatibility/indexable.d.ts","./node_modules/@types/node/compatibility/iterators.d.ts","./node_modules/@types/node/compatibility/index.d.ts","./node_modules/@types/node/globals.typedarray.d.ts","./node_modules/@types/node/buffer.buffer.d.ts","./node_modules/@types/node/globals.d.ts","./node_modules/@types/node/web-globals/abortcontroller.d.ts","./node_modules/@types/node/web-globals/domexception.d.ts","./node_modules/@types/node/web-globals/events.d.ts","./node_modules/undici-types/header.d.ts","./node_modules/undici-types/readable.d.ts","./node_modules/undici-types/file.d.ts","./node_modules/undici-types/fetch.d.ts","./node_modules/undici-types/formdata.d.ts","./node_modules/undici-types/connector.d.ts","./node_modules/undici-types/client.d.ts","./node_modules/undici-types/errors.d.ts","./node_modules/undici-types/dispatcher.d.ts","./node_modules/undici-types/global-dispatcher.d.ts","./node_modules/undici-types/global-origin.d.ts","./node_modules/undici-types/pool-stats.d.ts","./node_modules/undici-types/pool.d.ts","./node_modules/undici-types/handlers.d.ts","./node_modules/undici-types/balanced-pool.d.ts","./node_modules/undici-types/agent.d.ts","./node_modules/undici-types/mock-interceptor.d.ts","./node_modules/undici-types/mock-agent.d.ts","./node_modules/undici-types/mock-client.d.ts","./node_modules/undici-types/mock-pool.d.ts","./node_modules/undici-types/mock-errors.d.ts","./node_modules/undici-types/proxy-agent.d.ts","./node_modules/undici-types/env-http-proxy-agent.d.ts","./node_modules/undici-types/retry-handler.d.ts","./node_modules/undici-types/retry-agent.d.ts","./node_modules/undici-types/api.d.ts","./node_modules/undici-types/interceptors.d.ts","./node_modules/undici-types/util.d.ts","./node_modules/undici-types/cookies.d.ts","./node_modules/undici-types/patch.d.ts","./node_modules/undici-types/websocket.d.ts","./node_modules/undici-types/eventsource.d.ts","./node_modules/undici-types/filereader.d.ts","./node_modules/undici-types/diagnostics-channel.d.ts","./node_modules/undici-types/content-type.d.ts","./node_modules/undici-types/cache.d.ts","./node_modules/undici-types/index.d.ts","./node_modules/@types/node/web-globals/fetch.d.ts","./node_modules/@types/node/web-globals/navigator.d.ts","./node_modules/@types/node/web-globals/storage.d.ts","./node_modules/@types/node/assert.d.ts","./node_modules/@types/node/assert/strict.d.ts","./node_modules/@types/node/async_hooks.d.ts","./node_modules/@types/node/buffer.d.ts","./node_modules/@types/node/child_process.d.ts","./node_modules/@types/node/cluster.d.ts","./node_modules/@types/node/console.d.ts","./node_modules/@types/node/constants.d.ts","./node_modules/@types/node/crypto.d.ts","./node_modules/@types/node/dgram.d.ts","./node_modules/@types/node/diagnostics_channel.d.ts","./node_modules/@types/node/dns.d.ts","./node_modules/@types/node/dns/promises.d.ts","./node_modules/@types/node/domain.d.ts","./node_modules/@types/node/events.d.ts","./node_modules/@types/node/fs.d.ts","./node_modules/@types/node/fs/promises.d.ts","./node_modules/@types/node/http.d.ts","./node_modules/@types/node/http2.d.ts","./node_modules/@types/node/https.d.ts","./node_modules/@types/node/inspector.d.ts","./node_modules/@types/node/inspector.generated.d.ts","./node_modules/@types/node/module.d.ts","./node_modules/@types/node/net.d.ts","./node_modules/@types/node/os.d.ts","./node_modules/@types/node/path.d.ts","./node_modules/@types/node/perf_hooks.d.ts","./node_modules/@types/node/process.d.ts","./node_modules/@types/node/punycode.d.ts","./node_modules/@types/node/querystring.d.ts","./node_modules/@types/node/readline.d.ts","./node_modules/@types/node/readline/promises.d.ts","./node_modules/@types/node/repl.d.ts","./node_modules/@types/node/sea.d.ts","./node_modules/@types/node/sqlite.d.ts","./node_modules/@types/node/stream.d.ts","./node_modules/@types/node/stream/promises.d.ts","./node_modules/@types/node/stream/consumers.d.ts","./node_modules/@types/node/stream/web.d.ts","./node_modules/@types/node/string_decoder.d.ts","./node_modules/@types/node/test.d.ts","./node_modules/@types/node/timers.d.ts","./node_modules/@types/node/timers/promises.d.ts","./node_modules/@types/node/tls.d.ts","./node_modules/@types/node/trace_events.d.ts","./node_modules/@types/node/tty.d.ts","./node_modules/@types/node/url.d.ts","./node_modules/@types/node/util.d.ts","./node_modules/@types/node/v8.d.ts","./node_modules/@types/node/vm.d.ts","./node_modules/@types/node/wasi.d.ts","./node_modules/@types/node/worker_threads.d.ts","./node_modules/@types/node/zlib.d.ts","./node_modules/@types/node/index.d.ts","./node_modules/@types/react/canary.d.ts","./node_modules/@types/react/experimental.d.ts","./node_modules/@types/react-dom/index.d.ts","./node_modules/@types/react-dom/canary.d.ts","./node_modules/@types/react-dom/experimental.d.ts","./node_modules/next/dist/lib/fallback.d.ts","./node_modules/next/dist/compiled/webpack/webpack.d.ts","./node_modules/next/dist/server/config.d.ts","./node_modules/next/dist/lib/load-custom-routes.d.ts","./node_modules/next/dist/shared/lib/image-config.d.ts","./node_modules/next/dist/build/webpack/plugins/subresource-integrity-plugin.d.ts","./node_modules/next/dist/server/body-streams.d.ts","./node_modules/next/dist/server/lib/cache-control.d.ts","./node_modules/next/dist/lib/setup-exception-listeners.d.ts","./node_modules/next/dist/lib/worker.d.ts","./node_modules/next/dist/lib/constants.d.ts","./node_modules/next/dist/client/components/app-router-headers.d.ts","./node_modules/next/dist/build/rendering-mode.d.ts","./node_modules/next/dist/server/lib/router-utils/build-prefetch-segment-data-route.d.ts","./node_modules/next/dist/server/require-hook.d.ts","./node_modules/next/dist/server/lib/experimental/ppr.d.ts","./node_modules/next/dist/build/webpack/plugins/app-build-manifest-plugin.d.ts","./node_modules/next/dist/lib/page-types.d.ts","./node_modules/next/dist/build/segment-config/app/app-segment-config.d.ts","./node_modules/next/dist/build/segment-config/pages/pages-segment-config.d.ts","./node_modules/next/dist/build/analysis/get-page-static-info.d.ts","./node_modules/next/dist/build/webpack/loaders/get-module-build-info.d.ts","./node_modules/next/dist/build/webpack/plugins/middleware-plugin.d.ts","./node_modules/next/dist/server/node-polyfill-crypto.d.ts","./node_modules/next/dist/server/node-environment-baseline.d.ts","./node_modules/next/dist/server/node-environment-extensions/error-inspect.d.ts","./node_modules/next/dist/server/node-environment-extensions/random.d.ts","./node_modules/next/dist/server/node-environment-extensions/date.d.ts","./node_modules/next/dist/server/node-environment-extensions/web-crypto.d.ts","./node_modules/next/dist/server/node-environment-extensions/node-crypto.d.ts","./node_modules/next/dist/server/node-environment.d.ts","./node_modules/next/dist/build/page-extensions-type.d.ts","./node_modules/next/dist/build/webpack/plugins/flight-manifest-plugin.d.ts","./node_modules/next/dist/server/instrumentation/types.d.ts","./node_modules/next/dist/lib/coalesced-function.d.ts","./node_modules/next/dist/shared/lib/router/utils/middleware-route-matcher.d.ts","./node_modules/next/dist/server/lib/router-utils/types.d.ts","./node_modules/next/dist/shared/lib/modern-browserslist-target.d.ts","./node_modules/next/dist/shared/lib/constants.d.ts","./node_modules/next/dist/trace/types.d.ts","./node_modules/next/dist/trace/trace.d.ts","./node_modules/next/dist/trace/shared.d.ts","./node_modules/next/dist/trace/index.d.ts","./node_modules/next/dist/build/load-jsconfig.d.ts","./node_modules/@next/env/dist/index.d.ts","./node_modules/next/dist/build/webpack/plugins/telemetry-plugin/use-cache-tracker-utils.d.ts","./node_modules/next/dist/build/webpack/plugins/telemetry-plugin/telemetry-plugin.d.ts","./node_modules/next/dist/telemetry/storage.d.ts","./node_modules/next/dist/build/build-context.d.ts","./node_modules/next/dist/shared/lib/bloom-filter.d.ts","./node_modules/next/dist/build/webpack-config.d.ts","./node_modules/next/dist/server/route-kind.d.ts","./node_modules/next/dist/server/route-definitions/route-definition.d.ts","./node_modules/next/dist/build/swc/generated-native.d.ts","./node_modules/next/dist/build/swc/types.d.ts","./node_modules/next/dist/server/dev/parse-version-info.d.ts","./node_modules/next/dist/next-devtools/shared/types.d.ts","./node_modules/next/dist/server/dev/dev-indicator-server-state.d.ts","./node_modules/next/dist/server/lib/parse-stack.d.ts","./node_modules/next/dist/next-devtools/server/shared.d.ts","./node_modules/next/dist/next-devtools/shared/stack-frame.d.ts","./node_modules/next/dist/next-devtools/dev-overlay/utils/get-error-by-type.d.ts","./node_modules/@types/react/jsx-runtime.d.ts","./node_modules/next/dist/next-devtools/dev-overlay/container/runtime-error/render-error.d.ts","./node_modules/next/dist/next-devtools/dev-overlay/shared.d.ts","./node_modules/next/dist/server/dev/hot-reloader-types.d.ts","./node_modules/next/dist/server/lib/cache-handlers/types.d.ts","./node_modules/next/dist/server/response-cache/types.d.ts","./node_modules/next/dist/server/resume-data-cache/cache-store.d.ts","./node_modules/next/dist/server/resume-data-cache/resume-data-cache.d.ts","./node_modules/next/dist/server/render-result.d.ts","./node_modules/next/dist/server/lib/i18n-provider.d.ts","./node_modules/next/dist/server/web/next-url.d.ts","./node_modules/next/dist/compiled/@edge-runtime/cookies/index.d.ts","./node_modules/next/dist/server/web/spec-extension/cookies.d.ts","./node_modules/next/dist/server/web/spec-extension/request.d.ts","./node_modules/next/dist/server/after/builtin-request-context.d.ts","./node_modules/next/dist/server/web/spec-extension/fetch-event.d.ts","./node_modules/next/dist/server/web/spec-extension/response.d.ts","./node_modules/next/dist/build/segment-config/middleware/middleware-config.d.ts","./node_modules/next/dist/server/web/types.d.ts","./node_modules/next/dist/build/webpack/plugins/pages-manifest-plugin.d.ts","./node_modules/next/dist/shared/lib/router/utils/parse-url.d.ts","./node_modules/next/dist/server/base-http/node.d.ts","./node_modules/next/dist/build/webpack/plugins/next-font-manifest-plugin.d.ts","./node_modules/next/dist/server/route-definitions/locale-route-definition.d.ts","./node_modules/next/dist/server/route-definitions/pages-route-definition.d.ts","./node_modules/next/dist/shared/lib/mitt.d.ts","./node_modules/next/dist/client/with-router.d.ts","./node_modules/next/dist/client/router.d.ts","./node_modules/next/dist/client/route-loader.d.ts","./node_modules/next/dist/client/page-loader.d.ts","./node_modules/next/dist/shared/lib/router/router.d.ts","./node_modules/next/dist/shared/lib/router-context.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/loadable-context.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/loadable.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/image-config-context.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/hooks-client-context.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/head-manager-context.shared-runtime.d.ts","./node_modules/next/dist/server/route-definitions/app-page-route-definition.d.ts","./node_modules/next/dist/build/webpack/loaders/metadata/types.d.ts","./node_modules/next/dist/build/webpack/loaders/next-app-loader/index.d.ts","./node_modules/next/dist/server/lib/app-dir-module.d.ts","./node_modules/next/dist/server/web/spec-extension/adapters/request-cookies.d.ts","./node_modules/next/dist/server/async-storage/draft-mode-provider.d.ts","./node_modules/next/dist/server/web/spec-extension/adapters/headers.d.ts","./node_modules/next/dist/server/app-render/cache-signal.d.ts","./node_modules/next/dist/server/app-render/dynamic-rendering.d.ts","./node_modules/next/dist/server/request/fallback-params.d.ts","./node_modules/next/dist/server/app-render/work-unit-async-storage-instance.d.ts","./node_modules/next/dist/server/response-cache/index.d.ts","./node_modules/next/dist/server/lib/lazy-result.d.ts","./node_modules/next/dist/server/lib/implicit-tags.d.ts","./node_modules/next/dist/server/app-render/work-unit-async-storage.external.d.ts","./node_modules/next/dist/shared/lib/deep-readonly.d.ts","./node_modules/next/dist/shared/lib/router/utils/parse-relative-url.d.ts","./node_modules/next/dist/server/app-render/app-render.d.ts","./node_modules/next/dist/shared/lib/server-inserted-html.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/amp-context.shared-runtime.d.ts","./node_modules/next/dist/server/route-modules/app-page/vendored/contexts/entrypoints.d.ts","./node_modules/next/dist/server/route-modules/app-page/module.compiled.d.ts","./node_modules/next/dist/client/components/error-boundary.d.ts","./node_modules/next/dist/client/components/layout-router.d.ts","./node_modules/next/dist/client/components/render-from-template-context.d.ts","./node_modules/next/dist/server/app-render/action-async-storage-instance.d.ts","./node_modules/next/dist/server/app-render/action-async-storage.external.d.ts","./node_modules/next/dist/client/components/client-page.d.ts","./node_modules/next/dist/client/components/client-segment.d.ts","./node_modules/next/dist/server/request/search-params.d.ts","./node_modules/next/dist/client/components/hooks-server-context.d.ts","./node_modules/next/dist/client/components/http-access-fallback/error-boundary.d.ts","./node_modules/next/dist/lib/metadata/types/alternative-urls-types.d.ts","./node_modules/next/dist/lib/metadata/types/extra-types.d.ts","./node_modules/next/dist/lib/metadata/types/metadata-types.d.ts","./node_modules/next/dist/lib/metadata/types/manifest-types.d.ts","./node_modules/next/dist/lib/metadata/types/opengraph-types.d.ts","./node_modules/next/dist/lib/metadata/types/twitter-types.d.ts","./node_modules/next/dist/lib/metadata/types/metadata-interface.d.ts","./node_modules/next/dist/lib/metadata/types/resolvers.d.ts","./node_modules/next/dist/lib/metadata/types/icons.d.ts","./node_modules/next/dist/lib/metadata/resolve-metadata.d.ts","./node_modules/next/dist/lib/metadata/metadata.d.ts","./node_modules/next/dist/lib/framework/boundary-components.d.ts","./node_modules/next/dist/server/app-render/rsc/preloads.d.ts","./node_modules/next/dist/server/app-render/rsc/postpone.d.ts","./node_modules/next/dist/server/app-render/rsc/taint.d.ts","./node_modules/next/dist/shared/lib/segment-cache/segment-value-encoding.d.ts","./node_modules/next/dist/server/app-render/collect-segment-data.d.ts","./node_modules/next/dist/next-devtools/userspace/app/segment-explorer-node.d.ts","./node_modules/next/dist/server/app-render/entry-base.d.ts","./node_modules/next/dist/build/templates/app-page.d.ts","./node_modules/@types/react/jsx-dev-runtime.d.ts","./node_modules/@types/react/compiler-runtime.d.ts","./node_modules/next/dist/server/route-modules/app-page/vendored/rsc/entrypoints.d.ts","./node_modules/@types/react-dom/client.d.ts","./node_modules/@types/react-dom/static.d.ts","./node_modules/@types/react-dom/server.d.ts","./node_modules/next/dist/server/route-modules/app-page/vendored/ssr/entrypoints.d.ts","./node_modules/next/dist/server/route-modules/app-page/module.d.ts","./node_modules/next/dist/server/web/adapter.d.ts","./node_modules/next/dist/server/use-cache/cache-life.d.ts","./node_modules/next/dist/server/app-render/types.d.ts","./node_modules/next/dist/client/components/router-reducer/router-reducer-types.d.ts","./node_modules/next/dist/client/flight-data-helpers.d.ts","./node_modules/next/dist/client/components/router-reducer/fetch-server-response.d.ts","./node_modules/next/dist/shared/lib/app-router-context.shared-runtime.d.ts","./node_modules/next/dist/server/route-modules/pages/vendored/contexts/entrypoints.d.ts","./node_modules/next/dist/server/route-modules/pages/module.compiled.d.ts","./node_modules/next/dist/build/templates/pages.d.ts","./node_modules/next/dist/server/route-modules/pages/module.d.ts","./node_modules/next/dist/next-devtools/userspace/pages/pages-dev-overlay-setup.d.ts","./node_modules/next/dist/server/render.d.ts","./node_modules/next/dist/server/route-definitions/pages-api-route-definition.d.ts","./node_modules/next/dist/server/route-matches/pages-api-route-match.d.ts","./node_modules/next/dist/server/route-matchers/route-matcher.d.ts","./node_modules/next/dist/server/route-matcher-providers/route-matcher-provider.d.ts","./node_modules/next/dist/server/route-matcher-managers/route-matcher-manager.d.ts","./node_modules/next/dist/server/normalizers/normalizer.d.ts","./node_modules/next/dist/server/normalizers/locale-route-normalizer.d.ts","./node_modules/next/dist/server/normalizers/request/pathname-normalizer.d.ts","./node_modules/next/dist/server/normalizers/request/suffix.d.ts","./node_modules/next/dist/server/normalizers/request/rsc.d.ts","./node_modules/next/dist/server/normalizers/request/prefetch-rsc.d.ts","./node_modules/next/dist/server/normalizers/request/next-data.d.ts","./node_modules/next/dist/server/normalizers/request/segment-prefix-rsc.d.ts","./node_modules/next/dist/build/static-paths/types.d.ts","./node_modules/next/dist/server/base-server.d.ts","./node_modules/next/dist/server/lib/async-callback-set.d.ts","./node_modules/next/dist/shared/lib/router/utils/route-regex.d.ts","./node_modules/next/dist/shared/lib/router/utils/route-matcher.d.ts","./node_modules/sharp/lib/index.d.ts","./node_modules/next/dist/server/image-optimizer.d.ts","./node_modules/next/dist/server/next-server.d.ts","./node_modules/next/dist/server/lib/types.d.ts","./node_modules/next/dist/server/lib/lru-cache.d.ts","./node_modules/next/dist/server/lib/dev-bundler-service.d.ts","./node_modules/next/dist/server/dev/static-paths-worker.d.ts","./node_modules/next/dist/server/dev/next-dev-server.d.ts","./node_modules/next/dist/server/next.d.ts","./node_modules/next/dist/server/lib/render-server.d.ts","./node_modules/next/dist/server/lib/router-server.d.ts","./node_modules/next/dist/shared/lib/router/utils/path-match.d.ts","./node_modules/next/dist/server/lib/router-utils/filesystem.d.ts","./node_modules/next/dist/server/lib/router-utils/setup-dev-bundler.d.ts","./node_modules/next/dist/server/lib/router-utils/router-server-context.d.ts","./node_modules/next/dist/server/route-modules/route-module.d.ts","./node_modules/next/dist/server/load-components.d.ts","./node_modules/next/dist/server/route-definitions/app-route-route-definition.d.ts","./node_modules/next/dist/server/async-storage/work-store.d.ts","./node_modules/next/dist/server/web/http.d.ts","./node_modules/next/dist/server/route-modules/app-route/shared-modules.d.ts","./node_modules/next/dist/client/components/redirect-status-code.d.ts","./node_modules/next/dist/client/components/redirect-error.d.ts","./node_modules/next/dist/build/templates/app-route.d.ts","./node_modules/next/dist/server/route-modules/app-route/module.d.ts","./node_modules/next/dist/server/route-modules/app-route/module.compiled.d.ts","./node_modules/next/dist/build/segment-config/app/app-segments.d.ts","./node_modules/next/dist/build/utils.d.ts","./node_modules/next/dist/build/turborepo-access-trace/types.d.ts","./node_modules/next/dist/build/turborepo-access-trace/result.d.ts","./node_modules/next/dist/build/turborepo-access-trace/helpers.d.ts","./node_modules/next/dist/build/turborepo-access-trace/index.d.ts","./node_modules/next/dist/export/routes/types.d.ts","./node_modules/next/dist/export/types.d.ts","./node_modules/next/dist/export/worker.d.ts","./node_modules/next/dist/build/worker.d.ts","./node_modules/next/dist/build/index.d.ts","./node_modules/next/dist/server/lib/incremental-cache/index.d.ts","./node_modules/next/dist/server/after/after.d.ts","./node_modules/next/dist/server/after/after-context.d.ts","./node_modules/next/dist/server/app-render/work-async-storage-instance.d.ts","./node_modules/next/dist/server/app-render/work-async-storage.external.d.ts","./node_modules/next/dist/server/request/params.d.ts","./node_modules/next/dist/server/route-matches/route-match.d.ts","./node_modules/next/dist/server/request-meta.d.ts","./node_modules/next/dist/cli/next-test.d.ts","./node_modules/next/dist/server/config-shared.d.ts","./node_modules/next/dist/server/base-http/index.d.ts","./node_modules/next/dist/server/api-utils/index.d.ts","./node_modules/next/dist/types.d.ts","./node_modules/next/dist/shared/lib/html-context.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/utils.d.ts","./node_modules/next/dist/pages/_app.d.ts","./node_modules/next/app.d.ts","./node_modules/next/dist/server/web/spec-extension/unstable-cache.d.ts","./node_modules/next/dist/server/web/spec-extension/revalidate.d.ts","./node_modules/next/dist/server/web/spec-extension/unstable-no-store.d.ts","./node_modules/next/dist/server/use-cache/cache-tag.d.ts","./node_modules/next/cache.d.ts","./node_modules/next/dist/shared/lib/runtime-config.external.d.ts","./node_modules/next/config.d.ts","./node_modules/next/dist/pages/_document.d.ts","./node_modules/next/document.d.ts","./node_modules/next/dist/shared/lib/dynamic.d.ts","./node_modules/next/dynamic.d.ts","./node_modules/next/dist/pages/_error.d.ts","./node_modules/next/error.d.ts","./node_modules/next/dist/shared/lib/head.d.ts","./node_modules/next/head.d.ts","./node_modules/next/dist/server/request/cookies.d.ts","./node_modules/next/dist/server/request/headers.d.ts","./node_modules/next/dist/server/request/draft-mode.d.ts","./node_modules/next/headers.d.ts","./node_modules/next/dist/shared/lib/get-img-props.d.ts","./node_modules/next/dist/client/image-component.d.ts","./node_modules/next/dist/shared/lib/image-external.d.ts","./node_modules/next/image.d.ts","./node_modules/next/dist/client/link.d.ts","./node_modules/next/link.d.ts","./node_modules/next/dist/client/components/redirect.d.ts","./node_modules/next/dist/client/components/not-found.d.ts","./node_modules/next/dist/client/components/forbidden.d.ts","./node_modules/next/dist/client/components/unauthorized.d.ts","./node_modules/next/dist/client/components/unstable-rethrow.server.d.ts","./node_modules/next/dist/client/components/unstable-rethrow.d.ts","./node_modules/next/dist/client/components/navigation.react-server.d.ts","./node_modules/next/dist/client/components/unrecognized-action-error.d.ts","./node_modules/next/dist/client/components/navigation.d.ts","./node_modules/next/navigation.d.ts","./node_modules/next/router.d.ts","./node_modules/next/dist/client/script.d.ts","./node_modules/next/script.d.ts","./node_modules/next/dist/server/web/spec-extension/user-agent.d.ts","./node_modules/next/dist/compiled/@edge-runtime/primitives/url.d.ts","./node_modules/next/dist/server/web/spec-extension/image-response.d.ts","./node_modules/next/dist/compiled/@vercel/og/satori/index.d.ts","./node_modules/next/dist/compiled/@vercel/og/emoji/index.d.ts","./node_modules/next/dist/compiled/@vercel/og/types.d.ts","./node_modules/next/dist/server/after/index.d.ts","./node_modules/next/dist/server/request/root-params.d.ts","./node_modules/next/dist/server/request/connection.d.ts","./node_modules/next/server.d.ts","./node_modules/next/types/global.d.ts","./node_modules/next/types/compiled.d.ts","./node_modules/next/types.d.ts","./node_modules/next/index.d.ts","./node_modules/next/image-types/global.d.ts","./next-env.d.ts","./next.config.ts","./node_modules/source-map-js/source-map.d.ts","./node_modules/postcss/lib/previous-map.d.ts","./node_modules/postcss/lib/input.d.ts","./node_modules/postcss/lib/css-syntax-error.d.ts","./node_modules/postcss/lib/declaration.d.ts","./node_modules/postcss/lib/root.d.ts","./node_modules/postcss/lib/warning.d.ts","./node_modules/postcss/lib/lazy-result.d.ts","./node_modules/postcss/lib/no-work-result.d.ts","./node_modules/postcss/lib/processor.d.ts","./node_modules/postcss/lib/result.d.ts","./node_modules/postcss/lib/document.d.ts","./node_modules/postcss/lib/rule.d.ts","./node_modules/postcss/lib/node.d.ts","./node_modules/postcss/lib/comment.d.ts","./node_modules/postcss/lib/container.d.ts","./node_modules/postcss/lib/at-rule.d.ts","./node_modules/postcss/lib/list.d.ts","./node_modules/postcss/lib/postcss.d.ts","./node_modules/postcss/lib/postcss.d.mts","./node_modules/tailwindcss/types/generated/corepluginlist.d.ts","./node_modules/tailwindcss/types/generated/colors.d.ts","./node_modules/tailwindcss/types/config.d.ts","./node_modules/tailwindcss/types/index.d.ts","./tailwind.config.ts","./lib/config.ts","./node_modules/@azure/msal-common/dist/account/tokenclaims.d.ts","./node_modules/@azure/msal-common/dist/account/authtoken.d.ts","./node_modules/@azure/msal-common/dist/authority/authoritytype.d.ts","./node_modules/@azure/msal-common/dist/authority/openidconfigresponse.d.ts","./node_modules/@azure/msal-common/dist/url/iuri.d.ts","./node_modules/@azure/msal-common/dist/network/networkresponse.d.ts","./node_modules/@azure/msal-common/dist/network/inetworkmodule.d.ts","./node_modules/@azure/msal-common/dist/authority/protocolmode.d.ts","./node_modules/@azure/msal-common/dist/utils/constants.d.ts","./node_modules/@azure/msal-common/dist/logger/logger.d.ts","./node_modules/@azure/msal-common/dist/authority/oidcoptions.d.ts","./node_modules/@azure/msal-common/dist/authority/azureregion.d.ts","./node_modules/@azure/msal-common/dist/authority/azureregionconfiguration.d.ts","./node_modules/@azure/msal-common/dist/authority/clouddiscoverymetadata.d.ts","./node_modules/@azure/msal-common/dist/authority/cloudinstancediscoveryresponse.d.ts","./node_modules/@azure/msal-common/dist/authority/authorityoptions.d.ts","./node_modules/@azure/msal-common/dist/cache/entities/credentialentity.d.ts","./node_modules/@azure/msal-common/dist/cache/entities/idtokenentity.d.ts","./node_modules/@azure/msal-common/dist/cache/entities/accesstokenentity.d.ts","./node_modules/@azure/msal-common/dist/cache/entities/refreshtokenentity.d.ts","./node_modules/@azure/msal-common/dist/cache/entities/appmetadataentity.d.ts","./node_modules/@azure/msal-common/dist/cache/entities/cacherecord.d.ts","./node_modules/@azure/msal-common/dist/account/accountinfo.d.ts","./node_modules/@azure/msal-common/dist/cache/entities/servertelemetryentity.d.ts","./node_modules/@azure/msal-common/dist/cache/entities/throttlingentity.d.ts","./node_modules/@azure/msal-common/dist/cache/entities/authoritymetadataentity.d.ts","./node_modules/@azure/msal-common/dist/request/storeincache.d.ts","./node_modules/@azure/msal-common/dist/telemetry/performance/performanceevent.d.ts","./node_modules/@azure/msal-common/dist/telemetry/performance/iperformancemeasurement.d.ts","./node_modules/@azure/msal-common/dist/telemetry/performance/iperformanceclient.d.ts","./node_modules/@azure/msal-common/dist/cache/cachemanager.d.ts","./node_modules/@azure/msal-common/dist/telemetry/server/servertelemetryrequest.d.ts","./node_modules/@azure/msal-common/dist/authority/regiondiscoverymetadata.d.ts","./node_modules/@azure/msal-common/dist/telemetry/server/servertelemetrymanager.d.ts","./node_modules/@azure/msal-common/dist/cache/interface/iserializabletokencache.d.ts","./node_modules/@azure/msal-common/dist/cache/persistence/tokencachecontext.d.ts","./node_modules/@azure/msal-common/dist/cache/interface/icacheplugin.d.ts","./node_modules/@azure/msal-common/dist/account/clientcredentials.d.ts","./node_modules/@azure/msal-common/dist/config/clientconfiguration.d.ts","./node_modules/@azure/msal-common/dist/utils/msaltypes.d.ts","./node_modules/@azure/msal-common/dist/crypto/joseheader.d.ts","./node_modules/@azure/msal-common/dist/crypto/signedhttprequest.d.ts","./node_modules/@azure/msal-common/dist/request/baseauthrequest.d.ts","./node_modules/@azure/msal-common/dist/crypto/icrypto.d.ts","./node_modules/@azure/msal-common/dist/cache/entities/accountentity.d.ts","./node_modules/@azure/msal-common/dist/request/scopeset.d.ts","./node_modules/@azure/msal-common/dist/cache/utils/cachetypes.d.ts","./node_modules/@azure/msal-common/dist/cache/interface/icachemanager.d.ts","./node_modules/@azure/msal-common/dist/authority/authority.d.ts","./node_modules/@azure/msal-common/dist/authority/authorityfactory.d.ts","./node_modules/@azure/msal-common/dist/cache/utils/cachehelpers.d.ts","./node_modules/@azure/msal-common/dist/utils/timeutils.d.ts","./node_modules/@azure/msal-common/dist/response/serverauthorizationcoderesponse.d.ts","./node_modules/@azure/msal-common/dist/utils/urlutils.d.ts","./node_modules/@azure/msal-common/dist/constants/aadserverparamkeys.d.ts","./node_modules/@azure/msal-common/dist/response/serverauthorizationtokenresponse.d.ts","./node_modules/@azure/msal-common/dist/network/requestthumbprint.d.ts","./node_modules/@azure/msal-common/dist/account/ccscredential.d.ts","./node_modules/@azure/msal-common/dist/client/baseclient.d.ts","./node_modules/@azure/msal-common/dist/request/commonauthorizationurlrequest.d.ts","./node_modules/@azure/msal-common/dist/request/commonauthorizationcoderequest.d.ts","./node_modules/@azure/msal-common/dist/response/authenticationresult.d.ts","./node_modules/@azure/msal-common/dist/request/commonendsessionrequest.d.ts","./node_modules/@azure/msal-common/dist/response/authorizationcodepayload.d.ts","./node_modules/@azure/msal-common/dist/client/authorizationcodeclient.d.ts","./node_modules/@azure/msal-common/dist/request/commonrefreshtokenrequest.d.ts","./node_modules/@azure/msal-common/dist/request/commonsilentflowrequest.d.ts","./node_modules/@azure/msal-common/dist/client/refreshtokenclient.d.ts","./node_modules/@azure/msal-common/dist/client/silentflowclient.d.ts","./node_modules/@azure/msal-common/dist/account/clientinfo.d.ts","./node_modules/@azure/msal-common/dist/network/throttlingutils.d.ts","./node_modules/@azure/msal-common/dist/url/urlstring.d.ts","./node_modules/@azure/msal-common/dist/request/requestparameterbuilder.d.ts","./node_modules/@azure/msal-common/dist/utils/protocolutils.d.ts","./node_modules/@azure/msal-common/dist/response/responsehandler.d.ts","./node_modules/@azure/msal-common/dist/request/authenticationheaderparser.d.ts","./node_modules/@azure/msal-common/dist/error/autherrorcodes.d.ts","./node_modules/@azure/msal-common/dist/error/autherror.d.ts","./node_modules/@azure/msal-common/dist/error/interactionrequiredautherrorcodes.d.ts","./node_modules/@azure/msal-common/dist/error/interactionrequiredautherror.d.ts","./node_modules/@azure/msal-common/dist/error/servererror.d.ts","./node_modules/@azure/msal-common/dist/error/networkerror.d.ts","./node_modules/@azure/msal-common/dist/error/cacheerrorcodes.d.ts","./node_modules/@azure/msal-common/dist/error/cacheerror.d.ts","./node_modules/@azure/msal-common/dist/error/clientautherrorcodes.d.ts","./node_modules/@azure/msal-common/dist/error/clientautherror.d.ts","./node_modules/@azure/msal-common/dist/error/clientconfigurationerrorcodes.d.ts","./node_modules/@azure/msal-common/dist/error/clientconfigurationerror.d.ts","./node_modules/@azure/msal-common/dist/utils/stringutils.d.ts","./node_modules/@azure/msal-common/dist/utils/functionwrappers.d.ts","./node_modules/@azure/msal-common/dist/packagemetadata.d.ts","./node_modules/@azure/msal-common/dist/exports-common.d.ts","./node_modules/@azure/msal-common/dist/response/externaltokenresponse.d.ts","./node_modules/@azure/msal-common/dist/telemetry/performance/performanceclient.d.ts","./node_modules/@azure/msal-common/dist/telemetry/performance/stubperformanceclient.d.ts","./node_modules/@azure/msal-common/dist/crypto/poptokengenerator.d.ts","./node_modules/@azure/msal-common/dist/exports-browser-only.d.ts","./node_modules/@azure/msal-common/dist/index-browser.d.ts","./node_modules/@azure/msal-browser/dist/request/popupwindowattributes.d.ts","./node_modules/@azure/msal-browser/dist/request/popuprequest.d.ts","./node_modules/@azure/msal-browser/dist/request/redirectrequest.d.ts","./node_modules/@azure/msal-browser/dist/utils/browserconstants.d.ts","./node_modules/@azure/msal-browser/dist/navigation/navigationoptions.d.ts","./node_modules/@azure/msal-browser/dist/navigation/inavigationclient.d.ts","./node_modules/@azure/msal-browser/dist/config/configuration.d.ts","./node_modules/@azure/msal-browser/dist/utils/browserutils.d.ts","./node_modules/@azure/msal-browser/dist/request/silentrequest.d.ts","./node_modules/@azure/msal-browser/dist/cache/iwindowstorage.d.ts","./node_modules/@azure/msal-browser/dist/cache/memorystorage.d.ts","./node_modules/@azure/msal-browser/dist/broker/nativebroker/nativerequest.d.ts","./node_modules/@azure/msal-browser/dist/response/authenticationresult.d.ts","./node_modules/@azure/msal-browser/dist/request/ssosilentrequest.d.ts","./node_modules/@azure/msal-browser/dist/cache/cookiestorage.d.ts","./node_modules/@azure/msal-browser/dist/cache/browsercachemanager.d.ts","./node_modules/@azure/msal-browser/dist/cache/tokencache.d.ts","./node_modules/@azure/msal-browser/dist/cache/itokencache.d.ts","./node_modules/@azure/msal-browser/dist/request/authorizationcoderequest.d.ts","./node_modules/@azure/msal-browser/dist/request/endsessionrequest.d.ts","./node_modules/@azure/msal-browser/dist/request/endsessionpopuprequest.d.ts","./node_modules/@azure/msal-browser/dist/event/eventtype.d.ts","./node_modules/@azure/msal-browser/dist/event/eventmessage.d.ts","./node_modules/@azure/msal-browser/dist/request/clearcacherequest.d.ts","./node_modules/@azure/msal-browser/dist/request/initializeapplicationrequest.d.ts","./node_modules/@azure/msal-browser/dist/app/ipublicclientapplication.d.ts","./node_modules/@azure/msal-browser/dist/controllers/icontroller.d.ts","./node_modules/@azure/msal-browser/dist/app/publicclientapplication.d.ts","./node_modules/@azure/msal-browser/dist/app/publicclientnext.d.ts","./node_modules/@azure/msal-browser/dist/error/browserautherrorcodes.d.ts","./node_modules/@azure/msal-browser/dist/error/browserautherror.d.ts","./node_modules/@azure/msal-browser/dist/error/browserconfigurationautherrorcodes.d.ts","./node_modules/@azure/msal-browser/dist/error/browserconfigurationautherror.d.ts","./node_modules/@azure/msal-browser/dist/navigation/navigationclient.d.ts","./node_modules/@azure/msal-browser/dist/request/authorizationurlrequest.d.ts","./node_modules/@azure/msal-browser/dist/cache/browserstorage.d.ts","./node_modules/@azure/msal-browser/dist/cache/localstorage.d.ts","./node_modules/@azure/msal-browser/dist/cache/sessionstorage.d.ts","./node_modules/@azure/msal-browser/dist/event/eventhandler.d.ts","./node_modules/@azure/msal-browser/dist/crypto/signedhttprequest.d.ts","./node_modules/@azure/msal-browser/dist/telemetry/browserperformanceclient.d.ts","./node_modules/@azure/msal-browser/dist/telemetry/browserperformancemeasurement.d.ts","./node_modules/@azure/msal-browser/dist/packagemetadata.d.ts","./node_modules/@azure/msal-browser/dist/index.d.ts","./lib/msalconfig.ts","./node_modules/next/dist/compiled/@next/font/dist/types.d.ts","./node_modules/next/dist/compiled/@next/font/dist/google/index.d.ts","./node_modules/next/font/google/index.d.ts","./node_modules/@azure/msal-react/dist/msalcontext.d.ts","./node_modules/@azure/msal-react/dist/msalprovider.d.ts","./node_modules/@azure/msal-react/dist/types/accountidentifiers.d.ts","./node_modules/@azure/msal-react/dist/hooks/usemsalauthentication.d.ts","./node_modules/@azure/msal-react/dist/components/msalauthenticationtemplate.d.ts","./node_modules/@azure/msal-react/dist/components/authenticatedtemplate.d.ts","./node_modules/@azure/msal-react/dist/components/unauthenticatedtemplate.d.ts","./node_modules/@azure/msal-react/dist/utils/utilities.d.ts","./node_modules/@azure/msal-react/dist/components/withmsal.d.ts","./node_modules/@azure/msal-react/dist/hooks/usemsal.d.ts","./node_modules/@azure/msal-react/dist/hooks/useaccount.d.ts","./node_modules/@azure/msal-react/dist/hooks/useisauthenticated.d.ts","./node_modules/@azure/msal-react/dist/packagemetadata.d.ts","./node_modules/@azure/msal-react/dist/index.d.ts","./contexts/authcontext.tsx","./node_modules/@microsoft/signalr/dist/esm/abortcontroller.d.ts","./node_modules/@microsoft/signalr/dist/esm/itransport.d.ts","./node_modules/@microsoft/signalr/dist/esm/errors.d.ts","./node_modules/@microsoft/signalr/dist/esm/ilogger.d.ts","./node_modules/@microsoft/signalr/dist/esm/ihubprotocol.d.ts","./node_modules/@microsoft/signalr/dist/esm/httpclient.d.ts","./node_modules/@microsoft/signalr/dist/esm/defaulthttpclient.d.ts","./node_modules/@microsoft/signalr/dist/esm/ihttpconnectionoptions.d.ts","./node_modules/@microsoft/signalr/dist/esm/istatefulreconnectoptions.d.ts","./node_modules/@microsoft/signalr/dist/esm/stream.d.ts","./node_modules/@microsoft/signalr/dist/esm/hubconnection.d.ts","./node_modules/@microsoft/signalr/dist/esm/iretrypolicy.d.ts","./node_modules/@microsoft/signalr/dist/esm/hubconnectionbuilder.d.ts","./node_modules/@microsoft/signalr/dist/esm/loggers.d.ts","./node_modules/@microsoft/signalr/dist/esm/jsonhubprotocol.d.ts","./node_modules/@microsoft/signalr/dist/esm/subject.d.ts","./node_modules/@microsoft/signalr/dist/esm/pkg-version.d.ts","./node_modules/@microsoft/signalr/dist/esm/utils.d.ts","./node_modules/@microsoft/signalr/dist/esm/index.d.ts","./contexts/signalrcontext.tsx","./contexts/tenantcontext.tsx","./contexts/notificationcontext.tsx","./contexts/themecontext.tsx","./components/navbar.tsx","./app/layout.tsx","./components/protectedroute.tsx","./app/page.tsx","./app/about/page.tsx","./app/admin-configuration/page.tsx","./app/analyze-rules/page.tsx","./app/diagnosis/[sessionid]/page.tsx","./app/docs/page.tsx","./app/fleet-health/page.tsx","./app/flight-tracker/[sessionid]/page.tsx","./app/gather-rules/page.tsx","./app/health-check/page.tsx","./app/landing/page.tsx","./app/platform-usage-metrics/page.tsx","./app/progress/page.tsx","./components/performancechart.tsx","./components/downloadprogress.tsx","./app/sessions/[sessionid]/page.tsx","./app/settings/page.tsx","./app/usage-metrics/page.tsx","./.next/types/cache-life.d.ts","./.next/types/validator.ts","./.next/types/app/layout.ts","./.next/types/app/page.ts","./.next/types/app/about/page.ts","./.next/types/app/admin-configuration/page.ts","./.next/types/app/analyze-rules/page.ts","./.next/types/app/diagnosis/[sessionid]/page.ts","./.next/types/app/docs/page.ts","./.next/types/app/fleet-health/page.ts","./.next/types/app/flight-tracker/[sessionid]/page.ts","./.next/types/app/gather-rules/page.ts","./.next/types/app/health-check/page.ts","./.next/types/app/landing/page.ts","./.next/types/app/platform-usage-metrics/page.ts","./.next/types/app/progress/page.ts","./.next/types/app/sessions/[sessionid]/page.ts","./.next/types/app/settings/page.ts","./.next/types/app/usage-metrics/page.ts","./node_modules/@types/estree/index.d.ts","./node_modules/@types/json-schema/index.d.ts","./node_modules/@types/json5/index.d.ts"],"fileIdsList":[[100,148,165,166,341,717],[100,148,165,166,341,718],[100,148,165,166,341,719],[100,148,165,166,341,720],[100,148,165,166,341,721],[100,148,165,166,341,722],[100,148,165,166,341,723],[100,148,165,166,341,724],[100,148,165,166,341,725],[100,148,165,166,341,726],[100,148,165,166,341,714],[100,148,165,166,341,716],[100,148,165,166,341,727],[100,148,165,166,341,728],[100,148,165,166,341,731],[100,148,165,166,341,732],[100,148,165,166,341,733],[100,148,165,166,448,449,450,451],[100,148,165,166],[83,100,148,165,166,498,714,716,717,718,719,720,721,722,723,724,725,726,727,728,731,732,733],[100,148,165,166,472,482,715],[86,100,148,165,166,482,528,689,715],[86,100,148,165,166,482,528,689,710,715],[86,100,148,165,166,482,528,689,709,710,715],[100,148,165,166,472,715],[86,100,148,165,166,528,689,711],[86,100,148,165,166,482,689],[100,148,165,166,499,674,689,709,710,711,712,713],[86,100,148,165,166,482,528,689,709,710,711,715],[86,100,148,165,166,528,689,709,710,715],[86,100,148,165,166,482,528,689,709,710,715,729,730],[86,100,148,165,166],[86,100,148,165,166,472,482,528,689,711,712],[86,100,148,165,166,528,670,671,688],[86,100,148,165,166,528,689,708],[86,100,148,165,166,689],[100,148,165,166,670],[83,100,148,165,166,499,500],[100,148,165,166,499],[100,148,165,166,626,628,629,630,632,633,635,639,640,644,645,646,647,648,649,650,651],[100,148,165,166,626,628,629,630,632,633,635,639,640,644,645,646,647,648,649,650,651,652,653],[100,148,165,166,626,628,629,630,632,633,635,639,640,644,645,646,648,649,650,652,653],[100,148,165,166,626,630],[100,148,165,166,626,628,629,630,633,635,636,637,638,639,640,641],[100,148,165,166,636],[100,148,165,166,626,635,639,643],[100,148,165,166,626,633,635,639,642,644],[100,148,165,166,626,630,632],[100,148,165,166,626],[100,148,165,166,626,656],[100,148,165,166,626,658],[100,148,165,166,626,630,648,649],[100,148,165,166,626,628,629,630,635,639,640,646,648],[100,148,165,166,626,627,628,629,630,631,632,633,634,635,636,637,639,640,643,644,645,646,647,648,649,650,651,652,653,654,655,657,659,660,661,662,663,664,665,666,667,668,669],[100,148,165,166,631],[100,148,165,166,631,632],[100,148,165,166,630],[100,148,165,166,626,627],[100,148,165,166,626,633],[100,148,165,166,628,629],[100,148,165,166,529],[100,148,165,166,531,532,533,535,536,538,542,544,558,561,567,576],[100,148,165,166,535,538,544,558,576,577],[100,148,165,166,536,539,541,543],[100,148,165,166,540],[100,148,165,166,542],[100,148,165,166,537],[100,148,165,166,538,544,546,547,548,549,550,551,552,553,554,555,558,571,572,573,575,576],[100,148,165,166,537,545],[100,148,165,166,529,531,538,551,572,577],[100,148,165,166,546,547,548,549,573],[100,148,165,166,545],[100,148,165,166,546,547,548,549,550,551,552,553,554,555,573,575],[100,148,165,166,564],[100,148,165,166,563],[100,148,165,166,532,537,542,545,546,547,548,549,554],[100,148,165,166,537,546,547,548,549,551,552,553,554,573,574],[100,148,165,166,558,567,581,587,588,589,590,591,592],[100,148,165,166,534,535,538,558,559,562,567,571,572,577,584,585,586],[100,148,165,166,558,567,587,590,594,595],[100,148,165,166,537,558,567,587,590,595],[100,148,165,166,535,538,544,559,562,563,565,566,572,577],[100,148,165,166,570,571],[100,148,165,166,538,558,572],[100,148,165,166,569],[100,148,165,166,605],[100,148,165,166,611],[100,148,165,166,606,613],[100,148,165,166,606,615],[100,148,165,166,606,607],[100,148,165,166,606],[100,148,165,166,556,557,558,569,570,621,622,623,624],[100,148,165,166,529,530,531,533,534,535,536,537,538,539,540,541,544,545,546,547,548,549,550,551,552,553,554,555,559,560,562,567,568,571,572,573,574,575,577,578,579,580,581,582,583,584,585,586,587,588,589,590,591,592,593,594,595,596,597,598,599,600,601,602,603,604,606,608,609,610,612,614,616,617,618,619],[100,148,165,166,620,625],[100,148,165,166,567],[100,148,165,166,534],[100,148,165,166,537,570],[100,148,165,166,534,559,571,584,585],[100,148,165,166,537,555,567,568,570],[100,148,165,166,568,571,586],[100,148,165,166,537,551,568,571],[100,148,165,166,551,568],[100,148,165,166,551,568,571],[100,148,165,166,537,558,562,567,568,598],[100,148,165,166,551],[100,148,165,166,584],[100,148,165,166,529,538,550,558,559,563,565,571,572,573,577,581,584,590,592,602],[100,148,165,166,556,557],[100,148,165,166,538,556,557,558,567],[100,148,165,166,556,557,558],[100,148,165,166,537,552,559,560,561],[100,148,165,166,533],[100,148,165,166,538,558],[100,148,165,166,572],[100,148,165,166,581],[86,100,148,165,166,677],[86,100,148,165,166,670,675,677,678],[86,100,148,165,166,675,682],[100,148,165,166,670,677],[100,148,165,166,677],[100,148,165,166,675],[100,148,165,166,675,676,677,678,679,680,681,682,683,684,685,686,687],[86,100,148,165,166,670],[86,100,148,165,166,670,677],[100,148,165,166,693,695],[100,148,165,166,691],[100,148,165,166,690,694],[100,148,165,166,699],[100,148,165,166,691,693,694,697,698,700,701],[100,148,165,166,691,693,694,695],[100,148,165,166,691,693],[100,148,165,166,690,691,692,693,694,695,696,697,698,699,700,701,702,703,704,705,707],[100,148,165,166,691,693,694],[100,148,165,166,693],[100,148,165,166,693,695,697,699,705,706],[100,145,146,148,165,166],[100,147,148,165,166],[148,165,166],[100,148,153,165,166,183],[100,148,149,154,159,165,166,168,180,191],[100,148,149,150,159,165,166,168],[95,96,97,100,148,165,166],[100,148,151,165,166,192],[100,148,152,153,160,165,166,169],[100,148,153,165,166,180,188],[100,148,154,156,159,165,166,168],[100,147,148,155,165,166],[100,148,156,157,165,166],[100,148,158,159,165,166],[100,147,148,159,165,166],[100,148,159,160,161,165,166,180,191],[100,148,159,160,161,165,166,175,180,183],[100,141,148,156,159,162,165,166,168,180,191],[100,148,159,160,162,163,165,166,168,180,188,191],[100,148,162,164,165,166,180,188,191],[98,99,100,101,102,103,104,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197],[100,148,159,165,166],[100,148,165,166,167,191],[100,148,156,159,165,166,168,180],[100,148,165,166,169],[100,148,165,166,170],[100,147,148,165,166,171],[100,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197],[100,148,165,166,173],[100,148,165,166,174],[100,148,159,165,166,175,176],[100,148,165,166,175,177,192,194],[100,148,160,165,166],[100,148,159,165,166,180,181,183],[100,148,165,166,182,183],[100,148,165,166,180,181],[100,148,165,166,183],[100,148,165,166,184],[100,145,148,165,166,180,185],[100,148,159,165,166,186,187],[100,148,165,166,186,187],[100,148,153,165,166,168,180,188],[100,148,165,166,189],[100,148,165,166,168,190],[100,148,162,165,166,174,191],[100,148,153,165,166,192],[100,148,165,166,180,193],[100,148,165,166,167,194],[100,148,165,166,195],[100,141,148,165,166],[100,141,148,159,161,165,166,171,180,183,191,193,194,196],[100,148,165,166,180,197],[86,90,100,148,165,166,199,200,201,203,443,491],[86,90,100,148,165,166,199,200,201,202,358,443,491],[86,90,100,148,165,166,199,200,202,203,443,491],[86,100,148,165,166,203,358,359],[86,100,148,165,166,203,358],[86,90,100,148,165,166,200,201,202,203,443,491],[86,90,100,148,165,166,199,201,202,203,443,491],[84,85,100,148,165,166],[92,100,148,165,166],[100,148,165,166,446],[100,148,165,166,453],[100,148,165,166,207,221,222,223,225,440],[100,148,165,166,207,246,248,250,251,254,440,442],[100,148,165,166,207,211,213,214,215,216,217,429,440,442],[100,148,165,166,440],[100,148,165,166,222,324,410,419,436],[100,148,165,166,207],[100,148,165,166,204,436],[100,148,165,166,258],[100,148,165,166,257,440,442],[100,148,162,165,166,306,324,353,497],[100,148,162,165,166,317,333,419,435],[100,148,162,165,166,371],[100,148,165,166,423],[100,148,165,166,422,423,424],[100,148,165,166,422],[94,100,148,162,165,166,204,207,211,214,218,219,220,222,226,234,235,364,389,420,440,443],[100,148,165,166,207,224,242,246,247,252,253,440,497],[100,148,165,166,224,497],[100,148,165,166,235,242,304,440,497],[100,148,165,166,497],[100,148,165,166,207,224,225,497],[100,148,165,166,249,497],[100,148,165,166,218,421,428],[100,148,165,166,174,266,436],[100,148,165,166,266,436],[86,100,148,165,166,266],[86,100,148,165,166,325],[100,148,165,166,321,369,436,479,480],[100,148,165,166,416,473,474,475,476,478],[100,148,165,166,415],[100,148,165,166,415,416],[100,148,165,166,215,365,366,367],[100,148,165,166,365,368,369],[100,148,165,166,477],[100,148,165,166,365,369],[86,100,148,165,166,208,467],[86,100,148,165,166,191],[86,100,148,165,166,224,294],[86,100,148,165,166,224],[100,148,165,166,292,296],[86,100,148,165,166,293,445],[100,148,165,166,672],[86,90,100,148,162,165,166,198,199,200,201,202,203,443,489,490],[100,148,162,165,166],[100,148,162,165,166,211,273,365,375,390,410,425,426,440,441,497],[100,148,165,166,234,427],[100,148,165,166,443],[100,148,165,166,206],[86,100,148,165,166,306,320,332,342,344,435],[100,148,165,166,174,306,320,341,342,343,435,496],[100,148,165,166,335,336,337,338,339,340],[100,148,165,166,337],[100,148,165,166,341],[100,148,165,166,264,265,266,268],[86,100,148,165,166,259,260,261,267],[100,148,165,166,264,267],[100,148,165,166,262],[100,148,165,166,263],[86,100,148,165,166,266,293,445],[86,100,148,165,166,266,444,445],[86,100,148,165,166,266,445],[100,148,165,166,390,432],[100,148,165,166,432],[100,148,162,165,166,441,445],[100,148,165,166,329],[100,147,148,165,166,328],[100,148,165,166,236,274,312,314,316,317,318,319,362,365,435,438,441],[100,148,165,166,236,350,365,369],[100,148,165,166,317,435],[86,100,148,165,166,317,326,327,329,330,331,332,333,334,345,346,347,348,349,351,352,435,436,497],[100,148,165,166,311],[100,148,162,165,166,174,236,237,273,288,318,362,363,364,369,390,410,431,440,441,442,443,497],[100,148,165,166,435],[100,147,148,165,166,222,315,318,364,431,433,434,441],[100,148,165,166,317],[100,147,148,165,166,273,278,307,308,309,310,311,312,313,314,316,435,436],[100,148,162,165,166,278,279,307,441,442],[100,148,165,166,222,364,365,390,431,435,441],[100,148,162,165,166,440,442],[100,148,162,165,166,180,438,441,442],[100,148,162,165,166,174,191,204,211,224,236,237,239,274,275,280,285,288,314,318,365,375,377,380,382,385,386,387,388,389,410,430,431,436,438,440,441,442],[100,148,162,165,166,180],[100,148,165,166,207,208,209,211,216,219,224,242,430,438,439,443,445,497],[100,148,162,165,166,180,191,254,256,258,259,260,261,268,497],[100,148,165,166,174,191,204,246,256,284,285,286,287,314,365,380,389,390,396,399,400,410,431,436,438],[100,148,165,166,218,219,234,364,389,431,440],[100,148,162,165,166,191,208,211,314,394,438,440],[100,148,165,166,305],[100,148,162,165,166,397,398,407],[100,148,165,166,438,440],[100,148,165,166,312,315],[100,148,165,166,314,318,430,445],[100,148,162,165,166,174,240,246,287,380,390,396,399,402,438],[100,148,162,165,166,218,234,246,403],[100,148,165,166,207,239,405,430,440],[100,148,162,165,166,191,440],[100,148,162,165,166,224,238,239,240,251,269,404,406,430,440],[94,100,148,165,166,236,318,409,443,445],[100,148,162,165,166,174,191,211,218,226,234,237,274,280,284,285,286,287,288,314,365,377,390,391,393,395,410,430,431,436,437,438,445],[100,148,162,165,166,180,218,396,401,407,438],[100,148,165,166,229,230,231,232,233],[100,148,165,166,275,381],[100,148,165,166,383],[100,148,165,166,381],[100,148,165,166,383,384],[100,148,162,165,166,211,214,215,273,441],[100,148,162,165,166,174,206,208,236,274,288,318,373,374,410,438,442,443,445],[100,148,162,165,166,174,191,210,215,314,374,437,441],[100,148,165,166,307],[100,148,165,166,308],[100,148,165,166,309],[100,148,165,166,436],[100,148,165,166,255,271],[100,148,162,165,166,211,255,274],[100,148,165,166,270,271],[100,148,165,166,272],[100,148,165,166,255,256],[100,148,165,166,255,289],[100,148,165,166,255],[100,148,165,166,275,379,437],[100,148,165,166,378],[100,148,165,166,256,436,437],[100,148,165,166,376,437],[100,148,165,166,256,436],[100,148,165,166,362],[100,148,165,166,211,216,274,303,306,312,314,318,320,323,354,357,361,365,409,430,438,441],[100,148,165,166,297,300,301,302,321,322,369],[86,100,148,165,166,201,203,266,355,356],[86,100,148,165,166,201,203,266,355,356,360],[100,148,165,166,418],[100,148,165,166,222,279,317,318,329,333,365,409,411,412,413,414,416,417,420,430,435,440],[100,148,165,166,369],[100,148,165,166,373],[100,148,162,165,166,274,290,370,372,375,409,438,443,445],[100,148,165,166,297,298,299,300,301,302,321,322,369,444],[94,100,148,162,165,166,174,191,237,255,256,288,314,318,407,408,410,430,431,440,441,443],[100,148,165,166,279,281,284,431],[100,148,162,165,166,275,440],[100,148,165,166,278,317],[100,148,165,166,277],[100,148,165,166,279,280],[100,148,165,166,276,278,440],[100,148,162,165,166,210,279,281,282,283,440,441],[86,100,148,165,166,365,366,368],[100,148,165,166,241],[86,100,148,165,166,208],[86,100,148,165,166,436],[86,94,100,148,165,166,288,318,443,445],[100,148,165,166,208,467,468],[86,100,148,165,166,296],[86,100,148,165,166,174,191,206,253,291,293,295,445],[100,148,165,166,224,436,441],[100,148,165,166,392,436],[100,148,165,166,365],[86,100,148,160,162,165,166,174,206,242,248,296,443,444],[86,100,148,165,166,199,200,201,202,203,443,491],[86,87,88,89,90,100,148,165,166],[100,148,153,165,166],[100,148,165,166,243,244,245],[100,148,165,166,243],[86,90,100,148,162,164,165,166,174,198,199,200,201,202,203,204,206,237,341,402,440,442,445,491],[100,148,165,166,455],[100,148,165,166,457],[100,148,165,166,459],[100,148,165,166,673],[100,148,165,166,461],[100,148,165,166,463,464,465],[100,148,165,166,469],[91,93,100,148,165,166,447,452,454,456,458,460,462,466,470,472,482,483,485,495,496,497,498],[100,148,165,166,471],[100,148,165,166,481],[100,148,165,166,293],[100,148,165,166,484],[100,147,148,165,166,279,281,282,284,332,436,486,487,488,491,492,493,494],[100,148,165,166,198],[100,148,165,166,518],[100,148,165,166,516,518],[100,148,165,166,507,515,516,517,519,521],[100,148,165,166,505],[100,148,165,166,508,513,518,521],[100,148,165,166,504,521],[100,148,165,166,508,509,512,513,514,521],[100,148,165,166,508,509,510,512,513,521],[100,148,165,166,505,506,507,508,509,513,514,515,517,518,519,521],[100,148,165,166,521],[100,148,165,166,503,505,506,507,508,509,510,512,513,514,515,516,517,518,519,520],[100,148,165,166,503,521],[100,148,165,166,508,510,511,513,514,521],[100,148,165,166,512,521],[100,148,165,166,513,514,518,521],[100,148,165,166,506,516],[100,148,165,166,180,198],[100,148,165,166,523,524],[100,148,165,166,522,525],[100,113,117,148,165,166,191],[100,113,148,165,166,180,191],[100,108,148,165,166],[100,110,113,148,165,166,188,191],[100,148,165,166,168,188],[100,108,148,165,166,198],[100,110,113,148,165,166,168,191],[100,105,106,109,112,148,159,165,166,180,191],[100,113,120,148,165,166],[100,105,111,148,165,166],[100,113,134,135,148,165,166],[100,109,113,148,165,166,183,191,198],[100,134,148,165,166,198],[100,107,108,148,165,166,198],[100,113,148,165,166],[100,107,108,109,110,111,112,113,114,115,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,135,136,137,138,139,140,148,165,166],[100,113,128,148,165,166],[100,113,120,121,148,165,166],[100,111,113,121,122,148,165,166],[100,112,148,165,166],[100,105,108,113,148,165,166],[100,113,117,121,122,148,165,166],[100,117,148,165,166],[100,111,113,116,148,165,166,191],[100,105,110,113,120,148,165,166],[100,148,165,166,180],[100,108,113,134,148,165,166,196,198],[100,148,165,166,526]],"fileInfos":[{"version":"c430d44666289dae81f30fa7b2edebf186ecc91a2d4c71266ea6ae76388792e1","affectsGlobalScope":true,"impliedFormat":1},{"version":"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","impliedFormat":1},{"version":"3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","impliedFormat":1},{"version":"e44bb8bbac7f10ecc786703fe0a6a4b952189f908707980ba8f3c8975a760962","impliedFormat":1},{"version":"5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","impliedFormat":1},{"version":"68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","impliedFormat":1},{"version":"5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","impliedFormat":1},{"version":"feecb1be483ed332fad555aff858affd90a48ab19ba7272ee084704eb7167569","impliedFormat":1},{"version":"ee7bad0c15b58988daa84371e0b89d313b762ab83cb5b31b8a2d1162e8eb41c2","impliedFormat":1},{"version":"27bdc30a0e32783366a5abeda841bc22757c1797de8681bbe81fbc735eeb1c10","impliedFormat":1},{"version":"8fd575e12870e9944c7e1d62e1f5a73fcf23dd8d3a321f2a2c74c20d022283fe","impliedFormat":1},{"version":"2ab096661c711e4a81cc464fa1e6feb929a54f5340b46b0a07ac6bbf857471f0","impliedFormat":1},{"version":"080941d9f9ff9307f7e27a83bcd888b7c8270716c39af943532438932ec1d0b9","affectsGlobalScope":true,"impliedFormat":1},{"version":"2e80ee7a49e8ac312cc11b77f1475804bee36b3b2bc896bead8b6e1266befb43","affectsGlobalScope":true,"impliedFormat":1},{"version":"c57796738e7f83dbc4b8e65132f11a377649c00dd3eee333f672b8f0a6bea671","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true,"impliedFormat":1},{"version":"515d0b7b9bea2e31ea4ec968e9edd2c39d3eebf4a2d5cbd04e88639819ae3b71","affectsGlobalScope":true,"impliedFormat":1},{"version":"0559b1f683ac7505ae451f9a96ce4c3c92bdc71411651ca6ddb0e88baaaad6a3","affectsGlobalScope":true,"impliedFormat":1},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fb0f136d372979348d59b3f5020b4cdb81b5504192b1cacff5d1fbba29378aa1","affectsGlobalScope":true,"impliedFormat":1},{"version":"d15bea3d62cbbdb9797079416b8ac375ae99162a7fba5de2c6c505446486ac0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"68d18b664c9d32a7336a70235958b8997ebc1c3b8505f4f1ae2b7e7753b87618","affectsGlobalScope":true,"impliedFormat":1},{"version":"eb3d66c8327153d8fa7dd03f9c58d351107fe824c79e9b56b462935176cdf12a","affectsGlobalScope":true,"impliedFormat":1},{"version":"38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","affectsGlobalScope":true,"impliedFormat":1},{"version":"69ab18c3b76cd9b1be3d188eaf8bba06112ebbe2f47f6c322b5105a6fbc45a2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"a680117f487a4d2f30ea46f1b4b7f58bef1480456e18ba53ee85c2746eeca012","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","affectsGlobalScope":true,"impliedFormat":1},{"version":"4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"954296b30da6d508a104a3a0b5d96b76495c709785c1d11610908e63481ee667","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac9538681b19688c8eae65811b329d3744af679e0bdfa5d842d0e32524c73e1c","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a969edff4bd52585473d24995c5ef223f6652d6ef46193309b3921d65dd4376","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e9fbd7030c440b33d021da145d3232984c8bb7916f277e8ffd3dc2e3eae2bdb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","affectsGlobalScope":true,"impliedFormat":1},{"version":"717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","affectsGlobalScope":true,"impliedFormat":1},{"version":"71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","affectsGlobalScope":true,"impliedFormat":1},{"version":"576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","affectsGlobalScope":true,"impliedFormat":1},{"version":"89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","affectsGlobalScope":true,"impliedFormat":1},{"version":"74f7fa2d027d5b33eb0471c8e82a6c87216223181ec31247c357a3e8e2fddc5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"d6d7ae4d1f1f3772e2a3cde568ed08991a8ae34a080ff1151af28b7f798e22ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","affectsGlobalScope":true,"impliedFormat":1},{"version":"934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","affectsGlobalScope":true,"impliedFormat":1},{"version":"52ada8e0b6e0482b728070b7639ee42e83a9b1c22d205992756fe020fd9f4a47","affectsGlobalScope":true,"impliedFormat":1},{"version":"3bdefe1bfd4d6dee0e26f928f93ccc128f1b64d5d501ff4a8cf3c6371200e5e6","affectsGlobalScope":true,"impliedFormat":1},{"version":"59fb2c069260b4ba00b5643b907ef5d5341b167e7d1dbf58dfd895658bda2867","affectsGlobalScope":true,"impliedFormat":1},{"version":"639e512c0dfc3fad96a84caad71b8834d66329a1f28dc95e3946c9b58176c73a","affectsGlobalScope":true,"impliedFormat":1},{"version":"368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","affectsGlobalScope":true,"impliedFormat":1},{"version":"af3dd424cf267428f30ccfc376f47a2c0114546b55c44d8c0f1d57d841e28d74","affectsGlobalScope":true,"impliedFormat":1},{"version":"995c005ab91a498455ea8dfb63aa9f83fa2ea793c3d8aa344be4a1678d06d399","affectsGlobalScope":true,"impliedFormat":1},{"version":"959d36cddf5e7d572a65045b876f2956c973a586da58e5d26cde519184fd9b8a","affectsGlobalScope":true,"impliedFormat":1},{"version":"965f36eae237dd74e6cca203a43e9ca801ce38824ead814728a2807b1910117d","affectsGlobalScope":true,"impliedFormat":1},{"version":"3925a6c820dcb1a06506c90b1577db1fdbf7705d65b62b99dce4be75c637e26b","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a3d63ef2b853447ec4f749d3f368ce642264246e02911fcb1590d8c161b8005","affectsGlobalScope":true,"impliedFormat":1},{"version":"8cdf8847677ac7d20486e54dd3fcf09eda95812ac8ace44b4418da1bbbab6eb8","affectsGlobalScope":true,"impliedFormat":1},{"version":"8444af78980e3b20b49324f4a16ba35024fef3ee069a0eb67616ea6ca821c47a","affectsGlobalScope":true,"impliedFormat":1},{"version":"3287d9d085fbd618c3971944b65b4be57859f5415f495b33a6adc994edd2f004","affectsGlobalScope":true,"impliedFormat":1},{"version":"b4b67b1a91182421f5df999988c690f14d813b9850b40acd06ed44691f6727ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"df83c2a6c73228b625b0beb6669c7ee2a09c914637e2d35170723ad49c0f5cd4","affectsGlobalScope":true,"impliedFormat":1},{"version":"436aaf437562f276ec2ddbee2f2cdedac7664c1e4c1d2c36839ddd582eeb3d0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e3c06ea092138bf9fa5e874a1fdbc9d54805d074bee1de31b99a11e2fec239d","affectsGlobalScope":true,"impliedFormat":1},{"version":"87dc0f382502f5bbce5129bdc0aea21e19a3abbc19259e0b43ae038a9fc4e326","affectsGlobalScope":true,"impliedFormat":1},{"version":"b1cb28af0c891c8c96b2d6b7be76bd394fddcfdb4709a20ba05a7c1605eea0f9","affectsGlobalScope":true,"impliedFormat":1},{"version":"2fef54945a13095fdb9b84f705f2b5994597640c46afeb2ce78352fab4cb3279","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac77cb3e8c6d3565793eb90a8373ee8033146315a3dbead3bde8db5eaf5e5ec6","affectsGlobalScope":true,"impliedFormat":1},{"version":"56e4ed5aab5f5920980066a9409bfaf53e6d21d3f8d020c17e4de584d29600ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ece9f17b3866cc077099c73f4983bddbcb1dc7ddb943227f1ec070f529dedd1","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a6282c8827e4b9a95f4bf4f5c205673ada31b982f50572d27103df8ceb8013c","affectsGlobalScope":true,"impliedFormat":1},{"version":"1c9319a09485199c1f7b0498f2988d6d2249793ef67edda49d1e584746be9032","affectsGlobalScope":true,"impliedFormat":1},{"version":"e3a2a0cee0f03ffdde24d89660eba2685bfbdeae955a6c67e8c4c9fd28928eeb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811c71eee4aa0ac5f7adf713323a5c41b0cf6c4e17367a34fbce379e12bbf0a4","affectsGlobalScope":true,"impliedFormat":1},{"version":"51ad4c928303041605b4d7ae32e0c1ee387d43a24cd6f1ebf4a2699e1076d4fa","affectsGlobalScope":true,"impliedFormat":1},{"version":"60037901da1a425516449b9a20073aa03386cce92f7a1fd902d7602be3a7c2e9","affectsGlobalScope":true,"impliedFormat":1},{"version":"d4b1d2c51d058fc21ec2629fff7a76249dec2e36e12960ea056e3ef89174080f","affectsGlobalScope":true,"impliedFormat":1},{"version":"22adec94ef7047a6c9d1af3cb96be87a335908bf9ef386ae9fd50eeb37f44c47","affectsGlobalScope":true,"impliedFormat":1},{"version":"196cb558a13d4533a5163286f30b0509ce0210e4b316c56c38d4c0fd2fb38405","affectsGlobalScope":true,"impliedFormat":1},{"version":"73f78680d4c08509933daf80947902f6ff41b6230f94dd002ae372620adb0f60","affectsGlobalScope":true,"impliedFormat":1},{"version":"c5239f5c01bcfa9cd32f37c496cf19c61d69d37e48be9de612b541aac915805b","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e7f8264d0fb4c5339605a15daadb037bf238c10b654bb3eee14208f860a32ea","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},{"version":"cf65af7e8babd1ccfeaa95178ceb8e5fc8b724d8794f7361483b32c88871b481","affectsGlobalScope":true},{"version":"7e29f41b158de217f94cb9676bf9cbd0cd9b5a46e1985141ed36e075c52bf6ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac51dd7d31333793807a6abaa5ae168512b6131bd41d9c5b98477fc3b7800f9f","impliedFormat":1},{"version":"f123246a7b6c04d80b9b57fadfc6c90959ec6d5c0d4c8e620e06e2811ae3a052","impliedFormat":1},{"version":"acd8fd5090ac73902278889c38336ff3f48af6ba03aa665eb34a75e7ba1dccc4","impliedFormat":1},{"version":"d6258883868fb2680d2ca96bc8b1352cab69874581493e6d52680c5ffecdb6cc","impliedFormat":1},{"version":"1b61d259de5350f8b1e5db06290d31eaebebc6baafd5f79d314b5af9256d7153","impliedFormat":1},{"version":"f258e3960f324a956fc76a3d3d9e964fff2244ff5859dcc6ce5951e5413ca826","impliedFormat":1},{"version":"643f7232d07bf75e15bd8f658f664d6183a0efaca5eb84b48201c7671a266979","impliedFormat":1},{"version":"0f6666b58e9276ac3a38fdc80993d19208442d6027ab885580d93aec76b4ef00","impliedFormat":1},{"version":"05fd364b8ef02fb1e174fbac8b825bdb1e5a36a016997c8e421f5fab0a6da0a0","impliedFormat":1},{"version":"631eff75b0e35d1b1b31081d55209abc43e16b49426546ab5a9b40bdd40b1f60","impliedFormat":1},{"version":"6c7176368037af28cb72f2392010fa1cef295d6d6744bca8cfb54985f3a18c3e","affectsGlobalScope":true,"impliedFormat":1},{"version":"ab41ef1f2cdafb8df48be20cd969d875602483859dc194e9c97c8a576892c052","affectsGlobalScope":true,"impliedFormat":1},{"version":"437e20f2ba32abaeb7985e0afe0002de1917bc74e949ba585e49feba65da6ca1","affectsGlobalScope":true,"impliedFormat":1},{"version":"21d819c173c0cf7cc3ce57c3276e77fd9a8a01d35a06ad87158781515c9a438a","impliedFormat":1},{"version":"98cffbf06d6bab333473c70a893770dbe990783904002c4f1a960447b4b53dca","affectsGlobalScope":true,"impliedFormat":1},{"version":"3af97acf03cc97de58a3a4bc91f8f616408099bc4233f6d0852e72a8ffb91ac9","affectsGlobalScope":true,"impliedFormat":1},{"version":"808069bba06b6768b62fd22429b53362e7af342da4a236ed2d2e1c89fcca3b4a","affectsGlobalScope":true,"impliedFormat":1},{"version":"1db0b7dca579049ca4193d034d835f6bfe73096c73663e5ef9a0b5779939f3d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"9798340ffb0d067d69b1ae5b32faa17ab31b82466a3fc00d8f2f2df0c8554aaa","affectsGlobalScope":true,"impliedFormat":1},{"version":"f26b11d8d8e4b8028f1c7d618b22274c892e4b0ef5b3678a8ccbad85419aef43","affectsGlobalScope":true,"impliedFormat":1},{"version":"5929864ce17fba74232584d90cb721a89b7ad277220627cc97054ba15a98ea8f","impliedFormat":1},{"version":"763fe0f42b3d79b440a9b6e51e9ba3f3f91352469c1e4b3b67bfa4ff6352f3f4","impliedFormat":1},{"version":"25c8056edf4314820382a5fdb4bb7816999acdcb929c8f75e3f39473b87e85bc","impliedFormat":1},{"version":"c464d66b20788266e5353b48dc4aa6bc0dc4a707276df1e7152ab0c9ae21fad8","impliedFormat":1},{"version":"78d0d27c130d35c60b5e5566c9f1e5be77caf39804636bc1a40133919a949f21","impliedFormat":1},{"version":"c6fd2c5a395f2432786c9cb8deb870b9b0e8ff7e22c029954fabdd692bff6195","impliedFormat":1},{"version":"1d6e127068ea8e104a912e42fc0a110e2aa5a66a356a917a163e8cf9a65e4a75","impliedFormat":1},{"version":"5ded6427296cdf3b9542de4471d2aa8d3983671d4cac0f4bf9c637208d1ced43","impliedFormat":1},{"version":"7f182617db458e98fc18dfb272d40aa2fff3a353c44a89b2c0ccb3937709bfb5","impliedFormat":1},{"version":"cadc8aced301244057c4e7e73fbcae534b0f5b12a37b150d80e5a45aa4bebcbd","impliedFormat":1},{"version":"385aab901643aa54e1c36f5ef3107913b10d1b5bb8cbcd933d4263b80a0d7f20","impliedFormat":1},{"version":"9670d44354bab9d9982eca21945686b5c24a3f893db73c0dae0fd74217a4c219","impliedFormat":1},{"version":"0b8a9268adaf4da35e7fa830c8981cfa22adbbe5b3f6f5ab91f6658899e657a7","impliedFormat":1},{"version":"11396ed8a44c02ab9798b7dca436009f866e8dae3c9c25e8c1fbc396880bf1bb","impliedFormat":1},{"version":"ba7bc87d01492633cb5a0e5da8a4a42a1c86270e7b3d2dea5d156828a84e4882","impliedFormat":1},{"version":"4893a895ea92c85345017a04ed427cbd6a1710453338df26881a6019432febdd","impliedFormat":1},{"version":"c21dc52e277bcfc75fac0436ccb75c204f9e1b3fa5e12729670910639f27343e","impliedFormat":1},{"version":"13f6f39e12b1518c6650bbb220c8985999020fe0f21d818e28f512b7771d00f9","impliedFormat":1},{"version":"9b5369969f6e7175740bf51223112ff209f94ba43ecd3bb09eefff9fd675624a","impliedFormat":1},{"version":"4fe9e626e7164748e8769bbf74b538e09607f07ed17c2f20af8d680ee49fc1da","impliedFormat":1},{"version":"24515859bc0b836719105bb6cc3d68255042a9f02a6022b3187948b204946bd2","impliedFormat":1},{"version":"ea0148f897b45a76544ae179784c95af1bd6721b8610af9ffa467a518a086a43","impliedFormat":1},{"version":"24c6a117721e606c9984335f71711877293a9651e44f59f3d21c1ea0856f9cc9","impliedFormat":1},{"version":"dd3273ead9fbde62a72949c97dbec2247ea08e0c6952e701a483d74ef92d6a17","impliedFormat":1},{"version":"405822be75ad3e4d162e07439bac80c6bcc6dbae1929e179cf467ec0b9ee4e2e","impliedFormat":1},{"version":"0db18c6e78ea846316c012478888f33c11ffadab9efd1cc8bcc12daded7a60b6","impliedFormat":1},{"version":"e61be3f894b41b7baa1fbd6a66893f2579bfad01d208b4ff61daef21493ef0a8","impliedFormat":1},{"version":"bd0532fd6556073727d28da0edfd1736417a3f9f394877b6d5ef6ad88fba1d1a","impliedFormat":1},{"version":"89167d696a849fce5ca508032aabfe901c0868f833a8625d5a9c6e861ef935d2","impliedFormat":1},{"version":"615ba88d0128ed16bf83ef8ccbb6aff05c3ee2db1cc0f89ab50a4939bfc1943f","impliedFormat":1},{"version":"a4d551dbf8746780194d550c88f26cf937caf8d56f102969a110cfaed4b06656","impliedFormat":1},{"version":"8bd86b8e8f6a6aa6c49b71e14c4ffe1211a0e97c80f08d2c8cc98838006e4b88","impliedFormat":1},{"version":"317e63deeb21ac07f3992f5b50cdca8338f10acd4fbb7257ebf56735bf52ab00","impliedFormat":1},{"version":"4732aec92b20fb28c5fe9ad99521fb59974289ed1e45aecb282616202184064f","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","impliedFormat":1},{"version":"bf67d53d168abc1298888693338cb82854bdb2e69ef83f8a0092093c2d562107","impliedFormat":1},{"version":"b52476feb4a0cbcb25e5931b930fc73cb6643fb1a5060bf8a3dda0eeae5b4b68","affectsGlobalScope":true,"impliedFormat":1},{"version":"f9501cc13ce624c72b61f12b3963e84fad210fbdf0ffbc4590e08460a3f04eba","affectsGlobalScope":true,"impliedFormat":1},{"version":"e7721c4f69f93c91360c26a0a84ee885997d748237ef78ef665b153e622b36c1","affectsGlobalScope":true,"impliedFormat":1},{"version":"0fa06ada475b910e2106c98c68b10483dc8811d0c14a8a8dd36efb2672485b29","impliedFormat":1},{"version":"33e5e9aba62c3193d10d1d33ae1fa75c46a1171cf76fef750777377d53b0303f","impliedFormat":1},{"version":"2b06b93fd01bcd49d1a6bd1f9b65ddcae6480b9a86e9061634d6f8e354c1468f","impliedFormat":1},{"version":"6a0cd27e5dc2cfbe039e731cf879d12b0e2dded06d1b1dedad07f7712de0d7f4","affectsGlobalScope":true,"impliedFormat":1},{"version":"13f5c844119c43e51ce777c509267f14d6aaf31eafb2c2b002ca35584cd13b29","impliedFormat":1},{"version":"e60477649d6ad21542bd2dc7e3d9ff6853d0797ba9f689ba2f6653818999c264","impliedFormat":1},{"version":"c2510f124c0293ab80b1777c44d80f812b75612f297b9857406468c0f4dafe29","affectsGlobalScope":true,"impliedFormat":1},{"version":"5524481e56c48ff486f42926778c0a3cce1cc85dc46683b92b1271865bcf015a","impliedFormat":1},{"version":"4c829ab315f57c5442c6667b53769975acbf92003a66aef19bce151987675bd1","affectsGlobalScope":true,"impliedFormat":1},{"version":"b2ade7657e2db96d18315694789eff2ddd3d8aea7215b181f8a0b303277cc579","impliedFormat":1},{"version":"9855e02d837744303391e5623a531734443a5f8e6e8755e018c41d63ad797db2","impliedFormat":1},{"version":"4d631b81fa2f07a0e63a9a143d6a82c25c5f051298651a9b69176ba28930756d","impliedFormat":1},{"version":"836a356aae992ff3c28a0212e3eabcb76dd4b0cc06bcb9607aeef560661b860d","impliedFormat":1},{"version":"1e0d1f8b0adfa0b0330e028c7941b5a98c08b600efe7f14d2d2a00854fb2f393","impliedFormat":1},{"version":"41670ee38943d9cbb4924e436f56fc19ee94232bc96108562de1a734af20dc2c","affectsGlobalScope":true,"impliedFormat":1},{"version":"c906fb15bd2aabc9ed1e3f44eb6a8661199d6c320b3aa196b826121552cb3695","impliedFormat":1},{"version":"22295e8103f1d6d8ea4b5d6211e43421fe4564e34d0dd8e09e520e452d89e659","impliedFormat":1},{"version":"f949f7f6c7802a338039cfc2156d1fe285cdd1e092c64437ebe15ae8edc854e0","impliedFormat":1},{"version":"6b4e081d55ac24fc8a4631d5dd77fe249fa25900abd7d046abb87d90e3b45645","impliedFormat":1},{"version":"a10f0e1854f3316d7ee437b79649e5a6ae3ae14ffe6322b02d4987071a95362e","impliedFormat":1},{"version":"e208f73ef6a980104304b0d2ca5f6bf1b85de6009d2c7e404028b875020fa8f2","impliedFormat":1},{"version":"d163b6bc2372b4f07260747cbc6c0a6405ab3fbcea3852305e98ac43ca59f5bc","impliedFormat":1},{"version":"e6fa9ad47c5f71ff733744a029d1dc472c618de53804eae08ffc243b936f87ff","affectsGlobalScope":true,"impliedFormat":1},{"version":"83e63d6ccf8ec004a3bb6d58b9bb0104f60e002754b1e968024b320730cc5311","impliedFormat":1},{"version":"24826ed94a78d5c64bd857570fdbd96229ad41b5cb654c08d75a9845e3ab7dde","impliedFormat":1},{"version":"8b479a130ccb62e98f11f136d3ac80f2984fdc07616516d29881f3061f2dd472","impliedFormat":1},{"version":"928af3d90454bf656a52a48679f199f64c1435247d6189d1caf4c68f2eaf921f","affectsGlobalScope":true,"impliedFormat":1},{"version":"d2bc7425ef40526650d6db7e072c1ff4a51101c3ac2cc4b666623b19496a6e27","affectsGlobalScope":true,"impliedFormat":1},{"version":"3f16a7e4deafa527ed9995a772bb380eb7d3c2c0fd4ae178c5263ed18394db2c","impliedFormat":1},{"version":"933921f0bb0ec12ef45d1062a1fc0f27635318f4d294e4d99de9a5493e618ca2","impliedFormat":1},{"version":"71a0f3ad612c123b57239a7749770017ecfe6b66411488000aba83e4546fde25","impliedFormat":1},{"version":"77fbe5eecb6fac4b6242bbf6eebfc43e98ce5ccba8fa44e0ef6a95c945ff4d98","impliedFormat":1},{"version":"4f9d8ca0c417b67b69eeb54c7ca1bedd7b56034bb9bfd27c5d4f3bc4692daca7","impliedFormat":1},{"version":"814118df420c4e38fe5ae1b9a3bafb6e9c2aa40838e528cde908381867be6466","impliedFormat":1},{"version":"a3fc63c0d7b031693f665f5494412ba4b551fe644ededccc0ab5922401079c95","impliedFormat":1},{"version":"f27524f4bef4b6519c604bdb23bf4465bddcccbf3f003abb901acbd0d7404d99","impliedFormat":1},{"version":"37ba7b45141a45ce6e80e66f2a96c8a5ab1bcef0fc2d0f56bb58df96ec67e972","impliedFormat":1},{"version":"45650f47bfb376c8a8ed39d4bcda5902ab899a3150029684ee4c10676d9fbaee","impliedFormat":1},{"version":"6b039f55681caaf111d5eb84d292b9bee9e0131d0db1ad0871eef0964f533c73","affectsGlobalScope":true,"impliedFormat":1},{"version":"18fd40412d102c5564136f29735e5d1c3b455b8a37f920da79561f1fde068208","impliedFormat":1},{"version":"c959a391a75be9789b43c8468f71e3fa06488b4d691d5729dde1416dcd38225b","impliedFormat":1},{"version":"f0be1b8078cd549d91f37c30c222c2a187ac1cf981d994fb476a1adc61387b14","affectsGlobalScope":true,"impliedFormat":1},{"version":"0aaed1d72199b01234152f7a60046bc947f1f37d78d182e9ae09c4289e06a592","impliedFormat":1},{"version":"0dba70b3fb0dcd713fda33c2df64fa6751fff6460e536971cee917260fb17882","impliedFormat":1},{"version":"66ba1b2c3e3a3644a1011cd530fb444a96b1b2dfe2f5e837a002d41a1a799e60","impliedFormat":1},{"version":"7e514f5b852fdbc166b539fdd1f4e9114f29911592a5eb10a94bb3a13ccac3c4","impliedFormat":1},{"version":"5b7aa3c4c1a5d81b411e8cb302b45507fea9358d3569196b27eb1a27ae3a90ef","affectsGlobalScope":true,"impliedFormat":1},{"version":"5987a903da92c7462e0b35704ce7da94d7fdc4b89a984871c0e2b87a8aae9e69","affectsGlobalScope":true,"impliedFormat":1},{"version":"ea08a0345023ade2b47fbff5a76d0d0ed8bff10bc9d22b83f40858a8e941501c","impliedFormat":1},{"version":"47613031a5a31510831304405af561b0ffaedb734437c595256bb61a90f9311b","impliedFormat":1},{"version":"ae062ce7d9510060c5d7e7952ae379224fb3f8f2dd74e88959878af2057c143b","impliedFormat":1},{"version":"8a1a0d0a4a06a8d278947fcb66bf684f117bf147f89b06e50662d79a53be3e9f","affectsGlobalScope":true,"impliedFormat":1},{"version":"9f663c2f91127ef7024e8ca4b3b4383ff2770e5f826696005de382282794b127","impliedFormat":1},{"version":"9f55299850d4f0921e79b6bf344b47c420ce0f507b9dcf593e532b09ea7eeea1","impliedFormat":1},{"version":"2beff543f6e9a9701df88daeee3cdd70a34b4a1c11cb4c734472195a5cb2af54","impliedFormat":1},{"version":"bfffea552cca245df227337223c7554b35df629ba1d4e09edee4521ce7f24827","impliedFormat":1},{"version":"be1cc4d94ea60cbe567bc29ed479d42587bf1e6cba490f123d329976b0fe4ee5","impliedFormat":1},{"version":"42bc0e1a903408137c3df2b06dfd7e402cdab5bbfa5fcfb871b22ebfdb30bd0b","impliedFormat":1},{"version":"9894dafe342b976d251aac58e616ac6df8db91fb9d98934ff9dd103e9e82578f","impliedFormat":1},{"version":"413df52d4ea14472c2fa5bee62f7a40abd1eb49be0b9722ee01ee4e52e63beb2","impliedFormat":1},{"version":"db6d2d9daad8a6d83f281af12ce4355a20b9a3e71b82b9f57cddcca0a8964a96","impliedFormat":1},{"version":"829b9e6028b29e6a8b1c01ddb713efe59da04d857089298fa79acbdb3cfcfdef","impliedFormat":1},{"version":"24f8562308dd8ba6013120557fa7b44950b619610b2c6cb8784c79f11e3c4f90","impliedFormat":1},{"version":"5d8717b437b9d6afeb4da84b9082db35cafce3dfd025bc7c9ad7abbe50fa2778","impliedFormat":1},{"version":"a86f82d646a739041d6702101afa82dcb935c416dd93cbca7fd754fd0282ce1f","impliedFormat":1},{"version":"ad0d1d75d129b1c80f911be438d6b61bfa8703930a8ff2be2f0e1f8a91841c64","impliedFormat":1},{"version":"ce75b1aebb33d510ff28af960a9221410a3eaf7f18fc5f21f9404075fba77256","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"496bbf339f3838c41f164238543e9fe5f1f10659cb30b68903851618464b98ba","impliedFormat":1},{"version":"5178eb4415a172c287c711dc60a619e110c3fd0b7de01ed0627e51a5336aa09c","impliedFormat":1},{"version":"ca6e5264278b53345bc1ce95f42fb0a8b733a09e3d6479c6ccfca55cdc45038c","impliedFormat":1},{"version":"9e2739b32f741859263fdba0244c194ca8e96da49b430377930b8f721d77c000","impliedFormat":1},{"version":"fb1d8e814a3eeb5101ca13515e0548e112bd1ff3fb358ece535b93e94adf5a3a","impliedFormat":1},{"version":"ffa495b17a5ef1d0399586b590bd281056cee6ce3583e34f39926f8dcc6ecdb5","impliedFormat":1},{"version":"98b18458acb46072947aabeeeab1e410f047e0cacc972943059ca5500b0a5e95","impliedFormat":1},{"version":"361e2b13c6765d7f85bb7600b48fde782b90c7c41105b7dab1f6e7871071ba20","impliedFormat":1},{"version":"c86fe861cf1b4c46a0fb7d74dffe596cf679a2e5e8b1456881313170f092e3fa","impliedFormat":1},{"version":"b6db56e4903e9c32e533b78ac85522de734b3d3a8541bf24d256058d464bf04b","impliedFormat":1},{"version":"24daa0366f837d22c94a5c0bad5bf1fd0f6b29e1fae92dc47c3072c3fdb2fbd5","impliedFormat":1},{"version":"570bb5a00836ffad3e4127f6adf581bfc4535737d8ff763a4d6f4cc877e60d98","impliedFormat":1},{"version":"889c00f3d32091841268f0b994beba4dceaa5df7573be12c2c829d7c5fbc232c","impliedFormat":1},{"version":"65f43099ded6073336e697512d9b80f2d4fec3182b7b2316abf712e84104db00","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"acf5a2ac47b59ca07afa9abbd2b31d001bf7448b041927befae2ea5b1951d9f9","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"d71291eff1e19d8762a908ba947e891af44749f3a2cbc5bd2ec4b72f72ea795f","impliedFormat":1},{"version":"c0480e03db4b816dff2682b347c95f2177699525c54e7e6f6aa8ded890b76be7","impliedFormat":1},{"version":"27ab780875bcbb65e09da7496f2ca36288b0c541abaa75c311450a077d54ec15","impliedFormat":1},{"version":"b620391fe8060cf9bedc176a4d01366e6574d7a71e0ac0ab344a4e76576fcbb8","impliedFormat":1},{"version":"380647d8f3b7f852cca6d154a376dbf8ac620a2f12b936594504a8a852e71d2f","impliedFormat":1},{"version":"208c9af9429dd3c76f5927b971263174aaa4bc7621ddec63f163640cbd3c473c","impliedFormat":1},{"version":"6459054aabb306821a043e02b89d54da508e3a6966601a41e71c166e4ea1474f","impliedFormat":1},{"version":"a23185bc5ef590c287c28a91baf280367b50ae4ea40327366ad01f6f4a8edbc5","impliedFormat":1},{"version":"bb37588926aba35c9283fe8d46ebf4e79ffe976343105f5c6d45f282793352b2","impliedFormat":1},{"version":"002eae065e6960458bda3cf695e578b0d1e2785523476f8a9170b103c709cd4f","impliedFormat":1},{"version":"c83bb0c9c5645a46c68356c2f73fdc9de339ce77f7f45a954f560c7e0b8d5ebb","impliedFormat":1},{"version":"05c97cddbaf99978f83d96de2d8af86aded9332592f08ce4a284d72d0952c391","impliedFormat":1},{"version":"72179f9dd22a86deaad4cc3490eb0fe69ee084d503b686985965654013f1391b","impliedFormat":1},{"version":"2e6114a7dd6feeef85b2c80120fdbfb59a5529c0dcc5bfa8447b6996c97a69f5","impliedFormat":1},{"version":"7b6ff760c8a240b40dab6e4419b989f06a5b782f4710d2967e67c695ef3e93c4","impliedFormat":1},{"version":"c8f004e6036aa1c764ad4ec543cf89a5c1893a9535c80ef3f2b653e370de45e6","impliedFormat":1},{"version":"dd80b1e600d00f5c6a6ba23f455b84a7db121219e68f89f10552c54ba46e4dc9","impliedFormat":1},{"version":"b064c36f35de7387d71c599bfcf28875849a1dbc733e82bd26cae3d1cd060521","impliedFormat":1},{"version":"6a148329edecbda07c21098639ef4254ef7869fb25a69f58e5d6a8b7b69d4236","impliedFormat":1},{"version":"8de9fe97fa9e00ec00666fa77ab6e91b35d25af8ca75dabcb01e14ad3299b150","impliedFormat":1},{"version":"f63ab283a1c8f5c79fabe7ca4ef85f9633339c4f0e822fce6a767f9d59282af2","impliedFormat":1},{"version":"dba114fb6a32b355a9cfc26ca2276834d72fe0e94cd2c3494005547025015369","impliedFormat":1},{"version":"a54c996c8870ef1728a2c1fa9b8eaec0bf4a8001cd2583c02dd5869289465b10","impliedFormat":1},{"version":"3e7efde639c6a6c3edb9847b3f61e308bf7a69685b92f665048c45132f51c218","impliedFormat":1},{"version":"df45ca1176e6ac211eae7ddf51336dc075c5314bc5c253651bae639defd5eec5","impliedFormat":1},{"version":"3754982006a3b32c502cff0867ca83584f7a43b1035989ca73603f400de13c96","impliedFormat":1},{"version":"a30ae9bb8a8fa7b90f24b8a0496702063ae4fe75deb27da731ed4a03b2eb6631","impliedFormat":1},{"version":"f974e4a06953682a2c15d5bd5114c0284d5abf8bc0fe4da25cb9159427b70072","impliedFormat":1},{"version":"50256e9c31318487f3752b7ac12ff365c8949953e04568009c8705db802776fb","impliedFormat":1},{"version":"7d73b24e7bf31dfb8a931ca6c4245f6bb0814dfae17e4b60c9e194a631fe5f7b","impliedFormat":1},{"version":"413586add0cfe7369b64979d4ec2ed56c3f771c0667fbde1bf1f10063ede0b08","impliedFormat":1},{"version":"06472528e998d152375ad3bd8ebcb69ff4694fd8d2effaf60a9d9f25a37a097a","impliedFormat":1},{"version":"50b5bc34ce6b12eccb76214b51aadfa56572aa6cc79c2b9455cdbb3d6c76af1d","impliedFormat":1},{"version":"b7e16ef7f646a50991119b205794ebfd3a4d8f8e0f314981ebbe991639023d0e","impliedFormat":1},{"version":"42c169fb8c2d42f4f668c624a9a11e719d5d07dacbebb63cbcf7ef365b0a75b3","impliedFormat":1},{"version":"a401617604fa1f6ce437b81689563dfdc377069e4c58465dbd8d16069aede0a5","impliedFormat":1},{"version":"e9dd71cf12123419c60dab867d44fbee5c358169f99529121eaef277f5c83531","impliedFormat":1},{"version":"5b6a189ba3a0befa1f5d9cb028eb9eec2af2089c32f04ff50e2411f63d70f25d","impliedFormat":1},{"version":"d6e73f8010935b7b4c7487b6fb13ea197cc610f0965b759bec03a561ccf8423a","impliedFormat":1},{"version":"174f3864e398f3f33f9a446a4f403d55a892aa55328cf6686135dfaf9e171657","impliedFormat":1},{"version":"824c76aec8d8c7e65769688cbee102238c0ef421ed6686f41b2a7d8e7e78a931","impliedFormat":1},{"version":"75b868be3463d5a8cfc0d9396f0a3d973b8c297401d00bfb008a42ab16643f13","impliedFormat":1},{"version":"15a234e5031b19c48a69ccc1607522d6e4b50f57d308ecb7fe863d44cd9f9eb3","impliedFormat":1},{"version":"d682336018141807fb602709e2d95a192828fcb8d5ba06dda3833a8ea98f69e3","impliedFormat":1},{"version":"6124e973eab8c52cabf3c07575204efc1784aca6b0a30c79eb85fe240a857efa","impliedFormat":1},{"version":"0d891735a21edc75df51f3eb995e18149e119d1ce22fd40db2b260c5960b914e","impliedFormat":1},{"version":"3b414b99a73171e1c4b7b7714e26b87d6c5cb03d200352da5342ab4088a54c85","impliedFormat":1},{"version":"4fbd3116e00ed3a6410499924b6403cc9367fdca303e34838129b328058ede40","impliedFormat":1},{"version":"b01bd582a6e41457bc56e6f0f9de4cb17f33f5f3843a7cf8210ac9c18472fb0f","impliedFormat":1},{"version":"0a437ae178f999b46b6153d79095b60c42c996bc0458c04955f1c996dc68b971","impliedFormat":1},{"version":"74b2a5e5197bd0f2e0077a1ea7c07455bbea67b87b0869d9786d55104006784f","impliedFormat":1},{"version":"4a7baeb6325920044f66c0f8e5e6f1f52e06e6d87588d837bdf44feb6f35c664","impliedFormat":1},{"version":"6dcf60530c25194a9ee0962230e874ff29d34c59605d8e069a49928759a17e0a","impliedFormat":1},{"version":"7274fbffbd7c9589d8d0ffba68157237afd5cecff1e99881ea3399127e60572f","impliedFormat":1},{"version":"1a42d2ec31a1fe62fdc51591768695ed4a2dc64c01be113e7ff22890bebb5e3f","impliedFormat":1},{"version":"1a82deef4c1d39f6882f28d275cad4c01f907b9b39be9cbc472fcf2cf051e05b","impliedFormat":1},{"version":"c5426dbfc1cf90532f66965a7aa8c1136a78d4d0f96d8180ecbfc11d7722f1a5","impliedFormat":1},{"version":"65a15fc47900787c0bd18b603afb98d33ede930bed1798fc984d5ebb78b26cf9","impliedFormat":1},{"version":"9d202701f6e0744adb6314d03d2eb8fc994798fc83d91b691b75b07626a69801","impliedFormat":1},{"version":"de9d2df7663e64e3a91bf495f315a7577e23ba088f2949d5ce9ec96f44fba37d","impliedFormat":1},{"version":"c7af78a2ea7cb1cd009cfb5bdb48cd0b03dad3b54f6da7aab615c2e9e9d570c5","impliedFormat":1},{"version":"1ee45496b5f8bdee6f7abc233355898e5bf9bd51255db65f5ff7ede617ca0027","impliedFormat":1},{"version":"0c7c947ff881c4274c0800deaa0086971e0bfe51f89a33bd3048eaa3792d4876","affectsGlobalScope":true,"impliedFormat":1},{"version":"db01d18853469bcb5601b9fc9826931cc84cc1a1944b33cad76fd6f1e3d8c544","affectsGlobalScope":true,"impliedFormat":1},{"version":"a8f8e6ab2fa07b45251f403548b78eaf2022f3c2254df3dc186cb2671fe4996d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fa6c12a7c0f6b84d512f200690bfc74819e99efae69e4c95c4cd30f6884c526e","impliedFormat":1},{"version":"f1c32f9ce9c497da4dc215c3bc84b722ea02497d35f9134db3bb40a8d918b92b","impliedFormat":1},{"version":"b73c319af2cc3ef8f6421308a250f328836531ea3761823b4cabbd133047aefa","affectsGlobalScope":true,"impliedFormat":1},{"version":"e433b0337b8106909e7953015e8fa3f2d30797cea27141d1c5b135365bb975a6","impliedFormat":1},{"version":"15b36126e0089bfef173ab61329e8286ce74af5e809d8a72edcafd0cc049057f","impliedFormat":1},{"version":"ddff7fc6edbdc5163a09e22bf8df7bef75f75369ebd7ecea95ba55c4386e2441","impliedFormat":1},{"version":"106c6025f1d99fd468fd8bf6e5bda724e11e5905a4076c5d29790b6c3745e50c","impliedFormat":1},{"version":"a57b1802794433adec9ff3fed12aa79d671faed86c49b09e02e1ac41b4f1d33a","impliedFormat":1},{"version":"ad10d4f0517599cdeca7755b930f148804e3e0e5b5a3847adce0f1f71bbccd74","impliedFormat":1},{"version":"1042064ece5bb47d6aba91648fbe0635c17c600ebdf567588b4ca715602f0a9d","impliedFormat":1},{"version":"c49469a5349b3cc1965710b5b0f98ed6c028686aa8450bcb3796728873eb923e","impliedFormat":1},{"version":"4a889f2c763edb4d55cb624257272ac10d04a1cad2ed2948b10ed4a7fda2a428","impliedFormat":1},{"version":"7bb79aa2fead87d9d56294ef71e056487e848d7b550c9a367523ee5416c44cfa","impliedFormat":1},{"version":"72d63643a657c02d3e51cd99a08b47c9b020a565c55f246907050d3c8a5e77fb","impliedFormat":1},{"version":"1d415445ea58f8033ba199703e55ff7483c52ac6742075b803bd3e7bbe9f5d61","impliedFormat":1},{"version":"d6406c629bb3efc31aedb2de809bef471e475c86c7e67f3ef9b676b5d7e0d6b2","impliedFormat":1},{"version":"27ff4196654e6373c9af16b6165120e2dd2169f9ad6abb5c935af5abd8c7938c","impliedFormat":1},{"version":"71d8ba39a9e024d9e4bb922464d18542ed8d2c25ee78efa7890c27213cc6e5d3","impliedFormat":1},{"version":"8c030e515014c10a2b98f9f48408e3ba18023dfd3f56e3312c6c2f3ae1f55a16","impliedFormat":1},{"version":"dafc31e9e8751f437122eb8582b93d477e002839864410ff782504a12f2a550c","impliedFormat":1},{"version":"754498c5208ce3c5134f6eabd49b25cf5e1a042373515718953581636491f3c3","impliedFormat":1},{"version":"9c82171d836c47486074e4ca8e059735bf97b205e70b196535b5efd40cbe1bc5","impliedFormat":1},{"version":"f56bdc6884648806d34bc66d31cdb787c4718d04105ce2cd88535db214631f82","impliedFormat":1},{"version":"633d58a237f4bb25ec7d565e4ffa32cecdcee8660ac12189c4351c52557cee9e","impliedFormat":1},{"version":"2e4f37ffe8862b14d8e24ae8763daaa8340c0df0b859d9a9733def0eee7562d9","impliedFormat":1},{"version":"13283350547389802aa35d9f2188effaeac805499169a06ef5cd77ce2a0bd63f","impliedFormat":1},{"version":"ce791f6ea807560f08065d1af6014581eeb54a05abd73294777a281b6dfd73c2","impliedFormat":1},{"version":"6ac6715916fa75a1f7ebdfeacac09513b4d904b667d827b7535e84ff59679aff","impliedFormat":1},{"version":"49f95e989b4632c6c2a578cc0078ee19a5831832d79cc59abecf5160ea71abad","impliedFormat":1},{"version":"9666533332f26e8995e4d6fe472bdeec9f15d405693723e6497bf94120c566c8","impliedFormat":1},{"version":"ce0df82a9ae6f914ba08409d4d883983cc08e6d59eb2df02d8e4d68309e7848b","impliedFormat":1},{"version":"796273b2edc72e78a04e86d7c58ae94d370ab93a0ddf40b1aa85a37a1c29ecd7","impliedFormat":1},{"version":"5df15a69187d737d6d8d066e189ae4f97e41f4d53712a46b2710ff9f8563ec9f","impliedFormat":1},{"version":"e17cd049a1448de4944800399daa4a64c5db8657cc9be7ef46be66e2a2cd0e7c","impliedFormat":1},{"version":"43fa6ea8714e18adc312b30450b13562949ba2f205a1972a459180fa54471018","impliedFormat":1},{"version":"6e89c2c177347d90916bad67714d0fb473f7e37fb3ce912f4ed521fe2892cd0d","impliedFormat":1},{"version":"43ba4f2fa8c698f5c304d21a3ef596741e8e85a810b7c1f9b692653791d8d97a","impliedFormat":1},{"version":"4d4927cbee21750904af7acf940c5e3c491b4d5ebc676530211e389dd375607a","impliedFormat":1},{"version":"72105519d0390262cf0abe84cf41c926ade0ff475d35eb21307b2f94de985778","impliedFormat":1},{"version":"8a97e578a9bc40eb4f1b0ca78f476f2e9154ecbbfd5567ee72943bab37fc156a","impliedFormat":1},{"version":"c857e0aae3f5f444abd791ec81206020fbcc1223e187316677e026d1c1d6fe08","impliedFormat":1},{"version":"ccf6dd45b708fb74ba9ed0f2478d4eb9195c9dfef0ff83a6092fa3cf2ff53b4f","impliedFormat":1},{"version":"2d7db1d73456e8c5075387d4240c29a2a900847f9c1bff106a2e490da8fbd457","impliedFormat":1},{"version":"2b15c805f48e4e970f8ec0b1915f22d13ca6212375e8987663e2ef5f0205e832","impliedFormat":1},{"version":"f22d05663d873ee7a600faf78abb67f3f719d32266803440cf11d5db7ac0cab2","impliedFormat":1},{"version":"d93c544ad20197b3976b0716c6d5cd5994e71165985d31dcab6e1f77feb4b8f2","impliedFormat":1},{"version":"35069c2c417bd7443ae7c7cafd1de02f665bf015479fec998985ffbbf500628c","impliedFormat":1},{"version":"a8b1c79a833ee148251e88a2553d02ce1641d71d2921cce28e79678f3d8b96aa","impliedFormat":1},{"version":"126d4f950d2bba0bd45b3a86c76554d4126c16339e257e6d2fabf8b6bf1ce00c","impliedFormat":1},{"version":"7e0b7f91c5ab6e33f511efc640d36e6f933510b11be24f98836a20a2dc914c2d","impliedFormat":1},{"version":"045b752f44bf9bbdcaffd882424ab0e15cb8d11fa94e1448942e338c8ef19fba","impliedFormat":1},{"version":"2894c56cad581928bb37607810af011764a2f511f575d28c9f4af0f2ef02d1ab","impliedFormat":1},{"version":"0a72186f94215d020cb386f7dca81d7495ab6c17066eb07d0f44a5bf33c1b21a","impliedFormat":1},{"version":"2d3cc2211f352f46ea6b7cf2c751c141ffcdf514d6e7ae7ee20b7b6742da313f","impliedFormat":1},{"version":"c75445151ff8b77d9923191efed7203985b1a9e09eccf4b054e7be864e27923d","impliedFormat":1},{"version":"0aedb02516baf3e66b2c1db9fef50666d6ed257edac0f866ea32f1aa05aa474f","impliedFormat":1},{"version":"fa8a8fbf91ee2a4779496225f0312aac6635b0f21aa09cdafa4283fe32d519c5","affectsGlobalScope":true,"impliedFormat":1},{"version":"0e8aef93d79b000deb6ec336b5645c87de167168e184e84521886f9ecc69a4b5","impliedFormat":1},{"version":"56ccb49443bfb72e5952f7012f0de1a8679f9f75fc93a5c1ac0bafb28725fc5f","impliedFormat":1},{"version":"20fa37b636fdcc1746ea0738f733d0aed17890d1cd7cb1b2f37010222c23f13e","impliedFormat":1},{"version":"d90b9f1520366d713a73bd30c5a9eb0040d0fb6076aff370796bc776fd705943","impliedFormat":1},{"version":"bc03c3c352f689e38c0ddd50c39b1e65d59273991bfc8858a9e3c0ebb79c023b","impliedFormat":1},{"version":"19df3488557c2fc9b4d8f0bac0fd20fb59aa19dec67c81f93813951a81a867f8","affectsGlobalScope":true,"impliedFormat":1},{"version":"b25350193e103ae90423c5418ddb0ad1168dc9c393c9295ef34980b990030617","affectsGlobalScope":true,"impliedFormat":1},{"version":"bef86adb77316505c6b471da1d9b8c9e428867c2566270e8894d4d773a1c4dc2","impliedFormat":1},{"version":"de7052bfee2981443498239a90c04ea5cc07065d5b9bb61b12cb6c84313ad4ef","impliedFormat":1},{"version":"a3e7d932dc9c09daa99141a8e4800fc6c58c625af0d4bbb017773dc36da75426","impliedFormat":1},{"version":"43e96a3d5d1411ab40ba2f61d6a3192e58177bcf3b133a80ad2a16591611726d","impliedFormat":1},{"version":"4a2edd238d9104eac35b60d727f1123de5062f452b70ed8e0366cb36387dfdfd","impliedFormat":1},{"version":"ca921bf56756cb6fe957f6af693a35251b134fb932dc13f3dfff0bb7106f80b4","impliedFormat":1},{"version":"fee92c97f1aa59eb7098a0cc34ff4df7e6b11bae71526aca84359a2575f313d8","impliedFormat":1},{"version":"0bd0297484aacea217d0b76e55452862da3c5d9e33b24430e0719d1161657225","impliedFormat":1},{"version":"2ab6d334bcbf2aff3acfc4fd8c73ecd82b981d3c3aa47b3f3b89281772286904","impliedFormat":1},{"version":"d07cbc787a997d83f7bde3877fec5fb5b12ce8c1b7047eb792996ed9726b4dde","impliedFormat":1},{"version":"6ac6715916fa75a1f7ebdfeacac09513b4d904b667d827b7535e84ff59679aff","impliedFormat":1},{"version":"4805f6161c2c8cefb8d3b8bd96a080c0fe8dbc9315f6ad2e53238f9a79e528a6","impliedFormat":1},{"version":"b83cb14474fa60c5f3ec660146b97d122f0735627f80d82dd03e8caa39b4388c","impliedFormat":1},{"version":"f374cb24e93e7798c4d9e83ff872fa52d2cdb36306392b840a6ddf46cb925cb6","impliedFormat":1},{"version":"49179c6a23701c642bd99abe30d996919748014848b738d8e85181fc159685ff","impliedFormat":1},{"version":"b73cbf0a72c8800cf8f96a9acfe94f3ad32ca71342a8908b8ae484d61113f647","impliedFormat":1},{"version":"bae6dd176832f6423966647382c0d7ba9e63f8c167522f09a982f086cd4e8b23","impliedFormat":1},{"version":"20865ac316b8893c1a0cc383ccfc1801443fbcc2a7255be166cf90d03fac88c9","impliedFormat":1},{"version":"c9958eb32126a3843deedda8c22fb97024aa5d6dd588b90af2d7f2bfac540f23","impliedFormat":1},{"version":"461d0ad8ae5f2ff981778af912ba71b37a8426a33301daa00f21c6ccb27f8156","impliedFormat":1},{"version":"e927c2c13c4eaf0a7f17e6022eee8519eb29ef42c4c13a31e81a611ab8c95577","impliedFormat":1},{"version":"fcafff163ca5e66d3b87126e756e1b6dfa8c526aa9cd2a2b0a9da837d81bbd72","impliedFormat":1},{"version":"70246ad95ad8a22bdfe806cb5d383a26c0c6e58e7207ab9c431f1cb175aca657","impliedFormat":1},{"version":"f00f3aa5d64ff46e600648b55a79dcd1333458f7a10da2ed594d9f0a44b76d0b","impliedFormat":1},{"version":"772d8d5eb158b6c92412c03228bd9902ccb1457d7a705b8129814a5d1a6308fc","impliedFormat":1},{"version":"45490817629431853543adcb91c0673c25af52a456479588b6486daba34f68bb","impliedFormat":1},{"version":"802e797bcab5663b2c9f63f51bdf67eff7c41bc64c0fd65e6da3e7941359e2f7","impliedFormat":1},{"version":"8b4327413e5af38cd8cb97c59f48c3c866015d5d642f28518e3a891c469f240e","impliedFormat":1},{"version":"8514c62ce38e58457d967e9e73f128eedc1378115f712b9eef7127f7c88f82ae","impliedFormat":1},{"version":"f1289e05358c546a5b664fbb35a27738954ec2cc6eb4137350353099d154fc62","impliedFormat":1},{"version":"4b20fcf10a5413680e39f5666464859fc56b1003e7dfe2405ced82371ebd49b6","impliedFormat":1},{"version":"1d17ba45cfbe77a9c7e0df92f7d95f3eefd49ee23d1104d0548b215be56945ad","impliedFormat":1},{"version":"f7d628893c9fa52ba3ab01bcb5e79191636c4331ee5667ecc6373cbccff8ae12","impliedFormat":1},{"version":"1d879125d1ec570bf04bc1f362fdbe0cb538315c7ac4bcfcdf0c1e9670846aa6","impliedFormat":1},{"version":"e16344db9b8ee59d41abdb3a61e4470955b5712c0ee869fb47112e152aabe142","impliedFormat":1},{"version":"46273e8c29816125d0d0b56ce9a849cc77f60f9a5ba627447501d214466f0ff3","impliedFormat":1},{"version":"d663134457d8d669ae0df34eabd57028bddc04fc444c4bc04bc5215afc91e1f4","impliedFormat":1},{"version":"144bc326e90b894d1ec78a2af3ffb2eb3733f4d96761db0ca0b6239a8285f972","impliedFormat":1},{"version":"3af3584f79c57853028ef9421ec172539e1fe01853296dc05a9d615ade4ffaf6","impliedFormat":1},{"version":"f82579d87701d639ff4e3930a9b24f4ee13ca74221a9a3a792feb47f01881a9c","impliedFormat":1},{"version":"d7e5d5245a8ba34a274717d085174b2c9827722778129b0081fefd341cca8f55","impliedFormat":1},{"version":"d9d32f94056181c31f553b32ce41d0ef75004912e27450738d57efcd2409c324","impliedFormat":1},{"version":"752513f35f6cff294ffe02d6027c41373adf7bfa35e593dbfd53d95c203635ee","impliedFormat":1},{"version":"6c800b281b9e89e69165fd11536195488de3ff53004e55905e6c0059a2d8591e","impliedFormat":1},{"version":"7d4254b4c6c67a29d5e7f65e67d72540480ac2cfb041ca484847f5ae70480b62","impliedFormat":1},{"version":"1a7e2ea171726446850ec72f4d1525d547ff7e86724cc9e7eec509725752a758","impliedFormat":1},{"version":"8c901126d73f09ecdea4785e9a187d1ac4e793e07da308009db04a7283ec2f37","impliedFormat":1},{"version":"db97922b767bd2675fdfa71e08b49c38b7d2c847a1cc4a7274cb77be23b026f1","impliedFormat":1},{"version":"aab290b8e4b7c399f2c09b957666fc95335eb4522b2dd9ead1bf0cb64da6d6ee","impliedFormat":1},{"version":"94fe3281392e1015b22f39535878610b4fa6f1388dc8d78746be3bc4e4bb8950","impliedFormat":1},{"version":"2652448ac55a2010a1f71dd141f828b682298d39728f9871e1cdf8696ef443fd","impliedFormat":1},{"version":"06c25ddfc2242bd06c19f66c9eae4c46d937349a267810f89783680a1d7b5259","impliedFormat":1},{"version":"120599fd965257b1f4d0ff794bc696162832d9d8467224f4665f713a3119078b","impliedFormat":1},{"version":"5433f33b0a20300cca35d2f229a7fc20b0e8477c44be2affeb21cb464af60c76","impliedFormat":1},{"version":"db036c56f79186da50af66511d37d9fe77fa6793381927292d17f81f787bb195","impliedFormat":1},{"version":"bd4131091b773973ca5d2326c60b789ab1f5e02d8843b3587effe6e1ea7c9d86","impliedFormat":1},{"version":"c7f6485931085bf010fbaf46880a9b9ec1a285ad9dc8c695a9e936f5a48f34b4","impliedFormat":1},{"version":"14f6b927888a1112d662877a5966b05ac1bf7ed25d6c84386db4c23c95a5363b","impliedFormat":1},{"version":"6ac6715916fa75a1f7ebdfeacac09513b4d904b667d827b7535e84ff59679aff","impliedFormat":1},{"version":"0427df5c06fafc5fe126d14b9becd24160a288deff40e838bfbd92a35f8d0d00","impliedFormat":1},{"version":"90c54a02432d04e4246c87736e53a6a83084357acfeeba7a489c5422b22f5c7a","impliedFormat":1},{"version":"49c346823ba6d4b12278c12c977fb3a31c06b9ca719015978cb145eb86da1c61","impliedFormat":1},{"version":"bfac6e50eaa7e73bb66b7e052c38fdc8ccfc8dbde2777648642af33cf349f7f1","impliedFormat":1},{"version":"92f7c1a4da7fbfd67a2228d1687d5c2e1faa0ba865a94d3550a3941d7527a45d","impliedFormat":1},{"version":"f53b120213a9289d9a26f5af90c4c686dd71d91487a0aa5451a38366c70dc64b","impliedFormat":1},{"version":"83fe880c090afe485a5c02262c0b7cdd76a299a50c48d9bde02be8e908fb4ae6","impliedFormat":1},{"version":"0a372c2d12a259da78e21b25974d2878502f14d89c6d16b97bd9c5017ab1bc12","impliedFormat":1},{"version":"57d67b72e06059adc5e9454de26bbfe567d412b962a501d263c75c2db430f40e","impliedFormat":1},{"version":"6511e4503cf74c469c60aafd6589e4d14d5eb0a25f9bf043dcbecdf65f261972","impliedFormat":1},{"version":"ec1ca97598eda26b7a5e6c8053623acbd88e43be7c4d29c77ccd57abc4c43999","impliedFormat":1},{"version":"6e2261cd9836b2c25eecb13940d92c024ebed7f8efe23c4b084145cd3a13b8a6","impliedFormat":1},{"version":"a67b87d0281c97dfc1197ef28dfe397fc2c865ccd41f7e32b53f647184cc7307","impliedFormat":1},{"version":"771ffb773f1ddd562492a6b9aaca648192ac3f056f0e1d997678ff97dbb6bf9b","impliedFormat":1},{"version":"232f70c0cf2b432f3a6e56a8dc3417103eb162292a9fd376d51a3a9ea5fbbf6f","impliedFormat":1},{"version":"a47e6d954d22dd9ebb802e7e431b560ed7c581e79fb885e44dc92ed4f60d4c07","impliedFormat":1},{"version":"f019e57d2491c159d47a107fd90219a1734bdd2e25cd8d1db3c8fae5c6b414c4","impliedFormat":1},{"version":"8a0e762ceb20c7e72504feef83d709468a70af4abccb304f32d6b9bac1129b2c","impliedFormat":1},{"version":"d1c9bf292a54312888a77bb19dba5e2503ad803f5393beafd45d78d2f4fe9b48","impliedFormat":1},{"version":"9252d498a77517aab5d8d4b5eb9d71e4b225bbc7123df9713e08181de63180f6","impliedFormat":1},{"version":"cb8d8ef7b9ce8ed3e6f1c814fcbf3f90dab0cb8863079236784fc350746e27c4","impliedFormat":1},{"version":"35e6379c3f7cb27b111ad4c1aa69538fd8e788ab737b8ff7596a1b40e96f4f90","impliedFormat":1},{"version":"1fffe726740f9787f15b532e1dc870af3cd964dbe29e191e76121aa3dd8693f2","impliedFormat":1},{"version":"3be035da7bee86b4c3abf392e0edaa44fc6e45092995eefe36b39118c8a84068","affectsGlobalScope":true,"impliedFormat":1},{"version":"8f828825d077c2fa0ea606649faeb122749273a353daab23924fe674e98ba44c","impliedFormat":1},{"version":"2896c2e673a5d3bd9b4246811f79486a073cbb03950c3d252fba10003c57411a","impliedFormat":1},{"version":"616775f16134fa9d01fc677ad3f76e68c051a056c22ab552c64cc281a9686790","impliedFormat":1},{"version":"65c24a8baa2cca1de069a0ba9fba82a173690f52d7e2d0f1f7542d59d5eb4db0","impliedFormat":1},{"version":"f9fe6af238339a0e5f7563acee3178f51db37f32a2e7c09f85273098cee7ec49","impliedFormat":1},{"version":"407a06ba04eede4074eec470ecba2784cbb3bf4e7de56833b097dd90a2aa0651","impliedFormat":1},{"version":"77e71242e71ebf8528c5802993697878f0533db8f2299b4d36aa015bae08a79c","impliedFormat":1},{"version":"98a787be42bd92f8c2a37d7df5f13e5992da0d967fab794adbb7ee18370f9849","impliedFormat":1},{"version":"5c96bad5f78466785cdad664c056e9e2802d5482ca5f862ed19ba34ffbb7b3a4","impliedFormat":1},{"version":"81d8603ac527e75cfec72bb9391228b58f161c2b33514a9d814c7f3ebd3ef466","impliedFormat":1},{"version":"5f3dc10ae646f375776b4e028d2bed039a93eebbba105694d8b910feebbe8b9c","impliedFormat":1},{"version":"bb0cd7862b72f5eba39909c9889d566e198fcaddf7207c16737d0c2246112678","impliedFormat":1},{"version":"4545c1a1ceca170d5d83452dd7c4994644c35cf676a671412601689d9a62da35","impliedFormat":1},{"version":"320f4091e33548b554d2214ce5fc31c96631b513dffa806e2e3a60766c8c49d9","impliedFormat":1},{"version":"a2d648d333cf67b9aeac5d81a1a379d563a8ffa91ddd61c6179f68de724260ff","impliedFormat":1},{"version":"d90d5f524de38889d1e1dbc2aeef00060d779f8688c02766ddb9ca195e4a713d","impliedFormat":1},{"version":"a3f41ed1b4f2fc3049394b945a68ae4fdefd49fa1739c32f149d32c0545d67f5","impliedFormat":1},{"version":"bad68fd0401eb90fe7da408565c8aee9c7a7021c2577aec92fa1382e8876071a","impliedFormat":1},{"version":"47699512e6d8bebf7be488182427189f999affe3addc1c87c882d36b7f2d0b0e","impliedFormat":1},{"version":"fec01479923e169fb52bd4f668dbeef1d7a7ea6e6d491e15617b46f2cacfa37d","impliedFormat":1},{"version":"8a8fb3097ba52f0ae6530ec6ab34e43e316506eb1d9aa29420a4b1e92a81442d","impliedFormat":1},{"version":"44e09c831fefb6fe59b8e65ad8f68a7ecc0e708d152cfcbe7ba6d6080c31c61e","impliedFormat":1},{"version":"1c0a98de1323051010ce5b958ad47bc1c007f7921973123c999300e2b7b0ecc0","impliedFormat":1},{"version":"4655709c9cb3fd6db2b866cab7c418c40ed9533ce8ea4b66b5f17ec2feea46a9","impliedFormat":1},{"version":"87affad8e2243635d3a191fa72ef896842748d812e973b7510a55c6200b3c2a4","impliedFormat":1},{"version":"ad036a85efcd9e5b4f7dd5c1a7362c8478f9a3b6c3554654ca24a29aa850a9c5","impliedFormat":1},{"version":"fedebeae32c5cdd1a85b4e0504a01996e4a8adf3dfa72876920d3dd6e42978e7","impliedFormat":1},{"version":"3eecb25bb467a948c04874d70452b14ae7edb707660aac17dc053e42f2088b00","impliedFormat":1},{"version":"cdf21eee8007e339b1b9945abf4a7b44930b1d695cc528459e68a3adc39a622e","impliedFormat":1},{"version":"330896c1a2b9693edd617be24fbf9e5895d6e18c7955d6c08f028f272b37314d","impliedFormat":1},{"version":"1d9c0a9a6df4e8f29dc84c25c5aa0bb1da5456ebede7a03e03df08bb8b27bae6","impliedFormat":1},{"version":"84380af21da938a567c65ef95aefb5354f676368ee1a1cbb4cae81604a4c7d17","impliedFormat":1},{"version":"1af3e1f2a5d1332e136f8b0b95c0e6c0a02aaabd5092b36b64f3042a03debf28","impliedFormat":1},{"version":"30d8da250766efa99490fc02801047c2c6d72dd0da1bba6581c7e80d1d8842a4","impliedFormat":1},{"version":"03566202f5553bd2d9de22dfab0c61aa163cabb64f0223c08431fb3fc8f70280","impliedFormat":1},{"version":"5f0292a40df210ab94b9fb44c8b775c51e96777e14e073900e392b295ca1061b","impliedFormat":1},{"version":"bc9ee0192f056b3d5527bcd78dc3f9e527a9ba2bdc0a2c296fbc9027147df4b2","impliedFormat":1},{"version":"8627ad129bcf56e82adff0ab5951627c993937aa99f5949c33240d690088b803","impliedFormat":1},{"version":"1de80059b8078ea5749941c9f863aa970b4735bdbb003be4925c853a8b6b4450","impliedFormat":1},{"version":"1d079c37fa53e3c21ed3fa214a27507bda9991f2a41458705b19ed8c2b61173d","impliedFormat":1},{"version":"5bf5c7a44e779790d1eb54c234b668b15e34affa95e78eada73e5757f61ed76a","impliedFormat":1},{"version":"5835a6e0d7cd2738e56b671af0e561e7c1b4fb77751383672f4b009f4e161d70","impliedFormat":1},{"version":"5c634644d45a1b6bc7b05e71e05e52ec04f3d73d9ac85d5927f647a5f965181a","impliedFormat":1},{"version":"4b7f74b772140395e7af67c4841be1ab867c11b3b82a51b1aeb692822b76c872","impliedFormat":1},{"version":"27be6622e2922a1b412eb057faa854831b95db9db5035c3f6d4b677b902ab3b7","impliedFormat":1},{"version":"a68d4b3182e8d776cdede7ac9630c209a7bfbb59191f99a52479151816ef9f9e","impliedFormat":99},{"version":"39644b343e4e3d748344af8182111e3bbc594930fff0170256567e13bbdbebb0","impliedFormat":99},{"version":"ed7fd5160b47b0de3b1571c5c5578e8e7e3314e33ae0b8ea85a895774ee64749","impliedFormat":99},{"version":"63a7595a5015e65262557f883463f934904959da563b4f788306f699411e9bac","impliedFormat":1},{"version":"ecbaf0da125974be39c0aac869e403f72f033a4e7fd0d8cd821a8349b4159628","impliedFormat":1},{"version":"4ba137d6553965703b6b55fd2000b4e07ba365f8caeb0359162ad7247f9707a6","impliedFormat":1},{"version":"ceec3c81b2d81f5e3b855d9367c1d4c664ab5046dff8fd56552df015b7ccbe8f","affectsGlobalScope":true,"impliedFormat":1},{"version":"8fac4a15690b27612d8474fb2fc7cc00388df52d169791b78d1a3645d60b4c8b","affectsGlobalScope":true,"impliedFormat":1},{"version":"064ac1c2ac4b2867c2ceaa74bbdce0cb6a4c16e7c31a6497097159c18f74aa7c","impliedFormat":1},{"version":"3dc14e1ab45e497e5d5e4295271d54ff689aeae00b4277979fdd10fa563540ae","impliedFormat":1},{"version":"1d63055b690a582006435ddd3aa9c03aac16a696fac77ce2ed808f3e5a06efab","impliedFormat":1},{"version":"b789bf89eb19c777ed1e956dbad0925ca795701552d22e68fd130a032008b9f9","impliedFormat":1},"f4e8976c19fc926644d72610bf1058bd6bf52add97e46a02bc0b912a751625c0","a5b489982805de6273a048820c55ea6177db634e66a985aac765d93ba8174248",{"version":"402e5c534fb2b85fa771170595db3ac0dd532112c8fa44fc23f233bc6967488b","impliedFormat":1},{"version":"8885cf05f3e2abf117590bbb951dcf6359e3e5ac462af1c901cfd24c6a6472e2","impliedFormat":1},{"version":"333caa2bfff7f06017f114de738050dd99a765c7eb16571c6d25a38c0d5365dc","impliedFormat":1},{"version":"e61df3640a38d535fd4bc9f4a53aef17c296b58dc4b6394fd576b808dd2fe5e6","impliedFormat":1},{"version":"459920181700cec8cbdf2a5faca127f3f17fd8dd9d9e577ed3f5f3af5d12a2e4","impliedFormat":1},{"version":"4719c209b9c00b579553859407a7e5dcfaa1c472994bd62aa5dd3cc0757eb077","impliedFormat":1},{"version":"7ec359bbc29b69d4063fe7dad0baaf35f1856f914db16b3f4f6e3e1bca4099fa","impliedFormat":1},{"version":"70790a7f0040993ca66ab8a07a059a0f8256e7bb57d968ae945f696cbff4ac7a","impliedFormat":1},{"version":"d1b9a81e99a0050ca7f2d98d7eedc6cda768f0eb9fa90b602e7107433e64c04c","impliedFormat":1},{"version":"a022503e75d6953d0e82c2c564508a5c7f8556fad5d7f971372d2d40479e4034","impliedFormat":1},{"version":"b215c4f0096f108020f666ffcc1f072c81e9f2f95464e894a5d5f34c5ea2a8b1","impliedFormat":1},{"version":"644491cde678bd462bb922c1d0cfab8f17d626b195ccb7f008612dc31f445d2d","impliedFormat":1},{"version":"dfe54dab1fa4961a6bcfba68c4ca955f8b5bbeb5f2ab3c915aa7adaa2eabc03a","impliedFormat":1},{"version":"1251d53755b03cde02466064260bb88fd83c30006a46395b7d9167340bc59b73","impliedFormat":1},{"version":"47865c5e695a382a916b1eedda1b6523145426e48a2eae4647e96b3b5e52024f","impliedFormat":1},{"version":"4cdf27e29feae6c7826cdd5c91751cc35559125e8304f9e7aed8faef97dcf572","impliedFormat":1},{"version":"331b8f71bfae1df25d564f5ea9ee65a0d847c4a94baa45925b6f38c55c7039bf","impliedFormat":1},{"version":"2a771d907aebf9391ac1f50e4ad37952943515eeea0dcc7e78aa08f508294668","impliedFormat":1},{"version":"0146fd6262c3fd3da51cb0254bb6b9a4e42931eb2f56329edd4c199cb9aaf804","impliedFormat":1},{"version":"183f480885db5caa5a8acb833c2be04f98056bdcc5fb29e969ff86e07efe57ab","impliedFormat":99},{"version":"b558c9a18ea4e6e4157124465c3ef1063e64640da139e67be5edb22f534f2f08","impliedFormat":1},{"version":"01374379f82be05d25c08d2f30779fa4a4c41895a18b93b33f14aeef51768692","impliedFormat":1},{"version":"b0dee183d4e65cf938242efaf3d833c6b645afb35039d058496965014f158141","impliedFormat":1},{"version":"c0bbbf84d3fbd85dd60d040c81e8964cc00e38124a52e9c5dcdedf45fea3f213","impliedFormat":1},"9682a26e1b58dd1dff221d71d3be4b7c546fb27e7772919cd3010482184d0999","0eefc702c5de5c32835e290cbdcb5a76f6d98c956b87cdbf5da49c864d3ab88e",{"version":"848b257bdc73b54c167bed01b2db42fd44c1aab77350c9b1fc3a2ce3ff08f899","impliedFormat":99},{"version":"642b071c4d865ff593c1794e06fa1781a465338a37796a7d0e2501daa20172bb","impliedFormat":99},{"version":"ff87d4d02bfc715a3a038c825bb80d6a6cfd1750bec3a0fa6f0e407133b1cd2f","impliedFormat":99},{"version":"0da67df9ad0b921385c37c0eef3de0390b8ef7b0b36cf616fdf3590a67fed1e4","impliedFormat":99},{"version":"8b28fc7010a1cd9c54201b6015c7d6a04fc6a4981acc9eaa5d71d4523325c525","impliedFormat":99},{"version":"e72fd44d087aac63727f05fe82c0c4a984c6295f23fdb9a6290d4d9fda47cfc6","impliedFormat":99},{"version":"fa2cd67056be92f91c496c134c19c4e0a074103d79ffe5dd879c7ad0f3036b75","impliedFormat":99},{"version":"a5e9994461de1736556049ad37857577a0c1238cda95baed68d5997c45f353bb","impliedFormat":99},{"version":"479ae2d0df8346600082dab7eae56c7557bc0436ab0647c31063f3a02d7cc6c7","impliedFormat":99},{"version":"1faf72b15dbf252373a1f4b2867a199800a97cc7079dd3b01087a2f3c11f4a45","impliedFormat":99},{"version":"4af8ba68f5b509c7a7d353e7a91a4014d194779c537acd3afd330980e9cb972e","impliedFormat":99},{"version":"c4a78ea6e862ed9913ecf9b95c8b4a8f77384f2cf583eee7fc4b27c52f4fbaf7","impliedFormat":99},{"version":"ee9c59f4fa245924e3328a2a6620fe107b0c41860367a47a90d07a932e3a084a","impliedFormat":99},{"version":"e2a62c1f16bfc7e305e78143c1eeedb71ad1f272761aa07ff2ad70bb97248b13","impliedFormat":99},{"version":"1c1ba22485be7577e2a73cc103bf6e7a692ae0a025216619a4de42186eb1687f","impliedFormat":99},{"version":"ebbaa82abb5a1a7179dff85377f99c666dfa3a68f8a2ea405bbcf4f5e50149be","impliedFormat":99},{"version":"37673287f8bc80af36be1c23275ed9416bec11ee361b320a57a0b3a2797260ef","impliedFormat":99},{"version":"39e217bc229493f7b355cb2ce07f8600f6266500a6feaad43c254cc0a0f140e6","impliedFormat":99},{"version":"a33d889bdb82f43cdcd8158107c0e25d295d1838d82ee8649390ca0b88e87a07","impliedFormat":99},{"version":"f289d98b575f84044465ab3b7ac81e9a11b4d97cf2a9e92a822e948bc2f0ba7c","impliedFormat":99},{"version":"787a6b0d92df1148c7da1e75d705652030078c19e79985a72e622dc5b54487bc","impliedFormat":99},{"version":"ad09a94413ba6d37b86beaf348b252b378608dec764b67efc9e65d1dc4dff808","impliedFormat":99},{"version":"534611552e459b47ea008079ec9d520fa87c7b229c8a3a08993e8be650a8df71","impliedFormat":99},{"version":"d4ddcacf48fe1a273c71f4b04818eb912fd62bd7c0da092742a7e37a30cbc726","impliedFormat":99},{"version":"83098570d4ae37e31d8c788901c5d094313176a23f26870e78a5e98e5edd7777","impliedFormat":99},{"version":"9a43bf14da8556f0484d20c93074fd98845c06c85587f6b580fedda82685dc04","impliedFormat":99},{"version":"6a36631027099eca036047ab3bf0f262e23239c950ec1c65de700300d223ec44","impliedFormat":99},{"version":"fabd5be6cc2f406cbfcddb145a91a0e341a25e509e1c7521762441a46e58780d","impliedFormat":99},{"version":"80a9eb1f4fd46c863aec7d9c8490702ec6aee2b63ada21bcf231d558f5f0863f","impliedFormat":99},{"version":"72932bf7599a75bdd02788593ec6f060422f0c6e316f19629c568e2473d37db3","impliedFormat":99},{"version":"2d6244e9b7d19e7650ceaf09033ada34ebc2d0590a770707cba8da87bc125d59","impliedFormat":99},{"version":"6e0ce9899346df5cf80e1b45172eb655855837cdcad4539acbfc9305d8fc8976","impliedFormat":99},{"version":"ec56ed06f0a89fa2e3a20d7f7521d017e92decaebfa4d53afa0fd3a7b7a2be3b","impliedFormat":99},{"version":"236b90e223fa7a458b8561e87327d3737c8c83cac9ce4cdfbf04b3098da851fc","impliedFormat":99},{"version":"d64b03d8342c4f4d51706659a835c45197b9940b8938b0656bdc4df15d06f630","impliedFormat":99},{"version":"8ff0f34466d5136ab068a15358b99a958095d1c6e7e11c67d607111f81933fc4","impliedFormat":99},{"version":"98a72afb1b77b1c3f15beaed5662feb608060ccc604fa1747fba9d9deb070f80","impliedFormat":99},{"version":"cd5189682a722b13ec340b2bd112bd48b899bfeecd69d629bfc78166c15b64fe","impliedFormat":99},{"version":"a4017a3045a75c68509bc69bd314d91f7d4e1ec3f255416a86efe7dadfc0ee64","impliedFormat":99},{"version":"19599db9634ba72f2663db5b76de48dfe250280ffb4bb158109c97eb4e5b5e0c","impliedFormat":99},{"version":"f5a0ae8b389c104a6655117a8e4ee1b563c4d7cb33e045e4ce433cd6c458adb4","impliedFormat":99},{"version":"566247adad3c04b6c5b7e496700f3f23d05b07f31d983ab47fae1aa684b048c9","impliedFormat":99},{"version":"d10e1723404ce7559b75ab341c7e85b1ee2c97a871fe41630e47edcfb1bb1621","impliedFormat":99},{"version":"19b360e4fd7cd6683ab5455093e02c7c42dbbbd63ae5bd50f91f55a8709feb98","impliedFormat":99},{"version":"ee5ac98eb5ccf9bee886f916ae88856ac220858f4363362c094add66a0d44780","impliedFormat":99},{"version":"02efcdd69ab7a028c7fc316316748723763f7d40073f5b74f5136f9ded5c3073","impliedFormat":99},{"version":"35c834f84dabbdc3b595dd35c2208876af77af86ec3e0f4b622ff7f901843e80","impliedFormat":99},{"version":"dde618b9a6b780c35bffa2075f11ee29adabeaff3dbb73136ac530fd45035692","impliedFormat":99},{"version":"6902d565ebbc9b4f27b81f50a769ebbb673d299090146fc4d645c59b4433b36e","impliedFormat":99},{"version":"ac0e664aa993f918ba1006ca2bc107bbe326ec96341a61195f9b3054a9571a84","impliedFormat":99},{"version":"b184444a122ee085c61df92f39b23414dbe7adece24558bfd1a0dcb22687119a","impliedFormat":99},{"version":"b8ae8c7a851b19aa288e08fbd70e734f5aef3d1bca3a5844dd31658f1bd18f62","impliedFormat":99},{"version":"85377ed2b6029c8c1e851e1acf848d31fcfcc5a9264e726a00e046ccd68288ce","impliedFormat":99},{"version":"aa096547941633cc25eb4b24af41984906ca9e502d9c06c88e0b4bdc2a54b602","impliedFormat":99},{"version":"2b69aa435fea74564e7388f7530a8ce8dd5c9f9c6a67cf74aef6f99922f05885","impliedFormat":99},{"version":"6853837d5850cfd5322fa38f190d4afb7b1ad9d37b137a7fef26a1f489a48b61","impliedFormat":99},{"version":"60bc42d137352b88caf900df4bacbfee40c73c02b56b853e0a6d23a3edab7815","impliedFormat":99},{"version":"d90817a097f2792dc8ebaf9bc08298f1ab6cb18b4287c8cfc1cea297263df6d8","impliedFormat":99},{"version":"3cbfe6b48a9c4b5b6283f7fbce0c1f5ccf0796ac8b1a077893eeab13fc17ef73","impliedFormat":99},{"version":"01e1e0a0c437c0c64b0e55fd5b3123398c25b058fae214ffce0ff9a345d58777","impliedFormat":99},{"version":"23e97994d2f64dd504174c02b5ed713093f1f8c0ee4c3b587c7f12a641b8bcd2","impliedFormat":99},{"version":"fff0fc0ee248874a1f8d12dd439f1c26a8be008a18b7e36ee65c3e8cd634fe18","impliedFormat":99},{"version":"7e6a11a7c6f9070215e73bbf9620d274ef45979eadc8de39d50ffe6fb67f095f","impliedFormat":99},{"version":"c34715ff893e99af41dcf4f63b3eca2a45088ef0e4a025355204c440772278c8","impliedFormat":99},{"version":"cbc0e98cf745d22c786862ea94c3566321c06e04109f461d8f657a3c1151d057","impliedFormat":99},{"version":"35a5955d275c446b47dcbd8347f41e8ef6e230c563efd38f62ff715f7994b767","impliedFormat":99},{"version":"2bb58f627a12841ae367d97ee15993590a5cee4a826326e2a8be2bd750fca9dc","impliedFormat":99},{"version":"646d361245ec8052afcc7b90acb3c7f6098bf1340b88f32455e63f0369ddf1c5","impliedFormat":99},{"version":"2f6f7ba3522444f3d3e1f6d4a74ac37a096fc3dd292f902773d761a9eee57620","impliedFormat":99},{"version":"921a2668977ae600f6de2a4c19f53b237ed196ae5ae3297043b38781c11d9af4","impliedFormat":99},{"version":"37072b862cae4c819e335ac9eb855cc398719a50ebc60376de9ddae801610a3f","impliedFormat":99},{"version":"6473fc970e5fb3f18fece3e8681e9ad4454adf295be54463ebd74688b8b5051c","impliedFormat":99},{"version":"4e54895c51ee23f08f62fb429dbf4cfc1772ce34f6ec77855a3bb805dd16637e","impliedFormat":99},{"version":"800509db7d0b61907f28dea506dea01b57d2aa3cfffff03c78ccad04e8ceb976","impliedFormat":99},{"version":"887778667559bb4b7c64b006650210ca831f903c055ada3a069a8f08790c5718","impliedFormat":99},{"version":"bd96c9f72e9a439499bf19a4405d205fb8c2da019fdc9fea988c25005e76c200","impliedFormat":99},{"version":"9fe617a1d5e7c5c4290dd355f1bdbe1a80574d765a2c03bbf49181b664f11f0a","impliedFormat":99},{"version":"9afd17a7e1d2b474eb24bb37007d0f89ccbfb362ce904cd962a1eca5a2699efe","impliedFormat":99},{"version":"2841695cc12ded91f69e66e4ef44be524ad02c54bbe257bfe5802e3f6b851b5f","impliedFormat":99},{"version":"1132807f0a56b00d253a76bc21aeb23e79c8ba8478c52ec75ba9fcbd81738b6c","impliedFormat":99},{"version":"effbcf17a843123b20a54ce8389b29a26d12c056872f0dfc76a27460b22f716c","impliedFormat":99},{"version":"0e052acd94182df2373f96e362f064715c7a782452166e2344c91a126fb41d28","impliedFormat":99},{"version":"b7bce6b962d325bdd11ca9f3394b411f37354bffc995e798ddf66a7ee3b9de42","impliedFormat":99},{"version":"96d66cb0b5b2a0b1e2e6172e6bc163b5345f189c699852c61cb866812bc5f3a8","impliedFormat":99},{"version":"31e4839e4dc630cf7bc9f86c1afdf3dc2cdb4b8f6c3e11d12ba38ea18f4c421c","impliedFormat":99},{"version":"5b94968e8400015a4904bc1ba26ebcada2fd366c8fdeeb873904af54ccd4b423","impliedFormat":99},{"version":"1513e36e2863b615eaccce129355da5f5d8e169d4416d25e71e47dea8d2d35c5","impliedFormat":99},{"version":"7a128061e6e84e9375c9b46f84a0cfded49f537e8a41b42db0c72133666ab9e8","impliedFormat":99},{"version":"1140a35eddf2e3b9d706eeaf28ea74e969ecbe52e0d0f6a180d0753d2b8272b6","impliedFormat":99},{"version":"5e9cf2a12f0d82d2bb5989cbe663707bf1f3bdccf7cf0fdb84105712b9788e0d","impliedFormat":99},{"version":"ad1a1db6939b787bd9ae09f997f9b209cf0e689c72414090fb9cb7989163df77","impliedFormat":99},{"version":"95e857dd9e4984d5694b721cfe2d66430805db29660e212c2f681515ea08e8e3","impliedFormat":99},{"version":"2e8932517228de42f8b3db669c4d04d177367283d2da199b1a59cd94d009e65b","impliedFormat":99},{"version":"5fada2edbc2264cc0d7ab6080e8e4d4e3d5dfb9ef494b4eac9c8e49b90f30bdd","impliedFormat":99},{"version":"615d06216e7aaba7e4011cee475b9ef6d1d873886118f178bca21bc38ecc10a8","impliedFormat":99},{"version":"7ddf86abeff5aa521f606f9ea9ba2dfa19df8ee1c76ea838d9081afad9b28f68","impliedFormat":99},{"version":"ebd25c9c141e447ee6ba794c80c6878ebb39c42ad135a0c363d275af9fa6799f","impliedFormat":99},{"version":"4132803c2f4af9b16bd01853bc98cf15ec1ec9d11529665bc6ccff35bb742774","impliedFormat":99},{"version":"efb8e27827124bc3bf96b81288c0278c722d10f2372a1bbbb9de1943e784022b","impliedFormat":99},{"version":"1400f49fbe68fc416c2d47e19f6bc29a99a5480910bd560090a27fb6b68fbb45","impliedFormat":99},{"version":"98312a773a489dd6b924350b9ef0eeb1f9646b7e1b294501b582b67dd735cb3f","impliedFormat":99},{"version":"a9410e9f7d26ad51166dc6e55de1f0efe893750b665a4169cce9a1c46d793d18","impliedFormat":99},{"version":"4218cc9758b6c35b4e98131e96d6be902fddbc194936e0ba71a95767e11072b8","impliedFormat":99},{"version":"6f29d6927bf60c969a6a5ef57231d63b81e6820b6a30c22eef57821b5c429ac0","impliedFormat":99},{"version":"0df226906c522ff1903482f4fbb83c039fbd0dca93bd1b3e56baf10ab58bfa15","impliedFormat":99},{"version":"02f4852cbefa35edd7752e35e228adc060a2c98251be70a9446f64e196029bf0","impliedFormat":99},{"version":"4b70362f8a653d862fb4e5b65aec7943cb237e89c2409c76e6aabbe52952507a","impliedFormat":99},{"version":"8c83218e92cb960a020ce6ccf208c7fdd29fdba50f824ec9f7804b9a47c175d0","impliedFormat":99},{"version":"1da27ab546abd20e493012d7088a55cf1a00c92b61eb06aeb7a25581c6f9ff87","impliedFormat":99},{"version":"be74c0d9577d80d8dd6fc04a8830a2bc3ddbbad2570551305072dfd985534876","impliedFormat":99},{"version":"de3347bf58d986a47125ca7bcda6268d55c78d6006e165f59a03c20be861d95f","impliedFormat":99},{"version":"b1ef1a2530ef3a4308488bd172252a506f35fe16622d3117573865c2b6ad3485","impliedFormat":99},{"version":"840d2806690ca5764b0b3393d63fb4f4eed660f2fd1c60ed5a8aec3a851e775e","impliedFormat":99},{"version":"b222f67a18cb2bf55641e911467ca5f1cc4ce664824a3d4e2ffcc1eb91729be7","impliedFormat":99},{"version":"5a191a0753935e35e8560d6dff2a0bcbeccd0850d11a7429283e3061fa8bc8e9","impliedFormat":99},{"version":"e933510d79c03cb4b5ae2b910b72056332955385cd91774913ab04c9f7fdf00a","impliedFormat":99},{"version":"3a3dee0f5e3155384690cf45b07a217a6b3de5e1edb4e98ec8be5fbf3e500868","impliedFormat":99},{"version":"db2ed9c0a4fd561f950cea441d7833cd5763720e91856f7521d3d5f29fc4b7e7","impliedFormat":99},{"version":"c8a7aaf671a49f7f836c4bc4aa4fed23e5c8da16eccf72ef2b2a52197663e6cc","impliedFormat":99},{"version":"10856b8f8c6dc7fa2da62848f65f977524b073c6fd167578f21edda81832687d","impliedFormat":99},{"version":"067de9fe31618932f08e63ff479248118a39461669a06b6f6d438e7ef364cf29","impliedFormat":99},{"version":"f67169b04c2c933f4368c15bcea7f744f05a134dad020c6d418f7cca2f9926bb","impliedFormat":99},{"version":"078af8b67c2d6de37753072c1759f1215fb9846cd4344cf83a266b10fbbcd790","impliedFormat":99},{"version":"e27b61cd6a2953ce7955274ed4dafde236be2051b1a89d10b5640306b7d3b86f","impliedFormat":99},{"version":"91beef8d07b44eb328aa9d9c3db98bd79bde320adc5d1556d3fa7f463a90c3ce","impliedFormat":99},{"version":"7f7f86c7d1bc4a9f9e4d68fe0702f91667419056e6746494a66a7fd2882c8660","impliedFormat":99},{"version":"b87d0cb8854aa7f797bcc7f46ab0522fd091828dcce0463d4dc58b78e1f6ac10","impliedFormat":99},{"version":"934478c63db3268cc99351178a969840b2b93a2d21a4d165813cebe5eb6dc419","impliedFormat":99},{"version":"e1ac2ee125d07e130dafcc5727922a802f33bd894b3bdec61a58653cb25a105f","impliedFormat":99},{"version":"80d81e5352828e06dd7d79a0ad35f07087aa0c4f2aa80f8df7e67855ab544997","impliedFormat":99},{"version":"d78aa09669af770ed71d8a0d5b1e2ef23abb7a17dca11e1bc02adc9cf9816fd1","impliedFormat":99},{"version":"acc4b06dfbe56e355495b3571be65265b7bcba54ae6addebfc4c34823a3e8201","impliedFormat":99},{"version":"80d964c6d3fd00733c20f85ee9fa70c52e3459f1960adb17e5bf812cf9604ad6","impliedFormat":99},{"version":"1df19943bbd19525aa3e18c3f98f49ecdd788e07b5705d99c8579073771dcc06","impliedFormat":99},{"version":"8c2a083fb5fb302c0954709938e53e7810da0aa2daf489757af9dc6baa2f3b7a","impliedFormat":99},{"version":"fcc4a7940475065b3291a15623bc4e8db0c22f23dc29be00ec377892a805eba3","impliedFormat":99},{"version":"16f35e4256a94b314c98eb75040cb33ad9c430075bc29c53b43ede6405705700","impliedFormat":99},{"version":"a257b94f9f5403f63026dee4f4a95b9015742f8e23ff6eed7837f97f2ae501d9","impliedFormat":99},{"version":"254175b582a422c45bd9701e93d6ef9f6cf242c6247793a9a2e05770e1add917","impliedFormat":99},{"version":"c4dcab273dad7aaf0d3b4e0ca48e948aeb3cc269ac359301181fdfbb2efde033","impliedFormat":99},{"version":"d21671ff0f21081ecb19567351dadc8be7ad9443e26183d971ac658684479777","impliedFormat":99},{"version":"617be44c8811d411b4efc7dd13e57851bb1ab71f7e9d1a9eca04b7d0db0e1b35","impliedFormat":99},"5dbefdf7b46fb5883f6a38744edfc6301861587b22bf0d829fc40e917564c300",{"version":"fe93c474ab38ac02e30e3af073412b4f92b740152cf3a751fdaee8cbea982341","impliedFormat":1},{"version":"476e83e2c9e398265eed2c38773ae9081932b08ea5597b579a7d2e0c690ead56","impliedFormat":1},{"version":"1e00b8bf9e3766c958218cd6144ffe08418286f89ff44ba5a2cc830c03dd22c7","impliedFormat":1},{"version":"948af5f3081a8e683756251a3834ed5a4ae6e4faf16b66abcc44b8a13e72d08f","impliedFormat":99},{"version":"af437043c5e9a8105f0e9633fea7ae79d1f7f89357897e2df99c6f3697c744fb","impliedFormat":99},{"version":"d0bede428ef73b5da47f0f1bdc440480a863a1baff3e6a6b28db98535efcaa6d","impliedFormat":99},{"version":"44015602830f6f90e043f14702f04e7f48ec4f2ce8a9925116fceaea73a43b8b","impliedFormat":99},{"version":"edadfd0ecbfc573c016b93064b18ec0ab64b17ebd252e5636c2ee55f0f6cd82e","impliedFormat":99},{"version":"45ccec80a9c659104e28cbf3dbff13ab92c57c4e1fad9b38671a0c9299afd4f6","impliedFormat":99},{"version":"1b59bf4043df6f2b48ac1685a0a6c3953ccdb49b1c7affd3364624f77ff58f0b","impliedFormat":99},{"version":"ca4a3925cfe8fc5bac902ea30a5fd441987ee1776ddf5a353715dd1c6579722a","impliedFormat":99},{"version":"658f055f615e8232a1f5b30753a0a1ce369a70c1dcf0685953f48d7d4d508ba0","impliedFormat":99},{"version":"341aee100ffa277e0737387b9b9db69280e6dfd056166e18656de5bced0e7f01","impliedFormat":99},{"version":"2cac36c32274e1e7faf7fb3bd1666e69763b07b9f5330eee6911352d3e6eee96","impliedFormat":99},{"version":"aef33b3c102b63e7e89208ec4982f8eac7168774b08b05e8c43e967729ea91e5","impliedFormat":99},{"version":"5788b512a6212523eb032c6898beb98d56dd14c84e186a786901c1b3cae527cf","impliedFormat":99},{"version":"e2f146177df266680126ba19e4336ffc6639094feb27b37d276577f8c3d2c186","impliedFormat":99},"020c87fd5861d5feb650f37cfc039aeaf147bb7ed0aa801ac7f39d06a85b60fe",{"version":"ae00023c4fb6d8310666f6f047f455331ded1cd758182decd54d7f3f2bdc7e73","impliedFormat":1},{"version":"1e380bb9f7438543101f54ecd1b5c0b8216eea8d5650e98ec95e4c9aa116cdd5","impliedFormat":1},{"version":"d0b73f1df56fbd242fd78d55b29e1de340548048f19ac104fe2b201dc49529ff","impliedFormat":1},{"version":"287fa50a234cad0b96ebba3713fe57a7115f7b657dc44638fbce57c45ac71397","impliedFormat":1},{"version":"c42852405dff422a8b20dd3a9ada0130237ee9398a783151aa0f73474c246aeb","impliedFormat":1},{"version":"d3260c8d6fb8ab6b92c412c3c0b793dc524dbcc6737300cd4cf22198122479a4","impliedFormat":1},{"version":"f7ebfaa84846f84bd01665f4dd3773ff2b1c38c7992fd1042cd9132bf0afc82d","impliedFormat":1},{"version":"b03829b7141ddbc20c9da5de4f8021ef99b57b169e753d28ba5582d02bc9d5da","impliedFormat":1},{"version":"d1c49ba10ba80d18dc288f021c86c496d5581112ef6e107e9e9c20f746ee7b0a","impliedFormat":1},{"version":"f3c5ea78b54672f9440be1a2ae3f6aeb0184f6a4f641c3cca51949e9cd00a258","impliedFormat":1},{"version":"9ad68f979b78ae71d8cd991b87b5317b5959867dfe5970de811ec40a09d6e1f4","impliedFormat":1},{"version":"d91e9e625a2903192e9a63361b89330f0d95c340d9bb4602b89f485e9f93cdd6","impliedFormat":1},{"version":"176a47d228081ad51c1d62769b77b064abbeb6827115033cce1cdeb340a8d46c","impliedFormat":1},{"version":"b5eaf1cc561810ebfb369039a6e77a4d0f74bf3162d65421a52fc5b9b5158c2c","impliedFormat":1},{"version":"7d12ec184af986cc2a0fdc97f6c7f5a547ecdd8434856a323ea7ff064e15f858","impliedFormat":1},{"version":"8535298578313ba0f71a41619e193767baec9ccf6d8fad90bc144bcba444307a","impliedFormat":1},{"version":"50bfcce750d2a77d46e96e1d6c80f2a6309eb86f6ffee53ed07864b68af3d824","impliedFormat":1},{"version":"79b41324d28fb94f7304a0151990ad712346ceb1907fe1ce5e892145eae8f8eb","impliedFormat":1},{"version":"3239a3f8c29d2b0db0a4200b7e6513e95d16a183d4d823e54cb5c98f4c53e183","impliedFormat":1},"d05659bc83a21392bbc0110223f44f701562eced0c25ad812eab0ac8281e7da4","a362e637e88ac7c0d878debfcdb8de6b08158e6e54d648082c4a6a26ac832791","97f3f43cea913e85a12d72f8b1ec0521a4f36babd1550f3f769b895c98584c93","8088a8c902ec6a5ea2ef9964da4f30f54d6f6a5a559677f0425d319b82f34a1b","7d01e3ec2e14e60adba22e76e23ae3705e6d2fea99c0c65c632c90cebc4273d0","d2aa0398dde96a3972b4800e6ec316914d916080b523c28fd42e3f3924f6e42d","efcdda092037a762a10fa363b936fc5e54c61a345b39b64edcff8ebd15b96517","ac1ac781929f9be53cbcc041926e46f3fec7870623454cff566ee5c43f1acdae","ad3c3dec7cdeca9a495e3926b8702bbadfe14c435a0cbdb2c432e61c98a44553","3a78d15309b31552c73e2482147c6bc4bf5500cde9422dc6b2594f0e44e4afd1","2065832dd99cfd0a01cb21a2747e35598a13965a2edd05298cc561e3c32dac60","74dfde65890d55f05ad557f2fce299ea02ab7b98d1fc71c0118de24b03ee21db","fef91ae8defc8311952c4bc63101f868694b5e6a882c08341d4029bd94fa6f0e","0d9907abf7c7f70fe2b033bf118964fcbd5ea4b51ef0a021242315dcf390d822","7adfc4104c99078e409c45ef42360018e04b4aa1f4aab8be130d2f305d36d6cc","88b4b85fd86ff2b2025f764f454fe060f8e5f35b8ead8e43cdfc8ccffcac608f","cd7a8712d865de9b13853d0d9eb84118052c29d839e0f28a952daa9d2bfc57a4","e437f9801e722c286f8665b8ff67794debcb6e028f9b84823bd65ebec6fe5d88","4852224befd8e9c7988b52945ef31e8ac43c49a1182d39b35580ddae219bb7d8","6e9e4f8b1d1c34147905bd43a7d86e58d487ca5abd4f1bdc68f1357e2272d953","87b16feeb0bf1c5c17819b31621b4feef041fd371ecc5e3102552d79e6ab239c","dc0878ae9f51f476995626109cb017925e317d2e2158840753cfb23388d20249","bbf51680bdf0224bb96e79e576bc3d998f6e308773a2e6b99f7d5bbf1155aec0","2a138076231b76dc334464947f83616cbaa52832e370fbed780f4aa7eb5eae2b","d0f6af1efc88341c181143ced8539d5509c811a4ac6c476edeb25fdb0d5fd119","2552a31fad45a9ed1bde87e51b038dc0e786cd364b597162263abbf57018949b","e9cfeafacd30af2dc7719600ba08d05bb90cfe074b612ca9d1128202ceeb5a93","8721c0e54253a11b7cbb42c583d7260f8c033814e0b0595423372323ad14948a","dc94787cb4f02ed26312bfa7e08688fc988a75ec864fbe7ac019db1e5d941b4d","7dbadff629693d30090559c66fc1d7abd34a0eedb4a830ac9793df9f76584fb0","1709aacf07fe6142881570c41c2d74474e8b7d540bab167a617ed87abc77afaa","f219459cc78e2f6584122db61fa0faf2c21aaf50606895fe53d5da1bc8247231","8134e6b69208e8a8d9d82598c6dbd8b05ffac32422633fcf6e4660c226cd360d","55b69699c41e45f6b3c20fb48a0734f3621a92906fed7fa63e8825815c1b7721","728f9123fdb11574e1ff15ff1b620142e828630a811df050a4a56317e8473798","78566ba9c7d860eb0a994b81a699ac1c2c9c0f45850ab0d655cfe95ffb18d1a1","507ccebe35427617298af6ed0cb3b2a9b0a88433e71ff7e7a17f744a8f4b0195","4a308405daf9f5e2a2bc93d1bd3e3945486dca89aed2943edf98beac2e2ec748","a202f548ac00c22ed576d0eda82169b13b3ec469c050061326c943ad27746c19","f2db5d9f9c9293636e35ba5cc5a54a7b3ef7f4606df97e36d20fe08ef058c147","eb64a1b5296c45cfae713c16cdbbe7a291c7a653434215e05ca3991705184d4a","e8aefc9034cedd62ebe09f69dcfaced3eab4d121bf2f0274695734cf7a0d5b8c","5126e995ddf6bf98ead9841103f1e11554c3e62f3d4b6d51488a7e6e96d05ac3","2a6366b2a036a620b342699d37c5a97981fdda122cf3b2fa6654a97146619377",{"version":"151ff381ef9ff8da2da9b9663ebf657eac35c4c9a19183420c05728f31a6761d","impliedFormat":1},{"version":"f3d8c757e148ad968f0d98697987db363070abada5f503da3c06aefd9d4248c1","impliedFormat":1},{"version":"96d14f21b7652903852eef49379d04dbda28c16ed36468f8c9fa08f7c14c9538","impliedFormat":1}],"root":[83,501,502,527,528,671,689,[709,752]],"options":{"allowJs":true,"esModuleInterop":true,"jsx":1,"module":99,"skipLibCheck":true,"strict":true,"target":4},"referencedMap":[[738,1],[739,2],[740,3],[741,4],[742,5],[743,6],[744,7],[745,8],[746,9],[747,10],[736,11],[737,12],[748,13],[749,14],[750,15],[751,16],[752,17],[734,18],[83,19],[735,20],[717,21],[718,22],[719,23],[720,24],[721,25],[722,24],[723,24],[724,23],[725,26],[726,27],[714,28],[716,29],[727,22],[728,30],[731,31],[732,23],[733,23],[730,32],[713,33],[729,32],[715,27],[689,34],[711,32],[709,35],[710,36],[712,32],[528,19],[671,37],[501,38],[502,39],[652,40],[654,41],[655,42],[638,43],[642,44],[662,45],[641,45],[644,46],[636,19],[663,45],[637,45],[664,45],[643,47],[633,48],[653,40],[666,49],[657,50],[656,19],[659,51],[658,19],[665,52],[649,53],[648,19],[670,54],[632,55],[660,56],[631,57],[669,19],[645,49],[661,49],[650,49],[647,58],[646,49],[651,19],[628,58],[627,19],[629,49],[635,43],[640,49],[639,49],[667,59],[668,49],[630,60],[634,59],[551,61],[530,61],[586,19],[566,19],[598,19],[529,19],[577,62],[578,63],[544,64],[531,19],[540,19],[541,65],[542,19],[543,66],[539,67],[532,19],[536,19],[561,67],[559,68],[547,69],[573,70],[549,19],[554,19],[550,71],[545,67],[546,72],[548,72],[552,19],[553,19],[576,73],[565,74],[563,19],[564,75],[579,76],[575,77],[593,78],[587,79],[596,80],[597,81],[567,82],[583,19],[572,83],[569,67],[624,84],[570,85],[606,86],[605,19],[612,87],[611,19],[614,88],[613,19],[616,89],[615,19],[608,90],[607,19],[610,91],[609,91],[625,92],[620,93],[626,94],[538,95],[535,96],[534,19],[585,97],[599,98],[619,19],[604,19],[571,99],[589,100],[588,101],[591,102],[594,100],[595,103],[601,104],[574,19],[555,19],[590,105],[592,19],[621,106],[603,107],[581,19],[584,67],[558,108],[557,19],[622,109],[556,19],[623,110],[562,111],[560,19],[533,19],[600,112],[537,19],[618,113],[568,19],[602,114],[617,19],[580,19],[582,115],[680,116],[679,117],[681,116],[683,118],[685,119],[686,120],[684,121],[678,119],[688,122],[675,123],[676,123],[687,19],[677,37],[682,124],[690,19],[696,125],[692,126],[695,127],[700,128],[702,129],[697,130],[694,131],[693,19],[708,132],[701,19],[698,19],[691,19],[704,133],[703,134],[706,19],[699,19],[705,128],[707,135],[248,19],[753,19],[754,19],[755,19],[145,136],[146,136],[147,137],[100,138],[148,139],[149,140],[150,141],[95,19],[98,142],[96,19],[97,19],[151,143],[152,144],[153,145],[154,146],[155,147],[156,148],[157,148],[158,149],[159,150],[160,151],[161,152],[101,19],[99,19],[162,153],[163,154],[164,155],[198,156],[165,157],[166,19],[167,158],[168,159],[169,160],[170,161],[171,162],[172,163],[173,164],[174,165],[175,166],[176,166],[177,167],[178,19],[179,168],[180,169],[182,170],[181,171],[183,172],[184,173],[185,174],[186,175],[187,176],[188,177],[189,178],[190,179],[191,180],[192,181],[193,182],[194,183],[195,184],[102,19],[103,19],[104,19],[142,185],[143,19],[144,19],[196,186],[197,187],[202,188],[358,32],[203,189],[201,190],[360,191],[359,192],[199,193],[356,19],[200,194],[84,19],[86,195],[355,32],[266,32],[85,19],[93,196],[447,197],[452,18],[454,198],[224,199],[252,200],[430,201],[247,202],[235,19],[216,19],[222,19],[420,203],[283,204],[223,19],[389,205],[257,206],[258,207],[354,208],[417,209],[372,210],[424,211],[425,212],[423,213],[422,19],[421,214],[254,215],[225,216],[304,19],[305,217],[220,19],[236,218],[226,219],[288,218],[285,218],[209,218],[250,220],[249,19],[429,221],[439,19],[215,19],[330,222],[331,223],[325,32],[475,19],[333,19],[334,224],[326,225],[481,226],[479,227],[474,19],[416,228],[415,19],[473,229],[327,32],[368,230],[366,231],[476,19],[480,19],[478,232],[477,19],[367,233],[468,234],[471,235],[295,236],[294,237],[293,238],[484,32],[292,239],[277,19],[487,19],[673,240],[672,19],[490,19],[489,32],[491,241],[205,19],[426,242],[427,243],[428,244],[238,19],[214,245],[204,19],[346,32],[207,246],[345,247],[344,248],[335,19],[336,19],[343,19],[338,19],[341,249],[337,19],[339,250],[342,251],[340,250],[221,19],[212,19],[213,218],[267,252],[268,253],[265,254],[263,255],[264,256],[260,19],[352,224],[374,224],[446,257],[455,258],[459,259],[433,260],[432,19],[280,19],[492,261],[442,262],[328,263],[329,264],[320,265],[310,19],[351,266],[311,267],[353,268],[348,269],[347,19],[349,19],[365,270],[434,271],[435,272],[313,273],[317,274],[308,275],[412,276],[441,277],[287,278],[390,279],[210,280],[440,281],[206,202],[261,19],[269,282],[401,283],[259,19],[400,284],[94,19],[395,285],[237,19],[306,286],[391,19],[211,19],[270,19],[399,287],[219,19],[275,288],[316,289],[431,290],[315,19],[398,19],[262,19],[403,291],[404,292],[217,19],[406,293],[408,294],[407,295],[240,19],[397,280],[410,296],[396,297],[402,298],[228,19],[231,19],[229,19],[233,19],[230,19],[232,19],[234,299],[227,19],[382,300],[381,19],[387,301],[383,302],[386,303],[385,303],[388,301],[384,302],[274,304],[375,305],[438,306],[494,19],[463,307],[465,308],[312,19],[464,309],[436,271],[493,310],[332,271],[218,19],[314,311],[271,312],[272,313],[273,314],[303,315],[411,315],[289,315],[376,316],[290,316],[256,317],[255,19],[380,318],[379,319],[378,320],[377,321],[437,322],[324,323],[362,324],[323,325],[357,326],[361,327],[419,328],[418,329],[414,330],[371,331],[373,332],[370,333],[409,334],[364,19],[451,19],[363,335],[413,19],[276,336],[309,242],[307,337],[278,338],[281,339],[488,19],[279,340],[282,340],[449,19],[448,19],[450,19],[486,19],[284,341],[322,32],[92,19],[369,342],[253,19],[242,343],[318,19],[457,32],[467,344],[302,32],[461,224],[301,345],[444,346],[300,344],[208,19],[469,347],[298,32],[299,32],[291,19],[241,19],[297,348],[296,349],[239,350],[319,165],[286,165],[405,19],[393,351],[392,19],[453,19],[350,352],[321,32],[445,353],[87,32],[90,354],[91,355],[88,32],[89,19],[251,356],[246,357],[245,19],[244,358],[243,19],[443,359],[456,360],[458,361],[460,362],[674,363],[462,364],[466,365],[500,366],[470,366],[499,367],[472,368],[482,369],[483,370],[485,371],[495,372],[498,245],[497,19],[496,373],[519,374],[517,375],[518,376],[506,377],[507,375],[514,378],[505,379],[510,380],[520,19],[511,381],[516,382],[522,383],[521,384],[504,385],[512,386],[513,387],[508,388],[515,374],[509,389],[394,390],[503,19],[525,391],[524,19],[523,19],[526,392],[81,19],[82,19],[13,19],[14,19],[16,19],[15,19],[2,19],[17,19],[18,19],[19,19],[20,19],[21,19],[22,19],[23,19],[24,19],[3,19],[25,19],[26,19],[4,19],[27,19],[31,19],[28,19],[29,19],[30,19],[32,19],[33,19],[34,19],[5,19],[35,19],[36,19],[37,19],[38,19],[6,19],[42,19],[39,19],[40,19],[41,19],[43,19],[7,19],[44,19],[49,19],[50,19],[45,19],[46,19],[47,19],[48,19],[8,19],[54,19],[51,19],[52,19],[53,19],[55,19],[9,19],[56,19],[57,19],[58,19],[60,19],[59,19],[61,19],[62,19],[10,19],[63,19],[64,19],[65,19],[11,19],[66,19],[67,19],[68,19],[69,19],[70,19],[1,19],[71,19],[72,19],[12,19],[76,19],[74,19],[79,19],[78,19],[73,19],[77,19],[75,19],[80,19],[120,393],[130,394],[119,393],[140,395],[111,396],[110,397],[139,373],[133,398],[138,399],[113,400],[127,401],[112,402],[136,403],[108,404],[107,373],[137,405],[109,406],[114,407],[115,19],[118,407],[105,19],[141,408],[131,409],[122,410],[123,411],[125,412],[121,413],[124,414],[134,373],[116,415],[117,416],[126,417],[106,418],[129,409],[128,407],[132,19],[135,419],[527,420]],"affectedFilesPendingEmit":[738,739,740,741,742,743,744,745,746,747,736,737,748,749,750,751,752,735,717,718,719,720,721,722,723,724,725,726,714,716,727,728,731,732,733,730,713,729,715,689,711,709,710,712,528,671,502,527],"version":"5.9.3"} \ No newline at end of file +{"fileNames":["./node_modules/typescript/lib/lib.es5.d.ts","./node_modules/typescript/lib/lib.es2015.d.ts","./node_modules/typescript/lib/lib.es2016.d.ts","./node_modules/typescript/lib/lib.es2017.d.ts","./node_modules/typescript/lib/lib.es2018.d.ts","./node_modules/typescript/lib/lib.es2019.d.ts","./node_modules/typescript/lib/lib.es2020.d.ts","./node_modules/typescript/lib/lib.es2021.d.ts","./node_modules/typescript/lib/lib.es2022.d.ts","./node_modules/typescript/lib/lib.es2023.d.ts","./node_modules/typescript/lib/lib.es2024.d.ts","./node_modules/typescript/lib/lib.esnext.d.ts","./node_modules/typescript/lib/lib.dom.d.ts","./node_modules/typescript/lib/lib.dom.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.core.d.ts","./node_modules/typescript/lib/lib.es2015.collection.d.ts","./node_modules/typescript/lib/lib.es2015.generator.d.ts","./node_modules/typescript/lib/lib.es2015.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.promise.d.ts","./node_modules/typescript/lib/lib.es2015.proxy.d.ts","./node_modules/typescript/lib/lib.es2015.reflect.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2016.array.include.d.ts","./node_modules/typescript/lib/lib.es2016.intl.d.ts","./node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","./node_modules/typescript/lib/lib.es2017.date.d.ts","./node_modules/typescript/lib/lib.es2017.object.d.ts","./node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2017.string.d.ts","./node_modules/typescript/lib/lib.es2017.intl.d.ts","./node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","./node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","./node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","./node_modules/typescript/lib/lib.es2018.intl.d.ts","./node_modules/typescript/lib/lib.es2018.promise.d.ts","./node_modules/typescript/lib/lib.es2018.regexp.d.ts","./node_modules/typescript/lib/lib.es2019.array.d.ts","./node_modules/typescript/lib/lib.es2019.object.d.ts","./node_modules/typescript/lib/lib.es2019.string.d.ts","./node_modules/typescript/lib/lib.es2019.symbol.d.ts","./node_modules/typescript/lib/lib.es2019.intl.d.ts","./node_modules/typescript/lib/lib.es2020.bigint.d.ts","./node_modules/typescript/lib/lib.es2020.date.d.ts","./node_modules/typescript/lib/lib.es2020.promise.d.ts","./node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2020.string.d.ts","./node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2020.intl.d.ts","./node_modules/typescript/lib/lib.es2020.number.d.ts","./node_modules/typescript/lib/lib.es2021.promise.d.ts","./node_modules/typescript/lib/lib.es2021.string.d.ts","./node_modules/typescript/lib/lib.es2021.weakref.d.ts","./node_modules/typescript/lib/lib.es2021.intl.d.ts","./node_modules/typescript/lib/lib.es2022.array.d.ts","./node_modules/typescript/lib/lib.es2022.error.d.ts","./node_modules/typescript/lib/lib.es2022.intl.d.ts","./node_modules/typescript/lib/lib.es2022.object.d.ts","./node_modules/typescript/lib/lib.es2022.string.d.ts","./node_modules/typescript/lib/lib.es2022.regexp.d.ts","./node_modules/typescript/lib/lib.es2023.array.d.ts","./node_modules/typescript/lib/lib.es2023.collection.d.ts","./node_modules/typescript/lib/lib.es2023.intl.d.ts","./node_modules/typescript/lib/lib.es2024.arraybuffer.d.ts","./node_modules/typescript/lib/lib.es2024.collection.d.ts","./node_modules/typescript/lib/lib.es2024.object.d.ts","./node_modules/typescript/lib/lib.es2024.promise.d.ts","./node_modules/typescript/lib/lib.es2024.regexp.d.ts","./node_modules/typescript/lib/lib.es2024.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2024.string.d.ts","./node_modules/typescript/lib/lib.esnext.array.d.ts","./node_modules/typescript/lib/lib.esnext.collection.d.ts","./node_modules/typescript/lib/lib.esnext.intl.d.ts","./node_modules/typescript/lib/lib.esnext.disposable.d.ts","./node_modules/typescript/lib/lib.esnext.promise.d.ts","./node_modules/typescript/lib/lib.esnext.decorators.d.ts","./node_modules/typescript/lib/lib.esnext.iterator.d.ts","./node_modules/typescript/lib/lib.esnext.float16.d.ts","./node_modules/typescript/lib/lib.esnext.error.d.ts","./node_modules/typescript/lib/lib.esnext.sharedmemory.d.ts","./node_modules/typescript/lib/lib.decorators.d.ts","./node_modules/typescript/lib/lib.decorators.legacy.d.ts","./.next/types/routes.d.ts","./node_modules/@types/react/global.d.ts","./node_modules/csstype/index.d.ts","./node_modules/@types/react/index.d.ts","./node_modules/next/dist/styled-jsx/types/css.d.ts","./node_modules/next/dist/styled-jsx/types/macro.d.ts","./node_modules/next/dist/styled-jsx/types/style.d.ts","./node_modules/next/dist/styled-jsx/types/global.d.ts","./node_modules/next/dist/styled-jsx/types/index.d.ts","./node_modules/next/dist/shared/lib/amp.d.ts","./node_modules/next/amp.d.ts","./node_modules/next/dist/server/get-page-files.d.ts","./node_modules/@types/node/compatibility/disposable.d.ts","./node_modules/@types/node/compatibility/indexable.d.ts","./node_modules/@types/node/compatibility/iterators.d.ts","./node_modules/@types/node/compatibility/index.d.ts","./node_modules/@types/node/globals.typedarray.d.ts","./node_modules/@types/node/buffer.buffer.d.ts","./node_modules/@types/node/globals.d.ts","./node_modules/@types/node/web-globals/abortcontroller.d.ts","./node_modules/@types/node/web-globals/domexception.d.ts","./node_modules/@types/node/web-globals/events.d.ts","./node_modules/undici-types/header.d.ts","./node_modules/undici-types/readable.d.ts","./node_modules/undici-types/file.d.ts","./node_modules/undici-types/fetch.d.ts","./node_modules/undici-types/formdata.d.ts","./node_modules/undici-types/connector.d.ts","./node_modules/undici-types/client.d.ts","./node_modules/undici-types/errors.d.ts","./node_modules/undici-types/dispatcher.d.ts","./node_modules/undici-types/global-dispatcher.d.ts","./node_modules/undici-types/global-origin.d.ts","./node_modules/undici-types/pool-stats.d.ts","./node_modules/undici-types/pool.d.ts","./node_modules/undici-types/handlers.d.ts","./node_modules/undici-types/balanced-pool.d.ts","./node_modules/undici-types/agent.d.ts","./node_modules/undici-types/mock-interceptor.d.ts","./node_modules/undici-types/mock-agent.d.ts","./node_modules/undici-types/mock-client.d.ts","./node_modules/undici-types/mock-pool.d.ts","./node_modules/undici-types/mock-errors.d.ts","./node_modules/undici-types/proxy-agent.d.ts","./node_modules/undici-types/env-http-proxy-agent.d.ts","./node_modules/undici-types/retry-handler.d.ts","./node_modules/undici-types/retry-agent.d.ts","./node_modules/undici-types/api.d.ts","./node_modules/undici-types/interceptors.d.ts","./node_modules/undici-types/util.d.ts","./node_modules/undici-types/cookies.d.ts","./node_modules/undici-types/patch.d.ts","./node_modules/undici-types/websocket.d.ts","./node_modules/undici-types/eventsource.d.ts","./node_modules/undici-types/filereader.d.ts","./node_modules/undici-types/diagnostics-channel.d.ts","./node_modules/undici-types/content-type.d.ts","./node_modules/undici-types/cache.d.ts","./node_modules/undici-types/index.d.ts","./node_modules/@types/node/web-globals/fetch.d.ts","./node_modules/@types/node/web-globals/navigator.d.ts","./node_modules/@types/node/web-globals/storage.d.ts","./node_modules/@types/node/assert.d.ts","./node_modules/@types/node/assert/strict.d.ts","./node_modules/@types/node/async_hooks.d.ts","./node_modules/@types/node/buffer.d.ts","./node_modules/@types/node/child_process.d.ts","./node_modules/@types/node/cluster.d.ts","./node_modules/@types/node/console.d.ts","./node_modules/@types/node/constants.d.ts","./node_modules/@types/node/crypto.d.ts","./node_modules/@types/node/dgram.d.ts","./node_modules/@types/node/diagnostics_channel.d.ts","./node_modules/@types/node/dns.d.ts","./node_modules/@types/node/dns/promises.d.ts","./node_modules/@types/node/domain.d.ts","./node_modules/@types/node/events.d.ts","./node_modules/@types/node/fs.d.ts","./node_modules/@types/node/fs/promises.d.ts","./node_modules/@types/node/http.d.ts","./node_modules/@types/node/http2.d.ts","./node_modules/@types/node/https.d.ts","./node_modules/@types/node/inspector.d.ts","./node_modules/@types/node/inspector.generated.d.ts","./node_modules/@types/node/module.d.ts","./node_modules/@types/node/net.d.ts","./node_modules/@types/node/os.d.ts","./node_modules/@types/node/path.d.ts","./node_modules/@types/node/perf_hooks.d.ts","./node_modules/@types/node/process.d.ts","./node_modules/@types/node/punycode.d.ts","./node_modules/@types/node/querystring.d.ts","./node_modules/@types/node/readline.d.ts","./node_modules/@types/node/readline/promises.d.ts","./node_modules/@types/node/repl.d.ts","./node_modules/@types/node/sea.d.ts","./node_modules/@types/node/sqlite.d.ts","./node_modules/@types/node/stream.d.ts","./node_modules/@types/node/stream/promises.d.ts","./node_modules/@types/node/stream/consumers.d.ts","./node_modules/@types/node/stream/web.d.ts","./node_modules/@types/node/string_decoder.d.ts","./node_modules/@types/node/test.d.ts","./node_modules/@types/node/timers.d.ts","./node_modules/@types/node/timers/promises.d.ts","./node_modules/@types/node/tls.d.ts","./node_modules/@types/node/trace_events.d.ts","./node_modules/@types/node/tty.d.ts","./node_modules/@types/node/url.d.ts","./node_modules/@types/node/util.d.ts","./node_modules/@types/node/v8.d.ts","./node_modules/@types/node/vm.d.ts","./node_modules/@types/node/wasi.d.ts","./node_modules/@types/node/worker_threads.d.ts","./node_modules/@types/node/zlib.d.ts","./node_modules/@types/node/index.d.ts","./node_modules/@types/react/canary.d.ts","./node_modules/@types/react/experimental.d.ts","./node_modules/@types/react-dom/index.d.ts","./node_modules/@types/react-dom/canary.d.ts","./node_modules/@types/react-dom/experimental.d.ts","./node_modules/next/dist/lib/fallback.d.ts","./node_modules/next/dist/compiled/webpack/webpack.d.ts","./node_modules/next/dist/server/config.d.ts","./node_modules/next/dist/lib/load-custom-routes.d.ts","./node_modules/next/dist/shared/lib/image-config.d.ts","./node_modules/next/dist/build/webpack/plugins/subresource-integrity-plugin.d.ts","./node_modules/next/dist/server/body-streams.d.ts","./node_modules/next/dist/server/lib/cache-control.d.ts","./node_modules/next/dist/lib/setup-exception-listeners.d.ts","./node_modules/next/dist/lib/worker.d.ts","./node_modules/next/dist/lib/constants.d.ts","./node_modules/next/dist/client/components/app-router-headers.d.ts","./node_modules/next/dist/build/rendering-mode.d.ts","./node_modules/next/dist/server/lib/router-utils/build-prefetch-segment-data-route.d.ts","./node_modules/next/dist/server/require-hook.d.ts","./node_modules/next/dist/server/lib/experimental/ppr.d.ts","./node_modules/next/dist/build/webpack/plugins/app-build-manifest-plugin.d.ts","./node_modules/next/dist/lib/page-types.d.ts","./node_modules/next/dist/build/segment-config/app/app-segment-config.d.ts","./node_modules/next/dist/build/segment-config/pages/pages-segment-config.d.ts","./node_modules/next/dist/build/analysis/get-page-static-info.d.ts","./node_modules/next/dist/build/webpack/loaders/get-module-build-info.d.ts","./node_modules/next/dist/build/webpack/plugins/middleware-plugin.d.ts","./node_modules/next/dist/server/node-polyfill-crypto.d.ts","./node_modules/next/dist/server/node-environment-baseline.d.ts","./node_modules/next/dist/server/node-environment-extensions/error-inspect.d.ts","./node_modules/next/dist/server/node-environment-extensions/random.d.ts","./node_modules/next/dist/server/node-environment-extensions/date.d.ts","./node_modules/next/dist/server/node-environment-extensions/web-crypto.d.ts","./node_modules/next/dist/server/node-environment-extensions/node-crypto.d.ts","./node_modules/next/dist/server/node-environment.d.ts","./node_modules/next/dist/build/page-extensions-type.d.ts","./node_modules/next/dist/build/webpack/plugins/flight-manifest-plugin.d.ts","./node_modules/next/dist/server/instrumentation/types.d.ts","./node_modules/next/dist/lib/coalesced-function.d.ts","./node_modules/next/dist/shared/lib/router/utils/middleware-route-matcher.d.ts","./node_modules/next/dist/server/lib/router-utils/types.d.ts","./node_modules/next/dist/shared/lib/modern-browserslist-target.d.ts","./node_modules/next/dist/shared/lib/constants.d.ts","./node_modules/next/dist/trace/types.d.ts","./node_modules/next/dist/trace/trace.d.ts","./node_modules/next/dist/trace/shared.d.ts","./node_modules/next/dist/trace/index.d.ts","./node_modules/next/dist/build/load-jsconfig.d.ts","./node_modules/@next/env/dist/index.d.ts","./node_modules/next/dist/build/webpack/plugins/telemetry-plugin/use-cache-tracker-utils.d.ts","./node_modules/next/dist/build/webpack/plugins/telemetry-plugin/telemetry-plugin.d.ts","./node_modules/next/dist/telemetry/storage.d.ts","./node_modules/next/dist/build/build-context.d.ts","./node_modules/next/dist/shared/lib/bloom-filter.d.ts","./node_modules/next/dist/build/webpack-config.d.ts","./node_modules/next/dist/server/route-kind.d.ts","./node_modules/next/dist/server/route-definitions/route-definition.d.ts","./node_modules/next/dist/build/swc/generated-native.d.ts","./node_modules/next/dist/build/swc/types.d.ts","./node_modules/next/dist/server/dev/parse-version-info.d.ts","./node_modules/next/dist/next-devtools/shared/types.d.ts","./node_modules/next/dist/server/dev/dev-indicator-server-state.d.ts","./node_modules/next/dist/server/lib/parse-stack.d.ts","./node_modules/next/dist/next-devtools/server/shared.d.ts","./node_modules/next/dist/next-devtools/shared/stack-frame.d.ts","./node_modules/next/dist/next-devtools/dev-overlay/utils/get-error-by-type.d.ts","./node_modules/@types/react/jsx-runtime.d.ts","./node_modules/next/dist/next-devtools/dev-overlay/container/runtime-error/render-error.d.ts","./node_modules/next/dist/next-devtools/dev-overlay/shared.d.ts","./node_modules/next/dist/server/dev/hot-reloader-types.d.ts","./node_modules/next/dist/server/lib/cache-handlers/types.d.ts","./node_modules/next/dist/server/response-cache/types.d.ts","./node_modules/next/dist/server/resume-data-cache/cache-store.d.ts","./node_modules/next/dist/server/resume-data-cache/resume-data-cache.d.ts","./node_modules/next/dist/server/render-result.d.ts","./node_modules/next/dist/server/lib/i18n-provider.d.ts","./node_modules/next/dist/server/web/next-url.d.ts","./node_modules/next/dist/compiled/@edge-runtime/cookies/index.d.ts","./node_modules/next/dist/server/web/spec-extension/cookies.d.ts","./node_modules/next/dist/server/web/spec-extension/request.d.ts","./node_modules/next/dist/server/after/builtin-request-context.d.ts","./node_modules/next/dist/server/web/spec-extension/fetch-event.d.ts","./node_modules/next/dist/server/web/spec-extension/response.d.ts","./node_modules/next/dist/build/segment-config/middleware/middleware-config.d.ts","./node_modules/next/dist/server/web/types.d.ts","./node_modules/next/dist/build/webpack/plugins/pages-manifest-plugin.d.ts","./node_modules/next/dist/shared/lib/router/utils/parse-url.d.ts","./node_modules/next/dist/server/base-http/node.d.ts","./node_modules/next/dist/build/webpack/plugins/next-font-manifest-plugin.d.ts","./node_modules/next/dist/server/route-definitions/locale-route-definition.d.ts","./node_modules/next/dist/server/route-definitions/pages-route-definition.d.ts","./node_modules/next/dist/shared/lib/mitt.d.ts","./node_modules/next/dist/client/with-router.d.ts","./node_modules/next/dist/client/router.d.ts","./node_modules/next/dist/client/route-loader.d.ts","./node_modules/next/dist/client/page-loader.d.ts","./node_modules/next/dist/shared/lib/router/router.d.ts","./node_modules/next/dist/shared/lib/router-context.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/loadable-context.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/loadable.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/image-config-context.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/hooks-client-context.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/head-manager-context.shared-runtime.d.ts","./node_modules/next/dist/server/route-definitions/app-page-route-definition.d.ts","./node_modules/next/dist/build/webpack/loaders/metadata/types.d.ts","./node_modules/next/dist/build/webpack/loaders/next-app-loader/index.d.ts","./node_modules/next/dist/server/lib/app-dir-module.d.ts","./node_modules/next/dist/server/web/spec-extension/adapters/request-cookies.d.ts","./node_modules/next/dist/server/async-storage/draft-mode-provider.d.ts","./node_modules/next/dist/server/web/spec-extension/adapters/headers.d.ts","./node_modules/next/dist/server/app-render/cache-signal.d.ts","./node_modules/next/dist/server/app-render/dynamic-rendering.d.ts","./node_modules/next/dist/server/request/fallback-params.d.ts","./node_modules/next/dist/server/app-render/work-unit-async-storage-instance.d.ts","./node_modules/next/dist/server/response-cache/index.d.ts","./node_modules/next/dist/server/lib/lazy-result.d.ts","./node_modules/next/dist/server/lib/implicit-tags.d.ts","./node_modules/next/dist/server/app-render/work-unit-async-storage.external.d.ts","./node_modules/next/dist/shared/lib/deep-readonly.d.ts","./node_modules/next/dist/shared/lib/router/utils/parse-relative-url.d.ts","./node_modules/next/dist/server/app-render/app-render.d.ts","./node_modules/next/dist/shared/lib/server-inserted-html.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/amp-context.shared-runtime.d.ts","./node_modules/next/dist/server/route-modules/app-page/vendored/contexts/entrypoints.d.ts","./node_modules/next/dist/server/route-modules/app-page/module.compiled.d.ts","./node_modules/next/dist/client/components/error-boundary.d.ts","./node_modules/next/dist/client/components/layout-router.d.ts","./node_modules/next/dist/client/components/render-from-template-context.d.ts","./node_modules/next/dist/server/app-render/action-async-storage-instance.d.ts","./node_modules/next/dist/server/app-render/action-async-storage.external.d.ts","./node_modules/next/dist/client/components/client-page.d.ts","./node_modules/next/dist/client/components/client-segment.d.ts","./node_modules/next/dist/server/request/search-params.d.ts","./node_modules/next/dist/client/components/hooks-server-context.d.ts","./node_modules/next/dist/client/components/http-access-fallback/error-boundary.d.ts","./node_modules/next/dist/lib/metadata/types/alternative-urls-types.d.ts","./node_modules/next/dist/lib/metadata/types/extra-types.d.ts","./node_modules/next/dist/lib/metadata/types/metadata-types.d.ts","./node_modules/next/dist/lib/metadata/types/manifest-types.d.ts","./node_modules/next/dist/lib/metadata/types/opengraph-types.d.ts","./node_modules/next/dist/lib/metadata/types/twitter-types.d.ts","./node_modules/next/dist/lib/metadata/types/metadata-interface.d.ts","./node_modules/next/dist/lib/metadata/types/resolvers.d.ts","./node_modules/next/dist/lib/metadata/types/icons.d.ts","./node_modules/next/dist/lib/metadata/resolve-metadata.d.ts","./node_modules/next/dist/lib/metadata/metadata.d.ts","./node_modules/next/dist/lib/framework/boundary-components.d.ts","./node_modules/next/dist/server/app-render/rsc/preloads.d.ts","./node_modules/next/dist/server/app-render/rsc/postpone.d.ts","./node_modules/next/dist/server/app-render/rsc/taint.d.ts","./node_modules/next/dist/shared/lib/segment-cache/segment-value-encoding.d.ts","./node_modules/next/dist/server/app-render/collect-segment-data.d.ts","./node_modules/next/dist/next-devtools/userspace/app/segment-explorer-node.d.ts","./node_modules/next/dist/server/app-render/entry-base.d.ts","./node_modules/next/dist/build/templates/app-page.d.ts","./node_modules/@types/react/jsx-dev-runtime.d.ts","./node_modules/@types/react/compiler-runtime.d.ts","./node_modules/next/dist/server/route-modules/app-page/vendored/rsc/entrypoints.d.ts","./node_modules/@types/react-dom/client.d.ts","./node_modules/@types/react-dom/static.d.ts","./node_modules/@types/react-dom/server.d.ts","./node_modules/next/dist/server/route-modules/app-page/vendored/ssr/entrypoints.d.ts","./node_modules/next/dist/server/route-modules/app-page/module.d.ts","./node_modules/next/dist/server/web/adapter.d.ts","./node_modules/next/dist/server/use-cache/cache-life.d.ts","./node_modules/next/dist/server/app-render/types.d.ts","./node_modules/next/dist/client/components/router-reducer/router-reducer-types.d.ts","./node_modules/next/dist/client/flight-data-helpers.d.ts","./node_modules/next/dist/client/components/router-reducer/fetch-server-response.d.ts","./node_modules/next/dist/shared/lib/app-router-context.shared-runtime.d.ts","./node_modules/next/dist/server/route-modules/pages/vendored/contexts/entrypoints.d.ts","./node_modules/next/dist/server/route-modules/pages/module.compiled.d.ts","./node_modules/next/dist/build/templates/pages.d.ts","./node_modules/next/dist/server/route-modules/pages/module.d.ts","./node_modules/next/dist/next-devtools/userspace/pages/pages-dev-overlay-setup.d.ts","./node_modules/next/dist/server/render.d.ts","./node_modules/next/dist/server/route-definitions/pages-api-route-definition.d.ts","./node_modules/next/dist/server/route-matches/pages-api-route-match.d.ts","./node_modules/next/dist/server/route-matchers/route-matcher.d.ts","./node_modules/next/dist/server/route-matcher-providers/route-matcher-provider.d.ts","./node_modules/next/dist/server/route-matcher-managers/route-matcher-manager.d.ts","./node_modules/next/dist/server/normalizers/normalizer.d.ts","./node_modules/next/dist/server/normalizers/locale-route-normalizer.d.ts","./node_modules/next/dist/server/normalizers/request/pathname-normalizer.d.ts","./node_modules/next/dist/server/normalizers/request/suffix.d.ts","./node_modules/next/dist/server/normalizers/request/rsc.d.ts","./node_modules/next/dist/server/normalizers/request/prefetch-rsc.d.ts","./node_modules/next/dist/server/normalizers/request/next-data.d.ts","./node_modules/next/dist/server/normalizers/request/segment-prefix-rsc.d.ts","./node_modules/next/dist/build/static-paths/types.d.ts","./node_modules/next/dist/server/base-server.d.ts","./node_modules/next/dist/server/lib/async-callback-set.d.ts","./node_modules/next/dist/shared/lib/router/utils/route-regex.d.ts","./node_modules/next/dist/shared/lib/router/utils/route-matcher.d.ts","./node_modules/sharp/lib/index.d.ts","./node_modules/next/dist/server/image-optimizer.d.ts","./node_modules/next/dist/server/next-server.d.ts","./node_modules/next/dist/server/lib/types.d.ts","./node_modules/next/dist/server/lib/lru-cache.d.ts","./node_modules/next/dist/server/lib/dev-bundler-service.d.ts","./node_modules/next/dist/server/dev/static-paths-worker.d.ts","./node_modules/next/dist/server/dev/next-dev-server.d.ts","./node_modules/next/dist/server/next.d.ts","./node_modules/next/dist/server/lib/render-server.d.ts","./node_modules/next/dist/server/lib/router-server.d.ts","./node_modules/next/dist/shared/lib/router/utils/path-match.d.ts","./node_modules/next/dist/server/lib/router-utils/filesystem.d.ts","./node_modules/next/dist/server/lib/router-utils/setup-dev-bundler.d.ts","./node_modules/next/dist/server/lib/router-utils/router-server-context.d.ts","./node_modules/next/dist/server/route-modules/route-module.d.ts","./node_modules/next/dist/server/load-components.d.ts","./node_modules/next/dist/server/route-definitions/app-route-route-definition.d.ts","./node_modules/next/dist/server/async-storage/work-store.d.ts","./node_modules/next/dist/server/web/http.d.ts","./node_modules/next/dist/server/route-modules/app-route/shared-modules.d.ts","./node_modules/next/dist/client/components/redirect-status-code.d.ts","./node_modules/next/dist/client/components/redirect-error.d.ts","./node_modules/next/dist/build/templates/app-route.d.ts","./node_modules/next/dist/server/route-modules/app-route/module.d.ts","./node_modules/next/dist/server/route-modules/app-route/module.compiled.d.ts","./node_modules/next/dist/build/segment-config/app/app-segments.d.ts","./node_modules/next/dist/build/utils.d.ts","./node_modules/next/dist/build/turborepo-access-trace/types.d.ts","./node_modules/next/dist/build/turborepo-access-trace/result.d.ts","./node_modules/next/dist/build/turborepo-access-trace/helpers.d.ts","./node_modules/next/dist/build/turborepo-access-trace/index.d.ts","./node_modules/next/dist/export/routes/types.d.ts","./node_modules/next/dist/export/types.d.ts","./node_modules/next/dist/export/worker.d.ts","./node_modules/next/dist/build/worker.d.ts","./node_modules/next/dist/build/index.d.ts","./node_modules/next/dist/server/lib/incremental-cache/index.d.ts","./node_modules/next/dist/server/after/after.d.ts","./node_modules/next/dist/server/after/after-context.d.ts","./node_modules/next/dist/server/app-render/work-async-storage-instance.d.ts","./node_modules/next/dist/server/app-render/work-async-storage.external.d.ts","./node_modules/next/dist/server/request/params.d.ts","./node_modules/next/dist/server/route-matches/route-match.d.ts","./node_modules/next/dist/server/request-meta.d.ts","./node_modules/next/dist/cli/next-test.d.ts","./node_modules/next/dist/server/config-shared.d.ts","./node_modules/next/dist/server/base-http/index.d.ts","./node_modules/next/dist/server/api-utils/index.d.ts","./node_modules/next/dist/types.d.ts","./node_modules/next/dist/shared/lib/html-context.shared-runtime.d.ts","./node_modules/next/dist/shared/lib/utils.d.ts","./node_modules/next/dist/pages/_app.d.ts","./node_modules/next/app.d.ts","./node_modules/next/dist/server/web/spec-extension/unstable-cache.d.ts","./node_modules/next/dist/server/web/spec-extension/revalidate.d.ts","./node_modules/next/dist/server/web/spec-extension/unstable-no-store.d.ts","./node_modules/next/dist/server/use-cache/cache-tag.d.ts","./node_modules/next/cache.d.ts","./node_modules/next/dist/shared/lib/runtime-config.external.d.ts","./node_modules/next/config.d.ts","./node_modules/next/dist/pages/_document.d.ts","./node_modules/next/document.d.ts","./node_modules/next/dist/shared/lib/dynamic.d.ts","./node_modules/next/dynamic.d.ts","./node_modules/next/dist/pages/_error.d.ts","./node_modules/next/error.d.ts","./node_modules/next/dist/shared/lib/head.d.ts","./node_modules/next/head.d.ts","./node_modules/next/dist/server/request/cookies.d.ts","./node_modules/next/dist/server/request/headers.d.ts","./node_modules/next/dist/server/request/draft-mode.d.ts","./node_modules/next/headers.d.ts","./node_modules/next/dist/shared/lib/get-img-props.d.ts","./node_modules/next/dist/client/image-component.d.ts","./node_modules/next/dist/shared/lib/image-external.d.ts","./node_modules/next/image.d.ts","./node_modules/next/dist/client/link.d.ts","./node_modules/next/link.d.ts","./node_modules/next/dist/client/components/redirect.d.ts","./node_modules/next/dist/client/components/not-found.d.ts","./node_modules/next/dist/client/components/forbidden.d.ts","./node_modules/next/dist/client/components/unauthorized.d.ts","./node_modules/next/dist/client/components/unstable-rethrow.server.d.ts","./node_modules/next/dist/client/components/unstable-rethrow.d.ts","./node_modules/next/dist/client/components/navigation.react-server.d.ts","./node_modules/next/dist/client/components/unrecognized-action-error.d.ts","./node_modules/next/dist/client/components/navigation.d.ts","./node_modules/next/navigation.d.ts","./node_modules/next/router.d.ts","./node_modules/next/dist/client/script.d.ts","./node_modules/next/script.d.ts","./node_modules/next/dist/server/web/spec-extension/user-agent.d.ts","./node_modules/next/dist/compiled/@edge-runtime/primitives/url.d.ts","./node_modules/next/dist/server/web/spec-extension/image-response.d.ts","./node_modules/next/dist/compiled/@vercel/og/satori/index.d.ts","./node_modules/next/dist/compiled/@vercel/og/emoji/index.d.ts","./node_modules/next/dist/compiled/@vercel/og/types.d.ts","./node_modules/next/dist/server/after/index.d.ts","./node_modules/next/dist/server/request/root-params.d.ts","./node_modules/next/dist/server/request/connection.d.ts","./node_modules/next/server.d.ts","./node_modules/next/types/global.d.ts","./node_modules/next/types/compiled.d.ts","./node_modules/next/types.d.ts","./node_modules/next/index.d.ts","./node_modules/next/image-types/global.d.ts","./next-env.d.ts","./next.config.ts","./node_modules/source-map-js/source-map.d.ts","./node_modules/postcss/lib/previous-map.d.ts","./node_modules/postcss/lib/input.d.ts","./node_modules/postcss/lib/css-syntax-error.d.ts","./node_modules/postcss/lib/declaration.d.ts","./node_modules/postcss/lib/root.d.ts","./node_modules/postcss/lib/warning.d.ts","./node_modules/postcss/lib/lazy-result.d.ts","./node_modules/postcss/lib/no-work-result.d.ts","./node_modules/postcss/lib/processor.d.ts","./node_modules/postcss/lib/result.d.ts","./node_modules/postcss/lib/document.d.ts","./node_modules/postcss/lib/rule.d.ts","./node_modules/postcss/lib/node.d.ts","./node_modules/postcss/lib/comment.d.ts","./node_modules/postcss/lib/container.d.ts","./node_modules/postcss/lib/at-rule.d.ts","./node_modules/postcss/lib/list.d.ts","./node_modules/postcss/lib/postcss.d.ts","./node_modules/postcss/lib/postcss.d.mts","./node_modules/tailwindcss/types/generated/corepluginlist.d.ts","./node_modules/tailwindcss/types/generated/colors.d.ts","./node_modules/tailwindcss/types/config.d.ts","./node_modules/tailwindcss/types/index.d.ts","./tailwind.config.ts","./lib/config.ts","./node_modules/@azure/msal-common/dist/account/tokenclaims.d.ts","./node_modules/@azure/msal-common/dist/account/authtoken.d.ts","./node_modules/@azure/msal-common/dist/authority/authoritytype.d.ts","./node_modules/@azure/msal-common/dist/authority/openidconfigresponse.d.ts","./node_modules/@azure/msal-common/dist/url/iuri.d.ts","./node_modules/@azure/msal-common/dist/network/networkresponse.d.ts","./node_modules/@azure/msal-common/dist/network/inetworkmodule.d.ts","./node_modules/@azure/msal-common/dist/authority/protocolmode.d.ts","./node_modules/@azure/msal-common/dist/utils/constants.d.ts","./node_modules/@azure/msal-common/dist/logger/logger.d.ts","./node_modules/@azure/msal-common/dist/authority/oidcoptions.d.ts","./node_modules/@azure/msal-common/dist/authority/azureregion.d.ts","./node_modules/@azure/msal-common/dist/authority/azureregionconfiguration.d.ts","./node_modules/@azure/msal-common/dist/authority/clouddiscoverymetadata.d.ts","./node_modules/@azure/msal-common/dist/authority/cloudinstancediscoveryresponse.d.ts","./node_modules/@azure/msal-common/dist/authority/authorityoptions.d.ts","./node_modules/@azure/msal-common/dist/cache/entities/credentialentity.d.ts","./node_modules/@azure/msal-common/dist/cache/entities/idtokenentity.d.ts","./node_modules/@azure/msal-common/dist/cache/entities/accesstokenentity.d.ts","./node_modules/@azure/msal-common/dist/cache/entities/refreshtokenentity.d.ts","./node_modules/@azure/msal-common/dist/cache/entities/appmetadataentity.d.ts","./node_modules/@azure/msal-common/dist/cache/entities/cacherecord.d.ts","./node_modules/@azure/msal-common/dist/account/accountinfo.d.ts","./node_modules/@azure/msal-common/dist/cache/entities/servertelemetryentity.d.ts","./node_modules/@azure/msal-common/dist/cache/entities/throttlingentity.d.ts","./node_modules/@azure/msal-common/dist/cache/entities/authoritymetadataentity.d.ts","./node_modules/@azure/msal-common/dist/request/storeincache.d.ts","./node_modules/@azure/msal-common/dist/telemetry/performance/performanceevent.d.ts","./node_modules/@azure/msal-common/dist/telemetry/performance/iperformancemeasurement.d.ts","./node_modules/@azure/msal-common/dist/telemetry/performance/iperformanceclient.d.ts","./node_modules/@azure/msal-common/dist/cache/cachemanager.d.ts","./node_modules/@azure/msal-common/dist/telemetry/server/servertelemetryrequest.d.ts","./node_modules/@azure/msal-common/dist/authority/regiondiscoverymetadata.d.ts","./node_modules/@azure/msal-common/dist/telemetry/server/servertelemetrymanager.d.ts","./node_modules/@azure/msal-common/dist/cache/interface/iserializabletokencache.d.ts","./node_modules/@azure/msal-common/dist/cache/persistence/tokencachecontext.d.ts","./node_modules/@azure/msal-common/dist/cache/interface/icacheplugin.d.ts","./node_modules/@azure/msal-common/dist/account/clientcredentials.d.ts","./node_modules/@azure/msal-common/dist/config/clientconfiguration.d.ts","./node_modules/@azure/msal-common/dist/utils/msaltypes.d.ts","./node_modules/@azure/msal-common/dist/crypto/joseheader.d.ts","./node_modules/@azure/msal-common/dist/crypto/signedhttprequest.d.ts","./node_modules/@azure/msal-common/dist/request/baseauthrequest.d.ts","./node_modules/@azure/msal-common/dist/crypto/icrypto.d.ts","./node_modules/@azure/msal-common/dist/cache/entities/accountentity.d.ts","./node_modules/@azure/msal-common/dist/request/scopeset.d.ts","./node_modules/@azure/msal-common/dist/cache/utils/cachetypes.d.ts","./node_modules/@azure/msal-common/dist/cache/interface/icachemanager.d.ts","./node_modules/@azure/msal-common/dist/authority/authority.d.ts","./node_modules/@azure/msal-common/dist/authority/authorityfactory.d.ts","./node_modules/@azure/msal-common/dist/cache/utils/cachehelpers.d.ts","./node_modules/@azure/msal-common/dist/utils/timeutils.d.ts","./node_modules/@azure/msal-common/dist/response/serverauthorizationcoderesponse.d.ts","./node_modules/@azure/msal-common/dist/utils/urlutils.d.ts","./node_modules/@azure/msal-common/dist/constants/aadserverparamkeys.d.ts","./node_modules/@azure/msal-common/dist/response/serverauthorizationtokenresponse.d.ts","./node_modules/@azure/msal-common/dist/network/requestthumbprint.d.ts","./node_modules/@azure/msal-common/dist/account/ccscredential.d.ts","./node_modules/@azure/msal-common/dist/client/baseclient.d.ts","./node_modules/@azure/msal-common/dist/request/commonauthorizationurlrequest.d.ts","./node_modules/@azure/msal-common/dist/request/commonauthorizationcoderequest.d.ts","./node_modules/@azure/msal-common/dist/response/authenticationresult.d.ts","./node_modules/@azure/msal-common/dist/request/commonendsessionrequest.d.ts","./node_modules/@azure/msal-common/dist/response/authorizationcodepayload.d.ts","./node_modules/@azure/msal-common/dist/client/authorizationcodeclient.d.ts","./node_modules/@azure/msal-common/dist/request/commonrefreshtokenrequest.d.ts","./node_modules/@azure/msal-common/dist/request/commonsilentflowrequest.d.ts","./node_modules/@azure/msal-common/dist/client/refreshtokenclient.d.ts","./node_modules/@azure/msal-common/dist/client/silentflowclient.d.ts","./node_modules/@azure/msal-common/dist/account/clientinfo.d.ts","./node_modules/@azure/msal-common/dist/network/throttlingutils.d.ts","./node_modules/@azure/msal-common/dist/url/urlstring.d.ts","./node_modules/@azure/msal-common/dist/request/requestparameterbuilder.d.ts","./node_modules/@azure/msal-common/dist/utils/protocolutils.d.ts","./node_modules/@azure/msal-common/dist/response/responsehandler.d.ts","./node_modules/@azure/msal-common/dist/request/authenticationheaderparser.d.ts","./node_modules/@azure/msal-common/dist/error/autherrorcodes.d.ts","./node_modules/@azure/msal-common/dist/error/autherror.d.ts","./node_modules/@azure/msal-common/dist/error/interactionrequiredautherrorcodes.d.ts","./node_modules/@azure/msal-common/dist/error/interactionrequiredautherror.d.ts","./node_modules/@azure/msal-common/dist/error/servererror.d.ts","./node_modules/@azure/msal-common/dist/error/networkerror.d.ts","./node_modules/@azure/msal-common/dist/error/cacheerrorcodes.d.ts","./node_modules/@azure/msal-common/dist/error/cacheerror.d.ts","./node_modules/@azure/msal-common/dist/error/clientautherrorcodes.d.ts","./node_modules/@azure/msal-common/dist/error/clientautherror.d.ts","./node_modules/@azure/msal-common/dist/error/clientconfigurationerrorcodes.d.ts","./node_modules/@azure/msal-common/dist/error/clientconfigurationerror.d.ts","./node_modules/@azure/msal-common/dist/utils/stringutils.d.ts","./node_modules/@azure/msal-common/dist/utils/functionwrappers.d.ts","./node_modules/@azure/msal-common/dist/packagemetadata.d.ts","./node_modules/@azure/msal-common/dist/exports-common.d.ts","./node_modules/@azure/msal-common/dist/response/externaltokenresponse.d.ts","./node_modules/@azure/msal-common/dist/telemetry/performance/performanceclient.d.ts","./node_modules/@azure/msal-common/dist/telemetry/performance/stubperformanceclient.d.ts","./node_modules/@azure/msal-common/dist/crypto/poptokengenerator.d.ts","./node_modules/@azure/msal-common/dist/exports-browser-only.d.ts","./node_modules/@azure/msal-common/dist/index-browser.d.ts","./node_modules/@azure/msal-browser/dist/request/popupwindowattributes.d.ts","./node_modules/@azure/msal-browser/dist/request/popuprequest.d.ts","./node_modules/@azure/msal-browser/dist/request/redirectrequest.d.ts","./node_modules/@azure/msal-browser/dist/utils/browserconstants.d.ts","./node_modules/@azure/msal-browser/dist/navigation/navigationoptions.d.ts","./node_modules/@azure/msal-browser/dist/navigation/inavigationclient.d.ts","./node_modules/@azure/msal-browser/dist/config/configuration.d.ts","./node_modules/@azure/msal-browser/dist/utils/browserutils.d.ts","./node_modules/@azure/msal-browser/dist/request/silentrequest.d.ts","./node_modules/@azure/msal-browser/dist/cache/iwindowstorage.d.ts","./node_modules/@azure/msal-browser/dist/cache/memorystorage.d.ts","./node_modules/@azure/msal-browser/dist/broker/nativebroker/nativerequest.d.ts","./node_modules/@azure/msal-browser/dist/response/authenticationresult.d.ts","./node_modules/@azure/msal-browser/dist/request/ssosilentrequest.d.ts","./node_modules/@azure/msal-browser/dist/cache/cookiestorage.d.ts","./node_modules/@azure/msal-browser/dist/cache/browsercachemanager.d.ts","./node_modules/@azure/msal-browser/dist/cache/tokencache.d.ts","./node_modules/@azure/msal-browser/dist/cache/itokencache.d.ts","./node_modules/@azure/msal-browser/dist/request/authorizationcoderequest.d.ts","./node_modules/@azure/msal-browser/dist/request/endsessionrequest.d.ts","./node_modules/@azure/msal-browser/dist/request/endsessionpopuprequest.d.ts","./node_modules/@azure/msal-browser/dist/event/eventtype.d.ts","./node_modules/@azure/msal-browser/dist/event/eventmessage.d.ts","./node_modules/@azure/msal-browser/dist/request/clearcacherequest.d.ts","./node_modules/@azure/msal-browser/dist/request/initializeapplicationrequest.d.ts","./node_modules/@azure/msal-browser/dist/app/ipublicclientapplication.d.ts","./node_modules/@azure/msal-browser/dist/controllers/icontroller.d.ts","./node_modules/@azure/msal-browser/dist/app/publicclientapplication.d.ts","./node_modules/@azure/msal-browser/dist/app/publicclientnext.d.ts","./node_modules/@azure/msal-browser/dist/error/browserautherrorcodes.d.ts","./node_modules/@azure/msal-browser/dist/error/browserautherror.d.ts","./node_modules/@azure/msal-browser/dist/error/browserconfigurationautherrorcodes.d.ts","./node_modules/@azure/msal-browser/dist/error/browserconfigurationautherror.d.ts","./node_modules/@azure/msal-browser/dist/navigation/navigationclient.d.ts","./node_modules/@azure/msal-browser/dist/request/authorizationurlrequest.d.ts","./node_modules/@azure/msal-browser/dist/cache/browserstorage.d.ts","./node_modules/@azure/msal-browser/dist/cache/localstorage.d.ts","./node_modules/@azure/msal-browser/dist/cache/sessionstorage.d.ts","./node_modules/@azure/msal-browser/dist/event/eventhandler.d.ts","./node_modules/@azure/msal-browser/dist/crypto/signedhttprequest.d.ts","./node_modules/@azure/msal-browser/dist/telemetry/browserperformanceclient.d.ts","./node_modules/@azure/msal-browser/dist/telemetry/browserperformancemeasurement.d.ts","./node_modules/@azure/msal-browser/dist/packagemetadata.d.ts","./node_modules/@azure/msal-browser/dist/index.d.ts","./lib/msalconfig.ts","./node_modules/next/dist/compiled/@next/font/dist/types.d.ts","./node_modules/next/dist/compiled/@next/font/dist/google/index.d.ts","./node_modules/next/font/google/index.d.ts","./node_modules/@azure/msal-react/dist/msalcontext.d.ts","./node_modules/@azure/msal-react/dist/msalprovider.d.ts","./node_modules/@azure/msal-react/dist/types/accountidentifiers.d.ts","./node_modules/@azure/msal-react/dist/hooks/usemsalauthentication.d.ts","./node_modules/@azure/msal-react/dist/components/msalauthenticationtemplate.d.ts","./node_modules/@azure/msal-react/dist/components/authenticatedtemplate.d.ts","./node_modules/@azure/msal-react/dist/components/unauthenticatedtemplate.d.ts","./node_modules/@azure/msal-react/dist/utils/utilities.d.ts","./node_modules/@azure/msal-react/dist/components/withmsal.d.ts","./node_modules/@azure/msal-react/dist/hooks/usemsal.d.ts","./node_modules/@azure/msal-react/dist/hooks/useaccount.d.ts","./node_modules/@azure/msal-react/dist/hooks/useisauthenticated.d.ts","./node_modules/@azure/msal-react/dist/packagemetadata.d.ts","./node_modules/@azure/msal-react/dist/index.d.ts","./contexts/authcontext.tsx","./node_modules/@microsoft/signalr/dist/esm/abortcontroller.d.ts","./node_modules/@microsoft/signalr/dist/esm/itransport.d.ts","./node_modules/@microsoft/signalr/dist/esm/errors.d.ts","./node_modules/@microsoft/signalr/dist/esm/ilogger.d.ts","./node_modules/@microsoft/signalr/dist/esm/ihubprotocol.d.ts","./node_modules/@microsoft/signalr/dist/esm/httpclient.d.ts","./node_modules/@microsoft/signalr/dist/esm/defaulthttpclient.d.ts","./node_modules/@microsoft/signalr/dist/esm/ihttpconnectionoptions.d.ts","./node_modules/@microsoft/signalr/dist/esm/istatefulreconnectoptions.d.ts","./node_modules/@microsoft/signalr/dist/esm/stream.d.ts","./node_modules/@microsoft/signalr/dist/esm/hubconnection.d.ts","./node_modules/@microsoft/signalr/dist/esm/iretrypolicy.d.ts","./node_modules/@microsoft/signalr/dist/esm/hubconnectionbuilder.d.ts","./node_modules/@microsoft/signalr/dist/esm/loggers.d.ts","./node_modules/@microsoft/signalr/dist/esm/jsonhubprotocol.d.ts","./node_modules/@microsoft/signalr/dist/esm/subject.d.ts","./node_modules/@microsoft/signalr/dist/esm/pkg-version.d.ts","./node_modules/@microsoft/signalr/dist/esm/utils.d.ts","./node_modules/@microsoft/signalr/dist/esm/index.d.ts","./contexts/signalrcontext.tsx","./contexts/tenantcontext.tsx","./contexts/notificationcontext.tsx","./contexts/themecontext.tsx","./components/navbar.tsx","./app/layout.tsx","./components/protectedroute.tsx","./app/page.tsx","./app/about/page.tsx","./app/admin-configuration/page.tsx","./app/analyze-rules/page.tsx","./app/audit/page.tsx","./app/diagnosis/[sessionid]/page.tsx","./app/docs/page.tsx","./app/fleet-health/page.tsx","./app/flight-tracker/[sessionid]/page.tsx","./app/gather-rules/page.tsx","./app/health-check/page.tsx","./app/landing/page.tsx","./app/platform-usage-metrics/page.tsx","./app/progress/page.tsx","./components/performancechart.tsx","./components/downloadprogress.tsx","./app/sessions/[sessionid]/page.tsx","./app/settings/page.tsx","./app/usage-metrics/page.tsx","./.next/types/cache-life.d.ts","./.next/types/validator.ts","./.next/types/app/layout.ts","./.next/types/app/page.ts","./.next/types/app/gather-rules/page.ts","./.next/types/app/sessions/[sessionid]/page.ts","./node_modules/@types/estree/index.d.ts","./node_modules/@types/json-schema/index.d.ts","./node_modules/@types/json5/index.d.ts"],"fileIdsList":[[100,148,165,166,341,725],[100,148,165,166,341,714],[100,148,165,166,341,716],[100,148,165,166,341,732],[100,148,165,166,448,449,450,451],[100,148,165,166],[83,100,148,165,166,498,714,716,717,718,719,720,721,722,723,724,725,726,727,728,729,732,733,734],[100,148,165,166,472,482,715],[86,100,148,165,166,482,528,689,715],[86,100,148,165,166,482,528,689,710,715],[86,100,148,165,166,482,528,689,709,710,715],[100,148,165,166,472,715],[86,100,148,165,166,528,689,711],[86,100,148,165,166,482,689],[100,148,165,166,499,674,689,709,710,711,712,713],[86,100,148,165,166,482,528,689,709,710,711,715],[86,100,148,165,166,528,689,709,710,715],[86,100,148,165,166,482,528,689,709,710,715,730,731],[86,100,148,165,166],[86,100,148,165,166,472,482,528,689,711,712],[86,100,148,165,166,528,670,671,688],[86,100,148,165,166,528,689,708],[86,100,148,165,166,689],[100,148,165,166,670],[83,100,148,165,166,499,500],[100,148,165,166,499],[100,148,165,166,626,628,629,630,632,633,635,639,640,644,645,646,647,648,649,650,651],[100,148,165,166,626,628,629,630,632,633,635,639,640,644,645,646,647,648,649,650,651,652,653],[100,148,165,166,626,628,629,630,632,633,635,639,640,644,645,646,648,649,650,652,653],[100,148,165,166,626,630],[100,148,165,166,626,628,629,630,633,635,636,637,638,639,640,641],[100,148,165,166,636],[100,148,165,166,626,635,639,643],[100,148,165,166,626,633,635,639,642,644],[100,148,165,166,626,630,632],[100,148,165,166,626],[100,148,165,166,626,656],[100,148,165,166,626,658],[100,148,165,166,626,630,648,649],[100,148,165,166,626,628,629,630,635,639,640,646,648],[100,148,165,166,626,627,628,629,630,631,632,633,634,635,636,637,639,640,643,644,645,646,647,648,649,650,651,652,653,654,655,657,659,660,661,662,663,664,665,666,667,668,669],[100,148,165,166,631],[100,148,165,166,631,632],[100,148,165,166,630],[100,148,165,166,626,627],[100,148,165,166,626,633],[100,148,165,166,628,629],[100,148,165,166,529],[100,148,165,166,531,532,533,535,536,538,542,544,558,561,567,576],[100,148,165,166,535,538,544,558,576,577],[100,148,165,166,536,539,541,543],[100,148,165,166,540],[100,148,165,166,542],[100,148,165,166,537],[100,148,165,166,538,544,546,547,548,549,550,551,552,553,554,555,558,571,572,573,575,576],[100,148,165,166,537,545],[100,148,165,166,529,531,538,551,572,577],[100,148,165,166,546,547,548,549,573],[100,148,165,166,545],[100,148,165,166,546,547,548,549,550,551,552,553,554,555,573,575],[100,148,165,166,564],[100,148,165,166,563],[100,148,165,166,532,537,542,545,546,547,548,549,554],[100,148,165,166,537,546,547,548,549,551,552,553,554,573,574],[100,148,165,166,558,567,581,587,588,589,590,591,592],[100,148,165,166,534,535,538,558,559,562,567,571,572,577,584,585,586],[100,148,165,166,558,567,587,590,594,595],[100,148,165,166,537,558,567,587,590,595],[100,148,165,166,535,538,544,559,562,563,565,566,572,577],[100,148,165,166,570,571],[100,148,165,166,538,558,572],[100,148,165,166,569],[100,148,165,166,605],[100,148,165,166,611],[100,148,165,166,606,613],[100,148,165,166,606,615],[100,148,165,166,606,607],[100,148,165,166,606],[100,148,165,166,556,557,558,569,570,621,622,623,624],[100,148,165,166,529,530,531,533,534,535,536,537,538,539,540,541,544,545,546,547,548,549,550,551,552,553,554,555,559,560,562,567,568,571,572,573,574,575,577,578,579,580,581,582,583,584,585,586,587,588,589,590,591,592,593,594,595,596,597,598,599,600,601,602,603,604,606,608,609,610,612,614,616,617,618,619],[100,148,165,166,620,625],[100,148,165,166,567],[100,148,165,166,534],[100,148,165,166,537,570],[100,148,165,166,534,559,571,584,585],[100,148,165,166,537,555,567,568,570],[100,148,165,166,568,571,586],[100,148,165,166,537,551,568,571],[100,148,165,166,551,568],[100,148,165,166,551,568,571],[100,148,165,166,537,558,562,567,568,598],[100,148,165,166,551],[100,148,165,166,584],[100,148,165,166,529,538,550,558,559,563,565,571,572,573,577,581,584,590,592,602],[100,148,165,166,556,557],[100,148,165,166,538,556,557,558,567],[100,148,165,166,556,557,558],[100,148,165,166,537,552,559,560,561],[100,148,165,166,533],[100,148,165,166,538,558],[100,148,165,166,572],[100,148,165,166,581],[86,100,148,165,166,677],[86,100,148,165,166,670,675,677,678],[86,100,148,165,166,675,682],[100,148,165,166,670,677],[100,148,165,166,677],[100,148,165,166,675],[100,148,165,166,675,676,677,678,679,680,681,682,683,684,685,686,687],[86,100,148,165,166,670],[86,100,148,165,166,670,677],[100,148,165,166,693,695],[100,148,165,166,691],[100,148,165,166,690,694],[100,148,165,166,699],[100,148,165,166,691,693,694,697,698,700,701],[100,148,165,166,691,693,694,695],[100,148,165,166,691,693],[100,148,165,166,690,691,692,693,694,695,696,697,698,699,700,701,702,703,704,705,707],[100,148,165,166,691,693,694],[100,148,165,166,693],[100,148,165,166,693,695,697,699,705,706],[100,145,146,148,165,166],[100,147,148,165,166],[148,165,166],[100,148,153,165,166,183],[100,148,149,154,159,165,166,168,180,191],[100,148,149,150,159,165,166,168],[95,96,97,100,148,165,166],[100,148,151,165,166,192],[100,148,152,153,160,165,166,169],[100,148,153,165,166,180,188],[100,148,154,156,159,165,166,168],[100,147,148,155,165,166],[100,148,156,157,165,166],[100,148,158,159,165,166],[100,147,148,159,165,166],[100,148,159,160,161,165,166,180,191],[100,148,159,160,161,165,166,175,180,183],[100,141,148,156,159,162,165,166,168,180,191],[100,148,159,160,162,163,165,166,168,180,188,191],[100,148,162,164,165,166,180,188,191],[98,99,100,101,102,103,104,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197],[100,148,159,165,166],[100,148,165,166,167,191],[100,148,156,159,165,166,168,180],[100,148,165,166,169],[100,148,165,166,170],[100,147,148,165,166,171],[100,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197],[100,148,165,166,173],[100,148,165,166,174],[100,148,159,165,166,175,176],[100,148,165,166,175,177,192,194],[100,148,160,165,166],[100,148,159,165,166,180,181,183],[100,148,165,166,182,183],[100,148,165,166,180,181],[100,148,165,166,183],[100,148,165,166,184],[100,145,148,165,166,180,185],[100,148,159,165,166,186,187],[100,148,165,166,186,187],[100,148,153,165,166,168,180,188],[100,148,165,166,189],[100,148,165,166,168,190],[100,148,162,165,166,174,191],[100,148,153,165,166,192],[100,148,165,166,180,193],[100,148,165,166,167,194],[100,148,165,166,195],[100,141,148,165,166],[100,141,148,159,161,165,166,171,180,183,191,193,194,196],[100,148,165,166,180,197],[86,90,100,148,165,166,199,200,201,203,443,491],[86,90,100,148,165,166,199,200,201,202,358,443,491],[86,90,100,148,165,166,199,200,202,203,443,491],[86,100,148,165,166,203,358,359],[86,100,148,165,166,203,358],[86,90,100,148,165,166,200,201,202,203,443,491],[86,90,100,148,165,166,199,201,202,203,443,491],[84,85,100,148,165,166],[92,100,148,165,166],[100,148,165,166,446],[100,148,165,166,453],[100,148,165,166,207,221,222,223,225,440],[100,148,165,166,207,246,248,250,251,254,440,442],[100,148,165,166,207,211,213,214,215,216,217,429,440,442],[100,148,165,166,440],[100,148,165,166,222,324,410,419,436],[100,148,165,166,207],[100,148,165,166,204,436],[100,148,165,166,258],[100,148,165,166,257,440,442],[100,148,162,165,166,306,324,353,497],[100,148,162,165,166,317,333,419,435],[100,148,162,165,166,371],[100,148,165,166,423],[100,148,165,166,422,423,424],[100,148,165,166,422],[94,100,148,162,165,166,204,207,211,214,218,219,220,222,226,234,235,364,389,420,440,443],[100,148,165,166,207,224,242,246,247,252,253,440,497],[100,148,165,166,224,497],[100,148,165,166,235,242,304,440,497],[100,148,165,166,497],[100,148,165,166,207,224,225,497],[100,148,165,166,249,497],[100,148,165,166,218,421,428],[100,148,165,166,174,266,436],[100,148,165,166,266,436],[86,100,148,165,166,266],[86,100,148,165,166,325],[100,148,165,166,321,369,436,479,480],[100,148,165,166,416,473,474,475,476,478],[100,148,165,166,415],[100,148,165,166,415,416],[100,148,165,166,215,365,366,367],[100,148,165,166,365,368,369],[100,148,165,166,477],[100,148,165,166,365,369],[86,100,148,165,166,208,467],[86,100,148,165,166,191],[86,100,148,165,166,224,294],[86,100,148,165,166,224],[100,148,165,166,292,296],[86,100,148,165,166,293,445],[100,148,165,166,672],[86,90,100,148,162,165,166,198,199,200,201,202,203,443,489,490],[100,148,162,165,166],[100,148,162,165,166,211,273,365,375,390,410,425,426,440,441,497],[100,148,165,166,234,427],[100,148,165,166,443],[100,148,165,166,206],[86,100,148,165,166,306,320,332,342,344,435],[100,148,165,166,174,306,320,341,342,343,435,496],[100,148,165,166,335,336,337,338,339,340],[100,148,165,166,337],[100,148,165,166,341],[100,148,165,166,264,265,266,268],[86,100,148,165,166,259,260,261,267],[100,148,165,166,264,267],[100,148,165,166,262],[100,148,165,166,263],[86,100,148,165,166,266,293,445],[86,100,148,165,166,266,444,445],[86,100,148,165,166,266,445],[100,148,165,166,390,432],[100,148,165,166,432],[100,148,162,165,166,441,445],[100,148,165,166,329],[100,147,148,165,166,328],[100,148,165,166,236,274,312,314,316,317,318,319,362,365,435,438,441],[100,148,165,166,236,350,365,369],[100,148,165,166,317,435],[86,100,148,165,166,317,326,327,329,330,331,332,333,334,345,346,347,348,349,351,352,435,436,497],[100,148,165,166,311],[100,148,162,165,166,174,236,237,273,288,318,362,363,364,369,390,410,431,440,441,442,443,497],[100,148,165,166,435],[100,147,148,165,166,222,315,318,364,431,433,434,441],[100,148,165,166,317],[100,147,148,165,166,273,278,307,308,309,310,311,312,313,314,316,435,436],[100,148,162,165,166,278,279,307,441,442],[100,148,165,166,222,364,365,390,431,435,441],[100,148,162,165,166,440,442],[100,148,162,165,166,180,438,441,442],[100,148,162,165,166,174,191,204,211,224,236,237,239,274,275,280,285,288,314,318,365,375,377,380,382,385,386,387,388,389,410,430,431,436,438,440,441,442],[100,148,162,165,166,180],[100,148,165,166,207,208,209,211,216,219,224,242,430,438,439,443,445,497],[100,148,162,165,166,180,191,254,256,258,259,260,261,268,497],[100,148,165,166,174,191,204,246,256,284,285,286,287,314,365,380,389,390,396,399,400,410,431,436,438],[100,148,165,166,218,219,234,364,389,431,440],[100,148,162,165,166,191,208,211,314,394,438,440],[100,148,165,166,305],[100,148,162,165,166,397,398,407],[100,148,165,166,438,440],[100,148,165,166,312,315],[100,148,165,166,314,318,430,445],[100,148,162,165,166,174,240,246,287,380,390,396,399,402,438],[100,148,162,165,166,218,234,246,403],[100,148,165,166,207,239,405,430,440],[100,148,162,165,166,191,440],[100,148,162,165,166,224,238,239,240,251,269,404,406,430,440],[94,100,148,165,166,236,318,409,443,445],[100,148,162,165,166,174,191,211,218,226,234,237,274,280,284,285,286,287,288,314,365,377,390,391,393,395,410,430,431,436,437,438,445],[100,148,162,165,166,180,218,396,401,407,438],[100,148,165,166,229,230,231,232,233],[100,148,165,166,275,381],[100,148,165,166,383],[100,148,165,166,381],[100,148,165,166,383,384],[100,148,162,165,166,211,214,215,273,441],[100,148,162,165,166,174,206,208,236,274,288,318,373,374,410,438,442,443,445],[100,148,162,165,166,174,191,210,215,314,374,437,441],[100,148,165,166,307],[100,148,165,166,308],[100,148,165,166,309],[100,148,165,166,436],[100,148,165,166,255,271],[100,148,162,165,166,211,255,274],[100,148,165,166,270,271],[100,148,165,166,272],[100,148,165,166,255,256],[100,148,165,166,255,289],[100,148,165,166,255],[100,148,165,166,275,379,437],[100,148,165,166,378],[100,148,165,166,256,436,437],[100,148,165,166,376,437],[100,148,165,166,256,436],[100,148,165,166,362],[100,148,165,166,211,216,274,303,306,312,314,318,320,323,354,357,361,365,409,430,438,441],[100,148,165,166,297,300,301,302,321,322,369],[86,100,148,165,166,201,203,266,355,356],[86,100,148,165,166,201,203,266,355,356,360],[100,148,165,166,418],[100,148,165,166,222,279,317,318,329,333,365,409,411,412,413,414,416,417,420,430,435,440],[100,148,165,166,369],[100,148,165,166,373],[100,148,162,165,166,274,290,370,372,375,409,438,443,445],[100,148,165,166,297,298,299,300,301,302,321,322,369,444],[94,100,148,162,165,166,174,191,237,255,256,288,314,318,407,408,410,430,431,440,441,443],[100,148,165,166,279,281,284,431],[100,148,162,165,166,275,440],[100,148,165,166,278,317],[100,148,165,166,277],[100,148,165,166,279,280],[100,148,165,166,276,278,440],[100,148,162,165,166,210,279,281,282,283,440,441],[86,100,148,165,166,365,366,368],[100,148,165,166,241],[86,100,148,165,166,208],[86,100,148,165,166,436],[86,94,100,148,165,166,288,318,443,445],[100,148,165,166,208,467,468],[86,100,148,165,166,296],[86,100,148,165,166,174,191,206,253,291,293,295,445],[100,148,165,166,224,436,441],[100,148,165,166,392,436],[100,148,165,166,365],[86,100,148,160,162,165,166,174,206,242,248,296,443,444],[86,100,148,165,166,199,200,201,202,203,443,491],[86,87,88,89,90,100,148,165,166],[100,148,153,165,166],[100,148,165,166,243,244,245],[100,148,165,166,243],[86,90,100,148,162,164,165,166,174,198,199,200,201,202,203,204,206,237,341,402,440,442,445,491],[100,148,165,166,455],[100,148,165,166,457],[100,148,165,166,459],[100,148,165,166,673],[100,148,165,166,461],[100,148,165,166,463,464,465],[100,148,165,166,469],[91,93,100,148,165,166,447,452,454,456,458,460,462,466,470,472,482,483,485,495,496,497,498],[100,148,165,166,471],[100,148,165,166,481],[100,148,165,166,293],[100,148,165,166,484],[100,147,148,165,166,279,281,282,284,332,436,486,487,488,491,492,493,494],[100,148,165,166,198],[100,148,165,166,518],[100,148,165,166,516,518],[100,148,165,166,507,515,516,517,519,521],[100,148,165,166,505],[100,148,165,166,508,513,518,521],[100,148,165,166,504,521],[100,148,165,166,508,509,512,513,514,521],[100,148,165,166,508,509,510,512,513,521],[100,148,165,166,505,506,507,508,509,513,514,515,517,518,519,521],[100,148,165,166,521],[100,148,165,166,503,505,506,507,508,509,510,512,513,514,515,516,517,518,519,520],[100,148,165,166,503,521],[100,148,165,166,508,510,511,513,514,521],[100,148,165,166,512,521],[100,148,165,166,513,514,518,521],[100,148,165,166,506,516],[100,148,165,166,180,198],[100,148,165,166,523,524],[100,148,165,166,522,525],[100,113,117,148,165,166,191],[100,113,148,165,166,180,191],[100,108,148,165,166],[100,110,113,148,165,166,188,191],[100,148,165,166,168,188],[100,108,148,165,166,198],[100,110,113,148,165,166,168,191],[100,105,106,109,112,148,159,165,166,180,191],[100,113,120,148,165,166],[100,105,111,148,165,166],[100,113,134,135,148,165,166],[100,109,113,148,165,166,183,191,198],[100,134,148,165,166,198],[100,107,108,148,165,166,198],[100,113,148,165,166],[100,107,108,109,110,111,112,113,114,115,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,135,136,137,138,139,140,148,165,166],[100,113,128,148,165,166],[100,113,120,121,148,165,166],[100,111,113,121,122,148,165,166],[100,112,148,165,166],[100,105,108,113,148,165,166],[100,113,117,121,122,148,165,166],[100,117,148,165,166],[100,111,113,116,148,165,166,191],[100,105,110,113,120,148,165,166],[100,148,165,166,180],[100,108,113,134,148,165,166,196,198],[100,148,165,166,526]],"fileInfos":[{"version":"c430d44666289dae81f30fa7b2edebf186ecc91a2d4c71266ea6ae76388792e1","affectsGlobalScope":true,"impliedFormat":1},{"version":"45b7ab580deca34ae9729e97c13cfd999df04416a79116c3bfb483804f85ded4","impliedFormat":1},{"version":"3facaf05f0c5fc569c5649dd359892c98a85557e3e0c847964caeb67076f4d75","impliedFormat":1},{"version":"e44bb8bbac7f10ecc786703fe0a6a4b952189f908707980ba8f3c8975a760962","impliedFormat":1},{"version":"5e1c4c362065a6b95ff952c0eab010f04dcd2c3494e813b493ecfd4fcb9fc0d8","impliedFormat":1},{"version":"68d73b4a11549f9c0b7d352d10e91e5dca8faa3322bfb77b661839c42b1ddec7","impliedFormat":1},{"version":"5efce4fc3c29ea84e8928f97adec086e3dc876365e0982cc8479a07954a3efd4","impliedFormat":1},{"version":"feecb1be483ed332fad555aff858affd90a48ab19ba7272ee084704eb7167569","impliedFormat":1},{"version":"ee7bad0c15b58988daa84371e0b89d313b762ab83cb5b31b8a2d1162e8eb41c2","impliedFormat":1},{"version":"27bdc30a0e32783366a5abeda841bc22757c1797de8681bbe81fbc735eeb1c10","impliedFormat":1},{"version":"8fd575e12870e9944c7e1d62e1f5a73fcf23dd8d3a321f2a2c74c20d022283fe","impliedFormat":1},{"version":"2ab096661c711e4a81cc464fa1e6feb929a54f5340b46b0a07ac6bbf857471f0","impliedFormat":1},{"version":"080941d9f9ff9307f7e27a83bcd888b7c8270716c39af943532438932ec1d0b9","affectsGlobalScope":true,"impliedFormat":1},{"version":"2e80ee7a49e8ac312cc11b77f1475804bee36b3b2bc896bead8b6e1266befb43","affectsGlobalScope":true,"impliedFormat":1},{"version":"c57796738e7f83dbc4b8e65132f11a377649c00dd3eee333f672b8f0a6bea671","affectsGlobalScope":true,"impliedFormat":1},{"version":"dc2df20b1bcdc8c2d34af4926e2c3ab15ffe1160a63e58b7e09833f616efff44","affectsGlobalScope":true,"impliedFormat":1},{"version":"515d0b7b9bea2e31ea4ec968e9edd2c39d3eebf4a2d5cbd04e88639819ae3b71","affectsGlobalScope":true,"impliedFormat":1},{"version":"0559b1f683ac7505ae451f9a96ce4c3c92bdc71411651ca6ddb0e88baaaad6a3","affectsGlobalScope":true,"impliedFormat":1},{"version":"0dc1e7ceda9b8b9b455c3a2d67b0412feab00bd2f66656cd8850e8831b08b537","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce691fb9e5c64efb9547083e4a34091bcbe5bdb41027e310ebba8f7d96a98671","affectsGlobalScope":true,"impliedFormat":1},{"version":"8d697a2a929a5fcb38b7a65594020fcef05ec1630804a33748829c5ff53640d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ff2a353abf8a80ee399af572debb8faab2d33ad38c4b4474cff7f26e7653b8d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fb0f136d372979348d59b3f5020b4cdb81b5504192b1cacff5d1fbba29378aa1","affectsGlobalScope":true,"impliedFormat":1},{"version":"d15bea3d62cbbdb9797079416b8ac375ae99162a7fba5de2c6c505446486ac0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"68d18b664c9d32a7336a70235958b8997ebc1c3b8505f4f1ae2b7e7753b87618","affectsGlobalScope":true,"impliedFormat":1},{"version":"eb3d66c8327153d8fa7dd03f9c58d351107fe824c79e9b56b462935176cdf12a","affectsGlobalScope":true,"impliedFormat":1},{"version":"38f0219c9e23c915ef9790ab1d680440d95419ad264816fa15009a8851e79119","affectsGlobalScope":true,"impliedFormat":1},{"version":"69ab18c3b76cd9b1be3d188eaf8bba06112ebbe2f47f6c322b5105a6fbc45a2e","affectsGlobalScope":true,"impliedFormat":1},{"version":"a680117f487a4d2f30ea46f1b4b7f58bef1480456e18ba53ee85c2746eeca012","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f11ff796926e0832f9ae148008138ad583bd181899ab7dd768a2666700b1893","affectsGlobalScope":true,"impliedFormat":1},{"version":"4de680d5bb41c17f7f68e0419412ca23c98d5749dcaaea1896172f06435891fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"954296b30da6d508a104a3a0b5d96b76495c709785c1d11610908e63481ee667","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac9538681b19688c8eae65811b329d3744af679e0bdfa5d842d0e32524c73e1c","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a969edff4bd52585473d24995c5ef223f6652d6ef46193309b3921d65dd4376","affectsGlobalScope":true,"impliedFormat":1},{"version":"9e9fbd7030c440b33d021da145d3232984c8bb7916f277e8ffd3dc2e3eae2bdb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811ec78f7fefcabbda4bfa93b3eb67d9ae166ef95f9bff989d964061cbf81a0c","affectsGlobalScope":true,"impliedFormat":1},{"version":"717937616a17072082152a2ef351cb51f98802fb4b2fdabd32399843875974ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"d7e7d9b7b50e5f22c915b525acc5a49a7a6584cf8f62d0569e557c5cfc4b2ac2","affectsGlobalScope":true,"impliedFormat":1},{"version":"71c37f4c9543f31dfced6c7840e068c5a5aacb7b89111a4364b1d5276b852557","affectsGlobalScope":true,"impliedFormat":1},{"version":"576711e016cf4f1804676043e6a0a5414252560eb57de9faceee34d79798c850","affectsGlobalScope":true,"impliedFormat":1},{"version":"89c1b1281ba7b8a96efc676b11b264de7a8374c5ea1e6617f11880a13fc56dc6","affectsGlobalScope":true,"impliedFormat":1},{"version":"74f7fa2d027d5b33eb0471c8e82a6c87216223181ec31247c357a3e8e2fddc5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"d6d7ae4d1f1f3772e2a3cde568ed08991a8ae34a080ff1151af28b7f798e22ca","affectsGlobalScope":true,"impliedFormat":1},{"version":"063600664504610fe3e99b717a1223f8b1900087fab0b4cad1496a114744f8df","affectsGlobalScope":true,"impliedFormat":1},{"version":"934019d7e3c81950f9a8426d093458b65d5aff2c7c1511233c0fd5b941e608ab","affectsGlobalScope":true,"impliedFormat":1},{"version":"52ada8e0b6e0482b728070b7639ee42e83a9b1c22d205992756fe020fd9f4a47","affectsGlobalScope":true,"impliedFormat":1},{"version":"3bdefe1bfd4d6dee0e26f928f93ccc128f1b64d5d501ff4a8cf3c6371200e5e6","affectsGlobalScope":true,"impliedFormat":1},{"version":"59fb2c069260b4ba00b5643b907ef5d5341b167e7d1dbf58dfd895658bda2867","affectsGlobalScope":true,"impliedFormat":1},{"version":"639e512c0dfc3fad96a84caad71b8834d66329a1f28dc95e3946c9b58176c73a","affectsGlobalScope":true,"impliedFormat":1},{"version":"368af93f74c9c932edd84c58883e736c9e3d53cec1fe24c0b0ff451f529ceab1","affectsGlobalScope":true,"impliedFormat":1},{"version":"af3dd424cf267428f30ccfc376f47a2c0114546b55c44d8c0f1d57d841e28d74","affectsGlobalScope":true,"impliedFormat":1},{"version":"995c005ab91a498455ea8dfb63aa9f83fa2ea793c3d8aa344be4a1678d06d399","affectsGlobalScope":true,"impliedFormat":1},{"version":"959d36cddf5e7d572a65045b876f2956c973a586da58e5d26cde519184fd9b8a","affectsGlobalScope":true,"impliedFormat":1},{"version":"965f36eae237dd74e6cca203a43e9ca801ce38824ead814728a2807b1910117d","affectsGlobalScope":true,"impliedFormat":1},{"version":"3925a6c820dcb1a06506c90b1577db1fdbf7705d65b62b99dce4be75c637e26b","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a3d63ef2b853447ec4f749d3f368ce642264246e02911fcb1590d8c161b8005","affectsGlobalScope":true,"impliedFormat":1},{"version":"8cdf8847677ac7d20486e54dd3fcf09eda95812ac8ace44b4418da1bbbab6eb8","affectsGlobalScope":true,"impliedFormat":1},{"version":"8444af78980e3b20b49324f4a16ba35024fef3ee069a0eb67616ea6ca821c47a","affectsGlobalScope":true,"impliedFormat":1},{"version":"3287d9d085fbd618c3971944b65b4be57859f5415f495b33a6adc994edd2f004","affectsGlobalScope":true,"impliedFormat":1},{"version":"b4b67b1a91182421f5df999988c690f14d813b9850b40acd06ed44691f6727ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"df83c2a6c73228b625b0beb6669c7ee2a09c914637e2d35170723ad49c0f5cd4","affectsGlobalScope":true,"impliedFormat":1},{"version":"436aaf437562f276ec2ddbee2f2cdedac7664c1e4c1d2c36839ddd582eeb3d0a","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e3c06ea092138bf9fa5e874a1fdbc9d54805d074bee1de31b99a11e2fec239d","affectsGlobalScope":true,"impliedFormat":1},{"version":"87dc0f382502f5bbce5129bdc0aea21e19a3abbc19259e0b43ae038a9fc4e326","affectsGlobalScope":true,"impliedFormat":1},{"version":"b1cb28af0c891c8c96b2d6b7be76bd394fddcfdb4709a20ba05a7c1605eea0f9","affectsGlobalScope":true,"impliedFormat":1},{"version":"2fef54945a13095fdb9b84f705f2b5994597640c46afeb2ce78352fab4cb3279","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac77cb3e8c6d3565793eb90a8373ee8033146315a3dbead3bde8db5eaf5e5ec6","affectsGlobalScope":true,"impliedFormat":1},{"version":"56e4ed5aab5f5920980066a9409bfaf53e6d21d3f8d020c17e4de584d29600ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"4ece9f17b3866cc077099c73f4983bddbcb1dc7ddb943227f1ec070f529dedd1","affectsGlobalScope":true,"impliedFormat":1},{"version":"0a6282c8827e4b9a95f4bf4f5c205673ada31b982f50572d27103df8ceb8013c","affectsGlobalScope":true,"impliedFormat":1},{"version":"1c9319a09485199c1f7b0498f2988d6d2249793ef67edda49d1e584746be9032","affectsGlobalScope":true,"impliedFormat":1},{"version":"e3a2a0cee0f03ffdde24d89660eba2685bfbdeae955a6c67e8c4c9fd28928eeb","affectsGlobalScope":true,"impliedFormat":1},{"version":"811c71eee4aa0ac5f7adf713323a5c41b0cf6c4e17367a34fbce379e12bbf0a4","affectsGlobalScope":true,"impliedFormat":1},{"version":"51ad4c928303041605b4d7ae32e0c1ee387d43a24cd6f1ebf4a2699e1076d4fa","affectsGlobalScope":true,"impliedFormat":1},{"version":"60037901da1a425516449b9a20073aa03386cce92f7a1fd902d7602be3a7c2e9","affectsGlobalScope":true,"impliedFormat":1},{"version":"d4b1d2c51d058fc21ec2629fff7a76249dec2e36e12960ea056e3ef89174080f","affectsGlobalScope":true,"impliedFormat":1},{"version":"22adec94ef7047a6c9d1af3cb96be87a335908bf9ef386ae9fd50eeb37f44c47","affectsGlobalScope":true,"impliedFormat":1},{"version":"196cb558a13d4533a5163286f30b0509ce0210e4b316c56c38d4c0fd2fb38405","affectsGlobalScope":true,"impliedFormat":1},{"version":"73f78680d4c08509933daf80947902f6ff41b6230f94dd002ae372620adb0f60","affectsGlobalScope":true,"impliedFormat":1},{"version":"c5239f5c01bcfa9cd32f37c496cf19c61d69d37e48be9de612b541aac915805b","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e7f8264d0fb4c5339605a15daadb037bf238c10b654bb3eee14208f860a32ea","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},{"version":"48adfec0ef258094e506a6ca59ca5c808ffb4a758393600714a8911af447e742","affectsGlobalScope":true},{"version":"7e29f41b158de217f94cb9676bf9cbd0cd9b5a46e1985141ed36e075c52bf6ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"ac51dd7d31333793807a6abaa5ae168512b6131bd41d9c5b98477fc3b7800f9f","impliedFormat":1},{"version":"f123246a7b6c04d80b9b57fadfc6c90959ec6d5c0d4c8e620e06e2811ae3a052","impliedFormat":1},{"version":"acd8fd5090ac73902278889c38336ff3f48af6ba03aa665eb34a75e7ba1dccc4","impliedFormat":1},{"version":"d6258883868fb2680d2ca96bc8b1352cab69874581493e6d52680c5ffecdb6cc","impliedFormat":1},{"version":"1b61d259de5350f8b1e5db06290d31eaebebc6baafd5f79d314b5af9256d7153","impliedFormat":1},{"version":"f258e3960f324a956fc76a3d3d9e964fff2244ff5859dcc6ce5951e5413ca826","impliedFormat":1},{"version":"643f7232d07bf75e15bd8f658f664d6183a0efaca5eb84b48201c7671a266979","impliedFormat":1},{"version":"0f6666b58e9276ac3a38fdc80993d19208442d6027ab885580d93aec76b4ef00","impliedFormat":1},{"version":"05fd364b8ef02fb1e174fbac8b825bdb1e5a36a016997c8e421f5fab0a6da0a0","impliedFormat":1},{"version":"631eff75b0e35d1b1b31081d55209abc43e16b49426546ab5a9b40bdd40b1f60","impliedFormat":1},{"version":"6c7176368037af28cb72f2392010fa1cef295d6d6744bca8cfb54985f3a18c3e","affectsGlobalScope":true,"impliedFormat":1},{"version":"ab41ef1f2cdafb8df48be20cd969d875602483859dc194e9c97c8a576892c052","affectsGlobalScope":true,"impliedFormat":1},{"version":"437e20f2ba32abaeb7985e0afe0002de1917bc74e949ba585e49feba65da6ca1","affectsGlobalScope":true,"impliedFormat":1},{"version":"21d819c173c0cf7cc3ce57c3276e77fd9a8a01d35a06ad87158781515c9a438a","impliedFormat":1},{"version":"98cffbf06d6bab333473c70a893770dbe990783904002c4f1a960447b4b53dca","affectsGlobalScope":true,"impliedFormat":1},{"version":"3af97acf03cc97de58a3a4bc91f8f616408099bc4233f6d0852e72a8ffb91ac9","affectsGlobalScope":true,"impliedFormat":1},{"version":"808069bba06b6768b62fd22429b53362e7af342da4a236ed2d2e1c89fcca3b4a","affectsGlobalScope":true,"impliedFormat":1},{"version":"1db0b7dca579049ca4193d034d835f6bfe73096c73663e5ef9a0b5779939f3d0","affectsGlobalScope":true,"impliedFormat":1},{"version":"9798340ffb0d067d69b1ae5b32faa17ab31b82466a3fc00d8f2f2df0c8554aaa","affectsGlobalScope":true,"impliedFormat":1},{"version":"f26b11d8d8e4b8028f1c7d618b22274c892e4b0ef5b3678a8ccbad85419aef43","affectsGlobalScope":true,"impliedFormat":1},{"version":"5929864ce17fba74232584d90cb721a89b7ad277220627cc97054ba15a98ea8f","impliedFormat":1},{"version":"763fe0f42b3d79b440a9b6e51e9ba3f3f91352469c1e4b3b67bfa4ff6352f3f4","impliedFormat":1},{"version":"25c8056edf4314820382a5fdb4bb7816999acdcb929c8f75e3f39473b87e85bc","impliedFormat":1},{"version":"c464d66b20788266e5353b48dc4aa6bc0dc4a707276df1e7152ab0c9ae21fad8","impliedFormat":1},{"version":"78d0d27c130d35c60b5e5566c9f1e5be77caf39804636bc1a40133919a949f21","impliedFormat":1},{"version":"c6fd2c5a395f2432786c9cb8deb870b9b0e8ff7e22c029954fabdd692bff6195","impliedFormat":1},{"version":"1d6e127068ea8e104a912e42fc0a110e2aa5a66a356a917a163e8cf9a65e4a75","impliedFormat":1},{"version":"5ded6427296cdf3b9542de4471d2aa8d3983671d4cac0f4bf9c637208d1ced43","impliedFormat":1},{"version":"7f182617db458e98fc18dfb272d40aa2fff3a353c44a89b2c0ccb3937709bfb5","impliedFormat":1},{"version":"cadc8aced301244057c4e7e73fbcae534b0f5b12a37b150d80e5a45aa4bebcbd","impliedFormat":1},{"version":"385aab901643aa54e1c36f5ef3107913b10d1b5bb8cbcd933d4263b80a0d7f20","impliedFormat":1},{"version":"9670d44354bab9d9982eca21945686b5c24a3f893db73c0dae0fd74217a4c219","impliedFormat":1},{"version":"0b8a9268adaf4da35e7fa830c8981cfa22adbbe5b3f6f5ab91f6658899e657a7","impliedFormat":1},{"version":"11396ed8a44c02ab9798b7dca436009f866e8dae3c9c25e8c1fbc396880bf1bb","impliedFormat":1},{"version":"ba7bc87d01492633cb5a0e5da8a4a42a1c86270e7b3d2dea5d156828a84e4882","impliedFormat":1},{"version":"4893a895ea92c85345017a04ed427cbd6a1710453338df26881a6019432febdd","impliedFormat":1},{"version":"c21dc52e277bcfc75fac0436ccb75c204f9e1b3fa5e12729670910639f27343e","impliedFormat":1},{"version":"13f6f39e12b1518c6650bbb220c8985999020fe0f21d818e28f512b7771d00f9","impliedFormat":1},{"version":"9b5369969f6e7175740bf51223112ff209f94ba43ecd3bb09eefff9fd675624a","impliedFormat":1},{"version":"4fe9e626e7164748e8769bbf74b538e09607f07ed17c2f20af8d680ee49fc1da","impliedFormat":1},{"version":"24515859bc0b836719105bb6cc3d68255042a9f02a6022b3187948b204946bd2","impliedFormat":1},{"version":"ea0148f897b45a76544ae179784c95af1bd6721b8610af9ffa467a518a086a43","impliedFormat":1},{"version":"24c6a117721e606c9984335f71711877293a9651e44f59f3d21c1ea0856f9cc9","impliedFormat":1},{"version":"dd3273ead9fbde62a72949c97dbec2247ea08e0c6952e701a483d74ef92d6a17","impliedFormat":1},{"version":"405822be75ad3e4d162e07439bac80c6bcc6dbae1929e179cf467ec0b9ee4e2e","impliedFormat":1},{"version":"0db18c6e78ea846316c012478888f33c11ffadab9efd1cc8bcc12daded7a60b6","impliedFormat":1},{"version":"e61be3f894b41b7baa1fbd6a66893f2579bfad01d208b4ff61daef21493ef0a8","impliedFormat":1},{"version":"bd0532fd6556073727d28da0edfd1736417a3f9f394877b6d5ef6ad88fba1d1a","impliedFormat":1},{"version":"89167d696a849fce5ca508032aabfe901c0868f833a8625d5a9c6e861ef935d2","impliedFormat":1},{"version":"615ba88d0128ed16bf83ef8ccbb6aff05c3ee2db1cc0f89ab50a4939bfc1943f","impliedFormat":1},{"version":"a4d551dbf8746780194d550c88f26cf937caf8d56f102969a110cfaed4b06656","impliedFormat":1},{"version":"8bd86b8e8f6a6aa6c49b71e14c4ffe1211a0e97c80f08d2c8cc98838006e4b88","impliedFormat":1},{"version":"317e63deeb21ac07f3992f5b50cdca8338f10acd4fbb7257ebf56735bf52ab00","impliedFormat":1},{"version":"4732aec92b20fb28c5fe9ad99521fb59974289ed1e45aecb282616202184064f","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","impliedFormat":1},{"version":"bf67d53d168abc1298888693338cb82854bdb2e69ef83f8a0092093c2d562107","impliedFormat":1},{"version":"b52476feb4a0cbcb25e5931b930fc73cb6643fb1a5060bf8a3dda0eeae5b4b68","affectsGlobalScope":true,"impliedFormat":1},{"version":"f9501cc13ce624c72b61f12b3963e84fad210fbdf0ffbc4590e08460a3f04eba","affectsGlobalScope":true,"impliedFormat":1},{"version":"e7721c4f69f93c91360c26a0a84ee885997d748237ef78ef665b153e622b36c1","affectsGlobalScope":true,"impliedFormat":1},{"version":"0fa06ada475b910e2106c98c68b10483dc8811d0c14a8a8dd36efb2672485b29","impliedFormat":1},{"version":"33e5e9aba62c3193d10d1d33ae1fa75c46a1171cf76fef750777377d53b0303f","impliedFormat":1},{"version":"2b06b93fd01bcd49d1a6bd1f9b65ddcae6480b9a86e9061634d6f8e354c1468f","impliedFormat":1},{"version":"6a0cd27e5dc2cfbe039e731cf879d12b0e2dded06d1b1dedad07f7712de0d7f4","affectsGlobalScope":true,"impliedFormat":1},{"version":"13f5c844119c43e51ce777c509267f14d6aaf31eafb2c2b002ca35584cd13b29","impliedFormat":1},{"version":"e60477649d6ad21542bd2dc7e3d9ff6853d0797ba9f689ba2f6653818999c264","impliedFormat":1},{"version":"c2510f124c0293ab80b1777c44d80f812b75612f297b9857406468c0f4dafe29","affectsGlobalScope":true,"impliedFormat":1},{"version":"5524481e56c48ff486f42926778c0a3cce1cc85dc46683b92b1271865bcf015a","impliedFormat":1},{"version":"4c829ab315f57c5442c6667b53769975acbf92003a66aef19bce151987675bd1","affectsGlobalScope":true,"impliedFormat":1},{"version":"b2ade7657e2db96d18315694789eff2ddd3d8aea7215b181f8a0b303277cc579","impliedFormat":1},{"version":"9855e02d837744303391e5623a531734443a5f8e6e8755e018c41d63ad797db2","impliedFormat":1},{"version":"4d631b81fa2f07a0e63a9a143d6a82c25c5f051298651a9b69176ba28930756d","impliedFormat":1},{"version":"836a356aae992ff3c28a0212e3eabcb76dd4b0cc06bcb9607aeef560661b860d","impliedFormat":1},{"version":"1e0d1f8b0adfa0b0330e028c7941b5a98c08b600efe7f14d2d2a00854fb2f393","impliedFormat":1},{"version":"41670ee38943d9cbb4924e436f56fc19ee94232bc96108562de1a734af20dc2c","affectsGlobalScope":true,"impliedFormat":1},{"version":"c906fb15bd2aabc9ed1e3f44eb6a8661199d6c320b3aa196b826121552cb3695","impliedFormat":1},{"version":"22295e8103f1d6d8ea4b5d6211e43421fe4564e34d0dd8e09e520e452d89e659","impliedFormat":1},{"version":"f949f7f6c7802a338039cfc2156d1fe285cdd1e092c64437ebe15ae8edc854e0","impliedFormat":1},{"version":"6b4e081d55ac24fc8a4631d5dd77fe249fa25900abd7d046abb87d90e3b45645","impliedFormat":1},{"version":"a10f0e1854f3316d7ee437b79649e5a6ae3ae14ffe6322b02d4987071a95362e","impliedFormat":1},{"version":"e208f73ef6a980104304b0d2ca5f6bf1b85de6009d2c7e404028b875020fa8f2","impliedFormat":1},{"version":"d163b6bc2372b4f07260747cbc6c0a6405ab3fbcea3852305e98ac43ca59f5bc","impliedFormat":1},{"version":"e6fa9ad47c5f71ff733744a029d1dc472c618de53804eae08ffc243b936f87ff","affectsGlobalScope":true,"impliedFormat":1},{"version":"83e63d6ccf8ec004a3bb6d58b9bb0104f60e002754b1e968024b320730cc5311","impliedFormat":1},{"version":"24826ed94a78d5c64bd857570fdbd96229ad41b5cb654c08d75a9845e3ab7dde","impliedFormat":1},{"version":"8b479a130ccb62e98f11f136d3ac80f2984fdc07616516d29881f3061f2dd472","impliedFormat":1},{"version":"928af3d90454bf656a52a48679f199f64c1435247d6189d1caf4c68f2eaf921f","affectsGlobalScope":true,"impliedFormat":1},{"version":"d2bc7425ef40526650d6db7e072c1ff4a51101c3ac2cc4b666623b19496a6e27","affectsGlobalScope":true,"impliedFormat":1},{"version":"3f16a7e4deafa527ed9995a772bb380eb7d3c2c0fd4ae178c5263ed18394db2c","impliedFormat":1},{"version":"933921f0bb0ec12ef45d1062a1fc0f27635318f4d294e4d99de9a5493e618ca2","impliedFormat":1},{"version":"71a0f3ad612c123b57239a7749770017ecfe6b66411488000aba83e4546fde25","impliedFormat":1},{"version":"77fbe5eecb6fac4b6242bbf6eebfc43e98ce5ccba8fa44e0ef6a95c945ff4d98","impliedFormat":1},{"version":"4f9d8ca0c417b67b69eeb54c7ca1bedd7b56034bb9bfd27c5d4f3bc4692daca7","impliedFormat":1},{"version":"814118df420c4e38fe5ae1b9a3bafb6e9c2aa40838e528cde908381867be6466","impliedFormat":1},{"version":"a3fc63c0d7b031693f665f5494412ba4b551fe644ededccc0ab5922401079c95","impliedFormat":1},{"version":"f27524f4bef4b6519c604bdb23bf4465bddcccbf3f003abb901acbd0d7404d99","impliedFormat":1},{"version":"37ba7b45141a45ce6e80e66f2a96c8a5ab1bcef0fc2d0f56bb58df96ec67e972","impliedFormat":1},{"version":"45650f47bfb376c8a8ed39d4bcda5902ab899a3150029684ee4c10676d9fbaee","impliedFormat":1},{"version":"6b039f55681caaf111d5eb84d292b9bee9e0131d0db1ad0871eef0964f533c73","affectsGlobalScope":true,"impliedFormat":1},{"version":"18fd40412d102c5564136f29735e5d1c3b455b8a37f920da79561f1fde068208","impliedFormat":1},{"version":"c959a391a75be9789b43c8468f71e3fa06488b4d691d5729dde1416dcd38225b","impliedFormat":1},{"version":"f0be1b8078cd549d91f37c30c222c2a187ac1cf981d994fb476a1adc61387b14","affectsGlobalScope":true,"impliedFormat":1},{"version":"0aaed1d72199b01234152f7a60046bc947f1f37d78d182e9ae09c4289e06a592","impliedFormat":1},{"version":"0dba70b3fb0dcd713fda33c2df64fa6751fff6460e536971cee917260fb17882","impliedFormat":1},{"version":"66ba1b2c3e3a3644a1011cd530fb444a96b1b2dfe2f5e837a002d41a1a799e60","impliedFormat":1},{"version":"7e514f5b852fdbc166b539fdd1f4e9114f29911592a5eb10a94bb3a13ccac3c4","impliedFormat":1},{"version":"5b7aa3c4c1a5d81b411e8cb302b45507fea9358d3569196b27eb1a27ae3a90ef","affectsGlobalScope":true,"impliedFormat":1},{"version":"5987a903da92c7462e0b35704ce7da94d7fdc4b89a984871c0e2b87a8aae9e69","affectsGlobalScope":true,"impliedFormat":1},{"version":"ea08a0345023ade2b47fbff5a76d0d0ed8bff10bc9d22b83f40858a8e941501c","impliedFormat":1},{"version":"47613031a5a31510831304405af561b0ffaedb734437c595256bb61a90f9311b","impliedFormat":1},{"version":"ae062ce7d9510060c5d7e7952ae379224fb3f8f2dd74e88959878af2057c143b","impliedFormat":1},{"version":"8a1a0d0a4a06a8d278947fcb66bf684f117bf147f89b06e50662d79a53be3e9f","affectsGlobalScope":true,"impliedFormat":1},{"version":"9f663c2f91127ef7024e8ca4b3b4383ff2770e5f826696005de382282794b127","impliedFormat":1},{"version":"9f55299850d4f0921e79b6bf344b47c420ce0f507b9dcf593e532b09ea7eeea1","impliedFormat":1},{"version":"2beff543f6e9a9701df88daeee3cdd70a34b4a1c11cb4c734472195a5cb2af54","impliedFormat":1},{"version":"bfffea552cca245df227337223c7554b35df629ba1d4e09edee4521ce7f24827","impliedFormat":1},{"version":"be1cc4d94ea60cbe567bc29ed479d42587bf1e6cba490f123d329976b0fe4ee5","impliedFormat":1},{"version":"42bc0e1a903408137c3df2b06dfd7e402cdab5bbfa5fcfb871b22ebfdb30bd0b","impliedFormat":1},{"version":"9894dafe342b976d251aac58e616ac6df8db91fb9d98934ff9dd103e9e82578f","impliedFormat":1},{"version":"413df52d4ea14472c2fa5bee62f7a40abd1eb49be0b9722ee01ee4e52e63beb2","impliedFormat":1},{"version":"db6d2d9daad8a6d83f281af12ce4355a20b9a3e71b82b9f57cddcca0a8964a96","impliedFormat":1},{"version":"829b9e6028b29e6a8b1c01ddb713efe59da04d857089298fa79acbdb3cfcfdef","impliedFormat":1},{"version":"24f8562308dd8ba6013120557fa7b44950b619610b2c6cb8784c79f11e3c4f90","impliedFormat":1},{"version":"5d8717b437b9d6afeb4da84b9082db35cafce3dfd025bc7c9ad7abbe50fa2778","impliedFormat":1},{"version":"a86f82d646a739041d6702101afa82dcb935c416dd93cbca7fd754fd0282ce1f","impliedFormat":1},{"version":"ad0d1d75d129b1c80f911be438d6b61bfa8703930a8ff2be2f0e1f8a91841c64","impliedFormat":1},{"version":"ce75b1aebb33d510ff28af960a9221410a3eaf7f18fc5f21f9404075fba77256","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"496bbf339f3838c41f164238543e9fe5f1f10659cb30b68903851618464b98ba","impliedFormat":1},{"version":"5178eb4415a172c287c711dc60a619e110c3fd0b7de01ed0627e51a5336aa09c","impliedFormat":1},{"version":"ca6e5264278b53345bc1ce95f42fb0a8b733a09e3d6479c6ccfca55cdc45038c","impliedFormat":1},{"version":"9e2739b32f741859263fdba0244c194ca8e96da49b430377930b8f721d77c000","impliedFormat":1},{"version":"fb1d8e814a3eeb5101ca13515e0548e112bd1ff3fb358ece535b93e94adf5a3a","impliedFormat":1},{"version":"ffa495b17a5ef1d0399586b590bd281056cee6ce3583e34f39926f8dcc6ecdb5","impliedFormat":1},{"version":"98b18458acb46072947aabeeeab1e410f047e0cacc972943059ca5500b0a5e95","impliedFormat":1},{"version":"361e2b13c6765d7f85bb7600b48fde782b90c7c41105b7dab1f6e7871071ba20","impliedFormat":1},{"version":"c86fe861cf1b4c46a0fb7d74dffe596cf679a2e5e8b1456881313170f092e3fa","impliedFormat":1},{"version":"b6db56e4903e9c32e533b78ac85522de734b3d3a8541bf24d256058d464bf04b","impliedFormat":1},{"version":"24daa0366f837d22c94a5c0bad5bf1fd0f6b29e1fae92dc47c3072c3fdb2fbd5","impliedFormat":1},{"version":"570bb5a00836ffad3e4127f6adf581bfc4535737d8ff763a4d6f4cc877e60d98","impliedFormat":1},{"version":"889c00f3d32091841268f0b994beba4dceaa5df7573be12c2c829d7c5fbc232c","impliedFormat":1},{"version":"65f43099ded6073336e697512d9b80f2d4fec3182b7b2316abf712e84104db00","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"acf5a2ac47b59ca07afa9abbd2b31d001bf7448b041927befae2ea5b1951d9f9","impliedFormat":1},{"version":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881","impliedFormat":1},{"version":"d71291eff1e19d8762a908ba947e891af44749f3a2cbc5bd2ec4b72f72ea795f","impliedFormat":1},{"version":"c0480e03db4b816dff2682b347c95f2177699525c54e7e6f6aa8ded890b76be7","impliedFormat":1},{"version":"27ab780875bcbb65e09da7496f2ca36288b0c541abaa75c311450a077d54ec15","impliedFormat":1},{"version":"b620391fe8060cf9bedc176a4d01366e6574d7a71e0ac0ab344a4e76576fcbb8","impliedFormat":1},{"version":"380647d8f3b7f852cca6d154a376dbf8ac620a2f12b936594504a8a852e71d2f","impliedFormat":1},{"version":"208c9af9429dd3c76f5927b971263174aaa4bc7621ddec63f163640cbd3c473c","impliedFormat":1},{"version":"6459054aabb306821a043e02b89d54da508e3a6966601a41e71c166e4ea1474f","impliedFormat":1},{"version":"a23185bc5ef590c287c28a91baf280367b50ae4ea40327366ad01f6f4a8edbc5","impliedFormat":1},{"version":"bb37588926aba35c9283fe8d46ebf4e79ffe976343105f5c6d45f282793352b2","impliedFormat":1},{"version":"002eae065e6960458bda3cf695e578b0d1e2785523476f8a9170b103c709cd4f","impliedFormat":1},{"version":"c83bb0c9c5645a46c68356c2f73fdc9de339ce77f7f45a954f560c7e0b8d5ebb","impliedFormat":1},{"version":"05c97cddbaf99978f83d96de2d8af86aded9332592f08ce4a284d72d0952c391","impliedFormat":1},{"version":"72179f9dd22a86deaad4cc3490eb0fe69ee084d503b686985965654013f1391b","impliedFormat":1},{"version":"2e6114a7dd6feeef85b2c80120fdbfb59a5529c0dcc5bfa8447b6996c97a69f5","impliedFormat":1},{"version":"7b6ff760c8a240b40dab6e4419b989f06a5b782f4710d2967e67c695ef3e93c4","impliedFormat":1},{"version":"c8f004e6036aa1c764ad4ec543cf89a5c1893a9535c80ef3f2b653e370de45e6","impliedFormat":1},{"version":"dd80b1e600d00f5c6a6ba23f455b84a7db121219e68f89f10552c54ba46e4dc9","impliedFormat":1},{"version":"b064c36f35de7387d71c599bfcf28875849a1dbc733e82bd26cae3d1cd060521","impliedFormat":1},{"version":"6a148329edecbda07c21098639ef4254ef7869fb25a69f58e5d6a8b7b69d4236","impliedFormat":1},{"version":"8de9fe97fa9e00ec00666fa77ab6e91b35d25af8ca75dabcb01e14ad3299b150","impliedFormat":1},{"version":"f63ab283a1c8f5c79fabe7ca4ef85f9633339c4f0e822fce6a767f9d59282af2","impliedFormat":1},{"version":"dba114fb6a32b355a9cfc26ca2276834d72fe0e94cd2c3494005547025015369","impliedFormat":1},{"version":"a54c996c8870ef1728a2c1fa9b8eaec0bf4a8001cd2583c02dd5869289465b10","impliedFormat":1},{"version":"3e7efde639c6a6c3edb9847b3f61e308bf7a69685b92f665048c45132f51c218","impliedFormat":1},{"version":"df45ca1176e6ac211eae7ddf51336dc075c5314bc5c253651bae639defd5eec5","impliedFormat":1},{"version":"3754982006a3b32c502cff0867ca83584f7a43b1035989ca73603f400de13c96","impliedFormat":1},{"version":"a30ae9bb8a8fa7b90f24b8a0496702063ae4fe75deb27da731ed4a03b2eb6631","impliedFormat":1},{"version":"f974e4a06953682a2c15d5bd5114c0284d5abf8bc0fe4da25cb9159427b70072","impliedFormat":1},{"version":"50256e9c31318487f3752b7ac12ff365c8949953e04568009c8705db802776fb","impliedFormat":1},{"version":"7d73b24e7bf31dfb8a931ca6c4245f6bb0814dfae17e4b60c9e194a631fe5f7b","impliedFormat":1},{"version":"413586add0cfe7369b64979d4ec2ed56c3f771c0667fbde1bf1f10063ede0b08","impliedFormat":1},{"version":"06472528e998d152375ad3bd8ebcb69ff4694fd8d2effaf60a9d9f25a37a097a","impliedFormat":1},{"version":"50b5bc34ce6b12eccb76214b51aadfa56572aa6cc79c2b9455cdbb3d6c76af1d","impliedFormat":1},{"version":"b7e16ef7f646a50991119b205794ebfd3a4d8f8e0f314981ebbe991639023d0e","impliedFormat":1},{"version":"42c169fb8c2d42f4f668c624a9a11e719d5d07dacbebb63cbcf7ef365b0a75b3","impliedFormat":1},{"version":"a401617604fa1f6ce437b81689563dfdc377069e4c58465dbd8d16069aede0a5","impliedFormat":1},{"version":"e9dd71cf12123419c60dab867d44fbee5c358169f99529121eaef277f5c83531","impliedFormat":1},{"version":"5b6a189ba3a0befa1f5d9cb028eb9eec2af2089c32f04ff50e2411f63d70f25d","impliedFormat":1},{"version":"d6e73f8010935b7b4c7487b6fb13ea197cc610f0965b759bec03a561ccf8423a","impliedFormat":1},{"version":"174f3864e398f3f33f9a446a4f403d55a892aa55328cf6686135dfaf9e171657","impliedFormat":1},{"version":"824c76aec8d8c7e65769688cbee102238c0ef421ed6686f41b2a7d8e7e78a931","impliedFormat":1},{"version":"75b868be3463d5a8cfc0d9396f0a3d973b8c297401d00bfb008a42ab16643f13","impliedFormat":1},{"version":"15a234e5031b19c48a69ccc1607522d6e4b50f57d308ecb7fe863d44cd9f9eb3","impliedFormat":1},{"version":"d682336018141807fb602709e2d95a192828fcb8d5ba06dda3833a8ea98f69e3","impliedFormat":1},{"version":"6124e973eab8c52cabf3c07575204efc1784aca6b0a30c79eb85fe240a857efa","impliedFormat":1},{"version":"0d891735a21edc75df51f3eb995e18149e119d1ce22fd40db2b260c5960b914e","impliedFormat":1},{"version":"3b414b99a73171e1c4b7b7714e26b87d6c5cb03d200352da5342ab4088a54c85","impliedFormat":1},{"version":"4fbd3116e00ed3a6410499924b6403cc9367fdca303e34838129b328058ede40","impliedFormat":1},{"version":"b01bd582a6e41457bc56e6f0f9de4cb17f33f5f3843a7cf8210ac9c18472fb0f","impliedFormat":1},{"version":"0a437ae178f999b46b6153d79095b60c42c996bc0458c04955f1c996dc68b971","impliedFormat":1},{"version":"74b2a5e5197bd0f2e0077a1ea7c07455bbea67b87b0869d9786d55104006784f","impliedFormat":1},{"version":"4a7baeb6325920044f66c0f8e5e6f1f52e06e6d87588d837bdf44feb6f35c664","impliedFormat":1},{"version":"6dcf60530c25194a9ee0962230e874ff29d34c59605d8e069a49928759a17e0a","impliedFormat":1},{"version":"7274fbffbd7c9589d8d0ffba68157237afd5cecff1e99881ea3399127e60572f","impliedFormat":1},{"version":"1a42d2ec31a1fe62fdc51591768695ed4a2dc64c01be113e7ff22890bebb5e3f","impliedFormat":1},{"version":"1a82deef4c1d39f6882f28d275cad4c01f907b9b39be9cbc472fcf2cf051e05b","impliedFormat":1},{"version":"c5426dbfc1cf90532f66965a7aa8c1136a78d4d0f96d8180ecbfc11d7722f1a5","impliedFormat":1},{"version":"65a15fc47900787c0bd18b603afb98d33ede930bed1798fc984d5ebb78b26cf9","impliedFormat":1},{"version":"9d202701f6e0744adb6314d03d2eb8fc994798fc83d91b691b75b07626a69801","impliedFormat":1},{"version":"de9d2df7663e64e3a91bf495f315a7577e23ba088f2949d5ce9ec96f44fba37d","impliedFormat":1},{"version":"c7af78a2ea7cb1cd009cfb5bdb48cd0b03dad3b54f6da7aab615c2e9e9d570c5","impliedFormat":1},{"version":"1ee45496b5f8bdee6f7abc233355898e5bf9bd51255db65f5ff7ede617ca0027","impliedFormat":1},{"version":"0c7c947ff881c4274c0800deaa0086971e0bfe51f89a33bd3048eaa3792d4876","affectsGlobalScope":true,"impliedFormat":1},{"version":"db01d18853469bcb5601b9fc9826931cc84cc1a1944b33cad76fd6f1e3d8c544","affectsGlobalScope":true,"impliedFormat":1},{"version":"a8f8e6ab2fa07b45251f403548b78eaf2022f3c2254df3dc186cb2671fe4996d","affectsGlobalScope":true,"impliedFormat":1},{"version":"fa6c12a7c0f6b84d512f200690bfc74819e99efae69e4c95c4cd30f6884c526e","impliedFormat":1},{"version":"f1c32f9ce9c497da4dc215c3bc84b722ea02497d35f9134db3bb40a8d918b92b","impliedFormat":1},{"version":"b73c319af2cc3ef8f6421308a250f328836531ea3761823b4cabbd133047aefa","affectsGlobalScope":true,"impliedFormat":1},{"version":"e433b0337b8106909e7953015e8fa3f2d30797cea27141d1c5b135365bb975a6","impliedFormat":1},{"version":"15b36126e0089bfef173ab61329e8286ce74af5e809d8a72edcafd0cc049057f","impliedFormat":1},{"version":"ddff7fc6edbdc5163a09e22bf8df7bef75f75369ebd7ecea95ba55c4386e2441","impliedFormat":1},{"version":"106c6025f1d99fd468fd8bf6e5bda724e11e5905a4076c5d29790b6c3745e50c","impliedFormat":1},{"version":"a57b1802794433adec9ff3fed12aa79d671faed86c49b09e02e1ac41b4f1d33a","impliedFormat":1},{"version":"ad10d4f0517599cdeca7755b930f148804e3e0e5b5a3847adce0f1f71bbccd74","impliedFormat":1},{"version":"1042064ece5bb47d6aba91648fbe0635c17c600ebdf567588b4ca715602f0a9d","impliedFormat":1},{"version":"c49469a5349b3cc1965710b5b0f98ed6c028686aa8450bcb3796728873eb923e","impliedFormat":1},{"version":"4a889f2c763edb4d55cb624257272ac10d04a1cad2ed2948b10ed4a7fda2a428","impliedFormat":1},{"version":"7bb79aa2fead87d9d56294ef71e056487e848d7b550c9a367523ee5416c44cfa","impliedFormat":1},{"version":"72d63643a657c02d3e51cd99a08b47c9b020a565c55f246907050d3c8a5e77fb","impliedFormat":1},{"version":"1d415445ea58f8033ba199703e55ff7483c52ac6742075b803bd3e7bbe9f5d61","impliedFormat":1},{"version":"d6406c629bb3efc31aedb2de809bef471e475c86c7e67f3ef9b676b5d7e0d6b2","impliedFormat":1},{"version":"27ff4196654e6373c9af16b6165120e2dd2169f9ad6abb5c935af5abd8c7938c","impliedFormat":1},{"version":"71d8ba39a9e024d9e4bb922464d18542ed8d2c25ee78efa7890c27213cc6e5d3","impliedFormat":1},{"version":"8c030e515014c10a2b98f9f48408e3ba18023dfd3f56e3312c6c2f3ae1f55a16","impliedFormat":1},{"version":"dafc31e9e8751f437122eb8582b93d477e002839864410ff782504a12f2a550c","impliedFormat":1},{"version":"754498c5208ce3c5134f6eabd49b25cf5e1a042373515718953581636491f3c3","impliedFormat":1},{"version":"9c82171d836c47486074e4ca8e059735bf97b205e70b196535b5efd40cbe1bc5","impliedFormat":1},{"version":"f56bdc6884648806d34bc66d31cdb787c4718d04105ce2cd88535db214631f82","impliedFormat":1},{"version":"633d58a237f4bb25ec7d565e4ffa32cecdcee8660ac12189c4351c52557cee9e","impliedFormat":1},{"version":"2e4f37ffe8862b14d8e24ae8763daaa8340c0df0b859d9a9733def0eee7562d9","impliedFormat":1},{"version":"13283350547389802aa35d9f2188effaeac805499169a06ef5cd77ce2a0bd63f","impliedFormat":1},{"version":"ce791f6ea807560f08065d1af6014581eeb54a05abd73294777a281b6dfd73c2","impliedFormat":1},{"version":"6ac6715916fa75a1f7ebdfeacac09513b4d904b667d827b7535e84ff59679aff","impliedFormat":1},{"version":"49f95e989b4632c6c2a578cc0078ee19a5831832d79cc59abecf5160ea71abad","impliedFormat":1},{"version":"9666533332f26e8995e4d6fe472bdeec9f15d405693723e6497bf94120c566c8","impliedFormat":1},{"version":"ce0df82a9ae6f914ba08409d4d883983cc08e6d59eb2df02d8e4d68309e7848b","impliedFormat":1},{"version":"796273b2edc72e78a04e86d7c58ae94d370ab93a0ddf40b1aa85a37a1c29ecd7","impliedFormat":1},{"version":"5df15a69187d737d6d8d066e189ae4f97e41f4d53712a46b2710ff9f8563ec9f","impliedFormat":1},{"version":"e17cd049a1448de4944800399daa4a64c5db8657cc9be7ef46be66e2a2cd0e7c","impliedFormat":1},{"version":"43fa6ea8714e18adc312b30450b13562949ba2f205a1972a459180fa54471018","impliedFormat":1},{"version":"6e89c2c177347d90916bad67714d0fb473f7e37fb3ce912f4ed521fe2892cd0d","impliedFormat":1},{"version":"43ba4f2fa8c698f5c304d21a3ef596741e8e85a810b7c1f9b692653791d8d97a","impliedFormat":1},{"version":"4d4927cbee21750904af7acf940c5e3c491b4d5ebc676530211e389dd375607a","impliedFormat":1},{"version":"72105519d0390262cf0abe84cf41c926ade0ff475d35eb21307b2f94de985778","impliedFormat":1},{"version":"8a97e578a9bc40eb4f1b0ca78f476f2e9154ecbbfd5567ee72943bab37fc156a","impliedFormat":1},{"version":"c857e0aae3f5f444abd791ec81206020fbcc1223e187316677e026d1c1d6fe08","impliedFormat":1},{"version":"ccf6dd45b708fb74ba9ed0f2478d4eb9195c9dfef0ff83a6092fa3cf2ff53b4f","impliedFormat":1},{"version":"2d7db1d73456e8c5075387d4240c29a2a900847f9c1bff106a2e490da8fbd457","impliedFormat":1},{"version":"2b15c805f48e4e970f8ec0b1915f22d13ca6212375e8987663e2ef5f0205e832","impliedFormat":1},{"version":"f22d05663d873ee7a600faf78abb67f3f719d32266803440cf11d5db7ac0cab2","impliedFormat":1},{"version":"d93c544ad20197b3976b0716c6d5cd5994e71165985d31dcab6e1f77feb4b8f2","impliedFormat":1},{"version":"35069c2c417bd7443ae7c7cafd1de02f665bf015479fec998985ffbbf500628c","impliedFormat":1},{"version":"a8b1c79a833ee148251e88a2553d02ce1641d71d2921cce28e79678f3d8b96aa","impliedFormat":1},{"version":"126d4f950d2bba0bd45b3a86c76554d4126c16339e257e6d2fabf8b6bf1ce00c","impliedFormat":1},{"version":"7e0b7f91c5ab6e33f511efc640d36e6f933510b11be24f98836a20a2dc914c2d","impliedFormat":1},{"version":"045b752f44bf9bbdcaffd882424ab0e15cb8d11fa94e1448942e338c8ef19fba","impliedFormat":1},{"version":"2894c56cad581928bb37607810af011764a2f511f575d28c9f4af0f2ef02d1ab","impliedFormat":1},{"version":"0a72186f94215d020cb386f7dca81d7495ab6c17066eb07d0f44a5bf33c1b21a","impliedFormat":1},{"version":"2d3cc2211f352f46ea6b7cf2c751c141ffcdf514d6e7ae7ee20b7b6742da313f","impliedFormat":1},{"version":"c75445151ff8b77d9923191efed7203985b1a9e09eccf4b054e7be864e27923d","impliedFormat":1},{"version":"0aedb02516baf3e66b2c1db9fef50666d6ed257edac0f866ea32f1aa05aa474f","impliedFormat":1},{"version":"fa8a8fbf91ee2a4779496225f0312aac6635b0f21aa09cdafa4283fe32d519c5","affectsGlobalScope":true,"impliedFormat":1},{"version":"0e8aef93d79b000deb6ec336b5645c87de167168e184e84521886f9ecc69a4b5","impliedFormat":1},{"version":"56ccb49443bfb72e5952f7012f0de1a8679f9f75fc93a5c1ac0bafb28725fc5f","impliedFormat":1},{"version":"20fa37b636fdcc1746ea0738f733d0aed17890d1cd7cb1b2f37010222c23f13e","impliedFormat":1},{"version":"d90b9f1520366d713a73bd30c5a9eb0040d0fb6076aff370796bc776fd705943","impliedFormat":1},{"version":"bc03c3c352f689e38c0ddd50c39b1e65d59273991bfc8858a9e3c0ebb79c023b","impliedFormat":1},{"version":"19df3488557c2fc9b4d8f0bac0fd20fb59aa19dec67c81f93813951a81a867f8","affectsGlobalScope":true,"impliedFormat":1},{"version":"b25350193e103ae90423c5418ddb0ad1168dc9c393c9295ef34980b990030617","affectsGlobalScope":true,"impliedFormat":1},{"version":"bef86adb77316505c6b471da1d9b8c9e428867c2566270e8894d4d773a1c4dc2","impliedFormat":1},{"version":"de7052bfee2981443498239a90c04ea5cc07065d5b9bb61b12cb6c84313ad4ef","impliedFormat":1},{"version":"a3e7d932dc9c09daa99141a8e4800fc6c58c625af0d4bbb017773dc36da75426","impliedFormat":1},{"version":"43e96a3d5d1411ab40ba2f61d6a3192e58177bcf3b133a80ad2a16591611726d","impliedFormat":1},{"version":"4a2edd238d9104eac35b60d727f1123de5062f452b70ed8e0366cb36387dfdfd","impliedFormat":1},{"version":"ca921bf56756cb6fe957f6af693a35251b134fb932dc13f3dfff0bb7106f80b4","impliedFormat":1},{"version":"fee92c97f1aa59eb7098a0cc34ff4df7e6b11bae71526aca84359a2575f313d8","impliedFormat":1},{"version":"0bd0297484aacea217d0b76e55452862da3c5d9e33b24430e0719d1161657225","impliedFormat":1},{"version":"2ab6d334bcbf2aff3acfc4fd8c73ecd82b981d3c3aa47b3f3b89281772286904","impliedFormat":1},{"version":"d07cbc787a997d83f7bde3877fec5fb5b12ce8c1b7047eb792996ed9726b4dde","impliedFormat":1},{"version":"6ac6715916fa75a1f7ebdfeacac09513b4d904b667d827b7535e84ff59679aff","impliedFormat":1},{"version":"4805f6161c2c8cefb8d3b8bd96a080c0fe8dbc9315f6ad2e53238f9a79e528a6","impliedFormat":1},{"version":"b83cb14474fa60c5f3ec660146b97d122f0735627f80d82dd03e8caa39b4388c","impliedFormat":1},{"version":"f374cb24e93e7798c4d9e83ff872fa52d2cdb36306392b840a6ddf46cb925cb6","impliedFormat":1},{"version":"49179c6a23701c642bd99abe30d996919748014848b738d8e85181fc159685ff","impliedFormat":1},{"version":"b73cbf0a72c8800cf8f96a9acfe94f3ad32ca71342a8908b8ae484d61113f647","impliedFormat":1},{"version":"bae6dd176832f6423966647382c0d7ba9e63f8c167522f09a982f086cd4e8b23","impliedFormat":1},{"version":"20865ac316b8893c1a0cc383ccfc1801443fbcc2a7255be166cf90d03fac88c9","impliedFormat":1},{"version":"c9958eb32126a3843deedda8c22fb97024aa5d6dd588b90af2d7f2bfac540f23","impliedFormat":1},{"version":"461d0ad8ae5f2ff981778af912ba71b37a8426a33301daa00f21c6ccb27f8156","impliedFormat":1},{"version":"e927c2c13c4eaf0a7f17e6022eee8519eb29ef42c4c13a31e81a611ab8c95577","impliedFormat":1},{"version":"fcafff163ca5e66d3b87126e756e1b6dfa8c526aa9cd2a2b0a9da837d81bbd72","impliedFormat":1},{"version":"70246ad95ad8a22bdfe806cb5d383a26c0c6e58e7207ab9c431f1cb175aca657","impliedFormat":1},{"version":"f00f3aa5d64ff46e600648b55a79dcd1333458f7a10da2ed594d9f0a44b76d0b","impliedFormat":1},{"version":"772d8d5eb158b6c92412c03228bd9902ccb1457d7a705b8129814a5d1a6308fc","impliedFormat":1},{"version":"45490817629431853543adcb91c0673c25af52a456479588b6486daba34f68bb","impliedFormat":1},{"version":"802e797bcab5663b2c9f63f51bdf67eff7c41bc64c0fd65e6da3e7941359e2f7","impliedFormat":1},{"version":"8b4327413e5af38cd8cb97c59f48c3c866015d5d642f28518e3a891c469f240e","impliedFormat":1},{"version":"8514c62ce38e58457d967e9e73f128eedc1378115f712b9eef7127f7c88f82ae","impliedFormat":1},{"version":"f1289e05358c546a5b664fbb35a27738954ec2cc6eb4137350353099d154fc62","impliedFormat":1},{"version":"4b20fcf10a5413680e39f5666464859fc56b1003e7dfe2405ced82371ebd49b6","impliedFormat":1},{"version":"1d17ba45cfbe77a9c7e0df92f7d95f3eefd49ee23d1104d0548b215be56945ad","impliedFormat":1},{"version":"f7d628893c9fa52ba3ab01bcb5e79191636c4331ee5667ecc6373cbccff8ae12","impliedFormat":1},{"version":"1d879125d1ec570bf04bc1f362fdbe0cb538315c7ac4bcfcdf0c1e9670846aa6","impliedFormat":1},{"version":"e16344db9b8ee59d41abdb3a61e4470955b5712c0ee869fb47112e152aabe142","impliedFormat":1},{"version":"46273e8c29816125d0d0b56ce9a849cc77f60f9a5ba627447501d214466f0ff3","impliedFormat":1},{"version":"d663134457d8d669ae0df34eabd57028bddc04fc444c4bc04bc5215afc91e1f4","impliedFormat":1},{"version":"144bc326e90b894d1ec78a2af3ffb2eb3733f4d96761db0ca0b6239a8285f972","impliedFormat":1},{"version":"3af3584f79c57853028ef9421ec172539e1fe01853296dc05a9d615ade4ffaf6","impliedFormat":1},{"version":"f82579d87701d639ff4e3930a9b24f4ee13ca74221a9a3a792feb47f01881a9c","impliedFormat":1},{"version":"d7e5d5245a8ba34a274717d085174b2c9827722778129b0081fefd341cca8f55","impliedFormat":1},{"version":"d9d32f94056181c31f553b32ce41d0ef75004912e27450738d57efcd2409c324","impliedFormat":1},{"version":"752513f35f6cff294ffe02d6027c41373adf7bfa35e593dbfd53d95c203635ee","impliedFormat":1},{"version":"6c800b281b9e89e69165fd11536195488de3ff53004e55905e6c0059a2d8591e","impliedFormat":1},{"version":"7d4254b4c6c67a29d5e7f65e67d72540480ac2cfb041ca484847f5ae70480b62","impliedFormat":1},{"version":"1a7e2ea171726446850ec72f4d1525d547ff7e86724cc9e7eec509725752a758","impliedFormat":1},{"version":"8c901126d73f09ecdea4785e9a187d1ac4e793e07da308009db04a7283ec2f37","impliedFormat":1},{"version":"db97922b767bd2675fdfa71e08b49c38b7d2c847a1cc4a7274cb77be23b026f1","impliedFormat":1},{"version":"aab290b8e4b7c399f2c09b957666fc95335eb4522b2dd9ead1bf0cb64da6d6ee","impliedFormat":1},{"version":"94fe3281392e1015b22f39535878610b4fa6f1388dc8d78746be3bc4e4bb8950","impliedFormat":1},{"version":"2652448ac55a2010a1f71dd141f828b682298d39728f9871e1cdf8696ef443fd","impliedFormat":1},{"version":"06c25ddfc2242bd06c19f66c9eae4c46d937349a267810f89783680a1d7b5259","impliedFormat":1},{"version":"120599fd965257b1f4d0ff794bc696162832d9d8467224f4665f713a3119078b","impliedFormat":1},{"version":"5433f33b0a20300cca35d2f229a7fc20b0e8477c44be2affeb21cb464af60c76","impliedFormat":1},{"version":"db036c56f79186da50af66511d37d9fe77fa6793381927292d17f81f787bb195","impliedFormat":1},{"version":"bd4131091b773973ca5d2326c60b789ab1f5e02d8843b3587effe6e1ea7c9d86","impliedFormat":1},{"version":"c7f6485931085bf010fbaf46880a9b9ec1a285ad9dc8c695a9e936f5a48f34b4","impliedFormat":1},{"version":"14f6b927888a1112d662877a5966b05ac1bf7ed25d6c84386db4c23c95a5363b","impliedFormat":1},{"version":"6ac6715916fa75a1f7ebdfeacac09513b4d904b667d827b7535e84ff59679aff","impliedFormat":1},{"version":"0427df5c06fafc5fe126d14b9becd24160a288deff40e838bfbd92a35f8d0d00","impliedFormat":1},{"version":"90c54a02432d04e4246c87736e53a6a83084357acfeeba7a489c5422b22f5c7a","impliedFormat":1},{"version":"49c346823ba6d4b12278c12c977fb3a31c06b9ca719015978cb145eb86da1c61","impliedFormat":1},{"version":"bfac6e50eaa7e73bb66b7e052c38fdc8ccfc8dbde2777648642af33cf349f7f1","impliedFormat":1},{"version":"92f7c1a4da7fbfd67a2228d1687d5c2e1faa0ba865a94d3550a3941d7527a45d","impliedFormat":1},{"version":"f53b120213a9289d9a26f5af90c4c686dd71d91487a0aa5451a38366c70dc64b","impliedFormat":1},{"version":"83fe880c090afe485a5c02262c0b7cdd76a299a50c48d9bde02be8e908fb4ae6","impliedFormat":1},{"version":"0a372c2d12a259da78e21b25974d2878502f14d89c6d16b97bd9c5017ab1bc12","impliedFormat":1},{"version":"57d67b72e06059adc5e9454de26bbfe567d412b962a501d263c75c2db430f40e","impliedFormat":1},{"version":"6511e4503cf74c469c60aafd6589e4d14d5eb0a25f9bf043dcbecdf65f261972","impliedFormat":1},{"version":"ec1ca97598eda26b7a5e6c8053623acbd88e43be7c4d29c77ccd57abc4c43999","impliedFormat":1},{"version":"6e2261cd9836b2c25eecb13940d92c024ebed7f8efe23c4b084145cd3a13b8a6","impliedFormat":1},{"version":"a67b87d0281c97dfc1197ef28dfe397fc2c865ccd41f7e32b53f647184cc7307","impliedFormat":1},{"version":"771ffb773f1ddd562492a6b9aaca648192ac3f056f0e1d997678ff97dbb6bf9b","impliedFormat":1},{"version":"232f70c0cf2b432f3a6e56a8dc3417103eb162292a9fd376d51a3a9ea5fbbf6f","impliedFormat":1},{"version":"a47e6d954d22dd9ebb802e7e431b560ed7c581e79fb885e44dc92ed4f60d4c07","impliedFormat":1},{"version":"f019e57d2491c159d47a107fd90219a1734bdd2e25cd8d1db3c8fae5c6b414c4","impliedFormat":1},{"version":"8a0e762ceb20c7e72504feef83d709468a70af4abccb304f32d6b9bac1129b2c","impliedFormat":1},{"version":"d1c9bf292a54312888a77bb19dba5e2503ad803f5393beafd45d78d2f4fe9b48","impliedFormat":1},{"version":"9252d498a77517aab5d8d4b5eb9d71e4b225bbc7123df9713e08181de63180f6","impliedFormat":1},{"version":"cb8d8ef7b9ce8ed3e6f1c814fcbf3f90dab0cb8863079236784fc350746e27c4","impliedFormat":1},{"version":"35e6379c3f7cb27b111ad4c1aa69538fd8e788ab737b8ff7596a1b40e96f4f90","impliedFormat":1},{"version":"1fffe726740f9787f15b532e1dc870af3cd964dbe29e191e76121aa3dd8693f2","impliedFormat":1},{"version":"3be035da7bee86b4c3abf392e0edaa44fc6e45092995eefe36b39118c8a84068","affectsGlobalScope":true,"impliedFormat":1},{"version":"8f828825d077c2fa0ea606649faeb122749273a353daab23924fe674e98ba44c","impliedFormat":1},{"version":"2896c2e673a5d3bd9b4246811f79486a073cbb03950c3d252fba10003c57411a","impliedFormat":1},{"version":"616775f16134fa9d01fc677ad3f76e68c051a056c22ab552c64cc281a9686790","impliedFormat":1},{"version":"65c24a8baa2cca1de069a0ba9fba82a173690f52d7e2d0f1f7542d59d5eb4db0","impliedFormat":1},{"version":"f9fe6af238339a0e5f7563acee3178f51db37f32a2e7c09f85273098cee7ec49","impliedFormat":1},{"version":"407a06ba04eede4074eec470ecba2784cbb3bf4e7de56833b097dd90a2aa0651","impliedFormat":1},{"version":"77e71242e71ebf8528c5802993697878f0533db8f2299b4d36aa015bae08a79c","impliedFormat":1},{"version":"98a787be42bd92f8c2a37d7df5f13e5992da0d967fab794adbb7ee18370f9849","impliedFormat":1},{"version":"5c96bad5f78466785cdad664c056e9e2802d5482ca5f862ed19ba34ffbb7b3a4","impliedFormat":1},{"version":"81d8603ac527e75cfec72bb9391228b58f161c2b33514a9d814c7f3ebd3ef466","impliedFormat":1},{"version":"5f3dc10ae646f375776b4e028d2bed039a93eebbba105694d8b910feebbe8b9c","impliedFormat":1},{"version":"bb0cd7862b72f5eba39909c9889d566e198fcaddf7207c16737d0c2246112678","impliedFormat":1},{"version":"4545c1a1ceca170d5d83452dd7c4994644c35cf676a671412601689d9a62da35","impliedFormat":1},{"version":"320f4091e33548b554d2214ce5fc31c96631b513dffa806e2e3a60766c8c49d9","impliedFormat":1},{"version":"a2d648d333cf67b9aeac5d81a1a379d563a8ffa91ddd61c6179f68de724260ff","impliedFormat":1},{"version":"d90d5f524de38889d1e1dbc2aeef00060d779f8688c02766ddb9ca195e4a713d","impliedFormat":1},{"version":"a3f41ed1b4f2fc3049394b945a68ae4fdefd49fa1739c32f149d32c0545d67f5","impliedFormat":1},{"version":"bad68fd0401eb90fe7da408565c8aee9c7a7021c2577aec92fa1382e8876071a","impliedFormat":1},{"version":"47699512e6d8bebf7be488182427189f999affe3addc1c87c882d36b7f2d0b0e","impliedFormat":1},{"version":"fec01479923e169fb52bd4f668dbeef1d7a7ea6e6d491e15617b46f2cacfa37d","impliedFormat":1},{"version":"8a8fb3097ba52f0ae6530ec6ab34e43e316506eb1d9aa29420a4b1e92a81442d","impliedFormat":1},{"version":"44e09c831fefb6fe59b8e65ad8f68a7ecc0e708d152cfcbe7ba6d6080c31c61e","impliedFormat":1},{"version":"1c0a98de1323051010ce5b958ad47bc1c007f7921973123c999300e2b7b0ecc0","impliedFormat":1},{"version":"4655709c9cb3fd6db2b866cab7c418c40ed9533ce8ea4b66b5f17ec2feea46a9","impliedFormat":1},{"version":"87affad8e2243635d3a191fa72ef896842748d812e973b7510a55c6200b3c2a4","impliedFormat":1},{"version":"ad036a85efcd9e5b4f7dd5c1a7362c8478f9a3b6c3554654ca24a29aa850a9c5","impliedFormat":1},{"version":"fedebeae32c5cdd1a85b4e0504a01996e4a8adf3dfa72876920d3dd6e42978e7","impliedFormat":1},{"version":"3eecb25bb467a948c04874d70452b14ae7edb707660aac17dc053e42f2088b00","impliedFormat":1},{"version":"cdf21eee8007e339b1b9945abf4a7b44930b1d695cc528459e68a3adc39a622e","impliedFormat":1},{"version":"330896c1a2b9693edd617be24fbf9e5895d6e18c7955d6c08f028f272b37314d","impliedFormat":1},{"version":"1d9c0a9a6df4e8f29dc84c25c5aa0bb1da5456ebede7a03e03df08bb8b27bae6","impliedFormat":1},{"version":"84380af21da938a567c65ef95aefb5354f676368ee1a1cbb4cae81604a4c7d17","impliedFormat":1},{"version":"1af3e1f2a5d1332e136f8b0b95c0e6c0a02aaabd5092b36b64f3042a03debf28","impliedFormat":1},{"version":"30d8da250766efa99490fc02801047c2c6d72dd0da1bba6581c7e80d1d8842a4","impliedFormat":1},{"version":"03566202f5553bd2d9de22dfab0c61aa163cabb64f0223c08431fb3fc8f70280","impliedFormat":1},{"version":"5f0292a40df210ab94b9fb44c8b775c51e96777e14e073900e392b295ca1061b","impliedFormat":1},{"version":"bc9ee0192f056b3d5527bcd78dc3f9e527a9ba2bdc0a2c296fbc9027147df4b2","impliedFormat":1},{"version":"8627ad129bcf56e82adff0ab5951627c993937aa99f5949c33240d690088b803","impliedFormat":1},{"version":"1de80059b8078ea5749941c9f863aa970b4735bdbb003be4925c853a8b6b4450","impliedFormat":1},{"version":"1d079c37fa53e3c21ed3fa214a27507bda9991f2a41458705b19ed8c2b61173d","impliedFormat":1},{"version":"5bf5c7a44e779790d1eb54c234b668b15e34affa95e78eada73e5757f61ed76a","impliedFormat":1},{"version":"5835a6e0d7cd2738e56b671af0e561e7c1b4fb77751383672f4b009f4e161d70","impliedFormat":1},{"version":"5c634644d45a1b6bc7b05e71e05e52ec04f3d73d9ac85d5927f647a5f965181a","impliedFormat":1},{"version":"4b7f74b772140395e7af67c4841be1ab867c11b3b82a51b1aeb692822b76c872","impliedFormat":1},{"version":"27be6622e2922a1b412eb057faa854831b95db9db5035c3f6d4b677b902ab3b7","impliedFormat":1},{"version":"a68d4b3182e8d776cdede7ac9630c209a7bfbb59191f99a52479151816ef9f9e","impliedFormat":99},{"version":"39644b343e4e3d748344af8182111e3bbc594930fff0170256567e13bbdbebb0","impliedFormat":99},{"version":"ed7fd5160b47b0de3b1571c5c5578e8e7e3314e33ae0b8ea85a895774ee64749","impliedFormat":99},{"version":"63a7595a5015e65262557f883463f934904959da563b4f788306f699411e9bac","impliedFormat":1},{"version":"ecbaf0da125974be39c0aac869e403f72f033a4e7fd0d8cd821a8349b4159628","impliedFormat":1},{"version":"4ba137d6553965703b6b55fd2000b4e07ba365f8caeb0359162ad7247f9707a6","impliedFormat":1},{"version":"ceec3c81b2d81f5e3b855d9367c1d4c664ab5046dff8fd56552df015b7ccbe8f","affectsGlobalScope":true,"impliedFormat":1},{"version":"8fac4a15690b27612d8474fb2fc7cc00388df52d169791b78d1a3645d60b4c8b","affectsGlobalScope":true,"impliedFormat":1},{"version":"064ac1c2ac4b2867c2ceaa74bbdce0cb6a4c16e7c31a6497097159c18f74aa7c","impliedFormat":1},{"version":"3dc14e1ab45e497e5d5e4295271d54ff689aeae00b4277979fdd10fa563540ae","impliedFormat":1},{"version":"1d63055b690a582006435ddd3aa9c03aac16a696fac77ce2ed808f3e5a06efab","impliedFormat":1},{"version":"b789bf89eb19c777ed1e956dbad0925ca795701552d22e68fd130a032008b9f9","impliedFormat":1},"f4e8976c19fc926644d72610bf1058bd6bf52add97e46a02bc0b912a751625c0",{"version":"a5b489982805de6273a048820c55ea6177db634e66a985aac765d93ba8174248","signature":"435a1e418e8338be3f39614b96b81a9aa2700bc8c27bc6b98f064ff9ce17c363"},{"version":"402e5c534fb2b85fa771170595db3ac0dd532112c8fa44fc23f233bc6967488b","impliedFormat":1},{"version":"8885cf05f3e2abf117590bbb951dcf6359e3e5ac462af1c901cfd24c6a6472e2","impliedFormat":1},{"version":"333caa2bfff7f06017f114de738050dd99a765c7eb16571c6d25a38c0d5365dc","impliedFormat":1},{"version":"e61df3640a38d535fd4bc9f4a53aef17c296b58dc4b6394fd576b808dd2fe5e6","impliedFormat":1},{"version":"459920181700cec8cbdf2a5faca127f3f17fd8dd9d9e577ed3f5f3af5d12a2e4","impliedFormat":1},{"version":"4719c209b9c00b579553859407a7e5dcfaa1c472994bd62aa5dd3cc0757eb077","impliedFormat":1},{"version":"7ec359bbc29b69d4063fe7dad0baaf35f1856f914db16b3f4f6e3e1bca4099fa","impliedFormat":1},{"version":"70790a7f0040993ca66ab8a07a059a0f8256e7bb57d968ae945f696cbff4ac7a","impliedFormat":1},{"version":"d1b9a81e99a0050ca7f2d98d7eedc6cda768f0eb9fa90b602e7107433e64c04c","impliedFormat":1},{"version":"a022503e75d6953d0e82c2c564508a5c7f8556fad5d7f971372d2d40479e4034","impliedFormat":1},{"version":"b215c4f0096f108020f666ffcc1f072c81e9f2f95464e894a5d5f34c5ea2a8b1","impliedFormat":1},{"version":"644491cde678bd462bb922c1d0cfab8f17d626b195ccb7f008612dc31f445d2d","impliedFormat":1},{"version":"dfe54dab1fa4961a6bcfba68c4ca955f8b5bbeb5f2ab3c915aa7adaa2eabc03a","impliedFormat":1},{"version":"1251d53755b03cde02466064260bb88fd83c30006a46395b7d9167340bc59b73","impliedFormat":1},{"version":"47865c5e695a382a916b1eedda1b6523145426e48a2eae4647e96b3b5e52024f","impliedFormat":1},{"version":"4cdf27e29feae6c7826cdd5c91751cc35559125e8304f9e7aed8faef97dcf572","impliedFormat":1},{"version":"331b8f71bfae1df25d564f5ea9ee65a0d847c4a94baa45925b6f38c55c7039bf","impliedFormat":1},{"version":"2a771d907aebf9391ac1f50e4ad37952943515eeea0dcc7e78aa08f508294668","impliedFormat":1},{"version":"0146fd6262c3fd3da51cb0254bb6b9a4e42931eb2f56329edd4c199cb9aaf804","impliedFormat":1},{"version":"183f480885db5caa5a8acb833c2be04f98056bdcc5fb29e969ff86e07efe57ab","impliedFormat":99},{"version":"b558c9a18ea4e6e4157124465c3ef1063e64640da139e67be5edb22f534f2f08","impliedFormat":1},{"version":"01374379f82be05d25c08d2f30779fa4a4c41895a18b93b33f14aeef51768692","impliedFormat":1},{"version":"b0dee183d4e65cf938242efaf3d833c6b645afb35039d058496965014f158141","impliedFormat":1},{"version":"c0bbbf84d3fbd85dd60d040c81e8964cc00e38124a52e9c5dcdedf45fea3f213","impliedFormat":1},{"version":"9682a26e1b58dd1dff221d71d3be4b7c546fb27e7772919cd3010482184d0999","signature":"f65ce75c9085571e6321abf2bf9833709f4897e381f89e9925521833dbb7ab16"},{"version":"0eefc702c5de5c32835e290cbdcb5a76f6d98c956b87cdbf5da49c864d3ab88e","signature":"0a4895bce639e10d5ef215a48b4bbc4d73e444e9a3e214594ebae5e00a7140f8"},{"version":"848b257bdc73b54c167bed01b2db42fd44c1aab77350c9b1fc3a2ce3ff08f899","impliedFormat":99},{"version":"642b071c4d865ff593c1794e06fa1781a465338a37796a7d0e2501daa20172bb","impliedFormat":99},{"version":"ff87d4d02bfc715a3a038c825bb80d6a6cfd1750bec3a0fa6f0e407133b1cd2f","impliedFormat":99},{"version":"0da67df9ad0b921385c37c0eef3de0390b8ef7b0b36cf616fdf3590a67fed1e4","impliedFormat":99},{"version":"8b28fc7010a1cd9c54201b6015c7d6a04fc6a4981acc9eaa5d71d4523325c525","impliedFormat":99},{"version":"e72fd44d087aac63727f05fe82c0c4a984c6295f23fdb9a6290d4d9fda47cfc6","impliedFormat":99},{"version":"fa2cd67056be92f91c496c134c19c4e0a074103d79ffe5dd879c7ad0f3036b75","impliedFormat":99},{"version":"a5e9994461de1736556049ad37857577a0c1238cda95baed68d5997c45f353bb","impliedFormat":99},{"version":"479ae2d0df8346600082dab7eae56c7557bc0436ab0647c31063f3a02d7cc6c7","impliedFormat":99},{"version":"1faf72b15dbf252373a1f4b2867a199800a97cc7079dd3b01087a2f3c11f4a45","impliedFormat":99},{"version":"4af8ba68f5b509c7a7d353e7a91a4014d194779c537acd3afd330980e9cb972e","impliedFormat":99},{"version":"c4a78ea6e862ed9913ecf9b95c8b4a8f77384f2cf583eee7fc4b27c52f4fbaf7","impliedFormat":99},{"version":"ee9c59f4fa245924e3328a2a6620fe107b0c41860367a47a90d07a932e3a084a","impliedFormat":99},{"version":"e2a62c1f16bfc7e305e78143c1eeedb71ad1f272761aa07ff2ad70bb97248b13","impliedFormat":99},{"version":"1c1ba22485be7577e2a73cc103bf6e7a692ae0a025216619a4de42186eb1687f","impliedFormat":99},{"version":"ebbaa82abb5a1a7179dff85377f99c666dfa3a68f8a2ea405bbcf4f5e50149be","impliedFormat":99},{"version":"37673287f8bc80af36be1c23275ed9416bec11ee361b320a57a0b3a2797260ef","impliedFormat":99},{"version":"39e217bc229493f7b355cb2ce07f8600f6266500a6feaad43c254cc0a0f140e6","impliedFormat":99},{"version":"a33d889bdb82f43cdcd8158107c0e25d295d1838d82ee8649390ca0b88e87a07","impliedFormat":99},{"version":"f289d98b575f84044465ab3b7ac81e9a11b4d97cf2a9e92a822e948bc2f0ba7c","impliedFormat":99},{"version":"787a6b0d92df1148c7da1e75d705652030078c19e79985a72e622dc5b54487bc","impliedFormat":99},{"version":"ad09a94413ba6d37b86beaf348b252b378608dec764b67efc9e65d1dc4dff808","impliedFormat":99},{"version":"534611552e459b47ea008079ec9d520fa87c7b229c8a3a08993e8be650a8df71","impliedFormat":99},{"version":"d4ddcacf48fe1a273c71f4b04818eb912fd62bd7c0da092742a7e37a30cbc726","impliedFormat":99},{"version":"83098570d4ae37e31d8c788901c5d094313176a23f26870e78a5e98e5edd7777","impliedFormat":99},{"version":"9a43bf14da8556f0484d20c93074fd98845c06c85587f6b580fedda82685dc04","impliedFormat":99},{"version":"6a36631027099eca036047ab3bf0f262e23239c950ec1c65de700300d223ec44","impliedFormat":99},{"version":"fabd5be6cc2f406cbfcddb145a91a0e341a25e509e1c7521762441a46e58780d","impliedFormat":99},{"version":"80a9eb1f4fd46c863aec7d9c8490702ec6aee2b63ada21bcf231d558f5f0863f","impliedFormat":99},{"version":"72932bf7599a75bdd02788593ec6f060422f0c6e316f19629c568e2473d37db3","impliedFormat":99},{"version":"2d6244e9b7d19e7650ceaf09033ada34ebc2d0590a770707cba8da87bc125d59","impliedFormat":99},{"version":"6e0ce9899346df5cf80e1b45172eb655855837cdcad4539acbfc9305d8fc8976","impliedFormat":99},{"version":"ec56ed06f0a89fa2e3a20d7f7521d017e92decaebfa4d53afa0fd3a7b7a2be3b","impliedFormat":99},{"version":"236b90e223fa7a458b8561e87327d3737c8c83cac9ce4cdfbf04b3098da851fc","impliedFormat":99},{"version":"d64b03d8342c4f4d51706659a835c45197b9940b8938b0656bdc4df15d06f630","impliedFormat":99},{"version":"8ff0f34466d5136ab068a15358b99a958095d1c6e7e11c67d607111f81933fc4","impliedFormat":99},{"version":"98a72afb1b77b1c3f15beaed5662feb608060ccc604fa1747fba9d9deb070f80","impliedFormat":99},{"version":"cd5189682a722b13ec340b2bd112bd48b899bfeecd69d629bfc78166c15b64fe","impliedFormat":99},{"version":"a4017a3045a75c68509bc69bd314d91f7d4e1ec3f255416a86efe7dadfc0ee64","impliedFormat":99},{"version":"19599db9634ba72f2663db5b76de48dfe250280ffb4bb158109c97eb4e5b5e0c","impliedFormat":99},{"version":"f5a0ae8b389c104a6655117a8e4ee1b563c4d7cb33e045e4ce433cd6c458adb4","impliedFormat":99},{"version":"566247adad3c04b6c5b7e496700f3f23d05b07f31d983ab47fae1aa684b048c9","impliedFormat":99},{"version":"d10e1723404ce7559b75ab341c7e85b1ee2c97a871fe41630e47edcfb1bb1621","impliedFormat":99},{"version":"19b360e4fd7cd6683ab5455093e02c7c42dbbbd63ae5bd50f91f55a8709feb98","impliedFormat":99},{"version":"ee5ac98eb5ccf9bee886f916ae88856ac220858f4363362c094add66a0d44780","impliedFormat":99},{"version":"02efcdd69ab7a028c7fc316316748723763f7d40073f5b74f5136f9ded5c3073","impliedFormat":99},{"version":"35c834f84dabbdc3b595dd35c2208876af77af86ec3e0f4b622ff7f901843e80","impliedFormat":99},{"version":"dde618b9a6b780c35bffa2075f11ee29adabeaff3dbb73136ac530fd45035692","impliedFormat":99},{"version":"6902d565ebbc9b4f27b81f50a769ebbb673d299090146fc4d645c59b4433b36e","impliedFormat":99},{"version":"ac0e664aa993f918ba1006ca2bc107bbe326ec96341a61195f9b3054a9571a84","impliedFormat":99},{"version":"b184444a122ee085c61df92f39b23414dbe7adece24558bfd1a0dcb22687119a","impliedFormat":99},{"version":"b8ae8c7a851b19aa288e08fbd70e734f5aef3d1bca3a5844dd31658f1bd18f62","impliedFormat":99},{"version":"85377ed2b6029c8c1e851e1acf848d31fcfcc5a9264e726a00e046ccd68288ce","impliedFormat":99},{"version":"aa096547941633cc25eb4b24af41984906ca9e502d9c06c88e0b4bdc2a54b602","impliedFormat":99},{"version":"2b69aa435fea74564e7388f7530a8ce8dd5c9f9c6a67cf74aef6f99922f05885","impliedFormat":99},{"version":"6853837d5850cfd5322fa38f190d4afb7b1ad9d37b137a7fef26a1f489a48b61","impliedFormat":99},{"version":"60bc42d137352b88caf900df4bacbfee40c73c02b56b853e0a6d23a3edab7815","impliedFormat":99},{"version":"d90817a097f2792dc8ebaf9bc08298f1ab6cb18b4287c8cfc1cea297263df6d8","impliedFormat":99},{"version":"3cbfe6b48a9c4b5b6283f7fbce0c1f5ccf0796ac8b1a077893eeab13fc17ef73","impliedFormat":99},{"version":"01e1e0a0c437c0c64b0e55fd5b3123398c25b058fae214ffce0ff9a345d58777","impliedFormat":99},{"version":"23e97994d2f64dd504174c02b5ed713093f1f8c0ee4c3b587c7f12a641b8bcd2","impliedFormat":99},{"version":"fff0fc0ee248874a1f8d12dd439f1c26a8be008a18b7e36ee65c3e8cd634fe18","impliedFormat":99},{"version":"7e6a11a7c6f9070215e73bbf9620d274ef45979eadc8de39d50ffe6fb67f095f","impliedFormat":99},{"version":"c34715ff893e99af41dcf4f63b3eca2a45088ef0e4a025355204c440772278c8","impliedFormat":99},{"version":"cbc0e98cf745d22c786862ea94c3566321c06e04109f461d8f657a3c1151d057","impliedFormat":99},{"version":"35a5955d275c446b47dcbd8347f41e8ef6e230c563efd38f62ff715f7994b767","impliedFormat":99},{"version":"2bb58f627a12841ae367d97ee15993590a5cee4a826326e2a8be2bd750fca9dc","impliedFormat":99},{"version":"646d361245ec8052afcc7b90acb3c7f6098bf1340b88f32455e63f0369ddf1c5","impliedFormat":99},{"version":"2f6f7ba3522444f3d3e1f6d4a74ac37a096fc3dd292f902773d761a9eee57620","impliedFormat":99},{"version":"921a2668977ae600f6de2a4c19f53b237ed196ae5ae3297043b38781c11d9af4","impliedFormat":99},{"version":"37072b862cae4c819e335ac9eb855cc398719a50ebc60376de9ddae801610a3f","impliedFormat":99},{"version":"6473fc970e5fb3f18fece3e8681e9ad4454adf295be54463ebd74688b8b5051c","impliedFormat":99},{"version":"4e54895c51ee23f08f62fb429dbf4cfc1772ce34f6ec77855a3bb805dd16637e","impliedFormat":99},{"version":"800509db7d0b61907f28dea506dea01b57d2aa3cfffff03c78ccad04e8ceb976","impliedFormat":99},{"version":"887778667559bb4b7c64b006650210ca831f903c055ada3a069a8f08790c5718","impliedFormat":99},{"version":"bd96c9f72e9a439499bf19a4405d205fb8c2da019fdc9fea988c25005e76c200","impliedFormat":99},{"version":"9fe617a1d5e7c5c4290dd355f1bdbe1a80574d765a2c03bbf49181b664f11f0a","impliedFormat":99},{"version":"9afd17a7e1d2b474eb24bb37007d0f89ccbfb362ce904cd962a1eca5a2699efe","impliedFormat":99},{"version":"2841695cc12ded91f69e66e4ef44be524ad02c54bbe257bfe5802e3f6b851b5f","impliedFormat":99},{"version":"1132807f0a56b00d253a76bc21aeb23e79c8ba8478c52ec75ba9fcbd81738b6c","impliedFormat":99},{"version":"effbcf17a843123b20a54ce8389b29a26d12c056872f0dfc76a27460b22f716c","impliedFormat":99},{"version":"0e052acd94182df2373f96e362f064715c7a782452166e2344c91a126fb41d28","impliedFormat":99},{"version":"b7bce6b962d325bdd11ca9f3394b411f37354bffc995e798ddf66a7ee3b9de42","impliedFormat":99},{"version":"96d66cb0b5b2a0b1e2e6172e6bc163b5345f189c699852c61cb866812bc5f3a8","impliedFormat":99},{"version":"31e4839e4dc630cf7bc9f86c1afdf3dc2cdb4b8f6c3e11d12ba38ea18f4c421c","impliedFormat":99},{"version":"5b94968e8400015a4904bc1ba26ebcada2fd366c8fdeeb873904af54ccd4b423","impliedFormat":99},{"version":"1513e36e2863b615eaccce129355da5f5d8e169d4416d25e71e47dea8d2d35c5","impliedFormat":99},{"version":"7a128061e6e84e9375c9b46f84a0cfded49f537e8a41b42db0c72133666ab9e8","impliedFormat":99},{"version":"1140a35eddf2e3b9d706eeaf28ea74e969ecbe52e0d0f6a180d0753d2b8272b6","impliedFormat":99},{"version":"5e9cf2a12f0d82d2bb5989cbe663707bf1f3bdccf7cf0fdb84105712b9788e0d","impliedFormat":99},{"version":"ad1a1db6939b787bd9ae09f997f9b209cf0e689c72414090fb9cb7989163df77","impliedFormat":99},{"version":"95e857dd9e4984d5694b721cfe2d66430805db29660e212c2f681515ea08e8e3","impliedFormat":99},{"version":"2e8932517228de42f8b3db669c4d04d177367283d2da199b1a59cd94d009e65b","impliedFormat":99},{"version":"5fada2edbc2264cc0d7ab6080e8e4d4e3d5dfb9ef494b4eac9c8e49b90f30bdd","impliedFormat":99},{"version":"615d06216e7aaba7e4011cee475b9ef6d1d873886118f178bca21bc38ecc10a8","impliedFormat":99},{"version":"7ddf86abeff5aa521f606f9ea9ba2dfa19df8ee1c76ea838d9081afad9b28f68","impliedFormat":99},{"version":"ebd25c9c141e447ee6ba794c80c6878ebb39c42ad135a0c363d275af9fa6799f","impliedFormat":99},{"version":"4132803c2f4af9b16bd01853bc98cf15ec1ec9d11529665bc6ccff35bb742774","impliedFormat":99},{"version":"efb8e27827124bc3bf96b81288c0278c722d10f2372a1bbbb9de1943e784022b","impliedFormat":99},{"version":"1400f49fbe68fc416c2d47e19f6bc29a99a5480910bd560090a27fb6b68fbb45","impliedFormat":99},{"version":"98312a773a489dd6b924350b9ef0eeb1f9646b7e1b294501b582b67dd735cb3f","impliedFormat":99},{"version":"a9410e9f7d26ad51166dc6e55de1f0efe893750b665a4169cce9a1c46d793d18","impliedFormat":99},{"version":"4218cc9758b6c35b4e98131e96d6be902fddbc194936e0ba71a95767e11072b8","impliedFormat":99},{"version":"6f29d6927bf60c969a6a5ef57231d63b81e6820b6a30c22eef57821b5c429ac0","impliedFormat":99},{"version":"0df226906c522ff1903482f4fbb83c039fbd0dca93bd1b3e56baf10ab58bfa15","impliedFormat":99},{"version":"02f4852cbefa35edd7752e35e228adc060a2c98251be70a9446f64e196029bf0","impliedFormat":99},{"version":"4b70362f8a653d862fb4e5b65aec7943cb237e89c2409c76e6aabbe52952507a","impliedFormat":99},{"version":"8c83218e92cb960a020ce6ccf208c7fdd29fdba50f824ec9f7804b9a47c175d0","impliedFormat":99},{"version":"1da27ab546abd20e493012d7088a55cf1a00c92b61eb06aeb7a25581c6f9ff87","impliedFormat":99},{"version":"be74c0d9577d80d8dd6fc04a8830a2bc3ddbbad2570551305072dfd985534876","impliedFormat":99},{"version":"de3347bf58d986a47125ca7bcda6268d55c78d6006e165f59a03c20be861d95f","impliedFormat":99},{"version":"b1ef1a2530ef3a4308488bd172252a506f35fe16622d3117573865c2b6ad3485","impliedFormat":99},{"version":"840d2806690ca5764b0b3393d63fb4f4eed660f2fd1c60ed5a8aec3a851e775e","impliedFormat":99},{"version":"b222f67a18cb2bf55641e911467ca5f1cc4ce664824a3d4e2ffcc1eb91729be7","impliedFormat":99},{"version":"5a191a0753935e35e8560d6dff2a0bcbeccd0850d11a7429283e3061fa8bc8e9","impliedFormat":99},{"version":"e933510d79c03cb4b5ae2b910b72056332955385cd91774913ab04c9f7fdf00a","impliedFormat":99},{"version":"3a3dee0f5e3155384690cf45b07a217a6b3de5e1edb4e98ec8be5fbf3e500868","impliedFormat":99},{"version":"db2ed9c0a4fd561f950cea441d7833cd5763720e91856f7521d3d5f29fc4b7e7","impliedFormat":99},{"version":"c8a7aaf671a49f7f836c4bc4aa4fed23e5c8da16eccf72ef2b2a52197663e6cc","impliedFormat":99},{"version":"10856b8f8c6dc7fa2da62848f65f977524b073c6fd167578f21edda81832687d","impliedFormat":99},{"version":"067de9fe31618932f08e63ff479248118a39461669a06b6f6d438e7ef364cf29","impliedFormat":99},{"version":"f67169b04c2c933f4368c15bcea7f744f05a134dad020c6d418f7cca2f9926bb","impliedFormat":99},{"version":"078af8b67c2d6de37753072c1759f1215fb9846cd4344cf83a266b10fbbcd790","impliedFormat":99},{"version":"e27b61cd6a2953ce7955274ed4dafde236be2051b1a89d10b5640306b7d3b86f","impliedFormat":99},{"version":"91beef8d07b44eb328aa9d9c3db98bd79bde320adc5d1556d3fa7f463a90c3ce","impliedFormat":99},{"version":"7f7f86c7d1bc4a9f9e4d68fe0702f91667419056e6746494a66a7fd2882c8660","impliedFormat":99},{"version":"b87d0cb8854aa7f797bcc7f46ab0522fd091828dcce0463d4dc58b78e1f6ac10","impliedFormat":99},{"version":"934478c63db3268cc99351178a969840b2b93a2d21a4d165813cebe5eb6dc419","impliedFormat":99},{"version":"e1ac2ee125d07e130dafcc5727922a802f33bd894b3bdec61a58653cb25a105f","impliedFormat":99},{"version":"80d81e5352828e06dd7d79a0ad35f07087aa0c4f2aa80f8df7e67855ab544997","impliedFormat":99},{"version":"d78aa09669af770ed71d8a0d5b1e2ef23abb7a17dca11e1bc02adc9cf9816fd1","impliedFormat":99},{"version":"acc4b06dfbe56e355495b3571be65265b7bcba54ae6addebfc4c34823a3e8201","impliedFormat":99},{"version":"80d964c6d3fd00733c20f85ee9fa70c52e3459f1960adb17e5bf812cf9604ad6","impliedFormat":99},{"version":"1df19943bbd19525aa3e18c3f98f49ecdd788e07b5705d99c8579073771dcc06","impliedFormat":99},{"version":"8c2a083fb5fb302c0954709938e53e7810da0aa2daf489757af9dc6baa2f3b7a","impliedFormat":99},{"version":"fcc4a7940475065b3291a15623bc4e8db0c22f23dc29be00ec377892a805eba3","impliedFormat":99},{"version":"16f35e4256a94b314c98eb75040cb33ad9c430075bc29c53b43ede6405705700","impliedFormat":99},{"version":"a257b94f9f5403f63026dee4f4a95b9015742f8e23ff6eed7837f97f2ae501d9","impliedFormat":99},{"version":"254175b582a422c45bd9701e93d6ef9f6cf242c6247793a9a2e05770e1add917","impliedFormat":99},{"version":"c4dcab273dad7aaf0d3b4e0ca48e948aeb3cc269ac359301181fdfbb2efde033","impliedFormat":99},{"version":"d21671ff0f21081ecb19567351dadc8be7ad9443e26183d971ac658684479777","impliedFormat":99},{"version":"617be44c8811d411b4efc7dd13e57851bb1ab71f7e9d1a9eca04b7d0db0e1b35","impliedFormat":99},{"version":"5dbefdf7b46fb5883f6a38744edfc6301861587b22bf0d829fc40e917564c300","signature":"103f705f4a34ae9a60a49227092fe628a86ec741699295ddb2c62447d4933e2f"},{"version":"fe93c474ab38ac02e30e3af073412b4f92b740152cf3a751fdaee8cbea982341","impliedFormat":1},{"version":"476e83e2c9e398265eed2c38773ae9081932b08ea5597b579a7d2e0c690ead56","impliedFormat":1},{"version":"1e00b8bf9e3766c958218cd6144ffe08418286f89ff44ba5a2cc830c03dd22c7","impliedFormat":1},{"version":"948af5f3081a8e683756251a3834ed5a4ae6e4faf16b66abcc44b8a13e72d08f","impliedFormat":99},{"version":"af437043c5e9a8105f0e9633fea7ae79d1f7f89357897e2df99c6f3697c744fb","impliedFormat":99},{"version":"d0bede428ef73b5da47f0f1bdc440480a863a1baff3e6a6b28db98535efcaa6d","impliedFormat":99},{"version":"44015602830f6f90e043f14702f04e7f48ec4f2ce8a9925116fceaea73a43b8b","impliedFormat":99},{"version":"edadfd0ecbfc573c016b93064b18ec0ab64b17ebd252e5636c2ee55f0f6cd82e","impliedFormat":99},{"version":"45ccec80a9c659104e28cbf3dbff13ab92c57c4e1fad9b38671a0c9299afd4f6","impliedFormat":99},{"version":"1b59bf4043df6f2b48ac1685a0a6c3953ccdb49b1c7affd3364624f77ff58f0b","impliedFormat":99},{"version":"ca4a3925cfe8fc5bac902ea30a5fd441987ee1776ddf5a353715dd1c6579722a","impliedFormat":99},{"version":"658f055f615e8232a1f5b30753a0a1ce369a70c1dcf0685953f48d7d4d508ba0","impliedFormat":99},{"version":"341aee100ffa277e0737387b9b9db69280e6dfd056166e18656de5bced0e7f01","impliedFormat":99},{"version":"2cac36c32274e1e7faf7fb3bd1666e69763b07b9f5330eee6911352d3e6eee96","impliedFormat":99},{"version":"aef33b3c102b63e7e89208ec4982f8eac7168774b08b05e8c43e967729ea91e5","impliedFormat":99},{"version":"5788b512a6212523eb032c6898beb98d56dd14c84e186a786901c1b3cae527cf","impliedFormat":99},{"version":"e2f146177df266680126ba19e4336ffc6639094feb27b37d276577f8c3d2c186","impliedFormat":99},{"version":"020c87fd5861d5feb650f37cfc039aeaf147bb7ed0aa801ac7f39d06a85b60fe","signature":"6dd1aa96df8d00b34f9352ba4fce5c2607e405a83bb319169fdfb06d1eae00d6"},{"version":"ae00023c4fb6d8310666f6f047f455331ded1cd758182decd54d7f3f2bdc7e73","impliedFormat":1},{"version":"1e380bb9f7438543101f54ecd1b5c0b8216eea8d5650e98ec95e4c9aa116cdd5","impliedFormat":1},{"version":"d0b73f1df56fbd242fd78d55b29e1de340548048f19ac104fe2b201dc49529ff","impliedFormat":1},{"version":"287fa50a234cad0b96ebba3713fe57a7115f7b657dc44638fbce57c45ac71397","impliedFormat":1},{"version":"c42852405dff422a8b20dd3a9ada0130237ee9398a783151aa0f73474c246aeb","impliedFormat":1},{"version":"d3260c8d6fb8ab6b92c412c3c0b793dc524dbcc6737300cd4cf22198122479a4","impliedFormat":1},{"version":"f7ebfaa84846f84bd01665f4dd3773ff2b1c38c7992fd1042cd9132bf0afc82d","impliedFormat":1},{"version":"b03829b7141ddbc20c9da5de4f8021ef99b57b169e753d28ba5582d02bc9d5da","impliedFormat":1},{"version":"d1c49ba10ba80d18dc288f021c86c496d5581112ef6e107e9e9c20f746ee7b0a","impliedFormat":1},{"version":"f3c5ea78b54672f9440be1a2ae3f6aeb0184f6a4f641c3cca51949e9cd00a258","impliedFormat":1},{"version":"9ad68f979b78ae71d8cd991b87b5317b5959867dfe5970de811ec40a09d6e1f4","impliedFormat":1},{"version":"d91e9e625a2903192e9a63361b89330f0d95c340d9bb4602b89f485e9f93cdd6","impliedFormat":1},{"version":"176a47d228081ad51c1d62769b77b064abbeb6827115033cce1cdeb340a8d46c","impliedFormat":1},{"version":"b5eaf1cc561810ebfb369039a6e77a4d0f74bf3162d65421a52fc5b9b5158c2c","impliedFormat":1},{"version":"7d12ec184af986cc2a0fdc97f6c7f5a547ecdd8434856a323ea7ff064e15f858","impliedFormat":1},{"version":"8535298578313ba0f71a41619e193767baec9ccf6d8fad90bc144bcba444307a","impliedFormat":1},{"version":"50bfcce750d2a77d46e96e1d6c80f2a6309eb86f6ffee53ed07864b68af3d824","impliedFormat":1},{"version":"79b41324d28fb94f7304a0151990ad712346ceb1907fe1ce5e892145eae8f8eb","impliedFormat":1},{"version":"3239a3f8c29d2b0db0a4200b7e6513e95d16a183d4d823e54cb5c98f4c53e183","impliedFormat":1},{"version":"d05659bc83a21392bbc0110223f44f701562eced0c25ad812eab0ac8281e7da4","signature":"8ef254845794a632db1c7d21f0b48f92af78ce733481da3a8a4b3f5b03d67acb"},{"version":"a362e637e88ac7c0d878debfcdb8de6b08158e6e54d648082c4a6a26ac832791","signature":"a880f8e696dd7ed55dfb6b7c53611e4df4966662443273960622fcb27c02362f"},{"version":"97f3f43cea913e85a12d72f8b1ec0521a4f36babd1550f3f769b895c98584c93","signature":"1792f3fd879a08a82c4c58e6392ae05ff6c33f56b1a629f23bc7812ee808cee2"},{"version":"8088a8c902ec6a5ea2ef9964da4f30f54d6f6a5a559677f0425d319b82f34a1b","signature":"07627fe71cb70f4617e8471c7919fda7e13b5187daa70c21c73b60ca0f55d9b6"},{"version":"f880590cc503e96e629c68f2bb9dd6dae1f9ad98c5b72332ea4d5aad840b8274","signature":"dcf05a557d3591a75d84be523c2710d0511f38ea6f31c659153f3908c8aeaf29"},{"version":"d2aa0398dde96a3972b4800e6ec316914d916080b523c28fd42e3f3924f6e42d","signature":"01a977ade994fe0de990222140f158a0dc3b03529994c449aa39333d0facac02"},{"version":"efcdda092037a762a10fa363b936fc5e54c61a345b39b64edcff8ebd15b96517","signature":"39f6d146b3d7a5a5d7e4b9c821bb5083a559734c5d0e0685bd42cad6c3756120"},{"version":"ac1ac781929f9be53cbcc041926e46f3fec7870623454cff566ee5c43f1acdae","signature":"3eb972ae325aa293fdb6077cdf956f209ee6ea34b4e874ff7ec8686b3079972f"},{"version":"ad3c3dec7cdeca9a495e3926b8702bbadfe14c435a0cbdb2c432e61c98a44553","signature":"68dfc22d24a462e2344abc4d521b2b7191d8e1a719f72b5d92fe3eee76d18d20"},{"version":"3a78d15309b31552c73e2482147c6bc4bf5500cde9422dc6b2594f0e44e4afd1","signature":"4cc0f63a064269bf0afef88c6d7877b70a54cf77827cd61195470a9ac0840533"},{"version":"2065832dd99cfd0a01cb21a2747e35598a13965a2edd05298cc561e3c32dac60","signature":"72fe197007d100723523beb4def1f7e6d19ad60ffa3c73f9100be9088a65ddb8"},{"version":"7b73abba27fe6a17840adffd8bea94d4bd6e9a3c6eb5bf8e6281fb5d659ca3cf","signature":"943aaf838f611dab905818c4606637f9df5439c87c6774a1cadfdcecfc465c72"},{"version":"74dfde65890d55f05ad557f2fce299ea02ab7b98d1fc71c0118de24b03ee21db","signature":"46df3b90500aa646e8ef62e459f6bb881545c69786b13b9d821db667dbc0dc79"},{"version":"fef91ae8defc8311952c4bc63101f868694b5e6a882c08341d4029bd94fa6f0e","signature":"68fd6f6e704ec852d58359de412d7914d8bb3a09489dc92030250a4a832e9ce4"},{"version":"0d9907abf7c7f70fe2b033bf118964fcbd5ea4b51ef0a021242315dcf390d822","signature":"934f32a7071a572c867a2f946405c62aa8448f12fa452a45d1f0a4e9c27305e2"},{"version":"7adfc4104c99078e409c45ef42360018e04b4aa1f4aab8be130d2f305d36d6cc","signature":"415d465c3007106b4b26ac792a54a909d22d26f905f1a9e7822248efd8ea7d80"},{"version":"88b4b85fd86ff2b2025f764f454fe060f8e5f35b8ead8e43cdfc8ccffcac608f","signature":"fc4a1909516c00b0a88e516f5564dba70d7c13db1fc6a650c424b4b81e67eac7"},{"version":"cd7a8712d865de9b13853d0d9eb84118052c29d839e0f28a952daa9d2bfc57a4","signature":"a74609c36a11ec7ee49d39f1180a3712c1d3b00c1de5f0dae5c494e6b396ee15"},{"version":"e437f9801e722c286f8665b8ff67794debcb6e028f9b84823bd65ebec6fe5d88","signature":"b08fbf391494dfd2b1b3681eb264ea04ae1192cd19bf67202c9e6e8bd6848c50"},{"version":"4852224befd8e9c7988b52945ef31e8ac43c49a1182d39b35580ddae219bb7d8","signature":"201d46aad36c4c3143ffbfdc1eee8ba45531a14e03034796f83d1bad178b1e58"},{"version":"6e9e4f8b1d1c34147905bd43a7d86e58d487ca5abd4f1bdc68f1357e2272d953","signature":"3383d7720a22c25cdd627089c4486b5f55d6de17c8ad98d5e44e2922a205384a"},{"version":"87b16feeb0bf1c5c17819b31621b4feef041fd371ecc5e3102552d79e6ab239c","signature":"a5eb1afaf07610bc164fd12c1ef9a4aa6f7efba5d2eeaafb71fdd3f38f6b6a06"},{"version":"dc0878ae9f51f476995626109cb017925e317d2e2158840753cfb23388d20249","signature":"92e433fafd88c7d3d4d2a128e8b0021a6d82c78fbbeb182ffd181b6cf608c889"},{"version":"820772dac24345893f7132e4d951089bba4b068ff66859479efc820810b8a04c","signature":"fc46160c59fbd31788690dfdb06d9cfd215d3519e82a617ee455324e77d4f8fc"},{"version":"2a138076231b76dc334464947f83616cbaa52832e370fbed780f4aa7eb5eae2b","signature":"3a8523bfa20e149df671c3166d3c2e0c8e5b9f015b121a6855deedf209096b5e"},{"version":"d0f6af1efc88341c181143ced8539d5509c811a4ac6c476edeb25fdb0d5fd119","signature":"7877583d2968dc42e5a9ed506825ac661bec8502394efca99a5fbd95dc029288"},"2552a31fad45a9ed1bde87e51b038dc0e786cd364b597162263abbf57018949b",{"version":"b14961ecfb0613b0592c280d1edee4beb03a4b360cb264e561d22ac433e3977e","signature":"8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"},{"version":"8721c0e54253a11b7cbb42c583d7260f8c033814e0b0595423372323ad14948a","signature":"89b0f68f8f0b901f9dfff2b9e7255520283a783d6af7f2bc2953d771232317a2"},{"version":"dc94787cb4f02ed26312bfa7e08688fc988a75ec864fbe7ac019db1e5d941b4d","signature":"89b0f68f8f0b901f9dfff2b9e7255520283a783d6af7f2bc2953d771232317a2"},{"version":"507ccebe35427617298af6ed0cb3b2a9b0a88433e71ff7e7a17f744a8f4b0195","signature":"89b0f68f8f0b901f9dfff2b9e7255520283a783d6af7f2bc2953d771232317a2"},{"version":"e8aefc9034cedd62ebe09f69dcfaced3eab4d121bf2f0274695734cf7a0d5b8c","signature":"89b0f68f8f0b901f9dfff2b9e7255520283a783d6af7f2bc2953d771232317a2"},{"version":"151ff381ef9ff8da2da9b9663ebf657eac35c4c9a19183420c05728f31a6761d","impliedFormat":1},{"version":"f3d8c757e148ad968f0d98697987db363070abada5f503da3c06aefd9d4248c1","impliedFormat":1},{"version":"96d14f21b7652903852eef49379d04dbda28c16ed36468f8c9fa08f7c14c9538","impliedFormat":1}],"root":[83,501,502,527,528,671,689,[709,740]],"options":{"allowJs":true,"esModuleInterop":true,"jsx":1,"module":99,"skipLibCheck":true,"strict":true,"target":4},"referencedMap":[[739,1],[737,2],[738,3],[740,4],[735,5],[83,6],[736,7],[717,8],[718,9],[719,10],[720,10],[721,11],[722,12],[723,11],[724,11],[725,10],[726,13],[727,14],[714,15],[716,16],[728,9],[729,17],[732,18],[733,10],[734,10],[731,19],[713,20],[730,19],[715,14],[689,21],[711,19],[709,22],[710,23],[712,19],[528,6],[671,24],[501,25],[502,26],[652,27],[654,28],[655,29],[638,30],[642,31],[662,32],[641,32],[644,33],[636,6],[663,32],[637,32],[664,32],[643,34],[633,35],[653,27],[666,36],[657,37],[656,6],[659,38],[658,6],[665,39],[649,40],[648,6],[670,41],[632,42],[660,43],[631,44],[669,6],[645,36],[661,36],[650,36],[647,45],[646,36],[651,6],[628,45],[627,6],[629,36],[635,30],[640,36],[639,36],[667,46],[668,36],[630,47],[634,46],[551,48],[530,48],[586,6],[566,6],[598,6],[529,6],[577,49],[578,50],[544,51],[531,6],[540,6],[541,52],[542,6],[543,53],[539,54],[532,6],[536,6],[561,54],[559,55],[547,56],[573,57],[549,6],[554,6],[550,58],[545,54],[546,59],[548,59],[552,6],[553,6],[576,60],[565,61],[563,6],[564,62],[579,63],[575,64],[593,65],[587,66],[596,67],[597,68],[567,69],[583,6],[572,70],[569,54],[624,71],[570,72],[606,73],[605,6],[612,74],[611,6],[614,75],[613,6],[616,76],[615,6],[608,77],[607,6],[610,78],[609,78],[625,79],[620,80],[626,81],[538,82],[535,83],[534,6],[585,84],[599,85],[619,6],[604,6],[571,86],[589,87],[588,88],[591,89],[594,87],[595,90],[601,91],[574,6],[555,6],[590,92],[592,6],[621,93],[603,94],[581,6],[584,54],[558,95],[557,6],[622,96],[556,6],[623,97],[562,98],[560,6],[533,6],[600,99],[537,6],[618,100],[568,6],[602,101],[617,6],[580,6],[582,102],[680,103],[679,104],[681,103],[683,105],[685,106],[686,107],[684,108],[678,106],[688,109],[675,110],[676,110],[687,6],[677,24],[682,111],[690,6],[696,112],[692,113],[695,114],[700,115],[702,116],[697,117],[694,118],[693,6],[708,119],[701,6],[698,6],[691,6],[704,120],[703,121],[706,6],[699,6],[705,115],[707,122],[248,6],[741,6],[742,6],[743,6],[145,123],[146,123],[147,124],[100,125],[148,126],[149,127],[150,128],[95,6],[98,129],[96,6],[97,6],[151,130],[152,131],[153,132],[154,133],[155,134],[156,135],[157,135],[158,136],[159,137],[160,138],[161,139],[101,6],[99,6],[162,140],[163,141],[164,142],[198,143],[165,144],[166,6],[167,145],[168,146],[169,147],[170,148],[171,149],[172,150],[173,151],[174,152],[175,153],[176,153],[177,154],[178,6],[179,155],[180,156],[182,157],[181,158],[183,159],[184,160],[185,161],[186,162],[187,163],[188,164],[189,165],[190,166],[191,167],[192,168],[193,169],[194,170],[195,171],[102,6],[103,6],[104,6],[142,172],[143,6],[144,6],[196,173],[197,174],[202,175],[358,19],[203,176],[201,177],[360,178],[359,179],[199,180],[356,6],[200,181],[84,6],[86,182],[355,19],[266,19],[85,6],[93,183],[447,184],[452,5],[454,185],[224,186],[252,187],[430,188],[247,189],[235,6],[216,6],[222,6],[420,190],[283,191],[223,6],[389,192],[257,193],[258,194],[354,195],[417,196],[372,197],[424,198],[425,199],[423,200],[422,6],[421,201],[254,202],[225,203],[304,6],[305,204],[220,6],[236,205],[226,206],[288,205],[285,205],[209,205],[250,207],[249,6],[429,208],[439,6],[215,6],[330,209],[331,210],[325,19],[475,6],[333,6],[334,211],[326,212],[481,213],[479,214],[474,6],[416,215],[415,6],[473,216],[327,19],[368,217],[366,218],[476,6],[480,6],[478,219],[477,6],[367,220],[468,221],[471,222],[295,223],[294,224],[293,225],[484,19],[292,226],[277,6],[487,6],[673,227],[672,6],[490,6],[489,19],[491,228],[205,6],[426,229],[427,230],[428,231],[238,6],[214,232],[204,6],[346,19],[207,233],[345,234],[344,235],[335,6],[336,6],[343,6],[338,6],[341,236],[337,6],[339,237],[342,238],[340,237],[221,6],[212,6],[213,205],[267,239],[268,240],[265,241],[263,242],[264,243],[260,6],[352,211],[374,211],[446,244],[455,245],[459,246],[433,247],[432,6],[280,6],[492,248],[442,249],[328,250],[329,251],[320,252],[310,6],[351,253],[311,254],[353,255],[348,256],[347,6],[349,6],[365,257],[434,258],[435,259],[313,260],[317,261],[308,262],[412,263],[441,264],[287,265],[390,266],[210,267],[440,268],[206,189],[261,6],[269,269],[401,270],[259,6],[400,271],[94,6],[395,272],[237,6],[306,273],[391,6],[211,6],[270,6],[399,274],[219,6],[275,275],[316,276],[431,277],[315,6],[398,6],[262,6],[403,278],[404,279],[217,6],[406,280],[408,281],[407,282],[240,6],[397,267],[410,283],[396,284],[402,285],[228,6],[231,6],[229,6],[233,6],[230,6],[232,6],[234,286],[227,6],[382,287],[381,6],[387,288],[383,289],[386,290],[385,290],[388,288],[384,289],[274,291],[375,292],[438,293],[494,6],[463,294],[465,295],[312,6],[464,296],[436,258],[493,297],[332,258],[218,6],[314,298],[271,299],[272,300],[273,301],[303,302],[411,302],[289,302],[376,303],[290,303],[256,304],[255,6],[380,305],[379,306],[378,307],[377,308],[437,309],[324,310],[362,311],[323,312],[357,313],[361,314],[419,315],[418,316],[414,317],[371,318],[373,319],[370,320],[409,321],[364,6],[451,6],[363,322],[413,6],[276,323],[309,229],[307,324],[278,325],[281,326],[488,6],[279,327],[282,327],[449,6],[448,6],[450,6],[486,6],[284,328],[322,19],[92,6],[369,329],[253,6],[242,330],[318,6],[457,19],[467,331],[302,19],[461,211],[301,332],[444,333],[300,331],[208,6],[469,334],[298,19],[299,19],[291,6],[241,6],[297,335],[296,336],[239,337],[319,152],[286,152],[405,6],[393,338],[392,6],[453,6],[350,339],[321,19],[445,340],[87,19],[90,341],[91,342],[88,19],[89,6],[251,343],[246,344],[245,6],[244,345],[243,6],[443,346],[456,347],[458,348],[460,349],[674,350],[462,351],[466,352],[500,353],[470,353],[499,354],[472,355],[482,356],[483,357],[485,358],[495,359],[498,232],[497,6],[496,360],[519,361],[517,362],[518,363],[506,364],[507,362],[514,365],[505,366],[510,367],[520,6],[511,368],[516,369],[522,370],[521,371],[504,372],[512,373],[513,374],[508,375],[515,361],[509,376],[394,377],[503,6],[525,378],[524,6],[523,6],[526,379],[81,6],[82,6],[13,6],[14,6],[16,6],[15,6],[2,6],[17,6],[18,6],[19,6],[20,6],[21,6],[22,6],[23,6],[24,6],[3,6],[25,6],[26,6],[4,6],[27,6],[31,6],[28,6],[29,6],[30,6],[32,6],[33,6],[34,6],[5,6],[35,6],[36,6],[37,6],[38,6],[6,6],[42,6],[39,6],[40,6],[41,6],[43,6],[7,6],[44,6],[49,6],[50,6],[45,6],[46,6],[47,6],[48,6],[8,6],[54,6],[51,6],[52,6],[53,6],[55,6],[9,6],[56,6],[57,6],[58,6],[60,6],[59,6],[61,6],[62,6],[10,6],[63,6],[64,6],[65,6],[11,6],[66,6],[67,6],[68,6],[69,6],[70,6],[1,6],[71,6],[72,6],[12,6],[76,6],[74,6],[79,6],[78,6],[73,6],[77,6],[75,6],[80,6],[120,380],[130,381],[119,380],[140,382],[111,383],[110,384],[139,360],[133,385],[138,386],[113,387],[127,388],[112,389],[136,390],[108,391],[107,360],[137,392],[109,393],[114,394],[115,6],[118,394],[105,6],[141,395],[131,396],[122,397],[123,398],[125,399],[121,400],[124,401],[134,360],[116,402],[117,403],[126,404],[106,405],[129,396],[128,394],[132,6],[135,406],[527,407]],"affectedFilesPendingEmit":[739,737,738,740,736,717,718,719,720,721,722,723,724,725,726,727,714,716,728,729,732,733,734,731,713,730,715,689,711,709,710,712,528,671,502,527],"version":"5.9.3"} \ No newline at end of file