// --------------------------------------------------------------------------- // GENERATED FILE — DO NOT EDIT. // // Source: contracts/schema/** // Generator: contracts/codegen/gen_cpp.py // Contract: v1.0.0 // // Hand-editing this file is a merge blocker. Fix the schema and regenerate: // python3 contracts/codegen/gen_cpp.py // Only lane PROTO commits to contracts/. // --------------------------------------------------------------------------- #pragma once #include #include #include #include #include #include #include #include // This is libveloxproto, NOT libveloxcore. The layering rule in CLAUDE.md forbids // JSON inside the engine; the engine does not link this target. See // docs/adr/0009-generated-protocol-library.md. namespace velox::proto { inline constexpr std::string_view kProtocolVersion = "1.0.0"; /// Why a payload could not be turned into a typed value. `path` is a JSON Pointer /// into the offending document, so a conformance failure names the exact field. struct ParseError { std::string path; std::string message; }; /// Every parse in this file returns one of these. Nothing here throws. template using Result = std::expected; /// Which listener a request arrived on. Decides whether a privileged method is /// allowed: see `is_allowed_on`. enum class Transport { Uds, Ws }; /// Every error code the daemon may return. Adding one is a minor bump; changing the meaning of /// one is a major bump. enum class ErrorCode : std::int32_t { /// Malformed JSON on the wire. ParseError = -32700, /// Not a valid JSON-RPC 2.0 request object. InvalidRequest = -32600, /// Unknown method name. MethodNotFound = -32601, /// Params failed schema validation. InvalidParams = -32602, /// Unhandled daemon-side failure. InternalError = -32603, /// Protocol major version mismatch. GUI renders this as 'Velox needs updating'. VersionMismatch = -32001, /// Missing or invalid token on the WebSocket transport. NotPaired = -32002, /// Method is privileged and was called over a transport that may not use it. TransportForbidden = -32003, /// No task with that id. TaskNotFound = -32010, /// Destination is outside the allowed roots, or is not writable. data.path is set. InvalidPath = -32011, /// Not enough free space to preallocate. DiskFull = -32012, /// Could not probe the URL. data.httpStatus is set when there was an HTTP response. ProbeFailed = -32013, /// Pairing brute-force lockout. data.retryAfterSec is set. RateLimited = -32014, }; std::string_view to_string(ErrorCode v) noexcept; std::optional errorcode_from_int(std::int32_t v) noexcept; /// Lifecycle of one download. The daemon is the only writer; clients render it and nothing /// more. Terminal states are complete, failed and cancelled. enum class TaskState { New, // "new" Probing, // "probing" Queued, // "queued" Connecting, // "connecting" Downloading, // "downloading" Paused, // "paused" RetryWait, // "retry_wait" Assembling, // "assembling" Verifying, // "verifying" Complete, // "complete" Failed, // "failed" Cancelled, // "cancelled" }; std::string_view to_string(TaskState v) noexcept; Result parse_TaskState(std::string_view s); /// The modifier key a user holds to make one click bypass capture and let Firefox download /// normally. Shared by Settings and CaptureRules so the daemon's setting and the extension's /// mirror of it are literally the same type. enum class BypassModifier { Alt, // "alt" Ctrl, // "ctrl" Shift, // "shift" None, // "none" }; std::string_view to_string(BypassModifier v) noexcept; Result parse_BypassModifier(std::string_view s); enum class ChecksumAlgorithm { Md5, // "md5" Sha1, // "sha1" Sha256, // "sha256" Sha512, // "sha512" }; std::string_view to_string(ChecksumAlgorithm v) noexcept; Result parse_ChecksumAlgorithm(std::string_view s); /// HTTP request headers, verbatim as the browser would have sent them. Needed for signed-URL /// and referrer-gated CDNs. using Headers = std::map; /// What the daemon does with a task the moment it is added. 'later' is the File Info dialog's /// Download Later button and lands the task in paused. enum class StartMode { Now, // "now" Later, // "later" Queue, // "queue" }; std::string_view to_string(StartMode v) noexcept; Result parse_StartMode(std::string_view s); enum class MediaVariantContainer { Ts, // "ts" Mp4, // "mp4" Webm, // "webm" Mkv, // "mkv" }; std::string_view to_string(MediaVariantContainer v) noexcept; Result parse_MediaVariantContainer(std::string_view s); enum class MediaVariantKind { Video, // "video" Audio, // "audio" Muxed, // "muxed" Subtitle, // "subtitle" }; std::string_view to_string(MediaVariantKind v) noexcept; Result parse_MediaVariantKind(std::string_view s); /// shutdown goes through org.freedesktop.login1 and must be confirmed by the user. enum class QueueOnComplete { Nothing, // "nothing" Exit, // "exit" Shutdown, // "shutdown" Hangup, // "hangup" }; std::string_view to_string(QueueOnComplete v) noexcept; Result parse_QueueOnComplete(std::string_view s); enum class QueueState { Running, // "running" Stopped, // "stopped" }; std::string_view to_string(QueueState v) noexcept; Result parse_QueueState(std::string_view s); enum class ScheduleMode { Once, // "once" Periodic, // "periodic" }; std::string_view to_string(ScheduleMode v) noexcept; Result parse_ScheduleMode(std::string_view s); /// Lets a rule veto capture for a host without touching the exclusion list. enum class RuleActionCapture { Take, // "take" Ignore, // "ignore" }; std::string_view to_string(RuleActionCapture v) noexcept; Result parse_RuleActionCapture(std::string_view s); /// 'downloading' is spelled as in TaskState, not 'receiving'. 'pending' is a range that has /// been planned but not yet dialled. enum class SegmentState { Pending, // "pending" Connecting, // "connecting" Downloading, // "downloading" Stalled, // "stalled" Complete, // "complete" Failed, // "failed" }; std::string_view to_string(SegmentState v) noexcept; Result parse_SegmentState(std::string_view s); /// Every settings key that exists. The Options dialog maps 1:1 onto this list and the GUI must /// not invent a key that is not here. Kept in lockstep with Settings.schema.json by a /// conformance check. enum class SettingKey { GeneralLaunchOnLogin, // "general.launchOnLogin" GeneralMinimizeToTray, // "general.minimizeToTray" GeneralShowDropTarget, // "general.showDropTarget" GeneralConfirmOnExit, // "general.confirmOnExit" GeneralLanguage, // "general.language" GeneralCheckForUpdates, // "general.checkForUpdates" CaptureEnabled, // "capture.enabled" CaptureMonitoredExtensions, // "capture.monitoredExtensions" CaptureMonitoredMimeTypes, // "capture.monitoredMimeTypes" CaptureMinSizeBytes, // "capture.minSizeBytes" CaptureExcludedHosts, // "capture.excludedHosts" CaptureBypassModifier, // "capture.bypassModifier" CaptureAutoStartTypes, // "capture.autoStartTypes" SaveToDefaultDir, // "saveTo.defaultDir" SaveToTempDir, // "saveTo.tempDir" SaveToAllowedRoots, // "saveTo.allowedRoots" SaveToFileExistsPolicy, // "saveTo.fileExistsPolicy" SaveToCreateSubfolderPerSite, // "saveTo.createSubfolderPerSite" ConnectionPreset, // "connection.preset" ConnectionMaxSegmentsPerDownload, // "connection.maxSegmentsPerDownload" ConnectionBufferBytes, // "connection.bufferBytes" ConnectionMaxConcurrentDownloads, // "connection.maxConcurrentDownloads" ConnectionTimeoutSec, // "connection.timeoutSec" ConnectionMaxRetries, // "connection.maxRetries" ConnectionRetryBackoffSec, // "connection.retryBackoffSec" DownloadsSpeedLimitBps, // "downloads.speedLimitBps" DownloadsSpeedLimitEnabled, // "downloads.speedLimitEnabled" DownloadsVirusScanCommand, // "downloads.virusScanCommand" DownloadsPostDownloadCommand, // "downloads.postDownloadCommand" DownloadsDuplicatePolicy, // "downloads.duplicatePolicy" DownloadsVerifyChecksums, // "downloads.verifyChecksums" ProxyMode, // "proxy.mode" ProxyHost, // "proxy.host" ProxyPort, // "proxy.port" ProxyUsername, // "proxy.username" ProxyBypassHosts, // "proxy.bypassHosts" ProxyPacUrl, // "proxy.pacUrl" SoundsEnabled, // "sounds.enabled" SoundsOnComplete, // "sounds.onComplete" SoundsOnQueueComplete, // "sounds.onQueueComplete" SoundsOnError, // "sounds.onError" }; std::string_view to_string(SettingKey v) noexcept; Result parse_SettingKey(std::string_view s); enum class SettingsConnectionPreset { Auto, // "auto" Lan, // "lan" Broadband, // "broadband" Slow, // "slow" }; std::string_view to_string(SettingsConnectionPreset v) noexcept; Result parse_SettingsConnectionPreset(std::string_view s); enum class SettingsDownloadsDuplicatePolicy { Ask, // "ask" Skip, // "skip" Rename, // "rename" Redownload, // "redownload" }; std::string_view to_string(SettingsDownloadsDuplicatePolicy v) noexcept; Result parse_SettingsDownloadsDuplicatePolicy(std::string_view s); enum class SettingsProxyMode { System, // "system" None, // "none" Http, // "http" Https, // "https" Socks5, // "socks5" Pac, // "pac" }; std::string_view to_string(SettingsProxyMode v) noexcept; Result parse_SettingsProxyMode(std::string_view s); enum class SettingsSaveToFileExistsPolicy { Ask, // "ask" Rename, // "rename" Overwrite, // "overwrite" Resume, // "resume" }; std::string_view to_string(SettingsSaveToFileExistsPolicy v) noexcept; Result parse_SettingsSaveToFileExistsPolicy(std::string_view s); /// Why a download failed. This is the WIRE failure taxonomy and it is deliberately NOT the /// JSON-RPC ErrorCode space: ErrorCode says why a *call* failed, TaskErrorCode says why a /// *download* failed. A task can fail while every RPC involved succeeded. The values mirror /// vdm::Error in core/include/vdm/util/error.hpp one-for-one, by name, so DAEMON's projection /// from the engine taxonomy onto the wire is lossless and the GUI can tell 'the file on the /// server changed' from 'the checksum did not match'. CORE's 'ok' has no wire spelling: a /// TaskError only exists when there is a failure. Adding a value here is a minor bump; renaming /// or removing one is major, and would desynchronise the engine. enum class TaskErrorCode { Canceled, // "canceled" ResolveFailed, // "resolve_failed" ConnectFailed, // "connect_failed" TlsFailed, // "tls_failed" ConnectionReset, // "connection_reset" Timeout, // "timeout" TooManyRedirects, // "too_many_redirects" HttpClientError, // "http_client_error" HttpServerError, // "http_server_error" AuthRequired, // "auth_required" Forbidden, // "forbidden" NotFound, // "not_found" RangeNotSatisfiable, // "range_not_satisfiable" Gone, // "gone" ServerFileChanged, // "server_file_changed" ContentLengthMismatch, // "content_length_mismatch" ChecksumMismatch, // "checksum_mismatch" DiskFull, // "disk_full" IoError, // "io_error" PathRejected, // "path_rejected" PermissionDenied, // "permission_denied" MetaCorrupt, // "meta_corrupt" MetaVersionUnsupported, // "meta_version_unsupported" ProbeFailed, // "probe_failed" UnsupportedUrlScheme, // "unsupported_url_scheme" MaxRetriesExhausted, // "max_retries_exhausted" Internal, // "internal" }; std::string_view to_string(TaskErrorCode v) noexcept; Result parse_TaskErrorCode(std::string_view s); enum class TaskSortDirection { Asc, // "asc" Desc, // "desc" }; std::string_view to_string(TaskSortDirection v) noexcept; Result parse_TaskSortDirection(std::string_view s); enum class TaskSortField { Filename, // "filename" SizeBytes, // "sizeBytes" State, // "state" EtaSeconds, // "etaSeconds" SpeedBps, // "speedBps" LastTryAt, // "lastTryAt" CreatedAt, // "createdAt" QueuePosition, // "queuePosition" Description, // "description" }; std::string_view to_string(TaskSortField v) noexcept; Result parse_TaskSortField(std::string_view s); enum class CaptureOfferParamsMethod { GET, // "GET" POST, // "POST" }; std::string_view to_string(CaptureOfferParamsMethod v) noexcept; Result parse_CaptureOfferParamsMethod(std::string_view s); enum class CaptureOfferResultAction { Take, // "take" Ignore, // "ignore" }; std::string_view to_string(CaptureOfferResultAction v) noexcept; Result parse_CaptureOfferResultAction(std::string_view s); /// Why the offer was declined. Set when action is 'ignore'; the extension logs it in the /// popup's diagnostics. enum class CaptureOfferResultReason { ExcludedHost, // "excluded_host" TypeNotMonitored, // "type_not_monitored" BelowMinSize, // "below_min_size" Duplicate, // "duplicate" CaptureDisabled, // "capture_disabled" UserDeclined, // "user_declined" RuleIgnore, // "rule_ignore" }; std::string_view to_string(CaptureOfferResultReason v) noexcept; Result parse_CaptureOfferResultReason(std::string_view s); enum class GrabberStatusResultState { Crawling, // "crawling" Done, // "done" Failed, // "failed" Cancelled, // "cancelled" }; std::string_view to_string(GrabberStatusResultState v) noexcept; Result parse_GrabberStatusResultState(std::string_view s); enum class MediaListVariantsResultManifestType { Hls, // "hls" Dash, // "dash" }; std::string_view to_string(MediaListVariantsResultManifestType v) noexcept; Result parse_MediaListVariantsResultManifestType(std::string_view s); enum class SessionHelloParamsClientType { Gui, // "gui" Cli, // "cli" Extension, // "extension" Nmhost, // "nmhost" Test, // "test" }; std::string_view to_string(SessionHelloParamsClientType v) noexcept; Result parse_SessionHelloParamsClientType(std::string_view s); /// How the daemon sees this connection. Lets a client know up front which privileged methods /// will be refused. enum class SessionHelloResultTransport { Uds, // "uds" Ws, // "ws" }; std::string_view to_string(SessionHelloResultTransport v) noexcept; Result parse_SessionHelloResultTransport(std::string_view s); enum class SessionSubscribeParamsEventsItem { EventTaskAdded, // "event.task.added" EventTaskRemoved, // "event.task.removed" EventTaskState, // "event.task.state" EventTaskProgress, // "event.task.progress" EventSpeedGlobal, // "event.speed.global" EventAuthRequired, // "event.auth.required" EventNotify, // "event.notify" EventSettingsChanged, // "event.settings.changed" EventGrabberProgress, // "event.grabber.progress" }; std::string_view to_string(SessionSubscribeParamsEventsItem v) noexcept; Result parse_SessionSubscribeParamsEventsItem(std::string_view s); enum class AuthRequiredEventScheme { Basic, // "basic" Digest, // "digest" Ntlm, // "ntlm" Negotiate, // "negotiate" Proxy, // "proxy" }; std::string_view to_string(AuthRequiredEventScheme v) noexcept; Result parse_AuthRequiredEventScheme(std::string_view s); enum class NotifyEventLevel { Info, // "info" Success, // "success" Warning, // "warning" Error, // "error" }; std::string_view to_string(NotifyEventLevel v) noexcept; Result parse_NotifyEventLevel(std::string_view s); enum class NotifyEventSound { Complete, // "complete" QueueComplete, // "queueComplete" Error, // "error" }; std::string_view to_string(NotifyEventSound v) noexcept; Result parse_NotifyEventSound(std::string_view s); struct BulkTaskResultFailedItem { std::string taskId{}; ErrorCode code{}; std::string message{}; }; struct BulkTaskResultUpdatedItem { std::string taskId{}; TaskState state{}; bool changed{}; }; /// Result of a state transition applied to many tasks. A bulk call never fails as a whole /// because one id was bad: the ids that moved come back in 'updated' and the rest are explained /// in 'failed'. This is what lets the GUI's toolbar act on a multi-selection without /// pre-validating it. struct BulkTaskResult { /// One entry per task that actually changed. A task already in the target state is reported /// here with changed false rather than as a failure. std::vector updated{}; std::vector failed{}; }; /// The daemon's capture policy, mirrored into the extension so the two can never disagree about /// what should be intercepted. The extension refreshes this on connect and on /// event.settings.changed. struct CaptureRules { bool enabled{}; std::vector monitoredExtensions{}; std::vector monitoredMimeTypes{}; std::int64_t minSizeBytes{}; std::vector excludedHosts{}; std::optional bypassModifier{}; /// Bumped on every change. The extension re-fetches when it sees a higher value. std::int64_t rulesVersion{}; }; /// A destination folder plus the extensions that route to it. The extension mirrors the /// extension lists so its capture decision agrees with the daemon's. struct Category { std::string categoryId{}; std::string name{}; std::string saveDir{}; /// Without the leading dot, lowercase. std::vector extensions{}; std::optional> mimeTypes{}; /// Compressed, Documents, Music, Programs, Video. Cannot be removed; can be renamed and /// re-pointed. bool builtin{}; std::optional sortOrder{}; }; /// Optional integrity check, verified during the verifying state. A mismatch moves the task to /// failed and never overwrites a good file. struct Checksum { ChecksumAlgorithm algorithm{}; std::string value{}; }; /// One cookie the daemon replays so an authenticated download works outside the browser. struct Cookie { std::string name{}; std::string value{}; std::optional domain{}; std::optional path{}; std::optional secure{}; std::optional httpOnly{}; }; /// Everything needed to create one task. Shared by download.add and each item of /// download.addBatch, so the two can never drift apart. struct DownloadSpec { std::string url{}; std::optional headers{}; std::optional> cookies{}; std::optional referrer{}; std::optional userAgent{}; /// Overrides the name derived from Content-Disposition or the URL. std::optional filename{}; /// Canonicalized and checked against the allowed roots before any write. -32011 if it fails. std::optional saveDir{}; /// null means the rules engine picks one. std::optional categoryId{}; /// Required when startMode is 'queue'. std::optional queueId{}; /// The REQUESTED connection count. An upper bound, not a promise: the daemon lowers it to the /// per-host cap, and to 1 when the source turns out not to be resumable. What is actually in /// use comes back as TaskSummary.segments. null means use connection.maxSegmentsPerDownload. std::optional segments{}; std::optional bufferBytes{}; std::optional startMode{}; std::optional description{}; std::optional checksum{}; }; /// One candidate found by the Site Grabber crawl. Nothing is downloaded until grabber.harvest /// selects it. struct GrabberFile { std::string fileId{}; std::string url{}; std::optional filename{}; /// From a HEAD, when the server answered one. std::optional sizeBytes{}; std::optional contentType{}; std::int64_t depth{}; /// The page this link was found on. std::optional foundOn{}; }; /// Global token-bucket speed limit. Applies across every active task, not per task. struct Limiter { bool enabled{}; /// Bytes per second. 0 with enabled true means 'stop everything', which the GUI must not offer. std::int64_t globalBps{}; /// Re-tune already-running transfers instead of waiting for the next task. std::optional applyToRunning{}; }; /// One quality rendition from an HLS or DASH manifest. The daemon parses the manifest; the /// extension only renders this list. DRM-protected variants are reported with drm true and must /// be shown greyed out rather than failing later. struct MediaVariant { std::string variantId{}; MediaVariantKind kind{}; std::optional resolution{}; std::optional bitrateBps{}; std::optional codec{}; std::optional container{}; std::optional frameRate{}; std::optional language{}; /// bitrate x duration. Never exact — the GUI must label it as approximate. std::optional sizeEstimate{}; /// Widevine/EME detected. Explicitly out of scope; refuse rather than fail mysteriously. bool drm{}; }; /// When a queue may run. Times are local wall-clock in HH:MM; the daemon re-evaluates them on a /// DST change rather than caching absolute instants. struct Schedule { bool enabled{}; ScheduleMode mode{}; std::optional startTime{}; /// null means run until the queue drains. std::optional stopTime{}; /// 0 = Sunday. Ignored when mode is 'once'. std::optional> daysOfWeek{}; /// Set only when mode is 'once'. std::optional onceDate{}; }; /// An ordered run of tasks with its own concurrency cap and optional schedule. struct Queue { std::string queueId{}; std::string name{}; QueueState state{}; std::int64_t maxConcurrent{}; /// In run order. queue.reorder rewrites this. std::optional> taskIds{}; std::optional schedule{}; /// shutdown goes through org.freedesktop.login1 and must be confirmed by the user. std::optional onComplete{}; }; /// What to do with a matching download. struct RuleAction { std::optional categoryId{}; std::optional saveDir{}; std::optional queueId{}; std::optional segments{}; std::optional startMode{}; /// Lets a rule veto capture for a host without touching the exclusion list. std::optional capture{}; }; /// All present clauses must match. An absent clause is not a constraint. struct RuleMatch { std::optional> extensions{}; std::optional> mimeTypes{}; /// Glob against the effective URL's host, e.g. *.example.com std::optional hostPattern{}; /// Glob against the whole effective URL. std::optional urlPattern{}; std::optional minSizeBytes{}; std::optional maxSizeBytes{}; }; /// One row of the rules engine: match on extension, MIME, host or size, then route. First match /// by priority wins; no rule matching means the default category. struct Rule { std::string ruleId{}; std::optional name{}; bool enabled{}; /// Lower runs first. std::int64_t priority{}; /// All present clauses must match. An absent clause is not a constraint. RuleMatch match{}; /// What to do with a matching download. RuleAction action{}; }; /// One byte range being fetched by one connection. This is the deepest the contract ever /// exposes the engine: the GUI draws a bar per segment and is never told what a segment steal /// is. RANGE CONVENTION — READ THIS BEFORE IMPLEMENTING. The range is CLOSED and INCLUSIVE on /// both ends: [startByte, endByte]. The segment covers endByte - startByte + 1 bytes, and /// endByte is the index of the LAST byte in the range, not one past it. This deliberately /// matches the HTTP Range header the engine actually sends ('Range: /// bytes=-' is a byte-for-byte copy of these two fields, and RFC 9110 /// ranges are inclusive), so no arithmetic happens between the wire and the socket and there is /// nowhere for an off-by-one to hide. CORE asked for half-open [start, end); PROTO chose /// inclusive for that reason and this note exists so nobody discovers the difference at /// integration. A segment always covers at least one byte: endByte >= startByte always holds. /// An empty range is not representable and is not needed — a zero-length download carries an /// empty segmentDetail array, and a segment that has donated its remainder to a steal keeps the /// bytes it already wrote. struct Segment { /// Position in TaskDetail.segmentDetail. Spelled 'index' here and in event.task.progress; there /// is no 'i' spelling anywhere in the contract. std::int64_t index{}; /// Absolute offset of the first byte of the range. Inclusive. std::int64_t startByte{}; /// Absolute offset of the LAST byte of the range. Inclusive — this is not one-past-the-end. /// Always >= startByte. std::int64_t endByte{}; /// Bytes written for this range so far, out of endByte - startByte + 1. std::int64_t downloadedBytes{}; std::optional speedBps{}; /// 'downloading' is spelled as in TaskState, not 'receiving'. 'pending' is a range that has /// been planned but not yet dialled. SegmentState state{}; /// The status this segment's request got. 206 on a healthy ranged fetch. std::optional httpStatus{}; }; /// A sparse bag of settings. Every property is optional because settings.get returns only the /// keys that were asked for and settings.set carries only the keys that changed. Property names /// must match SettingKey exactly. NOTE: no password lives here — proxy and site-login /// credentials go to the Secret Service, never to SQLite and never over the wire. struct Settings { std::optional general_launchOnLogin{}; std::optional general_minimizeToTray{}; std::optional general_showDropTarget{}; std::optional general_confirmOnExit{}; /// BCP 47, or 'system'. std::optional general_language{}; std::optional general_checkForUpdates{}; std::optional capture_enabled{}; std::optional> capture_monitoredExtensions{}; std::optional> capture_monitoredMimeTypes{}; std::optional capture_minSizeBytes{}; std::optional> capture_excludedHosts{}; std::optional capture_bypassModifier{}; /// Extensions that skip the File Info dialog and start immediately. std::optional> capture_autoStartTypes{}; std::optional saveTo_defaultDir{}; std::optional saveTo_tempDir{}; /// Every write target is canonicalized and must resolve inside one of these. Read-only over the /// WebSocket transport. std::optional> saveTo_allowedRoots{}; std::optional saveTo_fileExistsPolicy{}; std::optional saveTo_createSubfolderPerSite{}; std::optional connection_preset{}; std::optional connection_maxSegmentsPerDownload{}; std::optional connection_bufferBytes{}; std::optional connection_maxConcurrentDownloads{}; std::optional connection_timeoutSec{}; std::optional connection_maxRetries{}; std::optional connection_retryBackoffSec{}; std::optional downloads_speedLimitBps{}; std::optional downloads_speedLimitEnabled{}; std::optional downloads_virusScanCommand{}; std::optional downloads_postDownloadCommand{}; std::optional downloads_duplicatePolicy{}; std::optional downloads_verifyChecksums{}; std::optional proxy_mode{}; std::optional proxy_host{}; std::optional proxy_port{}; std::optional proxy_username{}; std::optional> proxy_bypassHosts{}; std::optional proxy_pacUrl{}; std::optional sounds_enabled{}; std::optional sounds_onComplete{}; std::optional sounds_onQueueComplete{}; std::optional sounds_onError{}; }; /// Why a task is in the failed or retry_wait state. Distinct from the JSON-RPC Error, which /// describes a failed call rather than a failed download — the two live in different code /// spaces on purpose, and `code` here is a TaskErrorCode string, never a JSON-RPC integer. struct TaskError { TaskErrorCode code{}; /// Human-readable, safe to show a user. Never carries a credential, a token or a full local /// path outside the download roots. std::string message{}; /// Set for the codes listed in TaskErrorCode's x-carriesHttpStatus, and null otherwise. std::optional httpStatus{}; /// Whether the scheduler will pick this task up again on its own. Carried per-occurrence rather /// than derived from the code, because 'probe_failed' is retryable or not depending on what the /// probe hit. bool retryable{}; /// The underlying failure, for codes that wrap one. max_retries_exhausted sets it to whatever /// the last attempt actually failed with, so a user learns the reason rather than just that /// Velox gave up. std::optional cause{}; /// How many attempts have been made so far. std::optional attempt{}; std::optional nextRetryAt{}; }; /// One row of the main download list. Everything the GUI table needs, and nothing more. /// TaskDetail is the same shape plus the fields only the progress dialog and File Info dialog /// need. struct TaskSummary { std::string taskId{}; std::string filename{}; /// Absolute, canonicalized, inside an allowed root. std::string saveDir{}; /// The URL as the user or the extension supplied it. std::string url{}; /// After redirects. null until the first probe succeeds. std::optional effectiveUrl{}; /// null when the server did not report a length. std::optional sizeBytes{}; std::int64_t downloadedBytes{}; TaskState state{}; std::int64_t speedBps{}; /// null when the size or the speed is unknown. std::optional etaSeconds{}; bool resumable{}; /// The EFFECTIVE connection count in use right now — not the number that was requested. It is /// what remains after the per-host connection cap has been applied and after the demotion to 1 /// for a non-resumable source, so a task the user asked for 16 connections on legitimately /// reports 4, or 1. The GUI displays this value and must not assume it equals what download.add /// asked for. The requested value lives in DownloadSpec.segments and is not echoed back on this /// type. TaskDetail.segmentDetail always has exactly this many entries. std::int64_t segments{}; std::optional categoryId{}; std::optional queueId{}; /// The Q column. std::optional queuePosition{}; std::optional description{}; std::string createdAt{}; std::optional lastTryAt{}; std::optional completedAt{}; std::optional error{}; }; /// Everything TaskSummary carries, plus what only the progress dialog and the File Info dialog /// need. Returned by download.get; never sent in a list or an event, because it is expensive to /// build. struct TaskDetail { TaskSummary summary{}; /// Exactly TaskSummary.segments entries, in index order, covering [0, sizeBytes) with no gaps /// and no overlaps. Empty for a zero-length download, and empty before the task has been /// segmented. std::vector segmentDetail{}; std::optional headers{}; std::optional referrer{}; std::optional userAgent{}; std::optional mime{}; std::optional bufferBytes{}; /// Absolute path of the .veloxpart file while the task is unfinished. std::optional partPath{}; std::optional checksum{}; /// null until the verifying state has run. std::optional checksumVerified{}; std::optional averageSpeedBps{}; std::optional retryCount{}; }; /// Which rows download.list returns. This is the category tree and the All/Unfinished/Finished /// nodes, expressed on the wire. Absent clauses are not constraints. struct TaskFilter { std::optional> states{}; std::optional categoryId{}; std::optional queueId{}; /// Case-insensitive substring of filename or url. std::optional query{}; std::optional addedAfter{}; std::optional addedBefore{}; }; /// Sort order for download.list. The GUI persists the user's choice and sends it on every list /// call; the daemon does the sorting so a 100k-row list never has to be materialized /// client-side. struct TaskSort { TaskSortField field{}; TaskSortDirection direction{}; }; struct CaptureGetRulesParams { // No fields: this method takes no parameters. }; struct CaptureOfferParams { std::string url{}; CaptureOfferParamsMethod method{}; std::string tabUrl{}; std::optional headers{}; /// Cookies for the URL, so authenticated downloads work outside the browser. std::optional> cookies{}; std::optional contentType{}; std::optional contentLength{}; std::optional contentDisposition{}; /// The extension's best guess; the daemon may override it. std::optional filename{}; std::optional userAgent{}; std::optional referrer{}; /// moz-extension://... The daemon verifies this on the WS transport and refuses anything else. std::optional origin{}; /// The extension's webRequest id, echoed in logs so a capture decision can be traced back to /// one browser request. std::optional requestId{}; }; struct CaptureOfferResult { CaptureOfferResultAction action{}; /// Set when action is 'take'. std::optional taskId{}; /// Why the offer was declined. Set when action is 'ignore'; the extension logs it in the /// popup's diagnostics. std::optional reason{}; }; struct CategoryListParams { // No fields: this method takes no parameters. }; struct CategoryListResult { std::vector items{}; }; struct CategoryRemoveParams { std::string categoryId{}; std::optional reassignTo{}; }; struct CategoryRemoveResult { bool removed{}; std::vector reassignedTaskIds{}; }; struct CategoryUpsertParams { Category category{}; }; /// The stored category, with categoryId filled in on create. struct CategoryUpsertResult { Category category{}; }; struct DownloadAddResult { std::string taskId{}; TaskState state{}; /// The existing task this URL matched, when downloads.duplicatePolicy resolved to 'skip'. /// taskId then names that existing task. std::optional duplicate{}; }; struct DownloadAddBatchParams { std::vector items{}; /// Applied to any field an item left unset. Its url is ignored. std::optional defaults{}; }; struct DownloadAddBatchResultFailedItem { std::int64_t index{}; ErrorCode code{}; std::string message{}; }; struct DownloadAddBatchResult { /// In the same order as the accepted items. std::vector taskIds{}; /// One entry per item that could not be added. index refers to params.items. std::vector failed{}; }; struct DownloadCancelParams { std::vector taskIds{}; }; struct DownloadGetParams { std::string taskId{}; }; struct DownloadListParams { std::optional filter{}; std::optional sort{}; std::optional offset{}; /// Defaults to 500. The GUI pages; the extension popup asks for far fewer. std::optional limit{}; }; struct DownloadListResult { /// Rows matching the filter, ignoring offset and limit. std::int64_t total{}; std::vector items{}; }; struct DownloadPauseParams { std::vector taskIds{}; }; struct DownloadProbeParams { std::string url{}; std::optional headers{}; std::optional> cookies{}; std::optional referrer{}; std::optional userAgent{}; }; struct DownloadProbeResult { /// From Content-Disposition when present, else the URL path, sanitized. std::string filename{}; std::optional sizeBytes{}; std::string mime{}; /// Accept-Ranges: bytes and a validator (ETag or Last-Modified) are both present. bool resumable{}; std::string effectiveUrl{}; /// What the rules engine would pick. The dialog preselects it; the user may override. std::string suggestedCategoryId{}; std::optional suggestedSaveDir{}; std::optional etag{}; std::optional lastModified{}; std::optional acceptRanges{}; /// Every hop, so the user can see where a shortener actually led. std::optional> redirectChain{}; /// The probe got a 401/407. The GUI should collect credentials before adding. std::optional requiresAuth{}; }; struct DownloadRefreshUrlParams { std::string taskId{}; std::string url{}; std::optional headers{}; std::optional> cookies{}; }; struct DownloadRefreshUrlResult { bool ok{}; bool resumable{}; /// true when size or validator differ from what was recorded. The GUI must ask before /// restarting from zero — never discard bytes without consent. bool contentChanged{}; std::optional sizeBytes{}; std::optional effectiveUrl{}; }; struct DownloadRemoveParams { std::vector taskIds{}; /// Explicit and required — there is no default for deleting a user's file. bool deleteFile{}; }; struct DownloadRemoveResultFailedItem { std::string taskId{}; ErrorCode code{}; std::string message{}; }; struct DownloadRemoveResult { std::vector removed{}; std::vector failed{}; }; struct DownloadResumeParams { std::vector taskIds{}; }; struct DownloadStartParams { std::vector taskIds{}; }; /// Only the present fields change. An explicit null clears a nullable field. struct DownloadUpdateParamsPatch { std::optional filename{}; std::optional saveDir{}; std::optional categoryId{}; std::optional queueId{}; std::optional description{}; /// The REQUESTED connection count, subject to the same per-host cap and non-resumable demotion /// as DownloadSpec.segments. Takes effect on the next start; a running task is not re-segmented /// underneath the user. std::optional segments{}; std::optional bufferBytes{}; std::optional checksum{}; }; struct DownloadUpdateParams { std::string taskId{}; /// Only the present fields change. An explicit null clears a nullable field. DownloadUpdateParamsPatch patch{}; }; struct GrabberHarvestParams { std::string jobId{}; /// fileIds from grabber.status. std::vector select{}; std::optional defaults{}; }; struct GrabberHarvestResultFailedItem { std::string fileId{}; ErrorCode code{}; std::string message{}; }; struct GrabberHarvestResult { std::vector taskIds{}; std::vector failed{}; }; struct GrabberStartParams { std::string startUrl{}; std::int64_t depth{}; std::optional> includePatterns{}; std::optional> excludePatterns{}; /// Extensions, without the dot. null means every type. std::optional> fileTypes{}; std::optional sameHostOnly{}; std::optional maxFiles{}; std::optional headers{}; std::optional> cookies{}; }; struct GrabberStartResult { std::string jobId{}; }; struct GrabberStatusParams { std::string jobId{}; }; struct GrabberStatusResult { std::string jobId{}; GrabberStatusResultState state{}; std::int64_t crawled{}; std::int64_t found{}; std::vector files{}; std::optional error{}; }; struct LimiterGetParams { // No fields: this method takes no parameters. }; /// spec carries the same destination and queueing fields as download.add; its url is ignored /// because the manifest and variant determine the source. struct MediaAddVariantParams { std::string manifestUrl{}; std::string variantId{}; /// For DASH and HLS renditions where audio is a separate track to be muxed in. std::optional audioVariantId{}; std::optional spec{}; }; struct MediaAddVariantResult { std::string taskId{}; TaskState state{}; std::optional estimatedBytes{}; }; struct MediaListVariantsParams { std::string manifestUrl{}; std::optional headers{}; std::optional> cookies{}; std::optional referrer{}; }; struct MediaListVariantsResult { std::vector variants{}; MediaListVariantsResultManifestType manifestType{}; std::optional durationSec{}; std::optional title{}; /// The manifest as a whole is DRM-protected. Refuse with a clear message rather than /// downloading undecryptable segments. bool drmProtected{}; }; struct QueueListParams { // No fields: this method takes no parameters. }; struct QueueListResult { std::vector items{}; }; struct QueueReorderParams { std::string queueId{}; std::vector taskIds{}; }; struct QueueReorderResult { Queue queue{}; }; struct QueueStartParams { std::string queueId{}; }; struct QueueStartResult { Queue queue{}; std::vector startedTaskIds{}; }; struct QueueStopParams { std::string queueId{}; std::optional pauseRunning{}; }; struct QueueStopResult { Queue queue{}; std::vector pausedTaskIds{}; }; struct QueueUpsertParams { Queue queue{}; }; struct QueueUpsertResult { Queue queue{}; }; struct RulesListParams { // No fields: this method takes no parameters. }; struct RulesListResult { std::vector items{}; }; struct RulesUpsertParams { std::vector upsert{}; std::optional> remove{}; }; /// The full table after the write, in priority order. struct RulesUpsertResult { std::vector items{}; }; struct ScheduleGetParams { std::optional queueId{}; }; struct ScheduleGetResultItemsItem { std::string queueId{}; std::optional schedule{}; }; struct ScheduleGetResult { std::vector items{}; }; struct ScheduleSetParams { std::string queueId{}; std::optional schedule{}; }; struct ScheduleSetResult { std::string queueId{}; std::optional schedule{}; std::optional nextRunAt{}; }; struct SessionHelloParams { SessionHelloParamsClientType clientType{}; /// Human-readable, shown in the pairing prompt and the logs. std::string clientName{}; std::string protocolVersion{}; /// Required on the WebSocket transport once paired. Ignored on the Unix socket, where /// SO_PEERCRED is the authorization. std::optional token{}; }; struct SessionHelloResult { std::string daemonVersion{}; std::string protocolVersion{}; /// Optional features this build has, e.g. 'media', 'grabber', 'secretservice'. A client must /// degrade gracefully when one is absent rather than assuming it. std::vector capabilities{}; std::string sessionId{}; /// How the daemon sees this connection. Lets a client know up front which privileged methods /// will be refused. std::optional transport{}; }; struct SessionPairParams { std::string clientName{}; /// The moz-extension origin UUID. Must match the Origin header verified on the WS upgrade. std::string extensionId{}; /// Set when the user typed the code into the extension's Options page instead of clicking Allow /// in the GUI. std::optional code{}; }; struct SessionPairResult { /// 256 bits, base64url. Stored by the extension in browser.storage.local and sent on every /// later connect. std::string token{}; /// null means the token does not expire; it is revoked from Options -> Unpair. std::optional expiresAt{}; }; struct SessionSubscribeParams { std::vector events{}; /// Narrow task events to these ids. The extension popup uses it to avoid receiving progress for /// downloads it is not showing. null means all tasks. std::optional> taskIds{}; }; struct SessionSubscribeResult { bool ok{}; /// Echoed back so a client can detect that it asked for an event this daemon does not emit. std::vector events{}; }; struct SettingsGetParams { std::optional> keys{}; }; struct SettingsGetResult { Settings values{}; }; struct SettingsSetParams { Settings values{}; }; /// The stored values for the keys that were set, and the list of keys that actually changed. struct SettingsSetResult { Settings values{}; std::vector changed{}; }; struct AuthRequiredEvent { std::string taskId{}; std::string host{}; std::optional realm{}; AuthRequiredEventScheme scheme{}; }; struct GrabberProgressEvent { std::string jobId{}; std::int64_t found{}; std::int64_t crawled{}; bool done{}; std::optional currentUrl{}; }; struct NotifyEvent { NotifyEventLevel level{}; std::string title{}; std::string body{}; std::optional taskId{}; std::optional sound{}; }; struct SettingsChangedEvent { std::vector keys{}; }; struct SpeedGlobalEvent { std::int64_t downBps{}; std::int64_t activeCount{}; std::optional queuedCount{}; /// null when the limiter is off. std::optional limitBps{}; }; struct TaskAddedEvent { std::string taskId{}; TaskSummary summary{}; }; /// Only what a segment bar needs. Full segment state comes from download.get. struct TaskProgressEventTasksItemSegmentsItem { std::int64_t index{}; std::int64_t downloadedBytes{}; std::int64_t speedBps{}; }; struct TaskProgressEventTasksItem { std::string taskId{}; std::int64_t downloadedBytes{}; std::int64_t speedBps{}; std::optional etaSeconds{}; std::optional> segments{}; }; struct TaskProgressEvent { std::vector tasks{}; std::string at{}; }; struct TaskRemovedEvent { std::string taskId{}; bool deletedFile{}; }; struct TaskStateEvent { std::string taskId{}; TaskState state{}; std::optional previousState{}; std::optional summary{}; std::optional error{}; }; // --- serialisation --------------------------------------------------------- // ADL hooks, so `nlohmann::json j = value;` works. Outbound only: there is no // generated from_json, because nlohmann's inbound path throws and the wire is // never trusted. Use parse below. void to_json(nlohmann::json& j, const ErrorCode& v); void to_json(nlohmann::json& j, const BulkTaskResultFailedItem& v); void to_json(nlohmann::json& j, const TaskState& v); void to_json(nlohmann::json& j, const BulkTaskResultUpdatedItem& v); void to_json(nlohmann::json& j, const BulkTaskResult& v); void to_json(nlohmann::json& j, const BypassModifier& v); void to_json(nlohmann::json& j, const CaptureRules& v); void to_json(nlohmann::json& j, const Category& v); void to_json(nlohmann::json& j, const ChecksumAlgorithm& v); void to_json(nlohmann::json& j, const Checksum& v); void to_json(nlohmann::json& j, const Cookie& v); void to_json(nlohmann::json& j, const StartMode& v); void to_json(nlohmann::json& j, const DownloadSpec& v); void to_json(nlohmann::json& j, const GrabberFile& v); void to_json(nlohmann::json& j, const Limiter& v); void to_json(nlohmann::json& j, const MediaVariantContainer& v); void to_json(nlohmann::json& j, const MediaVariantKind& v); void to_json(nlohmann::json& j, const MediaVariant& v); void to_json(nlohmann::json& j, const QueueOnComplete& v); void to_json(nlohmann::json& j, const QueueState& v); void to_json(nlohmann::json& j, const ScheduleMode& v); void to_json(nlohmann::json& j, const Schedule& v); void to_json(nlohmann::json& j, const Queue& v); void to_json(nlohmann::json& j, const RuleActionCapture& v); void to_json(nlohmann::json& j, const RuleAction& v); void to_json(nlohmann::json& j, const RuleMatch& v); void to_json(nlohmann::json& j, const Rule& v); void to_json(nlohmann::json& j, const SegmentState& v); void to_json(nlohmann::json& j, const Segment& v); void to_json(nlohmann::json& j, const SettingKey& v); void to_json(nlohmann::json& j, const SettingsConnectionPreset& v); void to_json(nlohmann::json& j, const SettingsDownloadsDuplicatePolicy& v); void to_json(nlohmann::json& j, const SettingsProxyMode& v); void to_json(nlohmann::json& j, const SettingsSaveToFileExistsPolicy& v); void to_json(nlohmann::json& j, const Settings& v); void to_json(nlohmann::json& j, const TaskErrorCode& v); void to_json(nlohmann::json& j, const TaskError& v); void to_json(nlohmann::json& j, const TaskSummary& v); void to_json(nlohmann::json& j, const TaskDetail& v); void to_json(nlohmann::json& j, const TaskFilter& v); void to_json(nlohmann::json& j, const TaskSortDirection& v); void to_json(nlohmann::json& j, const TaskSortField& v); void to_json(nlohmann::json& j, const TaskSort& v); void to_json(nlohmann::json& j, const CaptureGetRulesParams& v); void to_json(nlohmann::json& j, const CaptureOfferParamsMethod& v); void to_json(nlohmann::json& j, const CaptureOfferParams& v); void to_json(nlohmann::json& j, const CaptureOfferResultAction& v); void to_json(nlohmann::json& j, const CaptureOfferResultReason& v); void to_json(nlohmann::json& j, const CaptureOfferResult& v); void to_json(nlohmann::json& j, const CategoryListParams& v); void to_json(nlohmann::json& j, const CategoryListResult& v); void to_json(nlohmann::json& j, const CategoryRemoveParams& v); void to_json(nlohmann::json& j, const CategoryRemoveResult& v); void to_json(nlohmann::json& j, const CategoryUpsertParams& v); void to_json(nlohmann::json& j, const CategoryUpsertResult& v); void to_json(nlohmann::json& j, const DownloadAddResult& v); void to_json(nlohmann::json& j, const DownloadAddBatchParams& v); void to_json(nlohmann::json& j, const DownloadAddBatchResultFailedItem& v); void to_json(nlohmann::json& j, const DownloadAddBatchResult& v); void to_json(nlohmann::json& j, const DownloadCancelParams& v); void to_json(nlohmann::json& j, const DownloadGetParams& v); void to_json(nlohmann::json& j, const DownloadListParams& v); void to_json(nlohmann::json& j, const DownloadListResult& v); void to_json(nlohmann::json& j, const DownloadPauseParams& v); void to_json(nlohmann::json& j, const DownloadProbeParams& v); void to_json(nlohmann::json& j, const DownloadProbeResult& v); void to_json(nlohmann::json& j, const DownloadRefreshUrlParams& v); void to_json(nlohmann::json& j, const DownloadRefreshUrlResult& v); void to_json(nlohmann::json& j, const DownloadRemoveParams& v); void to_json(nlohmann::json& j, const DownloadRemoveResultFailedItem& v); void to_json(nlohmann::json& j, const DownloadRemoveResult& v); void to_json(nlohmann::json& j, const DownloadResumeParams& v); void to_json(nlohmann::json& j, const DownloadStartParams& v); void to_json(nlohmann::json& j, const DownloadUpdateParamsPatch& v); void to_json(nlohmann::json& j, const DownloadUpdateParams& v); void to_json(nlohmann::json& j, const GrabberHarvestParams& v); void to_json(nlohmann::json& j, const GrabberHarvestResultFailedItem& v); void to_json(nlohmann::json& j, const GrabberHarvestResult& v); void to_json(nlohmann::json& j, const GrabberStartParams& v); void to_json(nlohmann::json& j, const GrabberStartResult& v); void to_json(nlohmann::json& j, const GrabberStatusParams& v); void to_json(nlohmann::json& j, const GrabberStatusResultState& v); void to_json(nlohmann::json& j, const GrabberStatusResult& v); void to_json(nlohmann::json& j, const LimiterGetParams& v); void to_json(nlohmann::json& j, const MediaAddVariantParams& v); void to_json(nlohmann::json& j, const MediaAddVariantResult& v); void to_json(nlohmann::json& j, const MediaListVariantsParams& v); void to_json(nlohmann::json& j, const MediaListVariantsResultManifestType& v); void to_json(nlohmann::json& j, const MediaListVariantsResult& v); void to_json(nlohmann::json& j, const QueueListParams& v); void to_json(nlohmann::json& j, const QueueListResult& v); void to_json(nlohmann::json& j, const QueueReorderParams& v); void to_json(nlohmann::json& j, const QueueReorderResult& v); void to_json(nlohmann::json& j, const QueueStartParams& v); void to_json(nlohmann::json& j, const QueueStartResult& v); void to_json(nlohmann::json& j, const QueueStopParams& v); void to_json(nlohmann::json& j, const QueueStopResult& v); void to_json(nlohmann::json& j, const QueueUpsertParams& v); void to_json(nlohmann::json& j, const QueueUpsertResult& v); void to_json(nlohmann::json& j, const RulesListParams& v); void to_json(nlohmann::json& j, const RulesListResult& v); void to_json(nlohmann::json& j, const RulesUpsertParams& v); void to_json(nlohmann::json& j, const RulesUpsertResult& v); void to_json(nlohmann::json& j, const ScheduleGetParams& v); void to_json(nlohmann::json& j, const ScheduleGetResultItemsItem& v); void to_json(nlohmann::json& j, const ScheduleGetResult& v); void to_json(nlohmann::json& j, const ScheduleSetParams& v); void to_json(nlohmann::json& j, const ScheduleSetResult& v); void to_json(nlohmann::json& j, const SessionHelloParamsClientType& v); void to_json(nlohmann::json& j, const SessionHelloParams& v); void to_json(nlohmann::json& j, const SessionHelloResultTransport& v); void to_json(nlohmann::json& j, const SessionHelloResult& v); void to_json(nlohmann::json& j, const SessionPairParams& v); void to_json(nlohmann::json& j, const SessionPairResult& v); void to_json(nlohmann::json& j, const SessionSubscribeParamsEventsItem& v); void to_json(nlohmann::json& j, const SessionSubscribeParams& v); void to_json(nlohmann::json& j, const SessionSubscribeResult& v); void to_json(nlohmann::json& j, const SettingsGetParams& v); void to_json(nlohmann::json& j, const SettingsGetResult& v); void to_json(nlohmann::json& j, const SettingsSetParams& v); void to_json(nlohmann::json& j, const SettingsSetResult& v); void to_json(nlohmann::json& j, const AuthRequiredEventScheme& v); void to_json(nlohmann::json& j, const AuthRequiredEvent& v); void to_json(nlohmann::json& j, const GrabberProgressEvent& v); void to_json(nlohmann::json& j, const NotifyEventLevel& v); void to_json(nlohmann::json& j, const NotifyEventSound& v); void to_json(nlohmann::json& j, const NotifyEvent& v); void to_json(nlohmann::json& j, const SettingsChangedEvent& v); void to_json(nlohmann::json& j, const SpeedGlobalEvent& v); void to_json(nlohmann::json& j, const TaskAddedEvent& v); void to_json(nlohmann::json& j, const TaskProgressEventTasksItemSegmentsItem& v); void to_json(nlohmann::json& j, const TaskProgressEventTasksItem& v); void to_json(nlohmann::json& j, const TaskProgressEvent& v); void to_json(nlohmann::json& j, const TaskRemovedEvent& v); void to_json(nlohmann::json& j, const TaskStateEvent& v); // --- parsing --------------------------------------------------------------- /// Turn an untrusted JSON value into a typed one. Specialised below for every /// contract type; the primary template is intentionally not defined, so asking /// for a type the contract does not have is a compile error, not a runtime one. template Result parse(const nlohmann::json& j, std::string_view path = ""); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); template <> Result parse(const nlohmann::json& j, std::string_view path); // --- method surface -------------------------------------------------------- /// Every method in the contract. Generated, so a daemon cannot answer a method /// the contract does not define, and cannot silently fail to answer one it does. enum class Method { CaptureGetRules, // capture.getRules CaptureOffer, // capture.offer CategoryList, // category.list CategoryRemove, // category.remove CategoryUpsert, // category.upsert DownloadAdd, // download.add DownloadAddBatch, // download.addBatch DownloadCancel, // download.cancel DownloadGet, // download.get DownloadList, // download.list DownloadPause, // download.pause DownloadProbe, // download.probe DownloadRefreshUrl, // download.refreshUrl DownloadRemove, // download.remove DownloadResume, // download.resume DownloadStart, // download.start DownloadUpdate, // download.update GrabberHarvest, // grabber.harvest GrabberStart, // grabber.start GrabberStatus, // grabber.status LimiterGet, // limiter.get LimiterSet, // limiter.set MediaAddVariant, // media.addVariant MediaListVariants, // media.listVariants QueueList, // queue.list QueueReorder, // queue.reorder QueueStart, // queue.start QueueStop, // queue.stop QueueUpsert, // queue.upsert RulesList, // rules.list RulesUpsert, // rules.upsert ScheduleGet, // schedule.get ScheduleSet, // schedule.set SessionHello, // session.hello SessionPair, // session.pair SessionSubscribe, // session.subscribe SettingsGet, // settings.get SettingsSet, // settings.set }; inline constexpr std::size_t kMethodCount = 38; std::string_view to_string(Method m) noexcept; std::optional method_from_string(std::string_view s) noexcept; /// True for methods refused over the WebSocket transport with -32003. The /// extension is not allowed to reconfigure the daemon or destroy user data. bool is_privileged(Method m) noexcept; bool is_allowed_on(Method m, Transport t) noexcept; /// The contract's answer deadline. capture.offer's 750 ms is the one that /// matters: past it the extension has already let Firefox take the download. std::int32_t deadline_ms(Method m) noexcept; /// Server-to-client notifications. enum class Event { AuthRequired, // event.auth.required GrabberProgress, // event.grabber.progress Notify, // event.notify SettingsChanged, // event.settings.changed SpeedGlobal, // event.speed.global TaskAdded, // event.task.added TaskProgress, // event.task.progress TaskRemoved, // event.task.removed TaskState, // event.task.state }; std::string_view to_string(Event e) noexcept; std::optional event_from_string(std::string_view s) noexcept; // --- dispatch -------------------------------------------------------------- /// Build a JSON-RPC error response. `id` may be null for a request that could /// not be parsed far enough to have one. nlohmann::json make_error(const nlohmann::json& id, ErrorCode code, std::string_view message, nlohmann::json data = nullptr); nlohmann::json make_result(const nlohmann::json& id, nlohmann::json result); nlohmann::json make_notification(Event e, nlohmann::json params); /// One virtual per method. The daemon implements this; `dispatch` below does the /// envelope handling, the transport check and the parameter parsing, so a handler /// only ever sees a validated, typed params struct. class Dispatcher { public: virtual ~Dispatcher() = default; /// The daemon's capture policy, so the extension's shouldCapture decision cannot drift from the /// daemon's. Fetched on connect and whenever event.settings.changed names a capture.* key. If /// this call fails the extension keeps its last known rules and stays fail-open. virtual Result on_capture_getRules(const CaptureGetRulesParams& params) = 0; /// Firefox offers an intercepted response to the daemon. The daemon MUST reply within 750 ms; /// the extension abandons the offer and lets Firefox download normally on timeout. This /// deadline is the whole reason capture fails open, and it is conformance-tested: a daemon that /// is slow, down, or erroring must never cost the user a download. virtual Result on_capture_offer(const CaptureOfferParams& params) = 0; /// Every category with its folder and extension list. The extension calls this to populate its /// default-category picker, which is why it is not privileged; it is read-only and exposes only /// paths the user already configured. virtual Result on_category_list(const CategoryListParams& params) = 0; /// Delete a user-created category. Built-in categories are refused with -32602. Tasks filed /// under it are reassigned to reassignTo, or to the default category when that is null; no task /// is ever orphaned. virtual Result on_category_remove(const CategoryRemoveParams& params) = 0; /// Create or replace a category. Omit categoryId to create; supply it to replace. Changing /// saveDir does not move existing files — the GUI asks separately and issues download.update /// per task, so a re-point is never a surprise mass file move. virtual Result on_category_upsert(const CategoryUpsertParams& params) = 0; /// Create one task. saveDir is canonicalized and checked against saveTo.allowedRoots before /// anything is written; a path that escapes them is refused with -32011 and no file is created. virtual Result on_download_add(const DownloadSpec& params) = 0; /// Create many tasks in one call: the clipboard blob, the wildcard expander, and the /// extension's 'Download all links'. Partial success is normal and is reported per item rather /// than failing the whole batch. virtual Result on_download_addBatch(const DownloadAddBatchParams& params) = 0; /// Stop the given tasks and mark them cancelled. The .veloxpart file is kept so the user can /// still resume from the list; download.remove is what deletes bytes. virtual Result on_download_cancel(const DownloadCancelParams& params) = 0; /// Full detail for one task, including per-segment state. Backs the progress dialog. Poll it no /// faster than the progress dialog repaints; the table must use events instead. virtual Result on_download_get(const DownloadGetParams& params) = 0; /// The main table. Filtering, sorting and paging all happen in the daemon so the GUI never /// materializes 100k rows to show 40. Called once on connect; after that the table is /// maintained from events, never re-fetched on a progress tick. virtual Result on_download_list(const DownloadListParams& params) = 0; /// Suspend transfers and flush every segment's progress to the .veloxpart.meta file, so a pause /// is indistinguishable from a crash as far as resume is concerned. Never loses bytes already /// written. virtual Result on_download_pause(const DownloadPauseParams& params) = 0; /// Ask what is at a URL without creating a task. Populates the File Info dialog. Runs a HEAD, /// falling back to a ranged GET when HEAD is refused, which is also how resumability is /// established. Never blocks the RPC loop; the dialog opens immediately and fills in when this /// lands. virtual Result on_download_probe(const DownloadProbeParams& params) = 0; /// IDM's 'Refresh Download Address'. Point an existing task at a freshly-issued URL when a /// signed link has expired, keeping every byte already on disk. The daemon re-probes and /// compares size and validator: if they still match, the transfer resumes from where it /// stopped; if they do not, it says so rather than silently restarting. virtual Result on_download_refreshUrl(const DownloadRefreshUrlParams& params) = 0; /// Drop tasks from the list, optionally deleting the bytes on disk. Privileged: this is the /// only method that destroys user data, and the extension is never allowed to reach it. The /// daemon deletes the .veloxpart and .veloxpart.meta pair, and the finished file only when /// deleteFile is true. virtual Result on_download_remove(const DownloadRemoveParams& params) = 0; /// Continue paused tasks. Resumption is revalidated with If-Range against the stored ETag or /// Last-Modified; a 200 where 206 was expected means the file changed on the server, and the /// task moves to failed with a clear error rather than corrupting the part file. virtual Result on_download_resume(const DownloadResumeParams& params) = 0; /// Begin or restart the given tasks. A task in 'queued' jumps its queue; a task already /// downloading is a no-op reported as changed false. virtual Result on_download_start(const DownloadStartParams& params) = 0; /// Change a task's mutable fields. Moving saveDir or filename moves the file on disk in the /// same operation, which is what makes dragging a row onto a category work as one RPC. /// Privileged: it can name a destination path. virtual Result on_download_update(const DownloadUpdateParams& params) = 0; /// Turn selected crawl results into tasks. This is the only grabber call that creates /// downloads, and it names exactly the files the user ticked — a crawl never starts a download /// on its own. virtual Result on_grabber_harvest(const GrabberHarvestParams& params) = 0; /// Start a depth-limited crawl. Nothing is downloaded by this call: it only walks pages and /// collects candidate links, which the wizard then shows for selection. Privileged because an /// unbounded crawl is a resource commitment the browser must not be able to make on the user's /// behalf. virtual Result on_grabber_start(const GrabberStartParams& params) = 0; /// Poll one crawl. Also delivered as event.grabber.progress; the poll exists so the wizard can /// be reopened on a job it did not start and still catch up. virtual Result on_grabber_status(const GrabberStatusParams& params) = 0; /// Current global speed limit. Privileged: changing or reading the limiter belongs to the GUI /// and CLI; the extension shows throughput from event.speed.global instead. virtual Result on_limiter_get(const LimiterGetParams& params) = 0; /// Set the global token-bucket limit. With applyToRunning true the change re-tunes transfers /// already in flight instead of taking effect only on the next task — the Speed Limiter /// window's 'apply now' button. virtual Result on_limiter_set(const Limiter& params) = 0; /// Turn one enumerated variant into a task. The daemon fetches the segments in parallel and /// muxes them with ffmpeg; the result is an ordinary task that appears in the list like any /// other download. Refused with -32602 when the variant is DRM-protected. virtual Result on_media_addVariant(const MediaAddVariantParams& params) = 0; /// Parse an HLS or DASH manifest in the daemon and enumerate its renditions. The extension /// never parses a manifest — that logic lives in one language, in one place. Variants with drm /// true are reported so the UI can grey them out; DRM-protected streams are refused, not /// attempted. virtual Result on_media_listVariants(const MediaListVariantsParams& params) = 0; /// Every queue with its run state and ordering. Not privileged: the extension's 'Add to Queue' /// picker needs it. virtual Result on_queue_list(const QueueListParams& params) = 0; /// Rewrite a queue's run order. taskIds must be a permutation of the queue's current /// membership; anything else is -32602 rather than a partial reorder, so a stale drag from an /// out-of-date view cannot quietly reshuffle the queue. virtual Result on_queue_reorder(const QueueReorderParams& params) = 0; /// Start a queue running. The scheduler then admits up to maxConcurrent tasks from it, in /// order, and keeps that many running until the queue drains or is stopped. virtual Result on_queue_start(const QueueStartParams& params) = 0; /// Stop admitting new tasks from a queue. Tasks already running are paused when pauseRunning is /// true, and otherwise allowed to finish — the difference between 'stop the queue' and 'stop /// everything', which IDM conflates and users trip over. virtual Result on_queue_stop(const QueueStopParams& params) = 0; /// Create or replace a queue, including its schedule and concurrency cap. Omit queueId to /// create. taskIds in the payload is ignored — membership changes through download.update and /// queue.reorder so that two clients editing at once cannot silently drop a task. virtual Result on_queue_upsert(const QueueUpsertParams& params) = 0; /// The rules engine's table, in priority order. Privileged: these are the daemon's routing /// policy. The extension gets its own narrowed view through capture.getRules instead. virtual Result on_rules_list(const RulesListParams& params) = 0; /// Create, replace, or delete rules in one atomic write. 'upsert' carries the rules to store /// and 'remove' the ruleIds to drop; applying both at once means a reprioritisation never /// leaves the table in a half-valid state. virtual Result on_rules_upsert(const RulesUpsertParams& params) = 0; /// The schedule for one queue, or every schedule when queueId is null. Backs the Scheduler /// window. virtual Result on_schedule_get(const ScheduleGetParams& params) = 0; /// Set or clear a queue's schedule. A null schedule clears it and leaves the queue under manual /// control. Times are local wall-clock and are re-evaluated on a DST change rather than being /// resolved to absolute instants at set time. virtual Result on_schedule_set(const ScheduleSetParams& params) = 0; /// First call on every connection, on every transport. The daemon compares protocolVersion /// majors and refuses a mismatch with -32001 so a stale GUI or extension fails loudly on /// connect instead of subtly at the tenth field. On the WebSocket transport a valid token is /// required unless the client is about to call session.pair. virtual Result on_session_hello(const SessionHelloParams& params) = 0; /// WebSocket transport only. Triggers a GUI or desktop-notification prompt showing a four-digit /// code; the user must approve before a token is issued. Failed attempts are rate-limited to /// 5/min followed by a 60 s lockout (-32014) so a token cannot be brute-forced by another local /// process. The daemon stores only a hash of the token. virtual Result on_session_pair(const SessionPairParams& params) = 0; /// Choose which notifications this connection receives. Subscribing replaces the previous /// selection rather than adding to it, so a client can narrow its firehose without /// reconnecting. Nothing is delivered until this is called. virtual Result on_session_subscribe(const SessionSubscribeParams& params) = 0; /// Read settings. keys null means everything. Privileged: the settings bag names local /// filesystem paths and the allowed write roots, which the extension has no business /// enumerating — it gets capture.getRules instead. virtual Result on_settings_get(const SettingsGetParams& params) = 0; /// Write settings. Only the keys present in values change. Rejected with -32602 if a key is /// unknown or a value fails the Settings schema, and with -32011 if a directory key names a /// path that cannot be written. Emits event.settings.changed with exactly the keys that took /// effect. virtual Result on_settings_set(const SettingsSetParams& params) = 0; }; /// Parse one JSON-RPC request, route it, and return the response to write back. /// Never throws. Returns a null json for a notification that needs no reply. nlohmann::json dispatch(Dispatcher& handler, Transport transport, const nlohmann::json& request); } // namespace velox::proto