Skip to content

Commit ecdfe67

Browse files
committed
fixing build, bumping versions, updating release history for web4 api, adding ONODE client, manager and service (part of the new ONODE/ONET upgrades)
1 parent dc6a1a1 commit ecdfe67

7 files changed

Lines changed: 1086 additions & 9 deletions

File tree

Docs/Devs/OASIS-HyperDrive-Client-Design-Spec.md

Lines changed: 606 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 352 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,352 @@
1+
# OASIS HyperDrive Client — Implementation Reference
2+
3+
**Version:** 1.0
4+
**Date:** 2026-07-17
5+
**Status:** Complete (Phase 1 + Phase 2)
6+
**Design Spec:** [OASIS-HyperDrive-Client-Design-Spec.md](./OASIS-HyperDrive-Client-Design-Spec.md)
7+
**Repository:** `C:\Source\OasisHyperDriveClient`
8+
9+
---
10+
11+
## Table of Contents
12+
13+
1. [Implementation Status](#1-implementation-status)
14+
2. [Solution Layout](#2-solution-layout)
15+
3. [Core Library — OasisHyperDriveClient.Core](#3-core-library)
16+
4. [UI Project — OasisHyperDriveClient](#4-ui-project)
17+
5. [Key Implementation Decisions](#5-key-implementation-decisions)
18+
6. [Build & Run](#6-build--run)
19+
7. [Testing](#7-testing)
20+
8. [Known Gaps / Phase 3 Work](#8-known-gaps--phase-3-work)
21+
22+
---
23+
24+
## 1. Implementation Status
25+
26+
### Phase 1 — Complete
27+
28+
| Feature | Status |
29+
|---|---|
30+
| Avalonia project scaffold, system tray | Done |
31+
| Neon-O icon via SkiaSharp, colour states | Done |
32+
| Login window + JWT auth | Done |
33+
| File browser (DataGrid, sidebar, provider filter) | Done |
34+
| Load holons via `api/data/load-all-holons` | Done |
35+
| Provider filter dropdown | Done |
36+
| Rename dialog | Done |
37+
| Delete (soft + hard) with confirmation dialog | Done |
38+
| Metadata viewer window | Done |
39+
| 30-second dashboard poll → tray state updates | Done |
40+
| Windows + macOS + Linux builds | Done |
41+
42+
### Phase 2 — Complete
43+
44+
| Feature | Status |
45+
|---|---|
46+
| Upload files (file picker + `api/data/save-file`) | Done |
47+
| Download files (`api/data/load-file` + save picker) | Done |
48+
| Send to Avatar dialog (avatar search + transfer) | Done |
49+
| Right-click context menu on file list | Done |
50+
| Toolbar commands fully wired | Done |
51+
| OS toast notifications (`WindowNotificationManager`) | Done |
52+
| HyperDrive dashboard window | Done |
53+
| Settings window (all prefs, notification toggles) | Done |
54+
| Auto-start on login (Windows / macOS / Linux) | Done |
55+
| Dynamic provider status bar | Done |
56+
| SkiaSharp tray icon renderer (per-state neon-O) | Done |
57+
| Unit tests (17 passing) | Done |
58+
| Cross-platform build scripts | Done |
59+
60+
---
61+
62+
## 2. Solution Layout
63+
64+
```
65+
OasisHyperDriveClient/
66+
├── OasisHyperDriveClient.slnx
67+
├── build-win.ps1
68+
├── build-linux.sh
69+
├── build-mac.sh
70+
├── src/
71+
│ ├── OasisHyperDriveClient.Core/ (.NET 10 class library)
72+
│ └── OasisHyperDriveClient/ (.NET 10 Avalonia WinExe)
73+
└── tests/
74+
└── OasisHyperDriveClient.Tests/ (.NET 10 xUnit)
75+
```
76+
77+
**NuGet packages (UI project):**
78+
79+
| Package | Version |
80+
|---|---|
81+
| Avalonia | 12.1.0 |
82+
| Avalonia.Desktop | 12.1.0 |
83+
| Avalonia.Controls.DataGrid | 12.1.0 |
84+
| Avalonia.Themes.Fluent | 12.1.0 |
85+
| Avalonia.Fonts.Inter | 12.1.0 |
86+
| Avalonia.ReactiveUI | 11.3.8 |
87+
| ReactiveUI | 23.2.28 |
88+
| SkiaSharp | latest stable |
89+
| Microsoft.Extensions.Hosting | 10.0.10 |
90+
| Serilog.Sinks.File + Console | latest |
91+
92+
> **Note:** `Avalonia.ReactiveUI` uses its own version numbering independent of Avalonia itself. `11.3.8` is the correct version for Avalonia `12.1.0`.
93+
94+
---
95+
96+
## 3. Core Library
97+
98+
### `OasisHyperDriveClient.Core`
99+
100+
All models, API clients, and services that could be shared with other apps (e.g. ONODE Manager).
101+
102+
#### Models
103+
104+
| File | Contents |
105+
|---|---|
106+
| `Models/Holon.cs` | `Holon` (API model) + `HolonViewModel` (display model with `DisplayIcon`, `SizeDisplay`, `FromHolon` factory) |
107+
| `Models/Avatar.cs` | `AvatarInfo`, `AuthenticateRequest`, `AuthenticateResponse` |
108+
| `Models/TrayState.cs` | `TrayState` enum (Disabled, Connecting, Healthy, Degraded, Error, Syncing, Busy) + `TrayStateInfo` |
109+
| `Models/HyperDriveDashboard.cs` | `DashboardData`, `PerformanceMetrics`, `CostMetrics`, `DashboardAlert`, `ProviderPerformanceMetrics`, `HyperDriveConfig` |
110+
| `Models/OASISResult.cs` | `OASISResult<T>` — generic API result with `IsError`, `IsWarning`, `Message`, `ErrorCode`, `Result` |
111+
112+
#### API Services
113+
114+
| File | Key Methods |
115+
|---|---|
116+
| `Api/OasisApiClient.cs` | `SetBearerToken`, `ClearToken`, `GetAsync<T>`, `PostAsync<T>`, `DeleteAsync<T>` |
117+
| `Api/DataService.cs` | `LoadAllHolonsAsync`, `LoadHolonAsync`, `LoadFileAsync`, `SaveFileAsync`, `SaveHolonAsync`, `DeleteHolonAsync`, `LoadHolonsForParentAsync` |
118+
| `Api/HyperDriveService.cs` | `GetDashboardAsync`, `GetMetricsAsync`, `GetConfigurationAsync`, `EnableIntelligentModeAsync`, `DisableIntelligentModeAsync` |
119+
| `Api/AvatarService.cs` | `AuthenticateAsync`, `GetLoggedInAvatarAsync`, `SearchAvatarsAsync` |
120+
121+
#### Auth
122+
123+
| File | Purpose |
124+
|---|---|
125+
| `Auth/ICredentialStore.cs` | Interface: `SaveToken`, `LoadToken`, `ClearToken` |
126+
| `Auth/FileCredentialStore.cs` | Base64-encodes JWT to `%APPDATA%/OasisHyperDriveClient/.session` |
127+
| `Auth/AuthService.cs` | `TryRestoreSessionAsync` (reads stored JWT, validates via API), `LoginAsync` (authenticates + stores token), `Logout` |
128+
129+
#### Services
130+
131+
| File | Purpose |
132+
|---|---|
133+
| `Services/AppSettings.cs` | Settings POCO + static `Load()` / instance `Save()` to `%APPDATA%/.../settings.json` |
134+
| `Services/HyperDriveMonitorService.cs` | `BackgroundService` — polls `api/hyperDrive/dashboard` every N seconds; fires `StateChanged` and `AlertReceived` events |
135+
| `Services/IAutoStartService.cs` | Interface: `IsEnabled`, `Enable()`, `Disable()` |
136+
| `Services/INotificationService.cs` | Interface: `Show(title, message, level)` |
137+
138+
---
139+
140+
## 4. UI Project
141+
142+
### Startup Flow (`App.axaml.cs`)
143+
144+
1. `OnFrameworkInitializationCompleted``ShutdownMode.OnExplicitShutdown` (tray-only; no main window)
145+
2. `BuildServices()` — registers all DI services including platform-specific `IAutoStartService`
146+
3. `SetupTrayIcon()` — creates `TrayIcon`, subscribes to `TrayIconViewModel.CurrentState` → calls `TrayIconRenderer.Render(state)` to produce a live SkiaSharp PNG and sets `TrayIcon.Icon`
147+
4. `StartAsync()` — tries `TryRestoreSessionAsync`; on failure shows `LoginWindow` using `Show()` + `TaskCompletionSource<bool>` pattern (not `ShowDialog`, which requires a parent window that doesn't exist in tray-only mode)
148+
5. Starts `HyperDriveMonitorService` background polling
149+
150+
### Platform-Specific DI Registration
151+
152+
```csharp
153+
if (OperatingSystem.IsWindows())
154+
services.AddSingleton<IAutoStartService, WindowsAutoStartService>();
155+
else if (OperatingSystem.IsMacOS())
156+
services.AddSingleton<IAutoStartService, MacAutoStartService>();
157+
else
158+
services.AddSingleton<IAutoStartService, LinuxAutoStartService>();
159+
```
160+
161+
### Tray Icon Rendering (`Services/TrayIconRenderer.cs`)
162+
163+
Uses SkiaSharp to paint the neon-O at runtime:
164+
- Outer glow ring (blurred, semi-transparent) in the state colour
165+
- Solid inner ring
166+
- Inner dot for Connecting / Syncing / Busy states
167+
- No glow for Disabled state
168+
169+
No static PNG assets are needed. The icon is re-rendered on every `TrayState` change.
170+
171+
### Windows & Dialogs
172+
173+
| View | ViewModel | Purpose |
174+
|---|---|---|
175+
| `LoginWindow` | `LoginViewModel` | Email/password login; `LoginSucceeded` event drives startup |
176+
| `FileBrowserWindow` | `FileBrowserViewModel` | Main file browser; hidden on close (re-opened from tray) |
177+
| `MetadataWindow` | `MetadataViewModel` | Read-only holon metadata viewer |
178+
| `DashboardWindow` | `DashboardViewModel` | Live HyperDrive health dashboard |
179+
| `SettingsWindow` | `SettingsViewModel` | All app settings; saves to `AppSettings` on confirm |
180+
| `RenameDialog` | `RenameViewModel` | Single text field; fires `Confirmed(newName)` event |
181+
| `DeleteConfirmDialog` | `DeleteConfirmViewModel` | Soft / hard delete radio buttons |
182+
| `SendToAvatarDialog` | `SendToAvatarViewModel` | Avatar search list + send button |
183+
184+
### FileBrowserWindow — Event Wiring
185+
186+
`FileBrowserWindow.axaml.cs` subscribes to ViewModel events and handles dialogs:
187+
188+
```
189+
vm.ViewMetadataRequested → new MetadataWindow(item).ShowDialog(this)
190+
vm.RenameRequested → new RenameDialog(vm) + close on Confirmed
191+
vm.DeleteRequested → new DeleteConfirmDialog(vm) + close on Confirmed
192+
vm.SendToAvatarRequested → new SendToAvatarDialog(vm) + close on SendRequested
193+
vm.UploadRequested → StorageProvider.OpenFilePickerAsync → vm.UploadFilesAsync
194+
vm.DownloadRequested → StorageProvider.SaveFilePickerAsync → vm.DownloadAsync
195+
```
196+
197+
### FileBrowserViewModel — Key Behaviours
198+
199+
- `SelectedContentType` setter and `SelectedProvider` setter both immediately call `LoadItemsAsync()`
200+
- `ActiveProviderNames` collection is updated from `HyperDriveMonitorService.StateChanged`; bound to provider indicators in the status bar
201+
- `SelectContentTypeCommand` accepts a `string` parameter (the content type name), wired from sidebar `Button.CommandParameter`
202+
- Upload reads file bytes into memory, calls `DataService.SaveFileAsync` with the current avatar's `Id`
203+
- Download calls `DataService.LoadFileAsync` then writes bytes to the chosen `IStorageFile`
204+
- Rename replaces the list item with a fresh `HolonViewModel.FromHolon(saved)` (because `Name` is `init`-only)
205+
206+
### Notification Service (`Services/AvaloniaNotificationService.cs`)
207+
208+
`AvaloniaNotificationService` implements `INotificationService`. It must be attached to a `WindowNotificationManager` via `Attach(manager)` before use. This is done in `FileBrowserWindow.OnLoaded`:
209+
210+
```csharp
211+
var manager = new WindowNotificationManager(TopLevel.GetTopLevel(this)!)
212+
{
213+
Position = NotificationPosition.BottomRight,
214+
MaxItems = 3
215+
};
216+
_notifications.Attach(manager);
217+
```
218+
219+
Calls from any thread are marshalled to the UI thread via `Dispatcher.UIThread.Post`.
220+
221+
### Auto-Start Implementations
222+
223+
| Platform | File | Mechanism |
224+
|---|---|---|
225+
| Windows | `WindowsAutoStartService.cs` | `HKCU\Software\Microsoft\Windows\CurrentVersion\Run` via `Microsoft.Win32.Registry` |
226+
| macOS | `MacAutoStartService.cs` | `~/Library/LaunchAgents/com.oasis.hyperdrive.plist` |
227+
| Linux | `LinuxAutoStartService.cs` | `~/.config/autostart/oasis-hyperdrive.desktop` |
228+
229+
`WindowsAutoStartService` is annotated `[SupportedOSPlatform("windows")]` to suppress CA1416 cross-platform warnings.
230+
231+
---
232+
233+
## 5. Key Implementation Decisions
234+
235+
### Avalonia.ReactiveUI Version
236+
237+
`Avalonia.ReactiveUI` has its own version scheme. For Avalonia 12.1.0, the correct package is `Avalonia.ReactiveUI 11.3.8`. Using `12.x` will produce a NuGet resolution error.
238+
239+
### WhenAnyValue Ambiguity Fix
240+
241+
`WhenAnyValue` with an inline selector predicate is ambiguous between overloads. The fix is:
242+
243+
```csharp
244+
// Wrong — ambiguous:
245+
var canDo = this.WhenAnyValue(x => x.SelectedItem, x => x != null);
246+
247+
// Correct:
248+
var canDo = this.WhenAnyValue(x => x.SelectedItem).Select(x => x is not null);
249+
// Requires: using System.Reactive.Linq;
250+
```
251+
252+
### No MainWindow in Tray-Only Apps
253+
254+
`ShutdownMode.OnExplicitShutdown` means `MainWindow` is always `null`. `ShowDialog(null)` throws. The login window uses:
255+
256+
```csharp
257+
var tcs = new TaskCompletionSource<bool>();
258+
loginVm.LoginSucceeded += (_, _) => { tcs.TrySetResult(true); loginWin.Close(); };
259+
loginWin.Closed += (_, _) => tcs.TrySetResult(false);
260+
loginWin.Show();
261+
var loggedIn = await tcs.Task;
262+
```
263+
264+
### DataGrid Package
265+
266+
`DataGrid` is not in the core Avalonia package. Requires:
267+
1. `Avalonia.Controls.DataGrid` NuGet package
268+
2. `<StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml" />` in `App.axaml`
269+
3. `xmlns:dg="clr-namespace:Avalonia.Controls;assembly=Avalonia.Controls.DataGrid"` in views
270+
271+
### NativeMenu Compiled Bindings
272+
273+
`NativeMenu` commands use compiled bindings, which require `x:DataType` on the `<NativeMenu>` element. The `xmlns` for the ViewModel namespace must be on the root `<Application>` element, not the `<NativeMenu>`.
274+
275+
### HolonViewModel.Name is init-only
276+
277+
After a rename, a new `HolonViewModel` is created via `HolonViewModel.FromHolon(saved)` and the old item in `Items` is replaced by index. Mutating `Name` directly causes CS8852.
278+
279+
---
280+
281+
## 6. Build & Run
282+
283+
### Development
284+
285+
```bash
286+
# Build all projects
287+
cd C:\Source\OasisHyperDriveClient
288+
dotnet build
289+
290+
# Run the app
291+
dotnet run --project src/OasisHyperDriveClient/OasisHyperDriveClient.csproj
292+
293+
# Override API URL for local ONODE
294+
$env:OASIS_API_URL = "http://localhost:5000"
295+
dotnet run --project src/OasisHyperDriveClient/OasisHyperDriveClient.csproj
296+
```
297+
298+
### Release Builds
299+
300+
```powershell
301+
# Windows x64
302+
.\build-win.ps1 -Version 1.0.0
303+
# → dist\win\OasisHyperDriveClient.exe
304+
305+
# Linux x64
306+
bash build-linux.sh 1.0.0
307+
# → dist/linux/OasisHyperDriveClient
308+
309+
# macOS (both architectures)
310+
bash build-mac.sh 1.0.0
311+
# → dist/mac-x64/ and dist/mac-arm64/
312+
```
313+
314+
All builds use `--self-contained true -p:PublishSingleFile=true`.
315+
316+
---
317+
318+
## 7. Testing
319+
320+
```bash
321+
cd C:\Source\OasisHyperDriveClient
322+
dotnet test tests/OasisHyperDriveClient.Tests/
323+
```
324+
325+
**17 tests across 3 files:**
326+
327+
| Test File | What It Tests |
328+
|---|---|
329+
| `HyperDriveMonitorServiceTests.cs` | `TrayState` logic — Healthy / Degraded / Error / Warning alert cases |
330+
| `HolonViewModelTests.cs` | `DisplayIcon` emoji mapping, `SizeDisplay` formatting, `FromHolon` field mapping, default HolonType |
331+
| `AppSettingsTests.cs` | Default values for `AppSettings` and `NotificationSettings` |
332+
333+
Tests use xUnit and NSubstitute. No UI tests (Avalonia headless mode is a Phase 3 addition).
334+
335+
---
336+
337+
## 8. Known Gaps / Phase 3 Work
338+
339+
The following items from the design spec are not yet implemented:
340+
341+
| Item | Notes |
342+
|---|---|
343+
| Polly HTTP retry / circuit breaker | `OasisApiClient` makes bare calls. Retry policy is wired at `AddHttpClient` level but the circuit breaker is not yet configured |
344+
| OS Keychain credential store | `FileCredentialStore` (base64 file) is used. Windows Credential Manager / macOS Keychain / Linux libsecret implementations are planned |
345+
| Grid / icon view for NFTs | List view only. Toggle buttons exist in the spec but are not wired |
346+
| Version history viewer | `PreviousVersionId` chain is not traversed in the UI |
347+
| Drag-and-drop upload | Upload is via file picker only; drop target on `FileBrowserWindow` is not implemented |
348+
| "View on Provider" context menu item | Placeholder in spec; not implemented |
349+
| Breadcrumb navigation / Back / Forward | Navigation history stack is not implemented; sidebar type switching works |
350+
| Velopack installer + auto-update | `dotnet publish` single-file only; Velopack is planned for Phase 3 |
351+
| Certificate pinning | Settings UI placeholder; not yet wired to `HttpClientHandler` |
352+
| AI recommendations display | `HyperDriveService` would need a `GetRecommendationsAsync` method; endpoint not yet mapped |

OASIS Architecture/NextGenSoftware.OASIS.OASISBootLoader/OASISBootLoader.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,20 +59,20 @@ namespace NextGenSoftware.OASIS.OASISBootLoader
5959
public static class OASISBootLoader
6060
{
6161
//private static string _OASISVersion = null;
62-
private const string SYSTEM_EMAIL = "anorak@oasisweb4.com";
62+
private const string SYSTEM_EMAIL = "anorak@oasisomniverse.one";
6363
public static bool IsOASISBooted { get; private set; } = false;
6464
public static bool IsOASISBooting { get; private set; } = false;
6565

6666
public delegate void OASISBootLoaderError(object sender, OASISErrorEventArgs e);
6767
public static event OASISBootLoaderError OnOASISBootLoaderError;
6868

69-
public static string OASISRuntimeVersion { get; set; } = "4.5.1";
70-
public static string OASISAPIVersion { get; set; } = "4.6.0";
71-
public static string COSMICVersion { get; set; } = "2.1.1";
72-
public static string STARODKVersion { get; set; } = "3.5.0";
73-
public static string STARRuntimeVersion { get; set; } = "3.5.0";
74-
public static string STARNETVersion { get; set; } = "2.4.0";
75-
public static string STARAPIVersion { get; set; } = "1.3.0";
69+
public static string OASISRuntimeVersion { get; set; } = "5.0.0";
70+
public static string OASISAPIVersion { get; set; } = "5.0.0";
71+
public static string COSMICVersion { get; set; } = "2.2.2";
72+
public static string STARODKVersion { get; set; } = "4.0.0";
73+
public static string STARRuntimeVersion { get; set; } = "4.0.0";
74+
public static string STARNETVersion { get; set; } = "2.5.0";
75+
public static string STARAPIVersion { get; set; } = "1.4.0";
7676

7777
public static string DotNetVersion
7878
{

ONODE/NextGenSoftware.OASIS.API.ONODE.WebAPI/Hubs/ONODEWebSocketHub.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using WS = System.Net.WebSockets.WebSocket;
99
using WebSocketMessageType = System.Net.WebSockets.WebSocketMessageType;
1010
using WebSocketState = System.Net.WebSockets.WebSocketState;
11+
using System.Threading.Tasks;
1112

1213
namespace NextGenSoftware.OASIS.API.ONODE.WebAPI.Hubs;
1314

0 commit comments

Comments
 (0)