Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Docs/ASLM/content/docs/ASLM/MauiProgram.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ draft: false
---

## Public methods

#### `public static MauiApp CreateMauiApp()`

### Builder configuration
Expand All @@ -24,7 +25,7 @@ draft: false

### Singleton services

`AppDataStore`, `DockerService`, `EngineInstaller`, `ModuleEnvironmentResolver`, `ModuleTrustService`, `ModuleInstaller`, `ModuleConsoleStore`, `ProcessSnapshotReader`, `ProcessTracker`, `ModuleThemePayloadBuilder`, `ModuleLocalePayloadBuilder`, `AppLocalizationService`, `ModuleInteropHostState`, `ModuleStartThrottle`, `PortRegistry`, `ModuleRunner`, `ModuleDownloadBridge`, `DownloadStateStore`, `DownloadCatalog`, `DownloadInstaller`, `NotificationCenter`, `OllamaSettingsStore`, `GitHubUpdateClient`, `UpdateManager`, `UpdateScheduler`, `ModuleLaunchCoordinator`, `AslmModuleInteropServer`, `AslmApiServer`, `SettingsService`, `CustomThemesStore`, `ThemeService`.
`AppDataStore`, `DockerService`, `EngineInstaller`, `ModuleEnvironmentResolver`, `ModuleTrustService`, `ModuleInstaller`, `ModuleConsoleStore`, `ProcessSnapshotReader`, `ProcessTracker`, `ModuleThemePayloadBuilder`, `ModuleLocalePayloadBuilder`, `AppLocalizationService`, `ModuleInteropHostState`, `ModuleStartThrottle`, `PortRegistry`, `ModuleRunner`, `ModuleDownloadBridge`, `DownloadStateStore`, `DownloadCatalog`, `DownloadInstaller`, `NotificationCenter`, `OllamaSettingsStore`, `GitHubRateLimitStore`, `GitHubUpdateClient`, `UpdateManager`, `UpdateScheduler`, `ModuleLaunchCoordinator`, `AslmModuleInteropServer`, `AslmApiServer`, `SettingsService`, `CustomThemesStore`, `ThemeService`.

### Transient UI

Expand Down
126 changes: 126 additions & 0 deletions Docs/ASLM/content/docs/ASLM/Models/GitHubRateLimitModels.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
---
title: "GitHubRateLimitModels"
draft: false
---

## Classes in `ASLM/Models/GitHubRateLimitModels.cs`

This file contains models used for storing and persisting GitHub API rate limit data and request records.

---

## Static class `GitHubRequestSources`

**Purpose:** Known GitHub request source values persisted with each API call record.

| Constant | Value |
| --- | --- |
| `Auto` | `"auto"` |
| `Manual` | `"manual"` |

---

## Static class `GitHubRequestTypes`

**Purpose:** Known GitHub request type values persisted with each API call record.

| Constant | Value |
| --- | --- |
| `Releases` | `"releases"` |
| `Branches` | `"branches"` |
| `Download` | `"download"` |
| `RateLimit` | `"rate_limit"` |

---

## Class `GitHubRateLimitData`

**Purpose:** Stores persisted GitHub API usage for rate-limit budgeting across restarts. **`public sealed`**.

**Example Usage:**

```csharp
var data = new GitHubRateLimitData();
data.KnownLimit = 60;
data.KnownRemaining = 59;
data.ResetUtc = DateTime.UtcNow.AddHours(1).ToString("o");
data.Normalize();
```

### Properties

| Name | Type | Description |
| --- | --- | --- |
| `FileVersion` | `int` | Version of the file format, defaults to `1`. |
| `KnownLimit` | `int` | The most recently known rate limit, defaults to `60`. |
| `KnownRemaining` | `int` | Remaining requests in the current window, defaults to `60`. |
| `ResetUtc` | `string?` | ISO 8601 UTC timestamp of the next window reset. |
| `Requests` | `List<GitHubRequestRecord>` | Historical request records. |

### Methods

#### `public void Normalize()`

**Purpose:** Restores safe defaults and trims request history to the active rate-limit window. Clamps `KnownLimit` and `KnownRemaining`, prunes old requests, and bounds the history size.

#### `public void PruneRequestsOutsideCurrentWindow()`

**Purpose:** Removes request records that fall outside the current GitHub rate-limit window (resolved via `ResolveWindowStartUtc`).

#### `public void TrimRequestHistory()`

**Purpose:** Keeps the request history bounded to the configured GitHub rate limit (`KnownLimit`) by removing the oldest records.

#### `public DateTimeOffset ResolveWindowStartUtc()`

**Purpose:** Returns the UTC timestamp when the active GitHub rate-limit window started. Typically 1 hour before the `ResetUtc` timestamp.

---

## Class `GitHubRequestRecord`

**Purpose:** Describes one GitHub API request recorded for rate-limit planning. **`public sealed`**.

**Example Usage:**

```csharp
var record = new GitHubRequestRecord
{
Url = "https://api.github.com/repos/test/releases",
Type = GitHubRequestTypes.Releases,
Source = GitHubRequestSources.Auto,
StatusCode = 200
};
record.Normalize();
bool isAuto = record.IsAutoRequest();
```

### Properties

| Name | Type | Description |
| --- | --- | --- |
| `TimestampUtc` | `string` | ISO 8601 UTC timestamp of the request. |
| `Url` | `string` | URL requested. |
| `Type` | `string` | Request type (e.g., releases, branches, download). |
| `Source` | `string` | Source of the request (e.g., auto, manual). |
| `StatusCode` | `int?` | HTTP status code returned. |

### Methods

#### `public void Normalize()`

**Purpose:** Restores safe defaults after JSON deserialization. Trims strings and ensures valid values for timestamp, type, and source.

#### `public bool IsWithinWindow(DateTimeOffset windowStartUtc)`

**Purpose:** Returns whether this record belongs to the active GitHub rate-limit window.

- **Parameters:** `windowStartUtc` - the start timestamp of the window.

- **Returns:** `true` if `TimestampUtc` is after or equal to `windowStartUtc`.

#### `public bool IsAutoRequest()`

**Purpose:** Returns whether this record was initiated by automatic update checks.

- **Returns:** `true` if `Source` equals `GitHubRequestSources.Auto`.
19 changes: 2 additions & 17 deletions Docs/ASLM/content/docs/ASLM/Pages/AppShellPage.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ Root: **`Grid`** `ColumnDefinitions="Auto, *"`.
| 6 | `NavigateTo(HomeButton)` |
| 7 | Re-apply `ApplyAslmApiNavigationState()`, `ApplyConsoleNavigationState()` |
| 8 | `ScheduleEnsureModuleBrowserLeftToRight()` |
| 9 | Fire-and-forget `StartEnabledModulesAsync()`, `CheckStartupUpdatesAsync()` |
| 9 | Fire-and-forget `StartEnabledModulesAsync()` |

---

Expand Down Expand Up @@ -231,20 +231,6 @@ If not `_shellEventsHooked`, return. Otherwise unsubscribe the same events as **

---

#### `private async Task CheckStartupUpdatesAsync()

**Purpose:** Checks ASLM and modules for updates once after the main shell opens.

| Step | Action |
| --- | --- |
| 1 | `_appData.Data.Updates.Normalize()` |
| 2 | `autoUpdate = Updates.AutoUpdateEnabled`; `publishNotifications = !autoUpdate` |
| 3 | `CheckAllUpdatesAsync` on thread pool |
| 4 | If `autoUpdate` and updates found → `ApplyDiscoveredUpdatesAsync` on thread pool |
| — | On exception: debug log only (no user toast) |

---

## Module refresh

#### `private async Task RefreshModulesAsync()`
Expand Down Expand Up @@ -753,8 +739,7 @@ Resolves path via **`ResolveSidebarIconFile`**; **`PackagedIconTintCache.Get(pat
| `DownloadsButton` | `IconDownload` |
| `SettingsButton` | `IconSettings` |
| `ClassId="PAGE"` + `ModuleConfig` | `SidebarIconFullPath` if file exists, else `IconPage` |
| Other |
ull` |
| Other | `null` |

---

Expand Down
8 changes: 5 additions & 3 deletions Docs/ASLM/content/docs/ASLM/Pages/LoadingPage.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Layout: centered `VerticalStackLayout` on `BackgroundPrimary`.

## Constructor

#### `LoadingPage(AppDataStore, NotificationCenter, UpdateScheduler, AslmApiServer, AslmModuleInteropServer, ThemeService, CustomThemesStore, ModuleTrustService, AppLocalizationService, IServiceProvider)`
#### `LoadingPage(AppDataStore, NotificationCenter, GitHubRateLimitStore, GitHubUpdateClient, UpdateScheduler, AslmApiServer, AslmModuleInteropServer, ThemeService, CustomThemesStore, ModuleTrustService, AppLocalizationService, IServiceProvider)`

**Purpose:** Calls `InitializeComponent()`, stores services, **`LocalizableAttach.Hook(this, _localization, this)`**.

Expand All @@ -60,8 +60,10 @@ Layout: centered `VerticalStackLayout` on `BackgroundPrimary`.
| 5 | `_notifications.InitializeAsync()` | background |
| 6 | `_apiServer.StartIfEnabledAsync()` | background |
| 7 | `_moduleInteropServer.EnsureStartedAsync()` | background |
| 8 | `_updateScheduler.Start()` | background |
| 9 | `_themeService.ApplyFromSettings()` | UI |
| 8 | `_rateLimitStore.InitializeAsync()` | background |
| 9 | `_githubUpdateClient.RefreshRateLimitAsync()` | background |
| 10 | `_updateScheduler.Start()` | background |
| 11 | `_themeService.ApplyFromSettings()` | UI |

**Navigation:**

Expand Down
3 changes: 2 additions & 1 deletion Docs/ASLM/content/docs/ASLM/Pages/SetupWizardPage.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ draft: false

| Name | Value |
| --- | --- |
| `TotalSteps` | `3` | Numbered steps (excludes welcome step `0`) |
| `TotalSteps` | `3` (Numbered steps, excludes welcome step `0`) |

---

Expand Down Expand Up @@ -82,6 +82,7 @@ Three-row grid: **header** | **content** | **footer**.
---

## Member reference

#### `private async void OnLoaded(object? sender, EventArgs e)`

**Purpose:** Once: **`PopulateModuleListAsync()`**, set **`_skipDockerStep`** from **`DockerService.IsCliInstalledAsync()`**.
Expand Down
108 changes: 108 additions & 0 deletions Docs/ASLM/content/docs/ASLM/Services/GitHubRateLimitStore.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
---
title: "GitHubRateLimitStore"
draft: false
---

## Class `GitHubRateLimitStore`

`ASLM/Services/GitHubRateLimitStore.cs` — **`public sealed`** — Loads and saves GitHub API usage in `Data/App/ASLM_GitHubRateLimit.json`. This service helps manage rate limit budgeting across application restarts.

**Example Usage:**

```csharp
// Store is typically injected via DI
public class UpdateService
{
private readonly GitHubRateLimitStore _rateLimitStore;

public UpdateService(GitHubRateLimitStore rateLimitStore)
{
_rateLimitStore = rateLimitStore;
}

public async Task CheckUpdatesAsync()
{
if (_rateLimitStore.CanMakeAutoRequest())
{
// Proceed with automatic update check
_rateLimitStore.RecordRequest("https://api.github.com/...", GitHubRequestTypes.Releases, GitHubRequestSources.Auto, 200);
}
else
{
var delay = _rateLimitStore.CalculateInterCheckDelay();
// Wait before next check
}
}
}
```

---

### Constructor

#### `public GitHubRateLimitStore(ILogger<GitHubRateLimitStore> logger)`

**Purpose:** Creates the store and resolves the persisted data file path.

- **Parameters:** `logger` - Logger instance for dependency injection.

---

### Properties

| Name | Type | Description |
| --- | --- | --- |
| `Data` | `GitHubRateLimitData` | Gets the current persisted GitHub rate-limit state. |

---

### Methods

#### `public Task InitializeAsync()`

**Purpose:** Initializes the store by loading persisted data once at startup.

#### `public Task LoadAsync()`

**Purpose:** Loads persisted GitHub usage data or recreates defaults when the file is missing or invalid.

#### `public void UpdateFromHeaders(int limit, int remaining, long resetEpochSeconds)`

**Purpose:** Updates the known GitHub rate-limit window from response headers and saves the data.

- **Parameters:**
- `limit` - The maximum requests allowed in the current window.
- `remaining` - The requests remaining in the current window.
- `resetEpochSeconds` - Unix epoch timestamp when the current window resets.

#### `public void RecordRequest(string url, string type, string source, int? statusCode)`

**Purpose:** Records one GitHub API request and persists the updated history.

- **Parameters:**
- `url` - The requested URL.
- `type` - The request type (e.g., releases, download).
- `source` - The request source (e.g., auto, manual).
- `statusCode` - The HTTP response status code.

#### `public bool CanMakeAutoRequest()`

**Purpose:** Returns whether automatic update checks still have budget in the current window.

- **Returns:** `true` if the count of automatic requests is less than half the known limit.

#### `public int GetAutoRequestsRemaining()`

**Purpose:** Returns how many automatic requests remain in the current window budget (half the total limit).

#### `public TimeSpan GetDelayUntilReset()`

**Purpose:** Returns the remaining time until the GitHub rate-limit window resets.

#### `public TimeSpan CalculateInterCheckDelay()`

**Purpose:** Calculates the delay before the next automatic update check request based on the remaining budget and time until reset. Returns a timespan between 5 seconds and 30 minutes.

#### `public Task SaveAsync()`

**Purpose:** Saves the current GitHub usage data asynchronously to disk.
Loading
Loading