/** * `unsupported` covers an unpackaged build and a repository with no published releases, which is * the normal state for a fork. It is not an error the user needs to act on. Automatic checks stay * frozen after an empty feed; a manual check may retry when the install itself supports updates. */ export type DesktopUpdatePhase = | "idle" | "checking" | "available" | "downloading" | "ready" | "unsupported" | "error"; export interface DesktopUpdateState { phase: DesktopUpdatePhase; /** The installed desktop release, which can drift from the server this app points at. */ currentVersion: string; availableVersion: string | null; /** Download progress 0-100, only while `downloading`. */ percent: number | null; message: string | null; checkedAt: string | null; } export interface RakazoDesktopUpdate { state: () => Promise; check: () => Promise; download: () => Promise; /** Quits and relaunches into the downloaded release; only useful once `phase` is `ready`. */ install: () => Promise; } export interface RakazoDesktopOAuthCallback { code: string; state?: string; } export interface RakazoDesktopBrowserPreviewState { url: string; devServerRunning: boolean; } /** Optional desktop bridge for the in-app browser preview panel. Absent in the browser. */ export interface RakazoDesktopBrowserPreview { getState: () => Promise; navigate: (url: string) => Promise; reload: () => Promise; onNavigate: (listener: (url: string) => void) => () => void; onLoadingProgress: (listener: (progress: number) => void) => () => void; devServer: { start: () => Promise; stop: () => Promise; }; } export interface RakazoDesktop { /** Only the isolated local settings window is authorized to call this bridge. */ localSettings?: { request: (pathname: string, body: string) => Promise<{ status: number; body: string }>; }; files?: { /** Opens a native folder picker and returns an absolute host path, or null when cancelled. */ pickDirectory: () => Promise; }; platform: string; window: { close: () => Promise; minimize: () => Promise; toggleMaximize: () => Promise; state: () => Promise<{ minimized: boolean; maximized: boolean; fullScreen: boolean }>; }; update: RakazoDesktopUpdate; oauth: { /** * Open system-browser auth. A redirect_uri must be HTTP loopback with state; * URLs without a redirect use backend polling. Optional for older desktops. */ open?: (authorizationUrl: string) => Promise; cancel?: (authorizationUrl: string) => Promise; /** * Authorization codes captured from the system browser or a legacy popup. * Returns an unsubscribe function. */ onCallback: (listener: (callback: RakazoDesktopOAuthCallback) => void) => () => void; }; /** Present only when the desktop shell hosts an embedded browser preview. */ browserPreview?: RakazoDesktopBrowserPreview; } /** * How the desktop app was pointed at a Rakazo server during first-run setup. * `new` is the Docker Compose stack this app installs and runs on the same computer. */ export type DesktopInstanceMode = "new" | "existing"; export interface DesktopSetup { mode: DesktopInstanceMode; serverUrl: string; } export interface DesktopSetupState { defaultLocalUrl: string; saved: DesktopSetup | null; /** Present when a saved or newly selected server could not be reopened. */ error?: string; } export interface DesktopReachability { ok: boolean; /** HTTP status when the server answered, absent when it could not be reached. */ status?: number; /** Normalized URL that was probed, absent when the input was not a usable URL. */ url?: string; error?: string; } export interface DesktopStackProbeResponse { ok: true; imageTag: string; } /** * Lifecycle of the Docker Compose stack the desktop app manages for mode `new`. * `docker-missing` and `docker-not-running` wait for the person to act; `ready` and * `failed` are terminal until the next start. */ export type DesktopLocalStackPhase = | "idle" | "checking-docker" | "docker-missing" | "docker-not-running" | "preparing" | "pulling" | "starting" | "waiting-healthy" | "ready" | "failed"; export interface DesktopLocalStackState { phase: DesktopLocalStackPhase; /** One actionable sentence; null while the stack is progressing normally. */ message: string | null; /** Bounded tail of docker output for the current attempt. */ output: string[]; /** Bytes pulled per image layer in the current attempt; the sum is the only progress Docker reports. */ layerBytes: Record; /** Image tag this app launches (`v` for installed builds, `edge` otherwise). */ imageTag: string; } export type DesktopSetupLink = "docker-desktop" | "orbstack" | "docker-engine"; /** * Bridge exposed only to the first-run setup window. The app window keeps the * narrower `rakazoDesktop` bridge so a connected server can never re-point the app. */ export interface RakazoSetup { /** Used only to reserve space for native window controls in the local setup UI. */ platform: string; state: () => Promise; test: (url: string) => Promise; save: (setup: DesktopSetup) => Promise<{ ok: boolean; error?: string }>; quit: () => Promise; /** Opens one of the Docker install pages in the system browser. */ openLink: (link: DesktopSetupLink) => Promise; /** The Docker Compose stack this app installs and runs for mode `new`. */ stack: { state: () => Promise; /** Starts (or retries) the stack; a no-op while a start is already in flight. */ start: () => Promise; /** Fires on every state change so progress never depends on a renderer timer. */ onChange: (listener: (state: DesktopLocalStackState) => void) => void; }; }