client library

The app foundation: the OmnyShell service facade, state primitives, storage and routing.

Everything a browser app needs around the terminal, with no DOM framework and no app-specific coupling:

  • OmnyShellService — the only thing that touches omnyshell's ClientRuntime. It normalizes Hub URLs, translates exceptions into AppErrors, and exposes connect / nodes / sessions / shell operations. A ConnectionFactory can be injected to drive it against a fake Hub in tests.
  • Observable and AsyncState — the state model. Controllers own observables of immutable snapshots; screens subscribe and re-render their own subtree. AsyncState deliberately keeps data while loading or in error, so the UI can show last-known content under a spinner or a banner.
  • KeyValueStore / SettingsStore / NodeCache — persistence, behind an interface so the logic is testable on the VM. Pass your own prefix: localStorage is per-origin and one Hub can serve two apps, which would otherwise clobber each other's theme, Hub and token.
  • Router — hash-based (#/path), so the app deploys as static files with no server rewrites. matchRoute is pure and VM-testable.
  • ThemeController — light/dark/system, DOM-free (application is injected), so the pre-paint theme script can drive it without a flash.

The domain types themselves (NodeDescriptor, RemoteSession, Principal, …) come from package:omnyshell/omnyshell_client_web.dart. Note the split: the DOM-free half of this lives in foundation.dart, which this re-exports. A consuming app's service and controller layers should import that — it runs on the VM, so they stay unit-testable with dart test instead of needing headless Chrome. Only what actually touches the page (LocalStorageStore, Router) needs this library.

Classes

ActionResult
The outcome of a session action (kill/detach), surfaced to the UI as a toast.
AiSettingsController
Owns the user's AI preferences, persisting each change to SettingsStore and exposing the Hub's advertised default for the settings panel.
AsyncState<T>
An immutable snapshot of an asynchronous value with explicit loading/error states — the shape every data screen renders. Carries optional data even while LoadStatus.loading or LoadStatus.error so the UI can show last-known content (e.g. cached nodes) under a spinner or error banner.
AuthController
Orchestrates login/logout and session persistence between the OmnyShellService and the SettingsStore, exposing an observable AuthSnapshot for the UI.
AuthSnapshot
An immutable snapshot of auth state, rendered by the UI.
KeyValueStore
A minimal synchronous string key/value store. Abstracts localStorage so that storage-backed logic can be unit-tested with MemoryKeyValueStore without a browser.
LocalStorageStore
A KeyValueStore backed by the browser's window.localStorage.
MemoryKeyValueStore
An in-memory KeyValueStore for tests and non-browser contexts.
NodeCache
Caches the last-known node list so the nodes screen can paint instantly on reload while a fresh fetch runs. Backed by KeyValueStore; the cache is best-effort — any decode error yields null (treated as a cold cache).
NodesController
Loads and holds the discoverable node list, with cache-backed instant paint and explicit loading/error states.
Observable<T>
A minimal observable value: holds a current value and notifies listeners on change via a broadcast stream. The web app's lightweight alternative to a state-management framework — screens subscribe and re-render their DOM subtree when the value changes.
OmnyShellService
A thin facade over the OmnyShell ClientRuntime that the UI talks to.
PeekResult
A captured session screen, decoded to text for display.
RouteMatch
A matched route: the pattern that matched, the concrete path, and any extracted path params (e.g. /nodes/:id against /nodes/web-01 yields {id: web-01}).
Router
Hash-based router (#/path) — needs no server rewrites, so the app hosts as pure static files. Holds the current match observable and translates browser hashchange events into route updates. The matching logic lives in route_match.dart (DOM-free, unit-tested on the VM).
SessionsController
Loads and acts on the sessions of a single node: list, kill, detach, peek. Created per sessions-screen instance (sessions are per-node and ephemeral).
SettingsStore
Typed, namespaced access to persisted app settings over a KeyValueStore.
TerminalDisplayController
Owns the user's terminal display preferences — the dimension preset (and the custom column/row values it uses) and the text size — persisting each change and exposing them as observables the settings panel and session screen watch.
ThemeController
Owns the theme mode, persists it, and exposes the resolved theme that the UI applies as data-theme on the document root.

Enums

AppErrorKind
A user-facing error category, mapped from the lower-level OmnyShell exceptions so the UI can show a tailored message and recovery hint without switching on raw exception types everywhere.
AuthStatus
Where the user is in the authentication lifecycle.
LoadStatus
Where an async load currently stands.
ResolvedTheme
The concrete theme applied to the document: only light or dark.
ThemeMode
The user's theme preference.

Functions

hashToPath(String hash) String
Normalizes a URL hash (#/foo) to a path (/foo); empty hash → /.
matchRoute(List<String> patterns, String path) RouteMatch
Matches path against patterns, returning the first match. Patterns use :name segments for parameters. Pure and DOM-free.
relativeTime(DateTime time, DateTime now) String
Formats time as a compact relative age from now (e.g. 5m ago, 2h ago, 3d ago). Pure for unit testing — the live caller passes DateTime.now().
untilExpiry(DateTime expiresAt, DateTime now) String
Formats a Duration until expiry as in 5m / in 2h / expired.

Exceptions / Errors

AppError
A normalized error with a human-readable message and an optional recovery hint. Built from any thrown object via AppError.from.