2c0aee3979
ci / web (push) Successful in 1m11s
ci / docs-site (push) Successful in 1m13s
apple / swift (push) Successful in 1m22s
decky / build-publish (push) Successful in 32s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 44s
ci / bench (push) Successful in 6m40s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 11s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 15s
apple / screenshots (push) Successful in 6m26s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 9m52s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 8m57s
deb / build-publish (push) Successful in 13m13s
windows-host / package (push) Successful in 15m54s
arch / build-publish (push) Successful in 17m25s
android / android (push) Successful in 18m9s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 15m26s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 19m50s
ci / rust (push) Successful in 27m22s
docker / deploy-docs (push) Successful in 23s
Regenerated api/openapi.json (host built on Linux) with the plugins tag — registerPlugin/listPlugins/deregisterPlugin/getPluginUiCredential + the PluginRegistration/PluginSummary/PluginUi/PluginUiPublic/UiCredential schemas — and the SDK's generated client. Drift test + 44 mgmt/events host tests pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1317 lines
153 KiB
TypeScript
1317 lines
153 KiB
TypeScript
import * as Data from "effect/Data"
|
||
import * as Effect from "effect/Effect"
|
||
import type { SchemaError } from "effect/Schema"
|
||
import * as Schema from "effect/Schema"
|
||
import * as Stream from "effect/Stream"
|
||
import * as Sse from "effect/unstable/encoding/Sse"
|
||
import * as HttpClient from "effect/unstable/http/HttpClient"
|
||
import * as HttpClientError from "effect/unstable/http/HttpClientError"
|
||
import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest"
|
||
import * as HttpClientResponse from "effect/unstable/http/HttpClientResponse"
|
||
// non-recursive definitions
|
||
export type ApiCodec = "h264" | "hevc" | "av1" | "pyrowave"
|
||
export const ApiCodec = Schema.Literals(["h264", "hevc", "av1", "pyrowave"]).annotate({ "description": "Video codec identifier. The wire token matches the codec's canonical name used across the\nstack (SDP/GameStream advertisement, the stats-capture `CaptureMeta.codec`, and the encoder's\n[`Codec::label`]) — notably `H.265` serializes as `\"hevc\"`, not `\"h265\"`, so the same codec\nreads identically on every console page." })
|
||
export type ApiDisplayInfo = { readonly "backend": string, readonly "client"?: string | null, readonly "display_index": number, readonly "expires_in_ms"?: never, readonly "group": number, readonly "identity_slot"?: never, readonly "mode": string, readonly "sessions": number, readonly "slot": number, readonly "state": string, readonly "topology": string, readonly "x": number, readonly "y": number }
|
||
export const ApiDisplayInfo = Schema.Struct({ "backend": Schema.String.annotate({ "description": "Backend name (`pf-vdisplay`, `kwin`, …)." }), "client": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "Short client label, when the owner tracks it." })), "display_index": Schema.Number.annotate({ "description": "This display's ordinal within its group, in acquire order (0-based).", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "expires_in_ms": Schema.optionalKey(Schema.Never), "group": Schema.Number.annotate({ "description": "Display group (shared desktop) id — several displays with the same group form one desktop (§6A).", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "identity_slot": Schema.optionalKey(Schema.Never), "mode": Schema.String.annotate({ "description": "`WIDTHxHEIGHT@HZ`." }), "sessions": Schema.Number.annotate({ "description": "Live sessions holding the display.", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "slot": Schema.Number.annotate({ "description": "Stable-enough id for the `/display/release` `slot` argument.", "format": "int64" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "state": Schema.String.annotate({ "description": "`active` | `lingering` | `pinned`." }), "topology": Schema.String.annotate({ "description": "Effective topology for this display's group (`extend` | `primary` | `exclusive`)." }), "x": Schema.Number.annotate({ "description": "Desktop-space top-left `x` (auto-row or the console's manual arrangement, §6.2).", "format": "int32" }).check(Schema.isInt()), "y": Schema.Number.annotate({ "description": "Desktop-space top-left `y`.", "format": "int32" }).check(Schema.isInt()) }).annotate({ "description": "One live or kept virtual display." })
|
||
export type ApiError = { readonly "error": string }
|
||
export const ApiError = Schema.Struct({ "error": Schema.String }).annotate({ "description": "Error envelope for every non-2xx response." })
|
||
export type ApiGpu = { readonly "id": string, readonly "name": string, readonly "vendor": string, readonly "vram_mb": number }
|
||
export const ApiGpu = Schema.Struct({ "id": Schema.String.annotate({ "description": "Stable identifier (`vendorid-deviceid-occurrence`, hex PCI ids) — pass to `setGpuPreference`.\nStable across reboots and driver updates, unlike an adapter index or LUID." }), "name": Schema.String.annotate({ "description": "Adapter/marketing name." }), "vendor": Schema.String.annotate({ "description": "`nvidia` | `amd` | `intel` | `other`." }), "vram_mb": Schema.Number.annotate({ "description": "Dedicated VRAM in MiB (0 where the platform doesn't expose it).", "format": "int64" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)) }).annotate({ "description": "One hardware GPU on the host (software/WARP adapters are never listed)." })
|
||
export type ApprovePending = { readonly "name"?: string | null }
|
||
export const ApprovePending = Schema.Struct({ "name": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "Operator-chosen label for the device (defaults to the name it knocked with)." })) }).annotate({ "description": "Approve-pending-device request body. Send `{}` to keep the device's own name." })
|
||
export type ArmNativePairing = { readonly "fingerprint"?: string | null, readonly "ttl_secs"?: never }
|
||
export const ArmNativePairing = Schema.Struct({ "fingerprint": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "Optional: bind the window to ONE device fingerprint (hex SHA-256, e.g. from a pending knock).\nWhen set, only a pairing attempt from that fingerprint consumes the window — so an unpaired\nLAN peer can neither pair nor burn a window armed for a specific device (security-review #9).\nOmit for an unbound window (any device may use the PIN — trusted-LAN only)." })), "ttl_secs": Schema.optionalKey(Schema.Never) }).annotate({ "description": "Arm-native-pairing request body." })
|
||
export type Artwork = { readonly "header"?: string | null, readonly "hero"?: string | null, readonly "logo"?: string | null, readonly "portrait"?: string | null }
|
||
export const Artwork = Schema.Struct({ "header": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "Horizontal header (Steam `header.jpg`) — the universal fallback." })), "hero": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "Wide background (Steam `library_hero.jpg`)." })), "logo": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "Transparent title logo (Steam `logo.png`)." })), "portrait": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "Vertical capsule / poster (Steam `library_600x900.jpg`). Best for a grid." })) }).annotate({ "description": "Cover art for a title. All fields are URLs (the Steam CDN for Steam titles, user-supplied for\ncustom). The client prefers `portrait` for a grid and falls back to `header` when a title has\nno 600×900 capsule (common for older Steam apps)." })
|
||
export type AvailableCompositor = { readonly "available": boolean, readonly "default": boolean, readonly "id": string, readonly "label": string }
|
||
export const AvailableCompositor = Schema.Struct({ "available": Schema.Boolean.annotate({ "description": "Usable on this host right now: the live session's own compositor, or gamescope wherever\nits binary is installed." }), "default": Schema.Boolean.annotate({ "description": "True for the backend an `Auto` (unspecified) request resolves to right now." }), "id": Schema.String.annotate({ "description": "Stable identifier (`\"kwin\"` | `\"wlroots\"` | `\"mutter\"` | `\"gamescope\"`) — pass this to a\nclient's `--compositor` flag." }), "label": Schema.String.annotate({ "description": "Human-readable label for UIs." }) }).annotate({ "description": "A compositor backend the host can drive a virtual output on, and whether it's usable now." })
|
||
export type CaptureMeta = { readonly "client": string, readonly "codec": string, readonly "duration_ms": number, readonly "fps": number, readonly "height": number, readonly "id": string, readonly "kind": string, readonly "sample_count": number, readonly "started_unix_ms": number, readonly "width": number }
|
||
export const CaptureMeta = Schema.Struct({ "client": Schema.String.annotate({ "description": "Short label / fingerprint prefix, or `\"\"` if unknown." }), "codec": Schema.String.annotate({ "description": "`\"h264\" | \"hevc\" | \"av1\"`." }), "duration_ms": Schema.Number.annotate({ "format": "int64" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "fps": Schema.Number.annotate({ "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "height": Schema.Number.annotate({ "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "id": Schema.String.annotate({ "description": "e.g. `\"2026-06-26T20-14-03Z_5120x1440\"` — also the filename stem." }), "kind": Schema.String.annotate({ "description": "`\"native\" | \"gamestream\"`." }), "sample_count": Schema.Number.annotate({ "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "started_unix_ms": Schema.Number.annotate({ "format": "int64" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "width": Schema.Number.annotate({ "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)) }).annotate({ "description": "Capture summary — the filename stem plus the negotiated mode/codec/client. Stored at the head\nof each on-disk recording and listed standalone (without the sample body) by\n[`StatsRecorder::list`]." })
|
||
export type DisconnectReason = "quit" | "timeout" | "error"
|
||
export const DisconnectReason = Schema.Literals(["quit", "timeout", "error"]).annotate({ "description": "Why a client went away. `Quit` is a deliberate user \"stop\" (the typed close code);\n`Timeout` is a transport idle timeout (the client vanished); `Error` is everything else." })
|
||
export type GameSession = "auto" | "dedicated"
|
||
export const GameSession = Schema.Literals(["auto", "dedicated"]).annotate({ "description": "How a session that **launches a game** (a library id on the Hello / apps.json / Decky pin) is\nserved (`design/gamemode-and-dedicated-sessions.md` §5.2). Orthogonal to the preset/lifecycle axes\n— a top-level [`DisplayPolicy`] field, NOT part of [`EffectivePolicy`], so a preset never clobbers\nit. Linux-only in effect (a launching Windows session opens into the one desktop)." })
|
||
export type Health = { readonly "abi_version": number, readonly "status": string, readonly "version": string }
|
||
export const Health = Schema.Struct({ "abi_version": Schema.Number.annotate({ "description": "`punktfunk-core` C ABI version.", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "status": Schema.String.annotate({ "description": "Always `\"ok\"` when the host responds." }), "version": Schema.String.annotate({ "description": "`punktfunk-host` crate version." }) }).annotate({ "description": "Liveness + version probe." })
|
||
export type HookEntry = { readonly "debounce_ms"?: number, readonly "filter"?: null | { readonly "app"?: string | null, readonly "client"?: string | null, readonly "fingerprint"?: string | null, readonly "plane"?: null | "native" | "gamestream" }, readonly "hmac_secret_file"?: string | null, readonly "on": string, readonly "run"?: string | null, readonly "timeout_s"?: number, readonly "webhook"?: string | null }
|
||
export const HookEntry = Schema.Struct({ "debounce_ms": Schema.optionalKey(Schema.Number.annotate({ "description": "Minimum interval between firings of this hook, in milliseconds. 0 = fire every time.", "format": "int64" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0))), "filter": Schema.optionalKey(Schema.Union([Schema.Null, Schema.Struct({ "app": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "Launched app id/title (`stream.*` events)." })), "client": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "Client/device name (for `session.*`: the short client label the Dashboard shows)." })), "fingerprint": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "Certificate fingerprint (hex, case-insensitive)." })), "plane": Schema.optionalKey(Schema.Union([Schema.Null, Schema.Literals(["native", "gamestream"]).annotate({ "description": "Protocol plane (`native` / `gamestream`)." })], { mode: "oneOf" })) }).annotate({ "description": "Exact-match constraints on the event's fields; every present field must match." })], { mode: "oneOf" })), "hmac_secret_file": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "File holding the webhook HMAC secret (`X-Punktfunk-Signature: sha256=<hex>`). The file\nshould be operator-owned and private; a world-readable secret is warned about." })), "on": Schema.String.annotate({ "description": "Which events fire this hook: an exact kind (`stream.started`) or a `domain.*` prefix\n(`pairing.*`) — the same vocabulary as the SSE `?kinds=` filter." }), "run": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "Shell command to execute (detached, event JSON on stdin + `PF_EVENT_*` env)." })), "timeout_s": Schema.optionalKey(Schema.Number.annotate({ "description": "Exec timeout in seconds (1–600, default 30); the process group is killed on expiry.", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0))), "webhook": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "URL to POST the event JSON to." })) }).annotate({ "description": "One hook: fire `run` and/or `webhook` when an event matching `on` (+ `filter`) occurs." })
|
||
export type Identity = "shared" | "per-client" | "per-client-mode"
|
||
export const Identity = Schema.Literals(["shared", "per-client", "per-client-mode"]).annotate({ "description": "Stable display identity, so desktop environments persist per-display config (KDE scaling). Stored\nat Stage 0; carriers wired from the identity stage." })
|
||
export type KeepAlive = { readonly "mode": "off" } | { readonly "mode": "duration", readonly "seconds": number } | { readonly "mode": "forever" }
|
||
export const KeepAlive = Schema.Union([Schema.Struct({ "mode": Schema.Literal("off") }).annotate({ "description": "Tear the display down at session end (today's default on every backend but Windows, which\nlingers 10 s)." }), Schema.Struct({ "mode": Schema.Literal("duration"), "seconds": Schema.Number.annotate({ "description": "Linger window in seconds.", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)) }).annotate({ "description": "Keep the display for `seconds` after the last session leaves, then tear it down; a reconnect\ninside the window reuses it." }), Schema.Struct({ "mode": Schema.Literal("forever") }).annotate({ "description": "Keep the display until host shutdown or an explicit release (the `Pinned` lifecycle state).\n**Not honored until the display-lifecycle stage** — rejected by the mgmt PUT at Stage 0." })], { mode: "oneOf" }).annotate({ "description": "How long a virtual display (and, on gamescope's bare spawn, the nested session + its game)\nsurvives after the last client session detaches. Serialized as an object tagged on `mode`\n(`{\"mode\":\"off\"}` / `{\"mode\":\"duration\",\"seconds\":300}` / `{\"mode\":\"forever\"}`) so the web form\nand the OpenAPI schema stay simple." })
|
||
export type LaunchSpec = { readonly "kind": string, readonly "value": string }
|
||
export const LaunchSpec = Schema.Struct({ "kind": Schema.String.annotate({ "description": "`\"steam_appid\"` or `\"command\"`." }), "value": Schema.String.annotate({ "description": "The appid (for `steam_appid`) or the shell command (for `command`)." }) }).annotate({ "description": "How the host would launch a title (consumed by the session launcher in a later step). Kept\nopen-ended so new stores slot in: `steam_appid` → `steam steam://rungameid/<value>`;\n`command` → run `<value>` nested in a gamescope session." })
|
||
export type LayoutMode = "auto-row" | "manual"
|
||
export const LayoutMode = Schema.Literals(["auto-row", "manual"]).annotate({ "description": "How group members are arranged in the desktop coordinate space. Stored at Stage 0; applied from\nthe multi-monitor stage." })
|
||
export type LocalSummary = { readonly "audio_streaming": boolean, readonly "conflicts"?: ReadonlyArray<string>, readonly "kept_displays": number, readonly "native_paired_clients": number, readonly "paired_clients": number, readonly "pending_approvals": number, readonly "pin_pending": boolean, readonly "session"?: null | { readonly "fps": number, readonly "height": number, readonly "width": number }, readonly "version": string, readonly "video_streaming": boolean }
|
||
export const LocalSummary = Schema.Struct({ "audio_streaming": Schema.Boolean.annotate({ "description": "True while the audio stream thread is running." }), "conflicts": Schema.optionalKey(Schema.Array(Schema.String).annotate({ "description": "Other Moonlight-compatible hosts (Sunshine/Apollo/…) detected on this machine at startup —\nrunning one alongside Punktfunk is unsupported. Compact labels (e.g. `Sunshine (running)`);\nthe tray/console surface them so the clash is visible before pairing silently fails." })), "kept_displays": Schema.Number.annotate({ "description": "Virtual displays being KEPT with no live session — lingering (keep-alive window) or pinned\n(`keep_alive: forever`). Non-zero means a display (and, exclusive, your physical monitors) is\nheld; the tray surfaces it + a one-click release. Active (in-use) displays are not counted.", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "native_paired_clients": Schema.Number.annotate({ "description": "Number of paired native (punktfunk/1) devices.", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "paired_clients": Schema.Number.annotate({ "description": "Number of pinned (paired) GameStream client certificates.", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "pending_approvals": Schema.Number.annotate({ "description": "Native pairing knocks awaiting the operator's approval (count only).", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "pin_pending": Schema.Boolean.annotate({ "description": "True while a GameStream pairing handshake is parked waiting for the user's PIN." }), "session": Schema.optionalKey(Schema.Union([Schema.Null, Schema.Struct({ "fps": Schema.Number.annotate({ "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "height": Schema.Number.annotate({ "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "width": Schema.Number.annotate({ "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)) }).annotate({ "description": "The active launch session (set by Moonlight's `/launch`, cleared on cancel/stop)." })], { mode: "oneOf" })), "version": Schema.String.annotate({ "description": "Host version (mirrors `/health`)." }), "video_streaming": Schema.Boolean.annotate({ "description": "True while the video stream thread is running." }) }).annotate({ "description": "Non-sensitive host status for the local tray icon: counts and booleans only — no PIN values,\nno fingerprints, no device names. Served unauthenticated to LOOPBACK peers only (see\n`require_auth`): the bearer-token file is SYSTEM/Administrators-DACL'd on Windows, so the\nper-user tray process cannot authenticate — this narrow read-only route is its status source." })
|
||
export type LogEntry = { readonly "level": string, readonly "msg": string, readonly "seq": number, readonly "target": string, readonly "ts_ms": number }
|
||
export const LogEntry = Schema.Struct({ "level": Schema.String.annotate({ "description": "`ERROR` | `WARN` | `INFO` | `DEBUG` | `TRACE`." }), "msg": Schema.String.annotate({ "description": "The formatted message, structured fields appended as `key=value`." }), "seq": Schema.Number.annotate({ "description": "Monotonic sequence number (1-based) — pass the last one back as the `after` cursor.", "format": "int64" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "target": Schema.String.annotate({ "description": "The emitting module path (tracing target)." }), "ts_ms": Schema.Number.annotate({ "description": "Unix timestamp in milliseconds.", "format": "int64" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)) }).annotate({ "description": "One captured log event." })
|
||
export type ModeConflict = "separate" | "steal" | "join" | "reject"
|
||
export const ModeConflict = Schema.Literals(["separate", "steal", "join", "reject"]).annotate({ "description": "Admission when a *different* client connects while a display/session is already live and asks for\na different mode. Stored at Stage 0; enforced from the mode-conflict admission stage." })
|
||
export type NativeClient = { readonly "fingerprint": string, readonly "name": string }
|
||
export const NativeClient = Schema.Struct({ "fingerprint": Schema.String.annotate({ "description": "Hex SHA-256 of the client certificate — its stable id here." }), "name": Schema.String.annotate({ "description": "The name the client supplied when pairing." }) }).annotate({ "description": "A paired native (punktfunk/1) client." })
|
||
export type NativePairStatus = { readonly "armed": boolean, readonly "enabled": boolean, readonly "expires_in_secs"?: never, readonly "paired_clients": number, readonly "pin"?: string | null }
|
||
export const NativePairStatus = Schema.Struct({ "armed": Schema.Boolean.annotate({ "description": "True while a pairing window is open." }), "enabled": Schema.Boolean.annotate({ "description": "Whether the native host is running (the unified host started with `--native`)." }), "expires_in_secs": Schema.optionalKey(Schema.Never), "paired_clients": Schema.Number.annotate({ "description": "Number of paired native clients.", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "pin": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "The PIN to display while armed (null when disarmed)." })) }).annotate({ "description": "Native (punktfunk/1) pairing status. Unlike GameStream, the **host** mints the PIN (the SPAKE2\nceremony needs it client-side first), so the console **displays** `pin` for the user to enter on\ntheir device — armed on demand for a short window." })
|
||
export type PairedClient = { readonly "fingerprint": string, readonly "not_after_unix"?: never, readonly "not_before_unix"?: never, readonly "subject"?: string | null }
|
||
export const PairedClient = Schema.Struct({ "fingerprint": Schema.String.annotate({ "description": "Lowercase hex SHA-256 of the client certificate DER — the client's stable id here." }), "not_after_unix": Schema.optionalKey(Schema.Never), "not_before_unix": Schema.optionalKey(Schema.Never), "subject": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "Certificate subject (e.g. `CN=NVIDIA GameStream Client`), if the DER parses." })) }).annotate({ "description": "A paired (certificate-pinned) Moonlight client." })
|
||
export type PairingStatus = { readonly "pin_pending": boolean }
|
||
export const PairingStatus = Schema.Struct({ "pin_pending": Schema.Boolean.annotate({ "description": "True while a pairing handshake is parked waiting for the user's PIN." }) }).annotate({ "description": "Pairing-flow status." })
|
||
export type PendingDevice = { readonly "age_secs": number, readonly "fingerprint": string, readonly "id": number, readonly "name": string }
|
||
export const PendingDevice = Schema.Struct({ "age_secs": Schema.Number.annotate({ "description": "Seconds since the device last knocked.", "format": "int64" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "fingerprint": Schema.String.annotate({ "description": "Hex SHA-256 of the device's certificate — what approval pins." }), "id": Schema.Number.annotate({ "description": "Id to address approve/deny (per-process; entries expire after ~10 minutes).", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "name": Schema.String.annotate({ "description": "Best-effort device label (the client's own name, else fingerprint-derived)." }) }).annotate({ "description": "An unpaired device that tried to connect while the host requires pairing — awaiting\n**delegated approval** (approve it here instead of fetching the host PIN out of band)." })
|
||
export type Plane = "native" | "gamestream"
|
||
export const Plane = Schema.Literals(["native", "gamestream"]).annotate({ "description": "Which protocol plane an event originated from. Hooks and scripts filter on it — a hook\nthat fires for native clients but not Moonlight clients is a bug, not a v2 feature." })
|
||
export type PluginRegistration = { readonly "title": string, readonly "ui"?: null | { readonly "icon"?: string | null, readonly "port": number, readonly "secret": string }, readonly "version"?: string | null }
|
||
export const PluginRegistration = Schema.Struct({ "title": Schema.String.annotate({ "description": "Human-readable title for the console nav entry (1–64 chars; control chars stripped)." }), "ui": Schema.optionalKey(Schema.Union([Schema.Null, Schema.Struct({ "icon": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "Optional lucide icon name for the console nav entry (`^[a-z0-9-]{1,48}$`)." })), "port": Schema.Number.annotate({ "description": "The **loopback** port the plugin serves its UI on. The host and console only ever dial\n`127.0.0.1:<port>`; a registration can never carry a hostname.", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "secret": Schema.String.annotate({ "description": "Per-boot shared secret the console proxy must present (as `Authorization: Bearer`) on every\nrequest to the plugin's UI server. Rotated whenever the plugin restarts." }) }).annotate({ "description": "Present iff the plugin serves a UI surface. A registration with no `ui` is a liveness/phone-book\nentry only (e.g. a future runner-management listing) and grows no nav entry." })], { mode: "oneOf" })), "version": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "Optional plugin version, purely informational (≤32 chars)." })) }).annotate({ "description": "Register/renew body for `PUT /plugins/{id}`." })
|
||
export type PluginUiPublic = { readonly "icon"?: string | null, readonly "port": number }
|
||
export const PluginUiPublic = Schema.Struct({ "icon": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null])), "port": Schema.Number.annotate({ "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)) }).annotate({ "description": "The secret-free view of a plugin's UI surface — what [`list_plugins`] returns to the browser." })
|
||
export type PortMap = { readonly "audio": number, readonly "control": number, readonly "http": number, readonly "https": number, readonly "mgmt": number, readonly "rtsp": number, readonly "video": number }
|
||
export const PortMap = Schema.Struct({ "audio": Schema.Number.annotate({ "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "control": Schema.Number.annotate({ "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "http": Schema.Number.annotate({ "description": "nvhttp plain HTTP (serverinfo, pairing).", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "https": Schema.Number.annotate({ "description": "nvhttp mutual-TLS HTTPS (post-pairing).", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "mgmt": Schema.Number.annotate({ "description": "This management API.", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "rtsp": Schema.Number.annotate({ "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "video": Schema.Number.annotate({ "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)) }).annotate({ "description": "Every port a client integration may need (Moonlight derives the stream ports from the\nHTTP base; a control pane should not have to)." })
|
||
export type Position = { readonly "x": number, readonly "y": number }
|
||
export const Position = Schema.Struct({ "x": Schema.Number.annotate({ "format": "int32" }).check(Schema.isInt()), "y": Schema.Number.annotate({ "format": "int32" }).check(Schema.isInt()) }).annotate({ "description": "A desktop-space offset for a display (top-left origin)." })
|
||
export type PrepCmd = { readonly "do": string, readonly "undo"?: string | null }
|
||
export const PrepCmd = Schema.Struct({ "do": Schema.String.annotate({ "description": "Command run before launch. Same execution recipe and ownership checks as hook `run`\ncommands (event-less: stdin is empty JSON, env carries the `PF_APP_*` context)." }), "undo": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "Command run after the session ends. Skipped when its `do` failed (it never took effect)." })) }).annotate({ "description": "One per-app preparation step (RFC §6 — deliberate Sunshine `prep-cmd` parity): `do` runs\n**synchronously before the app launches** (an HDR toggle or a MangoHud env change must land\nfirst), `undo` runs at session end — reverse order across steps, best-effort, on every exit\npath including a crash-unwind (RAII via [`PrepGuard`])." })
|
||
export type Preset = "custom" | "default" | "gaming-rig" | "shared-desktop" | "hotdesk" | "workstation"
|
||
export const Preset = Schema.Literals(["custom", "default", "gaming-rig", "shared-desktop", "hotdesk", "workstation"]).annotate({ "description": "A named bundle of the fields below. `Custom` (the default) means the explicit fields rule; any\nother preset ignores the stored fields and expands to its own ([`DisplayPolicy::effective`])." })
|
||
export type ProviderRemoved = { readonly "removed": number }
|
||
export const ProviderRemoved = Schema.Struct({ "removed": Schema.Number.annotate({ "description": "How many entries the provider owned (and were removed)." }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)) }).annotate({ "description": "The count envelope a provider uninstall returns." })
|
||
export type ReleaseDisplayRequest = { readonly "slot"?: never }
|
||
export const ReleaseDisplayRequest = Schema.Struct({ "slot": Schema.optionalKey(Schema.Never) }).annotate({ "description": "Request body for `releaseDisplay`." })
|
||
export type ReleaseDisplayResult = { readonly "released": number }
|
||
export const ReleaseDisplayResult = Schema.Struct({ "released": Schema.Number.annotate({ "description": "Number of kept displays torn down." }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)) }).annotate({ "description": "Result of a `/display/release`." })
|
||
export type SessionRef = { readonly "client": string, readonly "hdr": boolean, readonly "id": number, readonly "mode": string }
|
||
export const SessionRef = Schema.Struct({ "client": Schema.String.annotate({ "description": "Short client label (cert-fingerprint prefix, or peer IP for an anonymous client)." }), "hdr": Schema.Boolean, "id": Schema.Number.annotate({ "description": "Host-local session id (unique within this host process).", "format": "int64" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "mode": Schema.String.annotate({ "description": "Negotiated mode, `WxH@Hz` (e.g. `\"3840x2160@120\"`)." }) }).annotate({ "description": "A live A/V session (the plane-neutral notion the Dashboard shows)." })
|
||
export type SetGpuPreference = { readonly "gpu_id"?: string | null, readonly "mode": string }
|
||
export const SetGpuPreference = Schema.Struct({ "gpu_id": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "Required when `mode` is `manual`: the stable `id` of a currently listed GPU\n(see `listGpus`)." })), "mode": Schema.String.annotate({ "description": "`auto` (env pin, else max dedicated VRAM — the default) or `manual`." }) }).annotate({ "description": "Request body for `setGpuPreference`." })
|
||
export type StageTiming = { readonly "name": string, readonly "p50_us": number, readonly "p99_us": number }
|
||
export const StageTiming = Schema.Struct({ "name": Schema.String.annotate({ "description": "`\"capture\" | \"submit\" | \"encode\" | \"packetize\" | \"send\"` (path-dependent)." }), "p50_us": Schema.Number.annotate({ "format": "float" }).check(Schema.isFinite()), "p99_us": Schema.Number.annotate({ "format": "float" }).check(Schema.isFinite()) }).annotate({ "description": "One pipeline stage's latency in an aggregation window (microseconds)." })
|
||
export type StatsStatus = { readonly "armed": boolean, readonly "elapsed_ms": number, readonly "kind": string, readonly "sample_count": number, readonly "started_unix_ms": number }
|
||
export const StatsStatus = Schema.Struct({ "armed": Schema.Boolean.annotate({ "description": "Capture currently running." }), "elapsed_ms": Schema.Number.annotate({ "description": "Host-measured elapsed time of the in-progress capture, in ms (`0` if idle). Computed from the\nhost's MONOTONIC clock, so a console can show elapsed time without subtracting `started_unix_ms`\nfrom its own (possibly skewed) wall clock.", "format": "int64" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "kind": Schema.String.annotate({ "description": "Path of the in-progress capture (`\"\"` if idle)." }), "sample_count": Schema.Number.annotate({ "description": "Samples in the in-progress capture.", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "started_unix_ms": Schema.Number.annotate({ "description": "Unix start time of the in-progress capture (`0` if idle).", "format": "int64" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)) }).annotate({ "description": "Snapshot of the in-progress capture for the management API." })
|
||
export type SubmitPin = { readonly "pin": string }
|
||
export const SubmitPin = Schema.Struct({ "pin": Schema.String.annotate({ "description": "1–16 ASCII digits (Moonlight shows 4)." }) }).annotate({ "description": "The PIN Moonlight displays during pairing." })
|
||
export type Topology = "auto" | "extend" | "primary" | "exclusive"
|
||
export const Topology = Schema.Literals(["auto", "extend", "primary", "exclusive"]).annotate({ "description": "What the host does to the box's display topology while managed virtual displays are up." })
|
||
export type UiCredential = { readonly "port": number, readonly "secret": string }
|
||
export const UiCredential = Schema.Struct({ "port": Schema.Number.annotate({ "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "secret": Schema.String }).annotate({ "description": "`GET /plugins/{id}/ui-credential` — the console proxy's server-side lookup (bearer + loopback).\nThis is the only endpoint that returns a secret; the console BFF denylists it from the browser." })
|
||
export type RuntimeStatus = { readonly "active_sessions": number, readonly "audio_streaming": boolean, readonly "paired_clients": number, readonly "pin_pending": boolean, readonly "session"?: null | { readonly "fps": number, readonly "height": number, readonly "width": number }, readonly "stream"?: null | { readonly "bitrate_kbps": number, readonly "codec": ApiCodec, readonly "fps": number, readonly "height": number, readonly "last_resize_ms"?: never, readonly "min_fec": number, readonly "packet_size": number, readonly "time_to_first_frame_ms"?: never, readonly "width": number }, readonly "video_streaming": boolean }
|
||
export const RuntimeStatus = Schema.Struct({ "active_sessions": Schema.Number.annotate({ "description": "Number of live streaming sessions across BOTH planes (GameStream + native punktfunk/1). The\nnative server admits concurrent sessions, so this can exceed 1; `session`/`stream` below\ndescribe a single representative session for the detail card.", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "audio_streaming": Schema.Boolean.annotate({ "description": "True while the audio stream thread is running." }), "paired_clients": Schema.Number.annotate({ "description": "Number of pinned (paired) client certificates.", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "pin_pending": Schema.Boolean.annotate({ "description": "True while a pairing handshake is parked waiting for the user's PIN\n(submit it via `POST /api/v1/pair/pin`)." }), "session": Schema.optionalKey(Schema.Union([Schema.Null, Schema.Struct({ "fps": Schema.Number.annotate({ "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "height": Schema.Number.annotate({ "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "width": Schema.Number.annotate({ "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)) }).annotate({ "description": "A representative active session. GameStream's launch (Moonlight `/launch`) when present, else\nthe first live native session. `null` when nothing is streaming." })], { mode: "oneOf" })), "stream": Schema.optionalKey(Schema.Union([Schema.Null, Schema.Struct({ "bitrate_kbps": Schema.Number.annotate({ "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "codec": ApiCodec, "fps": Schema.Number.annotate({ "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "height": Schema.Number.annotate({ "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "last_resize_ms": Schema.optionalKey(Schema.Never), "min_fec": Schema.Number.annotate({ "description": "Client's parity floor per FEC block (`minRequiredFecPackets`).", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "packet_size": Schema.Number.annotate({ "description": "Video payload size per packet (bytes).", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "time_to_first_frame_ms": Schema.optionalKey(Schema.Never), "width": Schema.Number.annotate({ "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)) }).annotate({ "description": "The active stream's parameters — RTSP-negotiated for GameStream, or the live native session's\nmode/codec/bitrate. `null` when nothing is streaming." })], { mode: "oneOf" })), "video_streaming": Schema.Boolean.annotate({ "description": "True while the video stream thread is running." }) }).annotate({ "description": "Live host status (changes as clients launch/end sessions)." })
|
||
export type DisplayStateResponse = { readonly "displays": ReadonlyArray<ApiDisplayInfo> }
|
||
export const DisplayStateResponse = Schema.Struct({ "displays": Schema.Array(ApiDisplayInfo) }).annotate({ "description": "The host's managed virtual displays right now." })
|
||
export type GpuState = { readonly "active"?: null | { readonly "backend": string, readonly "id": string, readonly "name": string, readonly "sessions": number, readonly "vendor": string }, readonly "env_override"?: string | null, readonly "gpus": ReadonlyArray<ApiGpu>, readonly "mode": string, readonly "preferred_available": boolean, readonly "preferred_id"?: string | null, readonly "preferred_name"?: string | null, readonly "selected"?: null | { readonly "id": string, readonly "name": string, readonly "source": string, readonly "vendor": string } }
|
||
export const GpuState = Schema.Struct({ "active": Schema.optionalKey(Schema.Union([Schema.Null, Schema.Struct({ "backend": Schema.String.annotate({ "description": "The encode backend in use (`nvenc` | `amf` | `qsv` | `vaapi` | `software`)." }), "id": Schema.String.annotate({ "description": "Stable id matching an entry of `gpus` (empty for the CPU/software encoder)." }), "name": Schema.String, "sessions": Schema.Number.annotate({ "description": "Number of live encode sessions on it.", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "vendor": Schema.String.annotate({ "description": "`nvidia` | `amd` | `intel` | `other`." }) }).annotate({ "description": "The GPU live sessions use right now (absent while nothing is streaming)." })], { mode: "oneOf" })), "env_override": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "`PUNKTFUNK_RENDER_ADAPTER` (the host.env pin), when set — it applies while `mode` is\n`auto`; a manual preference overrides it." })), "gpus": Schema.Array(ApiGpu).annotate({ "description": "The host's hardware GPUs." }), "mode": Schema.String.annotate({ "description": "`auto` or `manual`." }), "preferred_available": Schema.Boolean.annotate({ "description": "Whether the preferred GPU is currently present." }), "preferred_id": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "The manually preferred GPU's stable id, when one is stored (kept while `mode` is `auto` so\na console can offer returning to it). May reference a GPU that is currently absent." })), "preferred_name": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "The stored name of the preferred GPU (a usable label even when it is absent)." })), "selected": Schema.optionalKey(Schema.Union([Schema.Null, Schema.Struct({ "id": Schema.String, "name": Schema.String, "source": Schema.String.annotate({ "description": "Why this GPU was selected: `preference` (the manual choice), `env`\n(`PUNKTFUNK_RENDER_ADAPTER`), `auto` (max dedicated VRAM / platform default), or\n`preference_missing` (a manual choice is set but that GPU is absent — auto-selected\ninstead so the host keeps streaming)." }), "vendor": Schema.String.annotate({ "description": "`nvidia` | `amd` | `intel` | `other`." }) }).annotate({ "description": "The GPU the next session will use." })], { mode: "oneOf" })) }).annotate({ "description": "Full GPU-selection state for the console: inventory, the persisted preference, what the next\nsession will use, and what is in use right now." })
|
||
export type GameEntry = { readonly "art": Artwork, readonly "id": string, readonly "launch"?: null | { readonly "kind": string, readonly "value": string }, readonly "provider"?: string | null, readonly "store": string, readonly "title": string }
|
||
export const GameEntry = Schema.Struct({ "art": Artwork, "id": Schema.String.annotate({ "description": "Stable, store-qualified id: `steam:<appid>` or `custom:<id>`." }), "launch": Schema.optionalKey(Schema.Union([Schema.Null, Schema.Struct({ "kind": Schema.String.annotate({ "description": "`\"steam_appid\"` or `\"command\"`." }), "value": Schema.String.annotate({ "description": "The appid (for `steam_appid`) or the shell command (for `command`)." }) }).annotate({ "description": "How the host would launch it, when known." })], { mode: "oneOf" })), "provider": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "The external provider owning this entry (custom-store entries synced by a provider\nplugin, RFC §8) — `None` for installed-store titles and manual custom entries. The\nconsole uses it for attribution; `GET /library?provider=` filters on it." })), "store": Schema.String.annotate({ "description": "Which store surfaced it: `\"steam\"` or `\"custom\"`." }), "title": Schema.String }).annotate({ "description": "One title in the unified library, regardless of which store it came from." })
|
||
export type HooksConfig = { readonly "hooks"?: ReadonlyArray<HookEntry> }
|
||
export const HooksConfig = Schema.Struct({ "hooks": Schema.optionalKey(Schema.Array(HookEntry)) }).annotate({ "description": "The operator's hook configuration — the `hooks.json` document and the `/api/v1/hooks` body." })
|
||
export type LogPage = { readonly "dropped": boolean, readonly "entries": ReadonlyArray<LogEntry>, readonly "next": number }
|
||
export const LogPage = Schema.Struct({ "dropped": Schema.Boolean.annotate({ "description": "True when entries between `after` and the first returned one were already evicted." }), "entries": Schema.Array(LogEntry), "next": Schema.Number.annotate({ "description": "Cursor for the next poll (the last returned seq, or the request's `after` when empty).", "format": "int64" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)) }).annotate({ "description": "One poll's worth of log entries." })
|
||
export type ClientRef = { readonly "fingerprint"?: string | null, readonly "name": string, readonly "plane": Plane }
|
||
export const ClientRef = Schema.Struct({ "fingerprint": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "Hex SHA-256 certificate fingerprint, when the client presented one." })), "name": Schema.String.annotate({ "description": "Client-supplied device name; may be empty (an anonymous or compat-plane client)." }), "plane": Plane }).annotate({ "description": "The connecting/disconnecting client's identity." })
|
||
export type DeviceRef = { readonly "fingerprint": string, readonly "name": string, readonly "plane": Plane }
|
||
export const DeviceRef = Schema.Struct({ "fingerprint": Schema.String.annotate({ "description": "Hex certificate fingerprint." }), "name": Schema.String.annotate({ "description": "Sanitized device name (the pairing store's copy)." }), "plane": Plane }).annotate({ "description": "A device in the pairing flow." })
|
||
export type StreamRef = { readonly "app"?: string | null, readonly "client": string, readonly "hdr": boolean, readonly "mode": string, readonly "plane": Plane }
|
||
export const StreamRef = Schema.Struct({ "app": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "The launched app/title for this stream, when one was requested (store-qualified id on\nthe native plane, app title on the GameStream plane)." })), "client": Schema.String.annotate({ "description": "Client-supplied device name; may be empty." }), "hdr": Schema.Boolean, "mode": Schema.String.annotate({ "description": "Negotiated mode, `WxH@Hz`." }), "plane": Plane }).annotate({ "description": "A live video stream (what the stream marker file reflects)." })
|
||
export type PluginSummary = { readonly "id": string, readonly "title": string, readonly "ui"?: null | PluginUiPublic, readonly "version"?: string | null }
|
||
export const PluginSummary = Schema.Struct({ "id": Schema.String, "title": Schema.String, "ui": Schema.optionalKey(Schema.Union([Schema.Null, PluginUiPublic], { mode: "oneOf" })), "version": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null])) }).annotate({ "description": "One entry in `GET /plugins`. **Never carries the secret** — the browser learns a plugin exists\nand has a UI, nothing that lets it reach the plugin directly (it goes through the console proxy)." })
|
||
export type HostInfo = { readonly "abi_version": number, readonly "app_version": string, readonly "codecs": ReadonlyArray<ApiCodec>, readonly "gamestream": boolean, readonly "gfe_version": string, readonly "hostname": string, readonly "local_ip": string, readonly "ports": PortMap, readonly "uniqueid": string, readonly "version": string }
|
||
export const HostInfo = Schema.Struct({ "abi_version": Schema.Number.annotate({ "description": "`punktfunk-core` C ABI version.", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "app_version": Schema.String.annotate({ "description": "GameStream host version advertised to Moonlight clients." }), "codecs": Schema.Array(ApiCodec).annotate({ "description": "Codecs the host can encode (NVENC)." }), "gamestream": Schema.Boolean.annotate({ "description": "Whether the GameStream/Moonlight-compat planes are running (`--gamestream`). `false` on the\nsecure default (native punktfunk/1 only) — a console can hide Moonlight-only UI (e.g. the\nMoonlight PIN pairing card, which could never receive a PIN when this is `false`)." }), "gfe_version": Schema.String.annotate({ "description": "GFE version advertised to Moonlight clients." }), "hostname": Schema.String, "local_ip": Schema.String.annotate({ "description": "Best-effort primary LAN IP." }), "ports": PortMap, "uniqueid": Schema.String.annotate({ "description": "Stable per-host id (persisted across restarts), matched on pairing." }), "version": Schema.String.annotate({ "description": "`punktfunk-host` crate version." }) }).annotate({ "description": "Host identity and advertised capabilities (static for the life of the process)." })
|
||
export type DisplayLayoutRequest = { readonly "positions"?: { readonly [x: string]: Position } }
|
||
export const DisplayLayoutRequest = Schema.Struct({ "positions": Schema.optionalKey(Schema.Record(Schema.String, Position).annotate({ "description": "`{\"<identity_slot>\": {\"x\": …, \"y\": …}}` — where each arranged display's top-left sits." }).check(Schema.isPropertyNames(Schema.String))) }).annotate({ "description": "Request body for `setDisplayLayout`: per-identity-slot desktop offsets, keyed by the identity-slot\nid as a string (the same id `/display/state` reports as `identity_slot`)." })
|
||
export type Layout = { readonly "mode"?: LayoutMode, readonly "positions"?: { readonly [x: string]: Position } }
|
||
export const Layout = Schema.Struct({ "mode": Schema.optionalKey(LayoutMode), "positions": Schema.optionalKey(Schema.Record(Schema.String, Position).check(Schema.isPropertyNames(Schema.String))) }).annotate({ "description": "Group layout: the arrangement mode plus, for [`LayoutMode::Manual`], per-slot offsets keyed by\nidentity-slot id (string keys for stable JSON)." })
|
||
export type CustomEntry = { readonly "art"?: Artwork, readonly "external_id"?: string | null, readonly "id": string, readonly "launch"?: null | LaunchSpec, readonly "prep"?: ReadonlyArray<PrepCmd>, readonly "provider"?: string | null, readonly "title": string }
|
||
export const CustomEntry = Schema.Struct({ "art": Schema.optionalKey(Artwork), "external_id": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "The provider's own stable key for this title — the reconcile diff key, so the\nhost-assigned `id` stays stable across reconciles. Present iff `provider` is." })), "id": Schema.String.annotate({ "description": "Host-assigned, stable for the life of the entry (the `{id}` in the CRUD path)." }), "launch": Schema.optionalKey(Schema.Union([Schema.Null, LaunchSpec], { mode: "oneOf" })), "prep": Schema.optionalKey(Schema.Array(PrepCmd).annotate({ "description": "Per-title prep/undo steps (RFC §6): each `do` runs before this title launches, each\n`undo` at session end in reverse order (see [`crate::hooks::run_prep`])." })), "provider": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "The external provider owning this entry (RFC §8), set ONLY by the provider reconcile\nAPI — `None` = a manual entry, which no provider operation ever touches, and which the\nmanual CRUD alone may edit (the converse holds too: manual CRUD refuses provider-owned\nentries, so ownership is never ambiguous)." })), "title": Schema.String }).annotate({ "description": "A user-added title, persisted in the hardened host config dir's `library.json` (see\n[`custom_path`]). Same shape the API returns and the web console edits." })
|
||
export type CustomInput = { readonly "art"?: Artwork, readonly "launch"?: null | LaunchSpec, readonly "prep"?: ReadonlyArray<PrepCmd>, readonly "title": string }
|
||
export const CustomInput = Schema.Struct({ "art": Schema.optionalKey(Artwork), "launch": Schema.optionalKey(Schema.Union([Schema.Null, LaunchSpec], { mode: "oneOf" })), "prep": Schema.optionalKey(Schema.Array(PrepCmd).annotate({ "description": "Per-title prep/undo steps — commands run as the host user; operator-privileged config." })), "title": Schema.String }).annotate({ "description": "Request body to create or replace a custom entry (no `id` — the host owns it)." })
|
||
export type ProviderEntryInput = { readonly "art"?: Artwork, readonly "external_id": string, readonly "launch"?: null | LaunchSpec, readonly "prep"?: ReadonlyArray<PrepCmd>, readonly "title": string }
|
||
export const ProviderEntryInput = Schema.Struct({ "art": Schema.optionalKey(Artwork), "external_id": Schema.String.annotate({ "description": "The provider's stable id for this title (the reconcile diff key)." }), "launch": Schema.optionalKey(Schema.Union([Schema.Null, LaunchSpec], { mode: "oneOf" })), "prep": Schema.optionalKey(Schema.Array(PrepCmd).annotate({ "description": "Per-title prep/undo steps — commands run as the host user; operator-privileged config." })), "title": Schema.String }).annotate({ "description": "One title in a provider's declarative reconcile payload (RFC §8): [`CustomInput`] plus the\nprovider's required stable key." })
|
||
export type StatsSample = { readonly "bitrate_kbps": number, readonly "fec_recovered": number, readonly "fps": number, readonly "frames_dropped": number, readonly "mbps": number, readonly "packets_dropped": number, readonly "repeat_fps": number, readonly "send_dropped": number, readonly "session_id": number, readonly "stages": ReadonlyArray<StageTiming>, readonly "t_ms": number }
|
||
export const StatsSample = Schema.Struct({ "bitrate_kbps": Schema.Number.annotate({ "description": "Configured target bitrate.", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "fec_recovered": Schema.Number.annotate({ "description": "FEC shards recovered this window (delta).", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "fps": Schema.Number.annotate({ "description": "Genuine NEW frames/s from the source.", "format": "float" }).check(Schema.isFinite()), "frames_dropped": Schema.Number.annotate({ "description": "Frames dropped this window (delta).", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "mbps": Schema.Number.annotate({ "description": "Transmit goodput (Mb/s).", "format": "float" }).check(Schema.isFinite()), "packets_dropped": Schema.Number.annotate({ "description": "Packets dropped this window (receiver-side / reassembler, where known).", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "repeat_fps": Schema.Number.annotate({ "description": "Re-encoded holds/s (source-starvation indicator).", "format": "float" }).check(Schema.isFinite()), "send_dropped": Schema.Number.annotate({ "description": "Host send-buffer overflow / EAGAIN this window (delta).", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "session_id": Schema.Number.annotate({ "description": "Disambiguates concurrent sessions (usually constant).", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "stages": Schema.Array(StageTiming).annotate({ "description": "Ordered pipeline stages for this path." }), "t_ms": Schema.Number.annotate({ "description": "Milliseconds since capture start (monotonic; stamped by [`StatsRecorder::push_sample`]).", "format": "int64" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)) }).annotate({ "description": "One aggregated sample (~ every 2 s native, ~ every 1 s GameStream)." })
|
||
export type HostEvent = { readonly "client": ClientRef, readonly "kind": "client.connected", readonly "schema": number, readonly "seq": number, readonly "ts_ms": number } | { readonly "client": ClientRef, readonly "kind": "client.disconnected", readonly "reason": DisconnectReason, readonly "schema": number, readonly "seq": number, readonly "ts_ms": number } | { readonly "kind": "session.started", readonly "session": SessionRef, readonly "schema": number, readonly "seq": number, readonly "ts_ms": number } | { readonly "kind": "session.ended", readonly "session": SessionRef, readonly "schema": number, readonly "seq": number, readonly "ts_ms": number } | { readonly "kind": "stream.started", readonly "stream": StreamRef, readonly "schema": number, readonly "seq": number, readonly "ts_ms": number } | { readonly "kind": "stream.stopped", readonly "stream": StreamRef, readonly "schema": number, readonly "seq": number, readonly "ts_ms": number } | { readonly "device": DeviceRef, readonly "kind": "pairing.pending", readonly "schema": number, readonly "seq": number, readonly "ts_ms": number } | { readonly "device": DeviceRef, readonly "kind": "pairing.completed", readonly "schema": number, readonly "seq": number, readonly "ts_ms": number } | { readonly "device": DeviceRef, readonly "kind": "pairing.denied", readonly "schema": number, readonly "seq": number, readonly "ts_ms": number } | { readonly "backend": string, readonly "kind": "display.created", readonly "mode": string, readonly "schema": number, readonly "seq": number, readonly "ts_ms": number } | { readonly "count": number, readonly "kind": "display.released", readonly "schema": number, readonly "seq": number, readonly "ts_ms": number } | { readonly "kind": "library.changed", readonly "source": string, readonly "schema": number, readonly "seq": number, readonly "ts_ms": number } | { readonly "id": string, readonly "kind": "plugins.changed", readonly "schema": number, readonly "seq": number, readonly "ts_ms": number } | { readonly "gamestream": boolean, readonly "kind": "host.started", readonly "version": string, readonly "schema": number, readonly "seq": number, readonly "ts_ms": number } | { readonly "kind": "host.stopping", readonly "schema": number, readonly "seq": number, readonly "ts_ms": number }
|
||
export const HostEvent = Schema.Union([Schema.Struct({ "client": ClientRef, "kind": Schema.Literal("client.connected"), "schema": Schema.Number.annotate({ "description": "Wire-shape version ([`SCHEMA_VERSION`]).", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "seq": Schema.Number.annotate({ "description": "Monotonic sequence number (1-based) — a consumer resumes with `since = last seen`.", "format": "int64" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "ts_ms": Schema.Number.annotate({ "description": "Unix timestamp in milliseconds (the [`crate::log_capture::LogEntry`] convention).", "format": "int64" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)) }), Schema.Struct({ "client": ClientRef, "kind": Schema.Literal("client.disconnected"), "reason": DisconnectReason, "schema": Schema.Number.annotate({ "description": "Wire-shape version ([`SCHEMA_VERSION`]).", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "seq": Schema.Number.annotate({ "description": "Monotonic sequence number (1-based) — a consumer resumes with `since = last seen`.", "format": "int64" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "ts_ms": Schema.Number.annotate({ "description": "Unix timestamp in milliseconds (the [`crate::log_capture::LogEntry`] convention).", "format": "int64" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)) }), Schema.Struct({ "kind": Schema.Literal("session.started"), "session": SessionRef, "schema": Schema.Number.annotate({ "description": "Wire-shape version ([`SCHEMA_VERSION`]).", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "seq": Schema.Number.annotate({ "description": "Monotonic sequence number (1-based) — a consumer resumes with `since = last seen`.", "format": "int64" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "ts_ms": Schema.Number.annotate({ "description": "Unix timestamp in milliseconds (the [`crate::log_capture::LogEntry`] convention).", "format": "int64" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)) }), Schema.Struct({ "kind": Schema.Literal("session.ended"), "session": SessionRef, "schema": Schema.Number.annotate({ "description": "Wire-shape version ([`SCHEMA_VERSION`]).", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "seq": Schema.Number.annotate({ "description": "Monotonic sequence number (1-based) — a consumer resumes with `since = last seen`.", "format": "int64" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "ts_ms": Schema.Number.annotate({ "description": "Unix timestamp in milliseconds (the [`crate::log_capture::LogEntry`] convention).", "format": "int64" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)) }), Schema.Struct({ "kind": Schema.Literal("stream.started"), "stream": StreamRef, "schema": Schema.Number.annotate({ "description": "Wire-shape version ([`SCHEMA_VERSION`]).", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "seq": Schema.Number.annotate({ "description": "Monotonic sequence number (1-based) — a consumer resumes with `since = last seen`.", "format": "int64" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "ts_ms": Schema.Number.annotate({ "description": "Unix timestamp in milliseconds (the [`crate::log_capture::LogEntry`] convention).", "format": "int64" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)) }), Schema.Struct({ "kind": Schema.Literal("stream.stopped"), "stream": StreamRef, "schema": Schema.Number.annotate({ "description": "Wire-shape version ([`SCHEMA_VERSION`]).", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "seq": Schema.Number.annotate({ "description": "Monotonic sequence number (1-based) — a consumer resumes with `since = last seen`.", "format": "int64" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "ts_ms": Schema.Number.annotate({ "description": "Unix timestamp in milliseconds (the [`crate::log_capture::LogEntry`] convention).", "format": "int64" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)) }), Schema.Struct({ "device": DeviceRef, "kind": Schema.Literal("pairing.pending"), "schema": Schema.Number.annotate({ "description": "Wire-shape version ([`SCHEMA_VERSION`]).", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "seq": Schema.Number.annotate({ "description": "Monotonic sequence number (1-based) — a consumer resumes with `since = last seen`.", "format": "int64" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "ts_ms": Schema.Number.annotate({ "description": "Unix timestamp in milliseconds (the [`crate::log_capture::LogEntry`] convention).", "format": "int64" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)) }), Schema.Struct({ "device": DeviceRef, "kind": Schema.Literal("pairing.completed"), "schema": Schema.Number.annotate({ "description": "Wire-shape version ([`SCHEMA_VERSION`]).", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "seq": Schema.Number.annotate({ "description": "Monotonic sequence number (1-based) — a consumer resumes with `since = last seen`.", "format": "int64" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "ts_ms": Schema.Number.annotate({ "description": "Unix timestamp in milliseconds (the [`crate::log_capture::LogEntry`] convention).", "format": "int64" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)) }), Schema.Struct({ "device": DeviceRef, "kind": Schema.Literal("pairing.denied"), "schema": Schema.Number.annotate({ "description": "Wire-shape version ([`SCHEMA_VERSION`]).", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "seq": Schema.Number.annotate({ "description": "Monotonic sequence number (1-based) — a consumer resumes with `since = last seen`.", "format": "int64" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "ts_ms": Schema.Number.annotate({ "description": "Unix timestamp in milliseconds (the [`crate::log_capture::LogEntry`] convention).", "format": "int64" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)) }), Schema.Struct({ "backend": Schema.String.annotate({ "description": "The virtual-display backend that minted it (`VirtualDisplay::name`)." }), "kind": Schema.Literal("display.created"), "mode": Schema.String.annotate({ "description": "`WxH@Hz`." }), "schema": Schema.Number.annotate({ "description": "Wire-shape version ([`SCHEMA_VERSION`]).", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "seq": Schema.Number.annotate({ "description": "Monotonic sequence number (1-based) — a consumer resumes with `since = last seen`.", "format": "int64" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "ts_ms": Schema.Number.annotate({ "description": "Unix timestamp in milliseconds (the [`crate::log_capture::LogEntry`] convention).", "format": "int64" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)) }), Schema.Struct({ "count": Schema.Number.annotate({ "description": "How many kept displays this release retired.", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "kind": Schema.Literal("display.released"), "schema": Schema.Number.annotate({ "description": "Wire-shape version ([`SCHEMA_VERSION`]).", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "seq": Schema.Number.annotate({ "description": "Monotonic sequence number (1-based) — a consumer resumes with `since = last seen`.", "format": "int64" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "ts_ms": Schema.Number.annotate({ "description": "Unix timestamp in milliseconds (the [`crate::log_capture::LogEntry`] convention).", "format": "int64" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)) }), Schema.Struct({ "kind": Schema.Literal("library.changed"), "source": Schema.String.annotate({ "description": "What mutated the library: `\"manual\"` today; a provider id once the provider\nAPI (RFC §8) lands." }), "schema": Schema.Number.annotate({ "description": "Wire-shape version ([`SCHEMA_VERSION`]).", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "seq": Schema.Number.annotate({ "description": "Monotonic sequence number (1-based) — a consumer resumes with `since = last seen`.", "format": "int64" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "ts_ms": Schema.Number.annotate({ "description": "Unix timestamp in milliseconds (the [`crate::log_capture::LogEntry`] convention).", "format": "int64" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)) }), Schema.Struct({ "id": Schema.String.annotate({ "description": "The plugin whose registration changed (registered, restarted, deregistered, or\nlease-expired). A consumer re-reads `GET /api/v1/plugins` for the new set." }), "kind": Schema.Literal("plugins.changed"), "schema": Schema.Number.annotate({ "description": "Wire-shape version ([`SCHEMA_VERSION`]).", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "seq": Schema.Number.annotate({ "description": "Monotonic sequence number (1-based) — a consumer resumes with `since = last seen`.", "format": "int64" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "ts_ms": Schema.Number.annotate({ "description": "Unix timestamp in milliseconds (the [`crate::log_capture::LogEntry`] convention).", "format": "int64" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)) }), Schema.Struct({ "gamestream": Schema.Boolean.annotate({ "description": "Whether the GameStream/Moonlight compat plane is enabled." }), "kind": Schema.Literal("host.started"), "version": Schema.String, "schema": Schema.Number.annotate({ "description": "Wire-shape version ([`SCHEMA_VERSION`]).", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "seq": Schema.Number.annotate({ "description": "Monotonic sequence number (1-based) — a consumer resumes with `since = last seen`.", "format": "int64" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "ts_ms": Schema.Number.annotate({ "description": "Unix timestamp in milliseconds (the [`crate::log_capture::LogEntry`] convention).", "format": "int64" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)) }), Schema.Struct({ "kind": Schema.Literal("host.stopping"), "schema": Schema.Number.annotate({ "description": "Wire-shape version ([`SCHEMA_VERSION`]).", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "seq": Schema.Number.annotate({ "description": "Monotonic sequence number (1-based) — a consumer resumes with `since = last seen`.", "format": "int64" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "ts_ms": Schema.Number.annotate({ "description": "Unix timestamp in milliseconds (the [`crate::log_capture::LogEntry`] convention).", "format": "int64" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)) })], { mode: "oneOf" }).annotate({ "description": "The event kind + payload, flattened: `\"kind\": \"stream.started\", …payload…`." })
|
||
export type CustomPreset = { readonly "fields": { readonly "identity": Identity, readonly "keep_alive": KeepAlive, readonly "layout": Layout, readonly "max_displays": number, readonly "mode_conflict": ModeConflict, readonly "topology": Topology }, readonly "game_session"?: "auto" | "dedicated", readonly "id": string, readonly "name": string }
|
||
export const CustomPreset = Schema.Struct({ "fields": Schema.Struct({ "identity": Identity, "keep_alive": KeepAlive, "layout": Layout, "max_displays": Schema.Number.annotate({ "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "mode_conflict": ModeConflict, "topology": Topology }).annotate({ "description": "The six display-behavior axes this preset applies (the same shape a built-in preset expands to)." }), "game_session": Schema.optionalKey(Schema.Literals(["auto", "dedicated"]).annotate({ "description": "The game-session routing this preset applies (orthogonal to the six axes; see [`GameSession`]).\nA custom preset captures the operator's *full* setup, so — unlike a built-in preset — applying\none does set this axis." })), "id": Schema.String.annotate({ "description": "Host-assigned, stable for the life of the entry (the `{id}` in the CRUD path)." }), "name": Schema.String.annotate({ "description": "User-facing name shown on the preset card; editable." }) }).annotate({ "description": "A user-defined named preset: a saved bundle of the six display-behavior axes (exactly what a\nbuilt-in [`Preset`] expands to) plus the orthogonal game-session axis, that the operator names\nand applies from the console.\n\nUnlike the built-in [`Preset`]s (a closed enum), custom presets are **data** — a catalog stored in\n`<config>/display-presets.json`. Applying one writes a `Custom` [`DisplayPolicy`] carrying these\nfields (the console reuses `PUT /display/settings`), so [`DisplayPolicy::effective`] stays pure and\nthe built-in set is never touched. The catalog is decoupled from the active `display-settings.json`:\nediting or deleting a preset never mutates the running policy (re-apply to adopt a change)." })
|
||
export type DisplayPolicy = { readonly "ddc_power_off"?: boolean, readonly "game_session"?: "auto" | "dedicated", readonly "identity"?: Identity, readonly "keep_alive"?: KeepAlive, readonly "layout"?: Layout, readonly "max_displays"?: number, readonly "mode_conflict"?: ModeConflict, readonly "pnp_disable_monitors"?: boolean, readonly "preset"?: Preset, readonly "topology"?: Topology, readonly "version"?: number }
|
||
export const DisplayPolicy = Schema.Struct({ "ddc_power_off": Schema.optionalKey(Schema.Boolean.annotate({ "description": "EXPERIMENTAL (Windows): command physical monitors' panels off over DDC/CI (VCP 0xD6 →\nDPMS off) right before an `Exclusive` isolate deactivates them, and back on at restore.\nTargets the \"connected-but-dark head\" periodic-stutter class (monitor standby\nauto-input-scan / DP link churn while the virtual display is the sole active display) at\nthe monitor-firmware level. Best-effort — monitors without DDC/CI (or with it disabled in\nthe OSD) are skipped. Orthogonal to `preset` (like `game_session`): preserved across\npreset changes; `#[serde(default)]` = off so existing `display-settings.json` files are\nuntouched." })), "game_session": Schema.optionalKey(Schema.Literals(["auto", "dedicated"]).annotate({ "description": "How a game-launching session is served (`design/gamemode-and-dedicated-sessions.md` §5.2).\nOrthogonal to `preset`/lifecycle — preserved across preset changes; `#[serde(default)]` = `Auto`\nso existing `display-settings.json` files are untouched." })), "identity": Schema.optionalKey(Identity), "keep_alive": Schema.optionalKey(KeepAlive), "layout": Schema.optionalKey(Layout), "max_displays": Schema.optionalKey(Schema.Number.annotate({ "description": "Upper bound on simultaneously-live virtual displays (clamped to `1..=16` on write).", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0))), "mode_conflict": Schema.optionalKey(ModeConflict), "pnp_disable_monitors": Schema.optionalKey(Schema.Boolean.annotate({ "description": "EXPERIMENTAL (Windows): DISABLE physical monitors' PnP device nodes for the stream's\nduration (persistently, so a standby monitor/TV whose hot-plug events re-arrive stays\ndisabled) and re-enable them at teardown. Two selectors: the monitors an `Exclusive`\nisolate deactivated, plus — in ANY topology — external monitors that are connected but not\npart of the desktop (the standby TV that was never active, whose input auto-scan /\ninstant-on HPD cycling re-probes the link every few seconds). Targets the same\n\"connected-but-dark head\" periodic-stutter class as [`Self::ddc_power_off`], but at the\nWindows-reaction level: a disabled devnode's wake events trigger no PnP arrival, no CCD\nre-evaluation, no DWM invalidation. A crash-recovery journal re-enables leftovers on host\nstartup. Orthogonal to `preset` (like `game_session`); `#[serde(default)]` = off." })), "preset": Schema.optionalKey(Preset), "topology": Schema.optionalKey(Topology), "version": Schema.optionalKey(Schema.Number.annotate({ "description": "Schema version (currently 1) — lets a future field addition migrate rather than reject.", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0))) }).annotate({ "description": "The user-facing display-management policy — what `display-settings.json` holds and what the mgmt\nAPI GETs/PUTs. When [`preset`](Self::preset) is not [`Preset::Custom`] the explicit fields are\nignored (the console writes one or the other); [`effective`](Self::effective) resolves both to a\nsingle [`EffectivePolicy`]." })
|
||
export type EffectivePolicy = { readonly "identity": Identity, readonly "keep_alive": KeepAlive, readonly "layout": Layout, readonly "max_displays": number, readonly "mode_conflict": ModeConflict, readonly "topology": Topology }
|
||
export const EffectivePolicy = Schema.Struct({ "identity": Identity, "keep_alive": KeepAlive, "layout": Layout, "max_displays": Schema.Number.annotate({ "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "mode_conflict": ModeConflict, "topology": Topology }).annotate({ "description": "The six resolved fields after preset expansion — what the lifecycle/registry and the Stage-0 call\nsites read, and what the mgmt API echoes as the \"currently in force\" policy. Pure output of\n[`DisplayPolicy::effective`]." })
|
||
export type PresetInfo = { readonly "fields": { readonly "identity": Identity, readonly "keep_alive": KeepAlive, readonly "layout": Layout, readonly "max_displays": number, readonly "mode_conflict": ModeConflict, readonly "topology": Topology }, readonly "id": string, readonly "summary": string }
|
||
export const PresetInfo = Schema.Struct({ "fields": Schema.Struct({ "identity": Identity, "keep_alive": KeepAlive, "layout": Layout, "max_displays": Schema.Number.annotate({ "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "mode_conflict": ModeConflict, "topology": Topology }).annotate({ "description": "The effective policy this preset expands to (the same fields a `custom` policy carries)." }), "id": Schema.String.annotate({ "description": "The preset id (`default` | `gaming-rig` | `shared-desktop` | `hotdesk` | `workstation`)." }), "summary": Schema.String.annotate({ "description": "One-line story shown next to the option." }) }).annotate({ "description": "One preset's human-facing description + the fields it expands to, so the console can render a\npreset picker with an accurate \"what this does\" preview without hardcoding the expansion." })
|
||
export type Capture = { readonly "meta": CaptureMeta, readonly "samples": ReadonlyArray<StatsSample> }
|
||
export const Capture = Schema.Struct({ "meta": CaptureMeta, "samples": Schema.Array(StatsSample) }).annotate({ "description": "A full capture: summary + the sample time-series. The wire + on-disk shape." })
|
||
export type CustomPresetInput = { readonly "fields": EffectivePolicy, readonly "game_session"?: GameSession, readonly "name": string }
|
||
export const CustomPresetInput = Schema.Struct({ "fields": EffectivePolicy, "game_session": Schema.optionalKey(GameSession), "name": Schema.String }).annotate({ "description": "Request body to create or replace a custom preset (no `id` — the host owns it)." })
|
||
export type DisplaySettingsState = { readonly "configured": boolean, readonly "custom_presets": ReadonlyArray<CustomPreset>, readonly "effective": { readonly "identity": Identity, readonly "keep_alive": KeepAlive, readonly "layout": Layout, readonly "max_displays": number, readonly "mode_conflict": ModeConflict, readonly "topology": Topology }, readonly "enforced": ReadonlyArray<string>, readonly "presets": ReadonlyArray<PresetInfo>, readonly "settings": { readonly "ddc_power_off"?: boolean, readonly "game_session"?: "auto" | "dedicated", readonly "identity"?: Identity, readonly "keep_alive"?: KeepAlive, readonly "layout"?: Layout, readonly "max_displays"?: number, readonly "mode_conflict"?: ModeConflict, readonly "pnp_disable_monitors"?: boolean, readonly "preset"?: Preset, readonly "topology"?: Topology, readonly "version"?: number } }
|
||
export const DisplaySettingsState = Schema.Struct({ "configured": Schema.Boolean.annotate({ "description": "True once a `display-settings.json` exists (the console has configured this host)." }), "custom_presets": Schema.Array(CustomPreset).annotate({ "description": "The operator's saved custom presets (`display-presets.json`) — named field-bundles rendered\nalongside the built-ins. Managed via `POST/PUT/DELETE /display/presets`; applied by writing a\n`Custom` policy carrying the preset's fields." }), "effective": Schema.Struct({ "identity": Identity, "keep_alive": KeepAlive, "layout": Layout, "max_displays": Schema.Number.annotate({ "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0)), "mode_conflict": ModeConflict, "topology": Topology }).annotate({ "description": "The effective (preset-expanded) policy currently in force." }), "enforced": Schema.Array(Schema.String).annotate({ "description": "Option names this build enforces right now. All five axes are now acted on (keep_alive +\ntopology since Stage 0-2, identity Stage 3, mode_conflict Stage 4, layout Stage 5) — the console\nreads this to know which controls are live vs. \"coming soon\" (per-backend nuance, e.g. layout\nposition apply being KWin-only, is reported per display in `/display/state`)." }), "presets": Schema.Array(PresetInfo).annotate({ "description": "Every named preset and what it expands to (for the picker's preview)." }), "settings": Schema.Struct({ "ddc_power_off": Schema.optionalKey(Schema.Boolean.annotate({ "description": "EXPERIMENTAL (Windows): command physical monitors' panels off over DDC/CI (VCP 0xD6 →\nDPMS off) right before an `Exclusive` isolate deactivates them, and back on at restore.\nTargets the \"connected-but-dark head\" periodic-stutter class (monitor standby\nauto-input-scan / DP link churn while the virtual display is the sole active display) at\nthe monitor-firmware level. Best-effort — monitors without DDC/CI (or with it disabled in\nthe OSD) are skipped. Orthogonal to `preset` (like `game_session`): preserved across\npreset changes; `#[serde(default)]` = off so existing `display-settings.json` files are\nuntouched." })), "game_session": Schema.optionalKey(Schema.Literals(["auto", "dedicated"]).annotate({ "description": "How a game-launching session is served (`design/gamemode-and-dedicated-sessions.md` §5.2).\nOrthogonal to `preset`/lifecycle — preserved across preset changes; `#[serde(default)]` = `Auto`\nso existing `display-settings.json` files are untouched." })), "identity": Schema.optionalKey(Identity), "keep_alive": Schema.optionalKey(KeepAlive), "layout": Schema.optionalKey(Layout), "max_displays": Schema.optionalKey(Schema.Number.annotate({ "description": "Upper bound on simultaneously-live virtual displays (clamped to `1..=16` on write).", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0))), "mode_conflict": Schema.optionalKey(ModeConflict), "pnp_disable_monitors": Schema.optionalKey(Schema.Boolean.annotate({ "description": "EXPERIMENTAL (Windows): DISABLE physical monitors' PnP device nodes for the stream's\nduration (persistently, so a standby monitor/TV whose hot-plug events re-arrive stays\ndisabled) and re-enable them at teardown. Two selectors: the monitors an `Exclusive`\nisolate deactivated, plus — in ANY topology — external monitors that are connected but not\npart of the desktop (the standby TV that was never active, whose input auto-scan /\ninstant-on HPD cycling re-probes the link every few seconds). Targets the same\n\"connected-but-dark head\" periodic-stutter class as [`Self::ddc_power_off`], but at the\nWindows-reaction level: a disabled devnode's wake events trigger no PnP arrival, no CCD\nre-evaluation, no DWM invalidation. A crash-recovery journal re-enables leftovers on host\nstartup. Orthogonal to `preset` (like `game_session`); `#[serde(default)]` = off." })), "preset": Schema.optionalKey(Preset), "topology": Schema.optionalKey(Topology), "version": Schema.optionalKey(Schema.Number.annotate({ "description": "Schema version (currently 1) — lets a future field addition migrate rather than reject.", "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0))) }).annotate({ "description": "The stored policy (preset + custom fields), or the built-in default when unconfigured." }) }).annotate({ "description": "Full display-management state for the console: the stored policy, every preset's expansion, the\nresolved effective policy, and which options this build actually enforces yet (Stage 0 wires\nkeep-alive linger + topology; the rest are stored but not yet acted on)." })
|
||
// schemas
|
||
export type ListPairedClients200 = ReadonlyArray<PairedClient>
|
||
export const ListPairedClients200 = Schema.Array(PairedClient)
|
||
export type ListPairedClients401 = ApiError
|
||
export const ListPairedClients401 = ApiError
|
||
export type UnpairClient400 = ApiError
|
||
export const UnpairClient400 = ApiError
|
||
export type UnpairClient401 = ApiError
|
||
export const UnpairClient401 = ApiError
|
||
export type UnpairClient404 = ApiError
|
||
export const UnpairClient404 = ApiError
|
||
export type ListCompositors200 = ReadonlyArray<AvailableCompositor>
|
||
export const ListCompositors200 = Schema.Array(AvailableCompositor)
|
||
export type ListCompositors401 = ApiError
|
||
export const ListCompositors401 = ApiError
|
||
export type SetDisplayLayoutRequestJson = DisplayLayoutRequest
|
||
export const SetDisplayLayoutRequestJson = DisplayLayoutRequest
|
||
export type SetDisplayLayout200 = DisplaySettingsState
|
||
export const SetDisplayLayout200 = DisplaySettingsState
|
||
export type SetDisplayLayout401 = ApiError
|
||
export const SetDisplayLayout401 = ApiError
|
||
export type SetDisplayLayout500 = ApiError
|
||
export const SetDisplayLayout500 = ApiError
|
||
export type ListCustomPresets200 = ReadonlyArray<CustomPreset>
|
||
export const ListCustomPresets200 = Schema.Array(CustomPreset)
|
||
export type ListCustomPresets401 = ApiError
|
||
export const ListCustomPresets401 = ApiError
|
||
export type CreateCustomPresetRequestJson = CustomPresetInput
|
||
export const CreateCustomPresetRequestJson = CustomPresetInput
|
||
export type CreateCustomPreset201 = CustomPreset
|
||
export const CreateCustomPreset201 = CustomPreset
|
||
export type CreateCustomPreset400 = ApiError
|
||
export const CreateCustomPreset400 = ApiError
|
||
export type CreateCustomPreset401 = ApiError
|
||
export const CreateCustomPreset401 = ApiError
|
||
export type CreateCustomPreset500 = ApiError
|
||
export const CreateCustomPreset500 = ApiError
|
||
export type UpdateCustomPresetRequestJson = CustomPresetInput
|
||
export const UpdateCustomPresetRequestJson = CustomPresetInput
|
||
export type UpdateCustomPreset200 = CustomPreset
|
||
export const UpdateCustomPreset200 = CustomPreset
|
||
export type UpdateCustomPreset400 = ApiError
|
||
export const UpdateCustomPreset400 = ApiError
|
||
export type UpdateCustomPreset401 = ApiError
|
||
export const UpdateCustomPreset401 = ApiError
|
||
export type UpdateCustomPreset404 = ApiError
|
||
export const UpdateCustomPreset404 = ApiError
|
||
export type UpdateCustomPreset500 = ApiError
|
||
export const UpdateCustomPreset500 = ApiError
|
||
export type DeleteCustomPreset401 = ApiError
|
||
export const DeleteCustomPreset401 = ApiError
|
||
export type DeleteCustomPreset404 = ApiError
|
||
export const DeleteCustomPreset404 = ApiError
|
||
export type DeleteCustomPreset500 = ApiError
|
||
export const DeleteCustomPreset500 = ApiError
|
||
export type ReleaseDisplayRequestJson = ReleaseDisplayRequest
|
||
export const ReleaseDisplayRequestJson = ReleaseDisplayRequest
|
||
export type ReleaseDisplay200 = ReleaseDisplayResult
|
||
export const ReleaseDisplay200 = ReleaseDisplayResult
|
||
export type ReleaseDisplay401 = ApiError
|
||
export const ReleaseDisplay401 = ApiError
|
||
export type GetDisplaySettings200 = DisplaySettingsState
|
||
export const GetDisplaySettings200 = DisplaySettingsState
|
||
export type GetDisplaySettings401 = ApiError
|
||
export const GetDisplaySettings401 = ApiError
|
||
export type SetDisplaySettingsRequestJson = DisplayPolicy
|
||
export const SetDisplaySettingsRequestJson = DisplayPolicy
|
||
export type SetDisplaySettings200 = DisplaySettingsState
|
||
export const SetDisplaySettings200 = DisplaySettingsState
|
||
export type SetDisplaySettings400 = ApiError
|
||
export const SetDisplaySettings400 = ApiError
|
||
export type SetDisplaySettings401 = ApiError
|
||
export const SetDisplaySettings401 = ApiError
|
||
export type SetDisplaySettings500 = ApiError
|
||
export const SetDisplaySettings500 = ApiError
|
||
export type GetDisplayState200 = DisplayStateResponse
|
||
export const GetDisplayState200 = DisplayStateResponse
|
||
export type GetDisplayState401 = ApiError
|
||
export const GetDisplayState401 = ApiError
|
||
export type StreamEventsParams = { readonly "since"?: number, readonly "kinds"?: string, readonly "Last-Event-ID"?: never }
|
||
export const StreamEventsParams = Schema.Struct({ "since": Schema.optionalKey(Schema.Number.annotate({ "format": "int64" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0))), "kinds": Schema.optionalKey(Schema.String), "Last-Event-ID": Schema.optionalKey(Schema.Never) })
|
||
export type StreamEvents200Sse = HostEvent
|
||
export const StreamEvents200Sse = HostEvent
|
||
export type StreamEvents401 = ApiError
|
||
export const StreamEvents401 = ApiError
|
||
export type StreamEvents503 = ApiError
|
||
export const StreamEvents503 = ApiError
|
||
export type ListGpus200 = GpuState
|
||
export const ListGpus200 = GpuState
|
||
export type ListGpus401 = ApiError
|
||
export const ListGpus401 = ApiError
|
||
export type SetGpuPreferenceRequestJson = SetGpuPreference
|
||
export const SetGpuPreferenceRequestJson = SetGpuPreference
|
||
export type SetGpuPreference200 = GpuState
|
||
export const SetGpuPreference200 = GpuState
|
||
export type SetGpuPreference400 = ApiError
|
||
export const SetGpuPreference400 = ApiError
|
||
export type SetGpuPreference401 = ApiError
|
||
export const SetGpuPreference401 = ApiError
|
||
export type SetGpuPreference500 = ApiError
|
||
export const SetGpuPreference500 = ApiError
|
||
export type GetHealth200 = Health
|
||
export const GetHealth200 = Health
|
||
export type GetHooks200 = HooksConfig
|
||
export const GetHooks200 = HooksConfig
|
||
export type GetHooks401 = ApiError
|
||
export const GetHooks401 = ApiError
|
||
export type SetHooksRequestJson = HooksConfig
|
||
export const SetHooksRequestJson = HooksConfig
|
||
export type SetHooks200 = HooksConfig
|
||
export const SetHooks200 = HooksConfig
|
||
export type SetHooks400 = ApiError
|
||
export const SetHooks400 = ApiError
|
||
export type SetHooks401 = ApiError
|
||
export const SetHooks401 = ApiError
|
||
export type SetHooks500 = ApiError
|
||
export const SetHooks500 = ApiError
|
||
export type GetHostInfo200 = HostInfo
|
||
export const GetHostInfo200 = HostInfo
|
||
export type GetHostInfo401 = ApiError
|
||
export const GetHostInfo401 = ApiError
|
||
export type GetLibraryParams = { readonly "provider"?: string }
|
||
export const GetLibraryParams = Schema.Struct({ "provider": Schema.optionalKey(Schema.String) })
|
||
export type GetLibrary200 = ReadonlyArray<GameEntry>
|
||
export const GetLibrary200 = Schema.Array(GameEntry)
|
||
export type GetLibrary401 = ApiError
|
||
export const GetLibrary401 = ApiError
|
||
export type GetLibraryArt401 = ApiError
|
||
export const GetLibraryArt401 = ApiError
|
||
export type GetLibraryArt404 = ApiError
|
||
export const GetLibraryArt404 = ApiError
|
||
export type CreateCustomGameRequestJson = CustomInput
|
||
export const CreateCustomGameRequestJson = CustomInput
|
||
export type CreateCustomGame201 = CustomEntry
|
||
export const CreateCustomGame201 = CustomEntry
|
||
export type CreateCustomGame400 = ApiError
|
||
export const CreateCustomGame400 = ApiError
|
||
export type CreateCustomGame401 = ApiError
|
||
export const CreateCustomGame401 = ApiError
|
||
export type CreateCustomGame500 = ApiError
|
||
export const CreateCustomGame500 = ApiError
|
||
export type UpdateCustomGameRequestJson = CustomInput
|
||
export const UpdateCustomGameRequestJson = CustomInput
|
||
export type UpdateCustomGame200 = CustomEntry
|
||
export const UpdateCustomGame200 = CustomEntry
|
||
export type UpdateCustomGame400 = ApiError
|
||
export const UpdateCustomGame400 = ApiError
|
||
export type UpdateCustomGame401 = ApiError
|
||
export const UpdateCustomGame401 = ApiError
|
||
export type UpdateCustomGame404 = ApiError
|
||
export const UpdateCustomGame404 = ApiError
|
||
export type UpdateCustomGame500 = ApiError
|
||
export const UpdateCustomGame500 = ApiError
|
||
export type DeleteCustomGame401 = ApiError
|
||
export const DeleteCustomGame401 = ApiError
|
||
export type DeleteCustomGame404 = ApiError
|
||
export const DeleteCustomGame404 = ApiError
|
||
export type DeleteCustomGame500 = ApiError
|
||
export const DeleteCustomGame500 = ApiError
|
||
export type ReconcileProviderEntriesRequestJson = ReadonlyArray<ProviderEntryInput>
|
||
export const ReconcileProviderEntriesRequestJson = Schema.Array(ProviderEntryInput)
|
||
export type ReconcileProviderEntries200 = ReadonlyArray<CustomEntry>
|
||
export const ReconcileProviderEntries200 = Schema.Array(CustomEntry)
|
||
export type ReconcileProviderEntries400 = ApiError
|
||
export const ReconcileProviderEntries400 = ApiError
|
||
export type ReconcileProviderEntries401 = ApiError
|
||
export const ReconcileProviderEntries401 = ApiError
|
||
export type ReconcileProviderEntries500 = ApiError
|
||
export const ReconcileProviderEntries500 = ApiError
|
||
export type DeleteProviderEntries200 = ProviderRemoved
|
||
export const DeleteProviderEntries200 = ProviderRemoved
|
||
export type DeleteProviderEntries400 = ApiError
|
||
export const DeleteProviderEntries400 = ApiError
|
||
export type DeleteProviderEntries401 = ApiError
|
||
export const DeleteProviderEntries401 = ApiError
|
||
export type DeleteProviderEntries500 = ApiError
|
||
export const DeleteProviderEntries500 = ApiError
|
||
export type GetLocalSummary200 = LocalSummary
|
||
export const GetLocalSummary200 = LocalSummary
|
||
export type GetLocalSummary401 = ApiError
|
||
export const GetLocalSummary401 = ApiError
|
||
export type LogsGetParams = { readonly "after"?: number, readonly "limit"?: number }
|
||
export const LogsGetParams = Schema.Struct({ "after": Schema.optionalKey(Schema.Number.annotate({ "format": "int64" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0))), "limit": Schema.optionalKey(Schema.Number.annotate({ "format": "int32" }).check(Schema.isInt()).check(Schema.isGreaterThanOrEqualTo(0))) })
|
||
export type LogsGet200 = LogPage
|
||
export const LogsGet200 = LogPage
|
||
export type LogsGet401 = ApiError
|
||
export const LogsGet401 = ApiError
|
||
export type ListNativeClients200 = ReadonlyArray<NativeClient>
|
||
export const ListNativeClients200 = Schema.Array(NativeClient)
|
||
export type ListNativeClients401 = ApiError
|
||
export const ListNativeClients401 = ApiError
|
||
export type UnpairNativeClient401 = ApiError
|
||
export const UnpairNativeClient401 = ApiError
|
||
export type UnpairNativeClient404 = ApiError
|
||
export const UnpairNativeClient404 = ApiError
|
||
export type UnpairNativeClient503 = ApiError
|
||
export const UnpairNativeClient503 = ApiError
|
||
export type GetNativePairing200 = NativePairStatus
|
||
export const GetNativePairing200 = NativePairStatus
|
||
export type GetNativePairing401 = ApiError
|
||
export const GetNativePairing401 = ApiError
|
||
export type DisarmNativePairing401 = ApiError
|
||
export const DisarmNativePairing401 = ApiError
|
||
export type DisarmNativePairing503 = ApiError
|
||
export const DisarmNativePairing503 = ApiError
|
||
export type ArmNativePairingRequestJson = ArmNativePairing
|
||
export const ArmNativePairingRequestJson = ArmNativePairing
|
||
export type ArmNativePairing200 = NativePairStatus
|
||
export const ArmNativePairing200 = NativePairStatus
|
||
export type ArmNativePairing401 = ApiError
|
||
export const ArmNativePairing401 = ApiError
|
||
export type ArmNativePairing503 = ApiError
|
||
export const ArmNativePairing503 = ApiError
|
||
export type ListPendingDevices200 = ReadonlyArray<PendingDevice>
|
||
export const ListPendingDevices200 = Schema.Array(PendingDevice)
|
||
export type ListPendingDevices401 = ApiError
|
||
export const ListPendingDevices401 = ApiError
|
||
export type ApprovePendingDeviceRequestJson = ApprovePending
|
||
export const ApprovePendingDeviceRequestJson = ApprovePending
|
||
export type ApprovePendingDevice200 = NativeClient
|
||
export const ApprovePendingDevice200 = NativeClient
|
||
export type ApprovePendingDevice401 = ApiError
|
||
export const ApprovePendingDevice401 = ApiError
|
||
export type ApprovePendingDevice404 = ApiError
|
||
export const ApprovePendingDevice404 = ApiError
|
||
export type ApprovePendingDevice500 = ApiError
|
||
export const ApprovePendingDevice500 = ApiError
|
||
export type ApprovePendingDevice503 = ApiError
|
||
export const ApprovePendingDevice503 = ApiError
|
||
export type DenyPendingDevice401 = ApiError
|
||
export const DenyPendingDevice401 = ApiError
|
||
export type DenyPendingDevice404 = ApiError
|
||
export const DenyPendingDevice404 = ApiError
|
||
export type DenyPendingDevice503 = ApiError
|
||
export const DenyPendingDevice503 = ApiError
|
||
export type GetPairingStatus200 = PairingStatus
|
||
export const GetPairingStatus200 = PairingStatus
|
||
export type GetPairingStatus401 = ApiError
|
||
export const GetPairingStatus401 = ApiError
|
||
export type SubmitPairingPinRequestJson = SubmitPin
|
||
export const SubmitPairingPinRequestJson = SubmitPin
|
||
export type SubmitPairingPin400 = ApiError
|
||
export const SubmitPairingPin400 = ApiError
|
||
export type SubmitPairingPin401 = ApiError
|
||
export const SubmitPairingPin401 = ApiError
|
||
export type SubmitPairingPin409 = ApiError
|
||
export const SubmitPairingPin409 = ApiError
|
||
export type SubmitPairingPin415 = ApiError
|
||
export const SubmitPairingPin415 = ApiError
|
||
export type SubmitPairingPin422 = ApiError
|
||
export const SubmitPairingPin422 = ApiError
|
||
export type ListPlugins200 = ReadonlyArray<PluginSummary>
|
||
export const ListPlugins200 = Schema.Array(PluginSummary)
|
||
export type ListPlugins401 = ApiError
|
||
export const ListPlugins401 = ApiError
|
||
export type RegisterPluginRequestJson = PluginRegistration
|
||
export const RegisterPluginRequestJson = PluginRegistration
|
||
export type RegisterPlugin400 = ApiError
|
||
export const RegisterPlugin400 = ApiError
|
||
export type RegisterPlugin401 = ApiError
|
||
export const RegisterPlugin401 = ApiError
|
||
export type DeregisterPlugin401 = ApiError
|
||
export const DeregisterPlugin401 = ApiError
|
||
export type GetPluginUiCredential200 = UiCredential
|
||
export const GetPluginUiCredential200 = UiCredential
|
||
export type GetPluginUiCredential401 = ApiError
|
||
export const GetPluginUiCredential401 = ApiError
|
||
export type GetPluginUiCredential404 = ApiError
|
||
export const GetPluginUiCredential404 = ApiError
|
||
export type StopSession401 = ApiError
|
||
export const StopSession401 = ApiError
|
||
export type RequestIdr401 = ApiError
|
||
export const RequestIdr401 = ApiError
|
||
export type RequestIdr409 = ApiError
|
||
export const RequestIdr409 = ApiError
|
||
export type StatsCaptureLive200 = Capture
|
||
export const StatsCaptureLive200 = Capture
|
||
export type StatsCaptureLive401 = ApiError
|
||
export const StatsCaptureLive401 = ApiError
|
||
export type StatsCaptureLive404 = ApiError
|
||
export const StatsCaptureLive404 = ApiError
|
||
export type StatsCaptureStart200 = StatsStatus
|
||
export const StatsCaptureStart200 = StatsStatus
|
||
export type StatsCaptureStart401 = ApiError
|
||
export const StatsCaptureStart401 = ApiError
|
||
export type StatsCaptureStatus200 = StatsStatus
|
||
export const StatsCaptureStatus200 = StatsStatus
|
||
export type StatsCaptureStatus401 = ApiError
|
||
export const StatsCaptureStatus401 = ApiError
|
||
export type StatsCaptureStop200 = CaptureMeta
|
||
export const StatsCaptureStop200 = CaptureMeta
|
||
export type StatsCaptureStop401 = ApiError
|
||
export const StatsCaptureStop401 = ApiError
|
||
export type StatsCaptureStop500 = ApiError
|
||
export const StatsCaptureStop500 = ApiError
|
||
export type StatsRecordingsList200 = ReadonlyArray<CaptureMeta>
|
||
export const StatsRecordingsList200 = Schema.Array(CaptureMeta)
|
||
export type StatsRecordingsList401 = ApiError
|
||
export const StatsRecordingsList401 = ApiError
|
||
export type StatsRecordingGet200 = Capture
|
||
export const StatsRecordingGet200 = Capture
|
||
export type StatsRecordingGet401 = ApiError
|
||
export const StatsRecordingGet401 = ApiError
|
||
export type StatsRecordingGet404 = ApiError
|
||
export const StatsRecordingGet404 = ApiError
|
||
export type StatsRecordingGet500 = ApiError
|
||
export const StatsRecordingGet500 = ApiError
|
||
export type StatsRecordingDelete401 = ApiError
|
||
export const StatsRecordingDelete401 = ApiError
|
||
export type StatsRecordingDelete404 = ApiError
|
||
export const StatsRecordingDelete404 = ApiError
|
||
export type StatsRecordingDelete500 = ApiError
|
||
export const StatsRecordingDelete500 = ApiError
|
||
export type GetStatus200 = RuntimeStatus
|
||
export const GetStatus200 = RuntimeStatus
|
||
export type GetStatus401 = ApiError
|
||
export const GetStatus401 = ApiError
|
||
|
||
export interface OperationConfig {
|
||
/**
|
||
* Whether or not the response should be included in the value returned from
|
||
* an operation.
|
||
*
|
||
* If set to `true`, a tuple of `[A, HttpClientResponse]` will be returned,
|
||
* where `A` is the success type of the operation.
|
||
*
|
||
* If set to `false`, only the success type of the operation will be returned.
|
||
*/
|
||
readonly includeResponse?: boolean | undefined
|
||
}
|
||
|
||
/**
|
||
* A utility type which optionally includes the response in the return result
|
||
* of an operation based upon the value of the `includeResponse` configuration
|
||
* option.
|
||
*/
|
||
export type WithOptionalResponse<A, Config extends OperationConfig> = Config extends {
|
||
readonly includeResponse: true
|
||
} ? [A, HttpClientResponse.HttpClientResponse] : A
|
||
|
||
export const make = (
|
||
httpClient: HttpClient.HttpClient,
|
||
options: {
|
||
readonly transformClient?: ((client: HttpClient.HttpClient) => Effect.Effect<HttpClient.HttpClient>) | undefined
|
||
} = {}
|
||
): Punktfunk => {
|
||
const unexpectedStatus = (response: HttpClientResponse.HttpClientResponse) =>
|
||
Effect.flatMap(
|
||
Effect.orElseSucceed(response.json, () => "Unexpected status code"),
|
||
(description) =>
|
||
Effect.fail(
|
||
new HttpClientError.HttpClientError({
|
||
reason: new HttpClientError.StatusCodeError({
|
||
request: response.request,
|
||
response,
|
||
description: typeof description === "string" ? description : JSON.stringify(description),
|
||
}),
|
||
}),
|
||
),
|
||
)
|
||
const withResponse = <Config extends OperationConfig>(config: Config | undefined) => (
|
||
f: (response: HttpClientResponse.HttpClientResponse) => Effect.Effect<any, any>,
|
||
): (request: HttpClientRequest.HttpClientRequest) => Effect.Effect<any, any> => {
|
||
const withOptionalResponse = (
|
||
config?.includeResponse
|
||
? (response: HttpClientResponse.HttpClientResponse) => Effect.map(f(response), (a) => [a, response])
|
||
: (response: HttpClientResponse.HttpClientResponse) => f(response)
|
||
) as any
|
||
return options?.transformClient
|
||
? (request) =>
|
||
Effect.flatMap(
|
||
Effect.flatMap(options.transformClient!(httpClient), (client) => client.execute(request)),
|
||
withOptionalResponse
|
||
)
|
||
: (request) => Effect.flatMap(httpClient.execute(request), withOptionalResponse)
|
||
}
|
||
const sseRequest = <
|
||
Type,
|
||
DecodingServices
|
||
>(
|
||
schema: Schema.ConstraintDecoder<Type, DecodingServices>
|
||
) =>
|
||
(
|
||
request: HttpClientRequest.HttpClientRequest
|
||
): Stream.Stream<
|
||
{ readonly event: string; readonly id: string | undefined; readonly data: Type },
|
||
HttpClientError.HttpClientError | SchemaError | Sse.Retry,
|
||
DecodingServices
|
||
> =>
|
||
HttpClient.filterStatusOk(httpClient).execute(request).pipe(
|
||
Effect.map((response) => response.stream),
|
||
Stream.unwrap,
|
||
Stream.decodeText(),
|
||
Stream.pipeThroughChannel(Sse.decodeDataSchema(schema))
|
||
)
|
||
const decodeSuccess =
|
||
<Schema extends Schema.Constraint>(schema: Schema) =>
|
||
(response: HttpClientResponse.HttpClientResponse) =>
|
||
HttpClientResponse.schemaBodyJson(schema)(response)
|
||
const decodeError =
|
||
<const Tag extends string, Schema extends Schema.Constraint>(tag: Tag, schema: Schema) =>
|
||
(response: HttpClientResponse.HttpClientResponse) =>
|
||
Effect.flatMap(
|
||
HttpClientResponse.schemaBodyJson(schema)(response),
|
||
(cause) => Effect.fail(PunktfunkError(tag, cause, response)),
|
||
)
|
||
return {
|
||
httpClient,
|
||
"listPairedClients": (options) => HttpClientRequest.get(`/api/v1/clients`).pipe(
|
||
withResponse(options?.config)(HttpClientResponse.matchStatus({
|
||
"2xx": decodeSuccess(ListPairedClients200),
|
||
"401": decodeError("ListPairedClients401", ListPairedClients401),
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"unpairClient": (fingerprint, options) => HttpClientRequest.delete(`/api/v1/clients/${fingerprint}`).pipe(
|
||
withResponse(options?.config)(HttpClientResponse.matchStatus({
|
||
"400": decodeError("UnpairClient400", UnpairClient400),
|
||
"401": decodeError("UnpairClient401", UnpairClient401),
|
||
"404": decodeError("UnpairClient404", UnpairClient404),
|
||
"204": () => Effect.void,
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"listCompositors": (options) => HttpClientRequest.get(`/api/v1/compositors`).pipe(
|
||
withResponse(options?.config)(HttpClientResponse.matchStatus({
|
||
"2xx": decodeSuccess(ListCompositors200),
|
||
"401": decodeError("ListCompositors401", ListCompositors401),
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"setDisplayLayout": (options) => HttpClientRequest.put(`/api/v1/display/layout`).pipe(
|
||
HttpClientRequest.bodyJsonUnsafe(options.payload),
|
||
withResponse(options.config)(HttpClientResponse.matchStatus({
|
||
"2xx": decodeSuccess(SetDisplayLayout200),
|
||
"401": decodeError("SetDisplayLayout401", SetDisplayLayout401),
|
||
"500": decodeError("SetDisplayLayout500", SetDisplayLayout500),
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"listCustomPresets": (options) => HttpClientRequest.get(`/api/v1/display/presets`).pipe(
|
||
withResponse(options?.config)(HttpClientResponse.matchStatus({
|
||
"2xx": decodeSuccess(ListCustomPresets200),
|
||
"401": decodeError("ListCustomPresets401", ListCustomPresets401),
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"createCustomPreset": (options) => HttpClientRequest.post(`/api/v1/display/presets`).pipe(
|
||
HttpClientRequest.bodyJsonUnsafe(options.payload),
|
||
withResponse(options.config)(HttpClientResponse.matchStatus({
|
||
"2xx": decodeSuccess(CreateCustomPreset201),
|
||
"400": decodeError("CreateCustomPreset400", CreateCustomPreset400),
|
||
"401": decodeError("CreateCustomPreset401", CreateCustomPreset401),
|
||
"500": decodeError("CreateCustomPreset500", CreateCustomPreset500),
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"updateCustomPreset": (id, options) => HttpClientRequest.put(`/api/v1/display/presets/${id}`).pipe(
|
||
HttpClientRequest.bodyJsonUnsafe(options.payload),
|
||
withResponse(options.config)(HttpClientResponse.matchStatus({
|
||
"2xx": decodeSuccess(UpdateCustomPreset200),
|
||
"400": decodeError("UpdateCustomPreset400", UpdateCustomPreset400),
|
||
"401": decodeError("UpdateCustomPreset401", UpdateCustomPreset401),
|
||
"404": decodeError("UpdateCustomPreset404", UpdateCustomPreset404),
|
||
"500": decodeError("UpdateCustomPreset500", UpdateCustomPreset500),
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"deleteCustomPreset": (id, options) => HttpClientRequest.delete(`/api/v1/display/presets/${id}`).pipe(
|
||
withResponse(options?.config)(HttpClientResponse.matchStatus({
|
||
"401": decodeError("DeleteCustomPreset401", DeleteCustomPreset401),
|
||
"404": decodeError("DeleteCustomPreset404", DeleteCustomPreset404),
|
||
"500": decodeError("DeleteCustomPreset500", DeleteCustomPreset500),
|
||
"204": () => Effect.void,
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"releaseDisplay": (options) => HttpClientRequest.post(`/api/v1/display/release`).pipe(
|
||
HttpClientRequest.bodyJsonUnsafe(options.payload),
|
||
withResponse(options.config)(HttpClientResponse.matchStatus({
|
||
"2xx": decodeSuccess(ReleaseDisplay200),
|
||
"401": decodeError("ReleaseDisplay401", ReleaseDisplay401),
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"getDisplaySettings": (options) => HttpClientRequest.get(`/api/v1/display/settings`).pipe(
|
||
withResponse(options?.config)(HttpClientResponse.matchStatus({
|
||
"2xx": decodeSuccess(GetDisplaySettings200),
|
||
"401": decodeError("GetDisplaySettings401", GetDisplaySettings401),
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"setDisplaySettings": (options) => HttpClientRequest.put(`/api/v1/display/settings`).pipe(
|
||
HttpClientRequest.bodyJsonUnsafe(options.payload),
|
||
withResponse(options.config)(HttpClientResponse.matchStatus({
|
||
"2xx": decodeSuccess(SetDisplaySettings200),
|
||
"400": decodeError("SetDisplaySettings400", SetDisplaySettings400),
|
||
"401": decodeError("SetDisplaySettings401", SetDisplaySettings401),
|
||
"500": decodeError("SetDisplaySettings500", SetDisplaySettings500),
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"getDisplayState": (options) => HttpClientRequest.get(`/api/v1/display/state`).pipe(
|
||
withResponse(options?.config)(HttpClientResponse.matchStatus({
|
||
"2xx": decodeSuccess(GetDisplayState200),
|
||
"401": decodeError("GetDisplayState401", GetDisplayState401),
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"streamEvents": (options) => HttpClientRequest.get(`/api/v1/events`).pipe(
|
||
HttpClientRequest.setUrlParams({ "since": options?.params?.["since"] as any, "kinds": options?.params?.["kinds"] as any }),
|
||
HttpClientRequest.setHeaders({ "Last-Event-ID": options?.params?.["Last-Event-ID"] ?? undefined }),
|
||
withResponse(options?.config)(HttpClientResponse.matchStatus({
|
||
"401": decodeError("StreamEvents401", StreamEvents401),
|
||
"503": decodeError("StreamEvents503", StreamEvents503),
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"streamEventsSse": (options) => HttpClientRequest.get(`/api/v1/events`).pipe(
|
||
HttpClientRequest.setUrlParams({ "since": options?.params?.["since"] as any, "kinds": options?.params?.["kinds"] as any }),
|
||
HttpClientRequest.setHeaders({ "Last-Event-ID": options?.params?.["Last-Event-ID"] ?? undefined }),
|
||
sseRequest(StreamEvents200Sse)
|
||
),
|
||
"listGpus": (options) => HttpClientRequest.get(`/api/v1/gpus`).pipe(
|
||
withResponse(options?.config)(HttpClientResponse.matchStatus({
|
||
"2xx": decodeSuccess(ListGpus200),
|
||
"401": decodeError("ListGpus401", ListGpus401),
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"setGpuPreference": (options) => HttpClientRequest.put(`/api/v1/gpus/preference`).pipe(
|
||
HttpClientRequest.bodyJsonUnsafe(options.payload),
|
||
withResponse(options.config)(HttpClientResponse.matchStatus({
|
||
"2xx": decodeSuccess(SetGpuPreference200),
|
||
"400": decodeError("SetGpuPreference400", SetGpuPreference400),
|
||
"401": decodeError("SetGpuPreference401", SetGpuPreference401),
|
||
"500": decodeError("SetGpuPreference500", SetGpuPreference500),
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"getHealth": (options) => HttpClientRequest.get(`/api/v1/health`).pipe(
|
||
withResponse(options?.config)(HttpClientResponse.matchStatus({
|
||
"2xx": decodeSuccess(GetHealth200),
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"getHooks": (options) => HttpClientRequest.get(`/api/v1/hooks`).pipe(
|
||
withResponse(options?.config)(HttpClientResponse.matchStatus({
|
||
"2xx": decodeSuccess(GetHooks200),
|
||
"401": decodeError("GetHooks401", GetHooks401),
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"setHooks": (options) => HttpClientRequest.put(`/api/v1/hooks`).pipe(
|
||
HttpClientRequest.bodyJsonUnsafe(options.payload),
|
||
withResponse(options.config)(HttpClientResponse.matchStatus({
|
||
"2xx": decodeSuccess(SetHooks200),
|
||
"400": decodeError("SetHooks400", SetHooks400),
|
||
"401": decodeError("SetHooks401", SetHooks401),
|
||
"500": decodeError("SetHooks500", SetHooks500),
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"getHostInfo": (options) => HttpClientRequest.get(`/api/v1/host`).pipe(
|
||
withResponse(options?.config)(HttpClientResponse.matchStatus({
|
||
"2xx": decodeSuccess(GetHostInfo200),
|
||
"401": decodeError("GetHostInfo401", GetHostInfo401),
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"getLibrary": (options) => HttpClientRequest.get(`/api/v1/library`).pipe(
|
||
HttpClientRequest.setUrlParams({ "provider": options?.params?.["provider"] as any }),
|
||
withResponse(options?.config)(HttpClientResponse.matchStatus({
|
||
"2xx": decodeSuccess(GetLibrary200),
|
||
"401": decodeError("GetLibrary401", GetLibrary401),
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"getLibraryArt": (id, kind, options) => HttpClientRequest.get(`/api/v1/library/art/${id}/${kind}`).pipe(
|
||
withResponse(options?.config)(HttpClientResponse.matchStatus({
|
||
"401": decodeError("GetLibraryArt401", GetLibraryArt401),
|
||
"404": decodeError("GetLibraryArt404", GetLibraryArt404),
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"createCustomGame": (options) => HttpClientRequest.post(`/api/v1/library/custom`).pipe(
|
||
HttpClientRequest.bodyJsonUnsafe(options.payload),
|
||
withResponse(options.config)(HttpClientResponse.matchStatus({
|
||
"2xx": decodeSuccess(CreateCustomGame201),
|
||
"400": decodeError("CreateCustomGame400", CreateCustomGame400),
|
||
"401": decodeError("CreateCustomGame401", CreateCustomGame401),
|
||
"500": decodeError("CreateCustomGame500", CreateCustomGame500),
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"updateCustomGame": (id, options) => HttpClientRequest.put(`/api/v1/library/custom/${id}`).pipe(
|
||
HttpClientRequest.bodyJsonUnsafe(options.payload),
|
||
withResponse(options.config)(HttpClientResponse.matchStatus({
|
||
"2xx": decodeSuccess(UpdateCustomGame200),
|
||
"400": decodeError("UpdateCustomGame400", UpdateCustomGame400),
|
||
"401": decodeError("UpdateCustomGame401", UpdateCustomGame401),
|
||
"404": decodeError("UpdateCustomGame404", UpdateCustomGame404),
|
||
"500": decodeError("UpdateCustomGame500", UpdateCustomGame500),
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"deleteCustomGame": (id, options) => HttpClientRequest.delete(`/api/v1/library/custom/${id}`).pipe(
|
||
withResponse(options?.config)(HttpClientResponse.matchStatus({
|
||
"401": decodeError("DeleteCustomGame401", DeleteCustomGame401),
|
||
"404": decodeError("DeleteCustomGame404", DeleteCustomGame404),
|
||
"500": decodeError("DeleteCustomGame500", DeleteCustomGame500),
|
||
"204": () => Effect.void,
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"reconcileProviderEntries": (provider, options) => HttpClientRequest.put(`/api/v1/library/provider/${provider}`).pipe(
|
||
HttpClientRequest.bodyJsonUnsafe(options.payload),
|
||
withResponse(options.config)(HttpClientResponse.matchStatus({
|
||
"2xx": decodeSuccess(ReconcileProviderEntries200),
|
||
"400": decodeError("ReconcileProviderEntries400", ReconcileProviderEntries400),
|
||
"401": decodeError("ReconcileProviderEntries401", ReconcileProviderEntries401),
|
||
"500": decodeError("ReconcileProviderEntries500", ReconcileProviderEntries500),
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"deleteProviderEntries": (provider, options) => HttpClientRequest.delete(`/api/v1/library/provider/${provider}`).pipe(
|
||
withResponse(options?.config)(HttpClientResponse.matchStatus({
|
||
"2xx": decodeSuccess(DeleteProviderEntries200),
|
||
"400": decodeError("DeleteProviderEntries400", DeleteProviderEntries400),
|
||
"401": decodeError("DeleteProviderEntries401", DeleteProviderEntries401),
|
||
"500": decodeError("DeleteProviderEntries500", DeleteProviderEntries500),
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"getLocalSummary": (options) => HttpClientRequest.get(`/api/v1/local/summary`).pipe(
|
||
withResponse(options?.config)(HttpClientResponse.matchStatus({
|
||
"2xx": decodeSuccess(GetLocalSummary200),
|
||
"401": decodeError("GetLocalSummary401", GetLocalSummary401),
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"logsGet": (options) => HttpClientRequest.get(`/api/v1/logs`).pipe(
|
||
HttpClientRequest.setUrlParams({ "after": options?.params?.["after"] as any, "limit": options?.params?.["limit"] as any }),
|
||
withResponse(options?.config)(HttpClientResponse.matchStatus({
|
||
"2xx": decodeSuccess(LogsGet200),
|
||
"401": decodeError("LogsGet401", LogsGet401),
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"listNativeClients": (options) => HttpClientRequest.get(`/api/v1/native/clients`).pipe(
|
||
withResponse(options?.config)(HttpClientResponse.matchStatus({
|
||
"2xx": decodeSuccess(ListNativeClients200),
|
||
"401": decodeError("ListNativeClients401", ListNativeClients401),
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"unpairNativeClient": (fingerprint, options) => HttpClientRequest.delete(`/api/v1/native/clients/${fingerprint}`).pipe(
|
||
withResponse(options?.config)(HttpClientResponse.matchStatus({
|
||
"401": decodeError("UnpairNativeClient401", UnpairNativeClient401),
|
||
"404": decodeError("UnpairNativeClient404", UnpairNativeClient404),
|
||
"503": decodeError("UnpairNativeClient503", UnpairNativeClient503),
|
||
"204": () => Effect.void,
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"getNativePairing": (options) => HttpClientRequest.get(`/api/v1/native/pair`).pipe(
|
||
withResponse(options?.config)(HttpClientResponse.matchStatus({
|
||
"2xx": decodeSuccess(GetNativePairing200),
|
||
"401": decodeError("GetNativePairing401", GetNativePairing401),
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"disarmNativePairing": (options) => HttpClientRequest.delete(`/api/v1/native/pair`).pipe(
|
||
withResponse(options?.config)(HttpClientResponse.matchStatus({
|
||
"401": decodeError("DisarmNativePairing401", DisarmNativePairing401),
|
||
"503": decodeError("DisarmNativePairing503", DisarmNativePairing503),
|
||
"204": () => Effect.void,
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"armNativePairing": (options) => HttpClientRequest.post(`/api/v1/native/pair/arm`).pipe(
|
||
HttpClientRequest.bodyJsonUnsafe(options.payload),
|
||
withResponse(options.config)(HttpClientResponse.matchStatus({
|
||
"2xx": decodeSuccess(ArmNativePairing200),
|
||
"401": decodeError("ArmNativePairing401", ArmNativePairing401),
|
||
"503": decodeError("ArmNativePairing503", ArmNativePairing503),
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"listPendingDevices": (options) => HttpClientRequest.get(`/api/v1/native/pending`).pipe(
|
||
withResponse(options?.config)(HttpClientResponse.matchStatus({
|
||
"2xx": decodeSuccess(ListPendingDevices200),
|
||
"401": decodeError("ListPendingDevices401", ListPendingDevices401),
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"approvePendingDevice": (id, options) => HttpClientRequest.post(`/api/v1/native/pending/${id}/approve`).pipe(
|
||
HttpClientRequest.bodyJsonUnsafe(options.payload),
|
||
withResponse(options.config)(HttpClientResponse.matchStatus({
|
||
"2xx": decodeSuccess(ApprovePendingDevice200),
|
||
"401": decodeError("ApprovePendingDevice401", ApprovePendingDevice401),
|
||
"404": decodeError("ApprovePendingDevice404", ApprovePendingDevice404),
|
||
"500": decodeError("ApprovePendingDevice500", ApprovePendingDevice500),
|
||
"503": decodeError("ApprovePendingDevice503", ApprovePendingDevice503),
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"denyPendingDevice": (id, options) => HttpClientRequest.post(`/api/v1/native/pending/${id}/deny`).pipe(
|
||
withResponse(options?.config)(HttpClientResponse.matchStatus({
|
||
"401": decodeError("DenyPendingDevice401", DenyPendingDevice401),
|
||
"404": decodeError("DenyPendingDevice404", DenyPendingDevice404),
|
||
"503": decodeError("DenyPendingDevice503", DenyPendingDevice503),
|
||
"204": () => Effect.void,
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"getPairingStatus": (options) => HttpClientRequest.get(`/api/v1/pair`).pipe(
|
||
withResponse(options?.config)(HttpClientResponse.matchStatus({
|
||
"2xx": decodeSuccess(GetPairingStatus200),
|
||
"401": decodeError("GetPairingStatus401", GetPairingStatus401),
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"submitPairingPin": (options) => HttpClientRequest.post(`/api/v1/pair/pin`).pipe(
|
||
HttpClientRequest.bodyJsonUnsafe(options.payload),
|
||
withResponse(options.config)(HttpClientResponse.matchStatus({
|
||
"400": decodeError("SubmitPairingPin400", SubmitPairingPin400),
|
||
"401": decodeError("SubmitPairingPin401", SubmitPairingPin401),
|
||
"409": decodeError("SubmitPairingPin409", SubmitPairingPin409),
|
||
"415": decodeError("SubmitPairingPin415", SubmitPairingPin415),
|
||
"422": decodeError("SubmitPairingPin422", SubmitPairingPin422),
|
||
"204": () => Effect.void,
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"listPlugins": (options) => HttpClientRequest.get(`/api/v1/plugins`).pipe(
|
||
withResponse(options?.config)(HttpClientResponse.matchStatus({
|
||
"2xx": decodeSuccess(ListPlugins200),
|
||
"401": decodeError("ListPlugins401", ListPlugins401),
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"registerPlugin": (id, options) => HttpClientRequest.put(`/api/v1/plugins/${id}`).pipe(
|
||
HttpClientRequest.bodyJsonUnsafe(options.payload),
|
||
withResponse(options.config)(HttpClientResponse.matchStatus({
|
||
"400": decodeError("RegisterPlugin400", RegisterPlugin400),
|
||
"401": decodeError("RegisterPlugin401", RegisterPlugin401),
|
||
"204": () => Effect.void,
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"deregisterPlugin": (id, options) => HttpClientRequest.delete(`/api/v1/plugins/${id}`).pipe(
|
||
withResponse(options?.config)(HttpClientResponse.matchStatus({
|
||
"401": decodeError("DeregisterPlugin401", DeregisterPlugin401),
|
||
"204": () => Effect.void,
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"getPluginUiCredential": (id, options) => HttpClientRequest.get(`/api/v1/plugins/${id}/ui-credential`).pipe(
|
||
withResponse(options?.config)(HttpClientResponse.matchStatus({
|
||
"2xx": decodeSuccess(GetPluginUiCredential200),
|
||
"401": decodeError("GetPluginUiCredential401", GetPluginUiCredential401),
|
||
"404": decodeError("GetPluginUiCredential404", GetPluginUiCredential404),
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"stopSession": (options) => HttpClientRequest.delete(`/api/v1/session`).pipe(
|
||
withResponse(options?.config)(HttpClientResponse.matchStatus({
|
||
"401": decodeError("StopSession401", StopSession401),
|
||
"204": () => Effect.void,
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"requestIdr": (options) => HttpClientRequest.post(`/api/v1/session/idr`).pipe(
|
||
withResponse(options?.config)(HttpClientResponse.matchStatus({
|
||
"401": decodeError("RequestIdr401", RequestIdr401),
|
||
"409": decodeError("RequestIdr409", RequestIdr409),
|
||
"202": () => Effect.void,
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"statsCaptureLive": (options) => HttpClientRequest.get(`/api/v1/stats/capture/live`).pipe(
|
||
withResponse(options?.config)(HttpClientResponse.matchStatus({
|
||
"2xx": decodeSuccess(StatsCaptureLive200),
|
||
"401": decodeError("StatsCaptureLive401", StatsCaptureLive401),
|
||
"404": decodeError("StatsCaptureLive404", StatsCaptureLive404),
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"statsCaptureStart": (options) => HttpClientRequest.post(`/api/v1/stats/capture/start`).pipe(
|
||
withResponse(options?.config)(HttpClientResponse.matchStatus({
|
||
"2xx": decodeSuccess(StatsCaptureStart200),
|
||
"401": decodeError("StatsCaptureStart401", StatsCaptureStart401),
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"statsCaptureStatus": (options) => HttpClientRequest.get(`/api/v1/stats/capture/status`).pipe(
|
||
withResponse(options?.config)(HttpClientResponse.matchStatus({
|
||
"2xx": decodeSuccess(StatsCaptureStatus200),
|
||
"401": decodeError("StatsCaptureStatus401", StatsCaptureStatus401),
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"statsCaptureStop": (options) => HttpClientRequest.post(`/api/v1/stats/capture/stop`).pipe(
|
||
withResponse(options?.config)(HttpClientResponse.matchStatus({
|
||
"2xx": decodeSuccess(StatsCaptureStop200),
|
||
"401": decodeError("StatsCaptureStop401", StatsCaptureStop401),
|
||
"500": decodeError("StatsCaptureStop500", StatsCaptureStop500),
|
||
"204": () => Effect.void,
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"statsRecordingsList": (options) => HttpClientRequest.get(`/api/v1/stats/recordings`).pipe(
|
||
withResponse(options?.config)(HttpClientResponse.matchStatus({
|
||
"2xx": decodeSuccess(StatsRecordingsList200),
|
||
"401": decodeError("StatsRecordingsList401", StatsRecordingsList401),
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"statsRecordingGet": (id, options) => HttpClientRequest.get(`/api/v1/stats/recordings/${id}`).pipe(
|
||
withResponse(options?.config)(HttpClientResponse.matchStatus({
|
||
"2xx": decodeSuccess(StatsRecordingGet200),
|
||
"401": decodeError("StatsRecordingGet401", StatsRecordingGet401),
|
||
"404": decodeError("StatsRecordingGet404", StatsRecordingGet404),
|
||
"500": decodeError("StatsRecordingGet500", StatsRecordingGet500),
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"statsRecordingDelete": (id, options) => HttpClientRequest.delete(`/api/v1/stats/recordings/${id}`).pipe(
|
||
withResponse(options?.config)(HttpClientResponse.matchStatus({
|
||
"401": decodeError("StatsRecordingDelete401", StatsRecordingDelete401),
|
||
"404": decodeError("StatsRecordingDelete404", StatsRecordingDelete404),
|
||
"500": decodeError("StatsRecordingDelete500", StatsRecordingDelete500),
|
||
"204": () => Effect.void,
|
||
orElse: unexpectedStatus
|
||
}))
|
||
),
|
||
"getStatus": (options) => HttpClientRequest.get(`/api/v1/status`).pipe(
|
||
withResponse(options?.config)(HttpClientResponse.matchStatus({
|
||
"2xx": decodeSuccess(GetStatus200),
|
||
"401": decodeError("GetStatus401", GetStatus401),
|
||
orElse: unexpectedStatus
|
||
}))
|
||
)
|
||
}
|
||
}
|
||
|
||
export interface Punktfunk {
|
||
readonly httpClient: HttpClient.HttpClient
|
||
/**
|
||
* List paired clients
|
||
*/
|
||
readonly "listPairedClients": <Config extends OperationConfig>(options: { readonly config?: Config | undefined } | undefined) => Effect.Effect<WithOptionalResponse<typeof ListPairedClients200.Type, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"ListPairedClients401", typeof ListPairedClients401.Type>>
|
||
/**
|
||
* Removes the client's certificate from the pairing store. Caveat: the nvhttp TLS layer
|
||
* does not yet reject unlisted certificates (`gamestream/tls.rs` accepts any well-formed
|
||
* client cert — a planned hardening step), so until that lands this removes the client
|
||
* from the listing without severing its ability to reconnect.
|
||
*/
|
||
readonly "unpairClient": <Config extends OperationConfig>(fingerprint: string, options: { readonly config?: Config | undefined } | undefined) => Effect.Effect<WithOptionalResponse<void, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"UnpairClient400", typeof UnpairClient400.Type> | PunktfunkError<"UnpairClient401", typeof UnpairClient401.Type> | PunktfunkError<"UnpairClient404", typeof UnpairClient404.Type>>
|
||
/**
|
||
* Lists every backend the host knows how to drive, flags which are usable right now, and marks
|
||
* the one an unspecified (`Auto`) client request resolves to. Clients pass an `id` to their
|
||
* `--compositor` flag (or `PUNKTFUNK_COMPOSITOR_*` over the C ABI) to request it.
|
||
*/
|
||
readonly "listCompositors": <Config extends OperationConfig>(options: { readonly config?: Config | undefined } | undefined) => Effect.Effect<WithOptionalResponse<typeof ListCompositors200.Type, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"ListCompositors401", typeof ListCompositors401.Type>>
|
||
/**
|
||
* Set the **manual** desktop arrangement — per-identity-slot `(x, y)` offsets so a multi-monitor
|
||
* group (§6A/§6B) comes back where the operator placed it. Persisted into the policy's layout block
|
||
* and switched to manual mode; applied from the next connect (a live group re-applies on its next
|
||
* acquire). Locks in the current effective behavior as explicit fields, so arranging displays never
|
||
* silently changes keep-alive/topology/conflict/identity. See `design/display-management.md` §6.2.
|
||
*/
|
||
readonly "setDisplayLayout": <Config extends OperationConfig>(options: { readonly payload: typeof SetDisplayLayoutRequestJson.Encoded; readonly config?: Config | undefined }) => Effect.Effect<WithOptionalResponse<typeof SetDisplayLayout200.Type, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"SetDisplayLayout401", typeof SetDisplayLayout401.Type> | PunktfunkError<"SetDisplayLayout500", typeof SetDisplayLayout500.Type>>
|
||
/**
|
||
* The operator's named field-bundles (`display-presets.json`). These also ride the
|
||
* `GET /display/settings` response (`custom_presets`), so the console rarely needs this directly.
|
||
*/
|
||
readonly "listCustomPresets": <Config extends OperationConfig>(options: { readonly config?: Config | undefined } | undefined) => Effect.Effect<WithOptionalResponse<typeof ListCustomPresets200.Type, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"ListCustomPresets401", typeof ListCustomPresets401.Type>>
|
||
/**
|
||
* Stores a named bundle of the display-behavior axes (+ the game-session axis) the operator can
|
||
* apply later. The host assigns a stable id, returned in the body. Applying a preset is a
|
||
* `PUT /display/settings` with a `Custom` policy carrying its `fields` — no separate apply route.
|
||
*/
|
||
readonly "createCustomPreset": <Config extends OperationConfig>(options: { readonly payload: typeof CreateCustomPresetRequestJson.Encoded; readonly config?: Config | undefined }) => Effect.Effect<WithOptionalResponse<typeof CreateCustomPreset201.Type, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"CreateCustomPreset400", typeof CreateCustomPreset400.Type> | PunktfunkError<"CreateCustomPreset401", typeof CreateCustomPreset401.Type> | PunktfunkError<"CreateCustomPreset500", typeof CreateCustomPreset500.Type>>
|
||
/**
|
||
* Update a custom preset
|
||
*/
|
||
readonly "updateCustomPreset": <Config extends OperationConfig>(id: string, options: { readonly payload: typeof UpdateCustomPresetRequestJson.Encoded; readonly config?: Config | undefined }) => Effect.Effect<WithOptionalResponse<typeof UpdateCustomPreset200.Type, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"UpdateCustomPreset400", typeof UpdateCustomPreset400.Type> | PunktfunkError<"UpdateCustomPreset401", typeof UpdateCustomPreset401.Type> | PunktfunkError<"UpdateCustomPreset404", typeof UpdateCustomPreset404.Type> | PunktfunkError<"UpdateCustomPreset500", typeof UpdateCustomPreset500.Type>>
|
||
/**
|
||
* Removes it from the catalog. The active policy is untouched — if this preset was the one applied,
|
||
* the running behavior stays exactly as it was (the catalog and `display-settings.json` are decoupled).
|
||
*/
|
||
readonly "deleteCustomPreset": <Config extends OperationConfig>(id: string, options: { readonly config?: Config | undefined } | undefined) => Effect.Effect<WithOptionalResponse<void, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"DeleteCustomPreset401", typeof DeleteCustomPreset401.Type> | PunktfunkError<"DeleteCustomPreset404", typeof DeleteCustomPreset404.Type> | PunktfunkError<"DeleteCustomPreset500", typeof DeleteCustomPreset500.Type>>
|
||
/**
|
||
* Tear down lingering/pinned displays now — so a physical-screen user gets their screen back
|
||
* without waiting out the linger. `slot` releases one; omit it to release all kept displays.
|
||
* Active (streaming) displays are never torn down here (that is session control).
|
||
*/
|
||
readonly "releaseDisplay": <Config extends OperationConfig>(options: { readonly payload: typeof ReleaseDisplayRequestJson.Encoded; readonly config?: Config | undefined }) => Effect.Effect<WithOptionalResponse<typeof ReleaseDisplay200.Type, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"ReleaseDisplay401", typeof ReleaseDisplay401.Type>>
|
||
/**
|
||
* The stored virtual-display policy (lifecycle, topology, conflict handling, identity, layout),
|
||
* every preset's expansion, and which options this build enforces yet. See
|
||
* `design/display-management.md`.
|
||
*/
|
||
readonly "getDisplaySettings": <Config extends OperationConfig>(options: { readonly config?: Config | undefined } | undefined) => Effect.Effect<WithOptionalResponse<typeof GetDisplaySettings200.Type, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"GetDisplaySettings401", typeof GetDisplaySettings401.Type>>
|
||
/**
|
||
* Persists a new policy (validated + clamped) and applies it from the next connect/teardown — a
|
||
* running session keeps the display it opened on. `keep_alive: forever` (the gaming-rig preset) is
|
||
* honored (the display is Pinned; free it via `POST /display/release`).
|
||
*/
|
||
readonly "setDisplaySettings": <Config extends OperationConfig>(options: { readonly payload: typeof SetDisplaySettingsRequestJson.Encoded; readonly config?: Config | undefined }) => Effect.Effect<WithOptionalResponse<typeof SetDisplaySettings200.Type, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"SetDisplaySettings400", typeof SetDisplaySettings400.Type> | PunktfunkError<"SetDisplaySettings401", typeof SetDisplaySettings401.Type> | PunktfunkError<"SetDisplaySettings500", typeof SetDisplaySettings500.Type>>
|
||
/**
|
||
* The host's managed virtual displays right now — active (streaming), lingering (kept after
|
||
* disconnect, counting down to teardown), or pinned (kept indefinitely). See
|
||
* `design/display-management.md`.
|
||
*/
|
||
readonly "getDisplayState": <Config extends OperationConfig>(options: { readonly config?: Config | undefined } | undefined) => Effect.Effect<WithOptionalResponse<typeof GetDisplayState200.Type, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"GetDisplayState401", typeof GetDisplayState401.Type>>
|
||
/**
|
||
* Server-Sent Events stream of the host's lifecycle events: client connect/disconnect, session
|
||
* and stream start/end, pairing decisions, display create/release, library changes, host
|
||
* start/stop — both protocol planes. Frames carry `id:` = the event's monotonic `seq`,
|
||
* `event:` = its kind, and `data:` = the event JSON (schema-versioned, additive-only).
|
||
*
|
||
* Resume: standard `Last-Event-ID` (or `?since=`) replays from the in-memory ring; a consumer
|
||
* that fell off the ring receives an `event: dropped` frame first and should resync via the
|
||
* REST snapshots. Keep-alive comments are sent every 15 s.
|
||
*/
|
||
readonly "streamEvents": <Config extends OperationConfig>(options: { readonly params?: typeof StreamEventsParams.Encoded | undefined; readonly config?: Config | undefined } | undefined) => Effect.Effect<WithOptionalResponse<void, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"StreamEvents401", typeof StreamEvents401.Type> | PunktfunkError<"StreamEvents503", typeof StreamEvents503.Type>>
|
||
/**
|
||
* Server-Sent Events stream of the host's lifecycle events: client connect/disconnect, session
|
||
* and stream start/end, pairing decisions, display create/release, library changes, host
|
||
* start/stop — both protocol planes. Frames carry `id:` = the event's monotonic `seq`,
|
||
* `event:` = its kind, and `data:` = the event JSON (schema-versioned, additive-only).
|
||
*
|
||
* Resume: standard `Last-Event-ID` (or `?since=`) replays from the in-memory ring; a consumer
|
||
* that fell off the ring receives an `event: dropped` frame first and should resync via the
|
||
* REST snapshots. Keep-alive comments are sent every 15 s.
|
||
*/
|
||
readonly "streamEventsSse": (options: { readonly params?: typeof StreamEventsParams.Encoded | undefined } | undefined) => Stream.Stream<{ readonly event: string; readonly id: string | undefined; readonly data: typeof StreamEvents200Sse.Type }, HttpClientError.HttpClientError | SchemaError | Sse.Retry, typeof StreamEvents200Sse.DecodingServices>
|
||
/**
|
||
* Lists the host's hardware GPUs, the persisted auto/manual preference, the GPU the next session
|
||
* will use (and why), and the GPU live sessions encode on right now.
|
||
*/
|
||
readonly "listGpus": <Config extends OperationConfig>(options: { readonly config?: Config | undefined } | undefined) => Effect.Effect<WithOptionalResponse<typeof ListGpus200.Type, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"ListGpus401", typeof ListGpus401.Type>>
|
||
/**
|
||
* `auto` restores automatic selection (`PUNKTFUNK_RENDER_ADAPTER` pin, else max dedicated VRAM);
|
||
* `manual` pins capture + encode to the given GPU. Persisted across restarts; applies to the
|
||
* **next** session (a running session keeps its GPU). If the preferred GPU is absent at session
|
||
* start the host falls back to automatic selection rather than failing.
|
||
*/
|
||
readonly "setGpuPreference": <Config extends OperationConfig>(options: { readonly payload: typeof SetGpuPreferenceRequestJson.Encoded; readonly config?: Config | undefined }) => Effect.Effect<WithOptionalResponse<typeof SetGpuPreference200.Type, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"SetGpuPreference400", typeof SetGpuPreference400.Type> | PunktfunkError<"SetGpuPreference401", typeof SetGpuPreference401.Type> | PunktfunkError<"SetGpuPreference500", typeof SetGpuPreference500.Type>>
|
||
/**
|
||
* Always available without authentication.
|
||
*/
|
||
readonly "getHealth": <Config extends OperationConfig>(options: { readonly config?: Config | undefined } | undefined) => Effect.Effect<WithOptionalResponse<typeof GetHealth200.Type, Config>, HttpClientError.HttpClientError | SchemaError>
|
||
/**
|
||
* The operator's `hooks.json`: commands and webhooks fired on host lifecycle events. Empty
|
||
* when unconfigured.
|
||
*/
|
||
readonly "getHooks": <Config extends OperationConfig>(options: { readonly config?: Config | undefined } | undefined) => Effect.Effect<WithOptionalResponse<typeof GetHooks200.Type, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"GetHooks401", typeof GetHooks401.Type>>
|
||
/**
|
||
* Validates and persists a full `hooks.json` document (this is a whole-document PUT, not a
|
||
* patch). Applies from the next event — no restart. Hook commands run as the host user
|
||
* (interactive user session on Windows): treat this configuration as operator-privileged.
|
||
*/
|
||
readonly "setHooks": <Config extends OperationConfig>(options: { readonly payload: typeof SetHooksRequestJson.Encoded; readonly config?: Config | undefined }) => Effect.Effect<WithOptionalResponse<typeof SetHooks200.Type, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"SetHooks400", typeof SetHooks400.Type> | PunktfunkError<"SetHooks401", typeof SetHooks401.Type> | PunktfunkError<"SetHooks500", typeof SetHooks500.Type>>
|
||
/**
|
||
* Host identity and capabilities
|
||
*/
|
||
readonly "getHostInfo": <Config extends OperationConfig>(options: { readonly config?: Config | undefined } | undefined) => Effect.Effect<WithOptionalResponse<typeof GetHostInfo200.Type, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"GetHostInfo401", typeof GetHostInfo401.Type>>
|
||
/**
|
||
* Every installed-store title (Steam, read from the host's local files — no Steam API key)
|
||
* merged with the user's custom entries, sorted by title. Artwork fields are URLs the client
|
||
* fetches directly (the public Steam CDN for Steam titles). `?provider=` narrows to the
|
||
* entries a given external provider owns.
|
||
*/
|
||
readonly "getLibrary": <Config extends OperationConfig>(options: { readonly params?: typeof GetLibraryParams.Encoded | undefined; readonly config?: Config | undefined } | undefined) => Effect.Effect<WithOptionalResponse<typeof GetLibrary200.Type, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"GetLibrary401", typeof GetLibrary401.Type>>
|
||
/**
|
||
* Resolves `kind` (`portrait` | `hero` | `logo` | `header`) for the given library id and streams
|
||
* the image bytes. For a Steam title, the host's own local Steam cache is tried first (exact —
|
||
* it's what the user's Steam client already shows for it), the public Steam CDN's flat URL
|
||
* convention as a fallback (newer titles' CDN assets can live at a per-asset-hash path the host
|
||
* can't predict, in which case this 404s and the client falls through to its next art candidate).
|
||
* Only Steam ids are backed today; any other store 404s.
|
||
*/
|
||
readonly "getLibraryArt": <Config extends OperationConfig>(id: string, kind: string, options: { readonly config?: Config | undefined } | undefined) => Effect.Effect<WithOptionalResponse<void, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"GetLibraryArt401", typeof GetLibraryArt401.Type> | PunktfunkError<"GetLibraryArt404", typeof GetLibraryArt404.Type>>
|
||
/**
|
||
* Creates a user-curated title (e.g. a non-Steam game, an emulator, a ROM) with caller-supplied
|
||
* artwork URLs. The host assigns a stable id, returned in the body.
|
||
*/
|
||
readonly "createCustomGame": <Config extends OperationConfig>(options: { readonly payload: typeof CreateCustomGameRequestJson.Encoded; readonly config?: Config | undefined }) => Effect.Effect<WithOptionalResponse<typeof CreateCustomGame201.Type, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"CreateCustomGame400", typeof CreateCustomGame400.Type> | PunktfunkError<"CreateCustomGame401", typeof CreateCustomGame401.Type> | PunktfunkError<"CreateCustomGame500", typeof CreateCustomGame500.Type>>
|
||
/**
|
||
* Update a custom library entry
|
||
*/
|
||
readonly "updateCustomGame": <Config extends OperationConfig>(id: string, options: { readonly payload: typeof UpdateCustomGameRequestJson.Encoded; readonly config?: Config | undefined }) => Effect.Effect<WithOptionalResponse<typeof UpdateCustomGame200.Type, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"UpdateCustomGame400", typeof UpdateCustomGame400.Type> | PunktfunkError<"UpdateCustomGame401", typeof UpdateCustomGame401.Type> | PunktfunkError<"UpdateCustomGame404", typeof UpdateCustomGame404.Type> | PunktfunkError<"UpdateCustomGame500", typeof UpdateCustomGame500.Type>>
|
||
/**
|
||
* Delete a custom library entry
|
||
*/
|
||
readonly "deleteCustomGame": <Config extends OperationConfig>(id: string, options: { readonly config?: Config | undefined } | undefined) => Effect.Effect<WithOptionalResponse<void, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"DeleteCustomGame401", typeof DeleteCustomGame401.Type> | PunktfunkError<"DeleteCustomGame404", typeof DeleteCustomGame404.Type> | PunktfunkError<"DeleteCustomGame500", typeof DeleteCustomGame500.Type>>
|
||
/**
|
||
* Atomically replaces the full entry set owned by `{provider}` (RFC §8): the payload is the
|
||
* provider's desired list, keyed by its own stable `external_id` — the host diffs, keeps each
|
||
* surviving title's host id stable across reconciles, drops orphans, and never touches manual
|
||
* entries or other providers'. An empty array removes everything the provider owns. Emits
|
||
* `library.changed` with the provider as `source`.
|
||
*/
|
||
readonly "reconcileProviderEntries": <Config extends OperationConfig>(provider: string, options: { readonly payload: typeof ReconcileProviderEntriesRequestJson.Encoded; readonly config?: Config | undefined }) => Effect.Effect<WithOptionalResponse<typeof ReconcileProviderEntries200.Type, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"ReconcileProviderEntries400", typeof ReconcileProviderEntries400.Type> | PunktfunkError<"ReconcileProviderEntries401", typeof ReconcileProviderEntries401.Type> | PunktfunkError<"ReconcileProviderEntries500", typeof ReconcileProviderEntries500.Type>>
|
||
/**
|
||
* Deletes every entry owned by `{provider}` — the clean-uninstall path for a provider plugin
|
||
* (RFC §8). Emits `library.changed` when anything was removed.
|
||
*/
|
||
readonly "deleteProviderEntries": <Config extends OperationConfig>(provider: string, options: { readonly config?: Config | undefined } | undefined) => Effect.Effect<WithOptionalResponse<typeof DeleteProviderEntries200.Type, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"DeleteProviderEntries400", typeof DeleteProviderEntries400.Type> | PunktfunkError<"DeleteProviderEntries401", typeof DeleteProviderEntries401.Type> | PunktfunkError<"DeleteProviderEntries500", typeof DeleteProviderEntries500.Type>>
|
||
/**
|
||
* Non-sensitive status (counts and booleans only — no PIN values, no fingerprints, no device
|
||
* names). Unauthenticated, but served to loopback peers only.
|
||
*/
|
||
readonly "getLocalSummary": <Config extends OperationConfig>(options: { readonly config?: Config | undefined } | undefined) => Effect.Effect<WithOptionalResponse<typeof GetLocalSummary200.Type, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"GetLocalSummary401", typeof GetLocalSummary401.Type>>
|
||
/**
|
||
* The host's recent log entries — an in-memory ring of the newest few thousand, captured at
|
||
* DEBUG and above regardless of `RUST_LOG`. Follow live by polling with `after` set to the last
|
||
* response's `next` cursor; a `dropped: true` means entries were evicted between polls (the ring
|
||
* wrapped). Bearer-only: logs can reference client identities and host paths, so this is part of
|
||
* the loopback-only admin surface, never the LAN-readable mTLS one.
|
||
*/
|
||
readonly "logsGet": <Config extends OperationConfig>(options: { readonly params?: typeof LogsGetParams.Encoded | undefined; readonly config?: Config | undefined } | undefined) => Effect.Effect<WithOptionalResponse<typeof LogsGet200.Type, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"LogsGet401", typeof LogsGet401.Type>>
|
||
/**
|
||
* List native paired clients
|
||
*/
|
||
readonly "listNativeClients": <Config extends OperationConfig>(options: { readonly config?: Config | undefined } | undefined) => Effect.Effect<WithOptionalResponse<typeof ListNativeClients200.Type, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"ListNativeClients401", typeof ListNativeClients401.Type>>
|
||
/**
|
||
* Removes a punktfunk/1 client from the native trust store by fingerprint.
|
||
*/
|
||
readonly "unpairNativeClient": <Config extends OperationConfig>(fingerprint: string, options: { readonly config?: Config | undefined } | undefined) => Effect.Effect<WithOptionalResponse<void, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"UnpairNativeClient401", typeof UnpairNativeClient401.Type> | PunktfunkError<"UnpairNativeClient404", typeof UnpairNativeClient404.Type> | PunktfunkError<"UnpairNativeClient503", typeof UnpairNativeClient503.Type>>
|
||
/**
|
||
* The native (punktfunk/1) pairing window. Poll while armed to show the PIN + countdown.
|
||
* `enabled: false` means this host runs GameStream only (no `--native`).
|
||
*/
|
||
readonly "getNativePairing": <Config extends OperationConfig>(options: { readonly config?: Config | undefined } | undefined) => Effect.Effect<WithOptionalResponse<typeof GetNativePairing200.Type, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"GetNativePairing401", typeof GetNativePairing401.Type>>
|
||
/**
|
||
* Closes the pairing window immediately (no new ceremonies accepted).
|
||
*/
|
||
readonly "disarmNativePairing": <Config extends OperationConfig>(options: { readonly config?: Config | undefined } | undefined) => Effect.Effect<WithOptionalResponse<void, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"DisarmNativePairing401", typeof DisarmNativePairing401.Type> | PunktfunkError<"DisarmNativePairing503", typeof DisarmNativePairing503.Type>>
|
||
/**
|
||
* Opens a pairing window and mints a fresh PIN to display. The user enters it on their device
|
||
* within `ttl_secs`; the device then appears in the native client list.
|
||
*/
|
||
readonly "armNativePairing": <Config extends OperationConfig>(options: { readonly payload: typeof ArmNativePairingRequestJson.Encoded; readonly config?: Config | undefined }) => Effect.Effect<WithOptionalResponse<typeof ArmNativePairing200.Type, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"ArmNativePairing401", typeof ArmNativePairing401.Type> | PunktfunkError<"ArmNativePairing503", typeof ArmNativePairing503.Type>>
|
||
/**
|
||
* Unpaired devices that tried to connect while the host requires pairing. Approve one to pair
|
||
* it without a PIN (delegated approval); entries expire after ~10 minutes.
|
||
*/
|
||
readonly "listPendingDevices": <Config extends OperationConfig>(options: { readonly config?: Config | undefined } | undefined) => Effect.Effect<WithOptionalResponse<typeof ListPendingDevices200.Type, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"ListPendingDevices401", typeof ListPendingDevices401.Type>>
|
||
/**
|
||
* Pairs the device's certificate fingerprint — it can connect immediately (no PIN). Optionally
|
||
* relabel it via the body; send `{}` to keep the name it knocked with.
|
||
*/
|
||
readonly "approvePendingDevice": <Config extends OperationConfig>(id: string, options: { readonly payload: typeof ApprovePendingDeviceRequestJson.Encoded; readonly config?: Config | undefined }) => Effect.Effect<WithOptionalResponse<typeof ApprovePendingDevice200.Type, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"ApprovePendingDevice401", typeof ApprovePendingDevice401.Type> | PunktfunkError<"ApprovePendingDevice404", typeof ApprovePendingDevice404.Type> | PunktfunkError<"ApprovePendingDevice500", typeof ApprovePendingDevice500.Type> | PunktfunkError<"ApprovePendingDevice503", typeof ApprovePendingDevice503.Type>>
|
||
/**
|
||
* Drops the request. Not a blocklist — the device's next attempt knocks again.
|
||
*/
|
||
readonly "denyPendingDevice": <Config extends OperationConfig>(id: string, options: { readonly config?: Config | undefined } | undefined) => Effect.Effect<WithOptionalResponse<void, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"DenyPendingDevice401", typeof DenyPendingDevice401.Type> | PunktfunkError<"DenyPendingDevice404", typeof DenyPendingDevice404.Type> | PunktfunkError<"DenyPendingDevice503", typeof DenyPendingDevice503.Type>>
|
||
/**
|
||
* Poll this to know when to prompt the user for the PIN Moonlight displays.
|
||
*/
|
||
readonly "getPairingStatus": <Config extends OperationConfig>(options: { readonly config?: Config | undefined } | undefined) => Effect.Effect<WithOptionalResponse<typeof GetPairingStatus200.Type, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"GetPairingStatus401", typeof GetPairingStatus401.Type>>
|
||
/**
|
||
* Delivers the PIN the Moonlight client is displaying, completing the out-of-band half
|
||
* of the pairing handshake.
|
||
*/
|
||
readonly "submitPairingPin": <Config extends OperationConfig>(options: { readonly payload: typeof SubmitPairingPinRequestJson.Encoded; readonly config?: Config | undefined }) => Effect.Effect<WithOptionalResponse<void, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"SubmitPairingPin400", typeof SubmitPairingPin400.Type> | PunktfunkError<"SubmitPairingPin401", typeof SubmitPairingPin401.Type> | PunktfunkError<"SubmitPairingPin409", typeof SubmitPairingPin409.Type> | PunktfunkError<"SubmitPairingPin415", typeof SubmitPairingPin415.Type> | PunktfunkError<"SubmitPairingPin422", typeof SubmitPairingPin422.Type>>
|
||
/**
|
||
* The live plugin directory (lease not expired), sorted by title. **Secret-free**: each entry
|
||
* reports its id, title, optional version, and — for plugins that serve one — a UI descriptor
|
||
* (loopback port + icon). The console renders these as nav entries and proxies to the port; it
|
||
* fetches the secret separately, server-side.
|
||
*/
|
||
readonly "listPlugins": <Config extends OperationConfig>(options: { readonly config?: Config | undefined } | undefined) => Effect.Effect<WithOptionalResponse<typeof ListPlugins200.Type, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"ListPlugins401", typeof ListPlugins401.Type>>
|
||
/**
|
||
* Upserts the plugin's directory entry and renews its lease (TTL 90 s). Idempotent: a plugin PUTs
|
||
* this every ~30 s while it runs. The optional `ui` block declares a loopback UI surface the console
|
||
* will proxy and add to its nav. Emits `plugins.changed` when an operator-visible field changed
|
||
* (first registration, restart, or re-scan) — a pure renewal is silent.
|
||
*/
|
||
readonly "registerPlugin": <Config extends OperationConfig>(id: string, options: { readonly payload: typeof RegisterPluginRequestJson.Encoded; readonly config?: Config | undefined }) => Effect.Effect<WithOptionalResponse<void, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"RegisterPlugin400", typeof RegisterPlugin400.Type> | PunktfunkError<"RegisterPlugin401", typeof RegisterPlugin401.Type>>
|
||
/**
|
||
* The clean-shutdown path: removes the plugin's directory entry immediately (the SDK helper calls
|
||
* this from its scope finalizer on `SIGTERM`). Emits `plugins.changed` when a live entry was
|
||
* removed. Idempotent — deleting an unknown/expired id is a no-op `204`.
|
||
*/
|
||
readonly "deregisterPlugin": <Config extends OperationConfig>(id: string, options: { readonly config?: Config | undefined } | undefined) => Effect.Effect<WithOptionalResponse<void, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"DeregisterPlugin401", typeof DeregisterPlugin401.Type>>
|
||
/**
|
||
* Returns `{port, secret}` for a live plugin's loopback UI — the console proxy's server-side lookup.
|
||
* Bearer + loopback only (like every mutation), and additionally excluded from the console's browser
|
||
* passthrough: the secret never reaches a browser.
|
||
*/
|
||
readonly "getPluginUiCredential": <Config extends OperationConfig>(id: string, options: { readonly config?: Config | undefined } | undefined) => Effect.Effect<WithOptionalResponse<typeof GetPluginUiCredential200.Type, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"GetPluginUiCredential401", typeof GetPluginUiCredential401.Type> | PunktfunkError<"GetPluginUiCredential404", typeof GetPluginUiCredential404.Type>>
|
||
/**
|
||
* Kicks the connected client: stops the video/audio stream threads and clears the launch
|
||
* state. Idempotent — succeeds even when nothing is streaming.
|
||
*/
|
||
readonly "stopSession": <Config extends OperationConfig>(options: { readonly config?: Config | undefined } | undefined) => Effect.Effect<WithOptionalResponse<void, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"StopSession401", typeof StopSession401.Type>>
|
||
/**
|
||
* Asks the encoder for an IDR frame on the active video stream (what a client requests
|
||
* after unrecoverable loss — exposed for debugging).
|
||
*/
|
||
readonly "requestIdr": <Config extends OperationConfig>(options: { readonly config?: Config | undefined } | undefined) => Effect.Effect<WithOptionalResponse<void, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"RequestIdr401", typeof RequestIdr401.Type> | PunktfunkError<"RequestIdr409", typeof RequestIdr409.Type>>
|
||
/**
|
||
* The full sample time-series of the capture currently recording, for live graphing. `404` when
|
||
* nothing is armed.
|
||
*/
|
||
readonly "statsCaptureLive": <Config extends OperationConfig>(options: { readonly config?: Config | undefined } | undefined) => Effect.Effect<WithOptionalResponse<typeof StatsCaptureLive200.Type, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"StatsCaptureLive401", typeof StatsCaptureLive401.Type> | PunktfunkError<"StatsCaptureLive404", typeof StatsCaptureLive404.Type>>
|
||
/**
|
||
* Arms a new performance-stats capture. Idempotent: if a capture is already running this returns
|
||
* the current status unchanged. While armed, the streaming loops emit aggregated samples (~ every
|
||
* 1–2 s) into the in-progress capture, readable live via `GET /stats/capture/live`.
|
||
*/
|
||
readonly "statsCaptureStart": <Config extends OperationConfig>(options: { readonly config?: Config | undefined } | undefined) => Effect.Effect<WithOptionalResponse<typeof StatsCaptureStart200.Type, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"StatsCaptureStart401", typeof StatsCaptureStart401.Type>>
|
||
/**
|
||
* Whether a capture is armed, its sample count, and start time. Poll this (e.g. every 2 s) to
|
||
* drive the capture-control UI.
|
||
*/
|
||
readonly "statsCaptureStatus": <Config extends OperationConfig>(options: { readonly config?: Config | undefined } | undefined) => Effect.Effect<WithOptionalResponse<typeof StatsCaptureStatus200.Type, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"StatsCaptureStatus401", typeof StatsCaptureStatus401.Type>>
|
||
/**
|
||
* Disarms the in-progress capture and writes it to disk atomically, returning its summary. If
|
||
* nothing was recording, returns `204 No Content`.
|
||
*/
|
||
readonly "statsCaptureStop": <Config extends OperationConfig>(options: { readonly config?: Config | undefined } | undefined) => Effect.Effect<WithOptionalResponse<typeof StatsCaptureStop200.Type, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"StatsCaptureStop401", typeof StatsCaptureStop401.Type> | PunktfunkError<"StatsCaptureStop500", typeof StatsCaptureStop500.Type>>
|
||
/**
|
||
* Every saved capture's summary (the `meta` head only — not the sample body), newest first.
|
||
*/
|
||
readonly "statsRecordingsList": <Config extends OperationConfig>(options: { readonly config?: Config | undefined } | undefined) => Effect.Effect<WithOptionalResponse<typeof StatsRecordingsList200.Type, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"StatsRecordingsList401", typeof StatsRecordingsList401.Type>>
|
||
/**
|
||
* The full capture (meta + samples) for `id`, for graphing or download.
|
||
*/
|
||
readonly "statsRecordingGet": <Config extends OperationConfig>(id: string, options: { readonly config?: Config | undefined } | undefined) => Effect.Effect<WithOptionalResponse<typeof StatsRecordingGet200.Type, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"StatsRecordingGet401", typeof StatsRecordingGet401.Type> | PunktfunkError<"StatsRecordingGet404", typeof StatsRecordingGet404.Type> | PunktfunkError<"StatsRecordingGet500", typeof StatsRecordingGet500.Type>>
|
||
/**
|
||
* Removes the recording `id` from disk. `404` if there is no such recording.
|
||
*/
|
||
readonly "statsRecordingDelete": <Config extends OperationConfig>(id: string, options: { readonly config?: Config | undefined } | undefined) => Effect.Effect<WithOptionalResponse<void, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"StatsRecordingDelete401", typeof StatsRecordingDelete401.Type> | PunktfunkError<"StatsRecordingDelete404", typeof StatsRecordingDelete404.Type> | PunktfunkError<"StatsRecordingDelete500", typeof StatsRecordingDelete500.Type>>
|
||
/**
|
||
* Live host status
|
||
*/
|
||
readonly "getStatus": <Config extends OperationConfig>(options: { readonly config?: Config | undefined } | undefined) => Effect.Effect<WithOptionalResponse<typeof GetStatus200.Type, Config>, HttpClientError.HttpClientError | SchemaError | PunktfunkError<"GetStatus401", typeof GetStatus401.Type>>
|
||
}
|
||
|
||
export interface PunktfunkError<Tag extends string, E> {
|
||
readonly _tag: Tag
|
||
readonly request: HttpClientRequest.HttpClientRequest
|
||
readonly response: HttpClientResponse.HttpClientResponse
|
||
readonly cause: E
|
||
}
|
||
|
||
class PunktfunkErrorImpl extends Data.Error<{
|
||
_tag: string
|
||
cause: any
|
||
request: HttpClientRequest.HttpClientRequest
|
||
response: HttpClientResponse.HttpClientResponse
|
||
}> {}
|
||
|
||
export const PunktfunkError = <Tag extends string, E>(
|
||
tag: Tag,
|
||
cause: E,
|
||
response: HttpClientResponse.HttpClientResponse,
|
||
): PunktfunkError<Tag, E> =>
|
||
new PunktfunkErrorImpl({
|
||
_tag: tag,
|
||
cause,
|
||
response,
|
||
request: response.request,
|
||
}) as any
|