proto: freeze the wire contract at 1.0.0

Schemas for the whole v1 surface: 38 methods, 9 events, 25 named types and the
JSON-RPC envelope, with x-privileged / x-transports / x-deadlineMs / x-errors
annotations that both generators emit as data rather than prose.

Four generators over one IR (contracts/codegen/schema_ir.py), so the C++ structs,
the TypeScript types and the OpenRPC document cannot disagree about what the
contract says:

  gen_cpp.py             -> core/generated/velox_proto.{hpp,cpp}
  gen_ts.py              -> extension/src/shared/protocol/
  gen_openrpc.py         -> contracts/openrpc.json
  gen_cpp_conformance.py -> tests/conformance/cpp/fixture_dispatcher.hpp

Inbound parsing never throws: parse<T>() returns std::expected<T, ParseError> and
nlohmann's throwing ADL from_json is deliberately not emitted. Schema constraints
(minimum, maxLength, pattern, ...) become real runtime checks in both languages —
the daemon does not trust the extension and the extension does not trust the
daemon.

59 golden fixtures: a success case per method, 12 error cases, 9 events. Replayed
by tests/conformance/ against both the generated C++ and a live server over both
transports. tools/mockd serves the same fixtures with unhappy-path flags so the
GUI and EXT lanes never wait for veloxd.

run.sh also proves capture.offer fails open: with a daemon answering slower than
750 ms the client gives up and lets Firefox take the download.

core/generated/ is libveloxproto, a separate target from libveloxcore, which
still never sees JSON — see docs/adr/0009.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_012fgjnqFCS5h5L7gZTZo3rV
This commit is contained in:
2026-09-09 19:55:54 +04:00
co-authored by Claude Opus 5
parent a40585f419
commit 53421d6cb8
171 changed files with 29275 additions and 51 deletions
@@ -0,0 +1,38 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/BulkTaskResult.schema.json",
"title": "BulkTaskResult",
"description": "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.",
"type": "object",
"additionalProperties": false,
"required": ["updated", "failed"],
"properties": {
"updated": {
"type": "array",
"description": "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.",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["taskId", "state", "changed"],
"properties": {
"taskId": { "type": "string", "format": "uuid" },
"state": { "$ref": "https://velox.dev/schema/types/TaskState.schema.json" },
"changed": { "type": "boolean" }
}
}
},
"failed": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["taskId", "code", "message"],
"properties": {
"taskId": { "type": "string", "format": "uuid" },
"code": { "$ref": "https://velox.dev/schema/types/ErrorCode.schema.json" },
"message": { "type": "string" }
}
}
}
}
}
@@ -0,0 +1,8 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/BypassModifier.schema.json",
"title": "BypassModifier",
"description": "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.",
"type": "string",
"enum": ["alt", "ctrl", "shift", "none"]
}
@@ -0,0 +1,51 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/CaptureRules.schema.json",
"title": "CaptureRules",
"description": "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.",
"type": "object",
"additionalProperties": false,
"required": [
"enabled",
"monitoredExtensions",
"monitoredMimeTypes",
"minSizeBytes",
"excludedHosts",
"rulesVersion"
],
"properties": {
"enabled": {
"type": "boolean"
},
"monitoredExtensions": {
"type": "array",
"items": {
"type": "string"
}
},
"monitoredMimeTypes": {
"type": "array",
"items": {
"type": "string"
}
},
"minSizeBytes": {
"type": "integer",
"minimum": 0
},
"excludedHosts": {
"type": "array",
"items": {
"type": "string"
}
},
"bypassModifier": {
"$ref": "https://velox.dev/schema/types/BypassModifier.schema.json"
},
"rulesVersion": {
"type": "integer",
"minimum": 0,
"description": "Bumped on every change. The extension re-fetches when it sees a higher value."
}
}
}
@@ -0,0 +1,18 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/Category.schema.json",
"title": "Category",
"description": "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.",
"type": "object",
"additionalProperties": false,
"required": ["categoryId", "name", "saveDir", "extensions", "builtin"],
"properties": {
"categoryId": { "type": "string" },
"name": { "type": "string", "maxLength": 64 },
"saveDir": { "type": "string" },
"extensions": { "type": "array", "items": { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9+._-]*$" }, "description": "Without the leading dot, lowercase." },
"mimeTypes": { "type": "array", "items": { "type": "string" } },
"builtin": { "type": "boolean", "description": "Compressed, Documents, Music, Programs, Video. Cannot be removed; can be renamed and re-pointed." },
"sortOrder": { "type": "integer", "minimum": 0 }
}
}
@@ -0,0 +1,13 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/Checksum.schema.json",
"title": "Checksum",
"description": "Optional integrity check, verified during the verifying state. A mismatch moves the task to failed and never overwrites a good file.",
"type": "object",
"additionalProperties": false,
"required": ["algorithm", "value"],
"properties": {
"algorithm": { "type": "string", "enum": ["md5", "sha1", "sha256", "sha512"] },
"value": { "type": "string", "pattern": "^[0-9a-fA-F]{32,128}$" }
}
}
+17
View File
@@ -0,0 +1,17 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/Cookie.schema.json",
"title": "Cookie",
"description": "One cookie the daemon replays so an authenticated download works outside the browser.",
"type": "object",
"additionalProperties": false,
"required": ["name", "value"],
"properties": {
"name": { "type": "string" },
"value": { "type": "string" },
"domain": { "type": "string" },
"path": { "type": "string" },
"secure": { "type": "boolean" },
"httpOnly": { "type": "boolean" }
}
}
@@ -0,0 +1,25 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/DownloadSpec.schema.json",
"title": "DownloadSpec",
"description": "Everything needed to create one task. Shared by download.add and each item of download.addBatch, so the two can never drift apart.",
"type": "object",
"additionalProperties": false,
"required": ["url"],
"properties": {
"url": { "type": "string", "format": "uri" },
"headers": { "oneOf": [{ "$ref": "https://velox.dev/schema/types/Headers.schema.json" }, { "type": "null" }] },
"cookies": { "type": ["array", "null"], "items": { "$ref": "https://velox.dev/schema/types/Cookie.schema.json" } },
"referrer": { "type": ["string", "null"] },
"userAgent": { "type": ["string", "null"] },
"filename": { "type": ["string", "null"], "maxLength": 255, "description": "Overrides the name derived from Content-Disposition or the URL." },
"saveDir": { "type": ["string", "null"], "description": "Canonicalized and checked against the allowed roots before any write. -32011 if it fails." },
"categoryId": { "type": ["string", "null"], "description": "null means the rules engine picks one." },
"queueId": { "type": ["string", "null"], "description": "Required when startMode is 'queue'." },
"segments": { "type": ["integer", "null"], "minimum": 1, "maximum": 32 },
"bufferBytes": { "type": ["integer", "null"], "minimum": 4096, "maximum": 8388608 },
"startMode": { "$ref": "https://velox.dev/schema/types/StartMode.schema.json" },
"description": { "type": ["string", "null"], "maxLength": 1024 },
"checksum": { "oneOf": [{ "$ref": "https://velox.dev/schema/types/Checksum.schema.json" }, { "type": "null" }] }
}
}
@@ -0,0 +1,23 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/ErrorCode.schema.json",
"title": "ErrorCode",
"description": "Every error code the daemon may return. Adding one is a minor bump; changing the meaning of one is a major bump.",
"type": "integer",
"x-enum": [
{ "name": "ParseError", "value": -32700, "doc": "Malformed JSON on the wire." },
{ "name": "InvalidRequest", "value": -32600, "doc": "Not a valid JSON-RPC 2.0 request object." },
{ "name": "MethodNotFound", "value": -32601, "doc": "Unknown method name." },
{ "name": "InvalidParams", "value": -32602, "doc": "Params failed schema validation." },
{ "name": "InternalError", "value": -32603, "doc": "Unhandled daemon-side failure." },
{ "name": "VersionMismatch", "value": -32001, "doc": "Protocol major version mismatch. GUI renders this as 'Velox needs updating'." },
{ "name": "NotPaired", "value": -32002, "doc": "Missing or invalid token on the WebSocket transport." },
{ "name": "TransportForbidden", "value": -32003, "doc": "Method is privileged and was called over a transport that may not use it." },
{ "name": "TaskNotFound", "value": -32010, "doc": "No task with that id." },
{ "name": "InvalidPath", "value": -32011, "doc": "Destination is outside the allowed roots, or is not writable. data.path is set." },
{ "name": "DiskFull", "value": -32012, "doc": "Not enough free space to preallocate." },
{ "name": "ProbeFailed", "value": -32013, "doc": "Could not probe the URL. data.httpStatus is set when there was an HTTP response." },
{ "name": "RateLimited", "value": -32014, "doc": "Pairing brute-force lockout. data.retryAfterSec is set." }
],
"enum": [-32700, -32600, -32601, -32602, -32603, -32001, -32002, -32003, -32010, -32011, -32012, -32013, -32014]
}
@@ -0,0 +1,18 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/GrabberFile.schema.json",
"title": "GrabberFile",
"description": "One candidate found by the Site Grabber crawl. Nothing is downloaded until grabber.harvest selects it.",
"type": "object",
"additionalProperties": false,
"required": ["fileId", "url", "depth"],
"properties": {
"fileId": { "type": "string" },
"url": { "type": "string", "format": "uri" },
"filename": { "type": ["string", "null"] },
"sizeBytes": { "type": ["integer", "null"], "minimum": 0, "description": "From a HEAD, when the server answered one." },
"contentType": { "type": ["string", "null"] },
"depth": { "type": "integer", "minimum": 0 },
"foundOn": { "type": ["string", "null"], "format": "uri", "description": "The page this link was found on." }
}
}
@@ -0,0 +1,8 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/Headers.schema.json",
"title": "Headers",
"description": "HTTP request headers, verbatim as the browser would have sent them. Needed for signed-URL and referrer-gated CDNs.",
"type": "object",
"additionalProperties": { "type": "string" }
}
@@ -0,0 +1,14 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/Limiter.schema.json",
"title": "Limiter",
"description": "Global token-bucket speed limit. Applies across every active task, not per task.",
"type": "object",
"additionalProperties": false,
"required": ["enabled", "globalBps"],
"properties": {
"enabled": { "type": "boolean" },
"globalBps": { "type": "integer", "minimum": 0, "description": "Bytes per second. 0 with enabled true means 'stop everything', which the GUI must not offer." },
"applyToRunning": { "type": "boolean", "description": "Re-tune already-running transfers instead of waiting for the next task." }
}
}
@@ -0,0 +1,21 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/MediaVariant.schema.json",
"title": "MediaVariant",
"description": "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.",
"type": "object",
"additionalProperties": false,
"required": ["variantId", "kind", "drm"],
"properties": {
"variantId": { "type": "string" },
"kind": { "type": "string", "enum": ["video", "audio", "muxed", "subtitle"] },
"resolution": { "type": ["string", "null"], "pattern": "^[0-9]{2,5}x[0-9]{2,5}$" },
"bitrateBps": { "type": ["integer", "null"], "minimum": 0 },
"codec": { "type": ["string", "null"] },
"container": { "type": ["string", "null"], "enum": ["ts", "mp4", "webm", "mkv", null] },
"frameRate": { "type": ["number", "null"], "minimum": 0 },
"language": { "type": ["string", "null"] },
"sizeEstimate": { "type": ["integer", "null"], "minimum": 0, "description": "bitrate x duration. Never exact — the GUI must label it as approximate." },
"drm": { "type": "boolean", "description": "Widevine/EME detected. Explicitly out of scope; refuse rather than fail mysteriously." }
}
}
+18
View File
@@ -0,0 +1,18 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/Queue.schema.json",
"title": "Queue",
"description": "An ordered run of tasks with its own concurrency cap and optional schedule.",
"type": "object",
"additionalProperties": false,
"required": ["queueId", "name", "state", "maxConcurrent"],
"properties": {
"queueId": { "type": "string" },
"name": { "type": "string", "maxLength": 64 },
"state": { "type": "string", "enum": ["running", "stopped"] },
"maxConcurrent": { "type": "integer", "minimum": 1, "maximum": 32 },
"taskIds": { "type": "array", "items": { "type": "string", "format": "uuid" }, "description": "In run order. queue.reorder rewrites this." },
"schedule": { "oneOf": [{ "$ref": "https://velox.dev/schema/types/Schedule.schema.json" }, { "type": "null" }] },
"onComplete": { "type": "string", "enum": ["nothing", "exit", "shutdown", "hangup"], "description": "shutdown goes through org.freedesktop.login1 and must be confirmed by the user." }
}
}
+41
View File
@@ -0,0 +1,41 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/Rule.schema.json",
"title": "Rule",
"description": "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.",
"type": "object",
"additionalProperties": false,
"required": ["ruleId", "enabled", "priority", "match", "action"],
"properties": {
"ruleId": { "type": "string" },
"name": { "type": ["string", "null"], "maxLength": 64 },
"enabled": { "type": "boolean" },
"priority": { "type": "integer", "minimum": 0, "description": "Lower runs first." },
"match": {
"type": "object",
"additionalProperties": false,
"description": "All present clauses must match. An absent clause is not a constraint.",
"properties": {
"extensions": { "type": ["array", "null"], "items": { "type": "string" } },
"mimeTypes": { "type": ["array", "null"], "items": { "type": "string" } },
"hostPattern": { "type": ["string", "null"], "description": "Glob against the effective URL's host, e.g. *.example.com" },
"urlPattern": { "type": ["string", "null"], "description": "Glob against the whole effective URL." },
"minSizeBytes":{ "type": ["integer", "null"], "minimum": 0 },
"maxSizeBytes":{ "type": ["integer", "null"], "minimum": 0 }
}
},
"action": {
"type": "object",
"additionalProperties": false,
"description": "What to do with a matching download.",
"properties": {
"categoryId": { "type": ["string", "null"] },
"saveDir": { "type": ["string", "null"] },
"queueId": { "type": ["string", "null"] },
"segments": { "type": ["integer", "null"], "minimum": 1, "maximum": 32 },
"startMode": { "oneOf": [{ "$ref": "https://velox.dev/schema/types/StartMode.schema.json" }, { "type": "null" }] },
"capture": { "type": ["string", "null"], "enum": ["take", "ignore", null], "description": "Lets a rule veto capture for a host without touching the exclusion list." }
}
}
}
}
@@ -0,0 +1,17 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/Schedule.schema.json",
"title": "Schedule",
"description": "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.",
"type": "object",
"additionalProperties": false,
"required": ["enabled", "mode"],
"properties": {
"enabled": { "type": "boolean" },
"mode": { "type": "string", "enum": ["once", "periodic"] },
"startTime": { "type": ["string", "null"], "pattern": "^([01][0-9]|2[0-3]):[0-5][0-9]$" },
"stopTime": { "type": ["string", "null"], "pattern": "^([01][0-9]|2[0-3]):[0-5][0-9]$", "description": "null means run until the queue drains." },
"daysOfWeek": { "type": "array", "items": { "type": "integer", "minimum": 0, "maximum": 6 }, "description": "0 = Sunday. Ignored when mode is 'once'." },
"onceDate": { "type": ["string", "null"], "format": "date", "description": "Set only when mode is 'once'." }
}
}
@@ -0,0 +1,18 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/Segment.schema.json",
"title": "Segment",
"description": "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 never learns what a segment steal is.",
"type": "object",
"additionalProperties": false,
"required": ["index", "startByte", "endByte", "downloadedBytes", "state"],
"properties": {
"index": { "type": "integer", "minimum": 0, "maximum": 31 },
"startByte": { "type": "integer", "minimum": 0 },
"endByte": { "type": "integer", "minimum": 0, "description": "Inclusive. Equal to startByte - 1 for an empty segment." },
"downloadedBytes": { "type": "integer", "minimum": 0 },
"speedBps": { "type": "integer", "minimum": 0 },
"state": { "type": "string", "enum": ["pending", "connecting", "receiving", "stalled", "complete", "failed"] },
"httpStatus": { "type": ["integer", "null"] }
}
}
@@ -0,0 +1,29 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/SettingKey.schema.json",
"title": "SettingKey",
"description": "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.",
"type": "string",
"enum": [
"general.launchOnLogin", "general.minimizeToTray", "general.showDropTarget",
"general.confirmOnExit", "general.language", "general.checkForUpdates",
"capture.enabled", "capture.monitoredExtensions", "capture.monitoredMimeTypes",
"capture.minSizeBytes", "capture.excludedHosts", "capture.bypassModifier",
"capture.autoStartTypes",
"saveTo.defaultDir", "saveTo.tempDir", "saveTo.allowedRoots",
"saveTo.fileExistsPolicy", "saveTo.createSubfolderPerSite",
"connection.preset", "connection.maxSegmentsPerDownload", "connection.bufferBytes",
"connection.maxConcurrentDownloads", "connection.timeoutSec", "connection.maxRetries",
"connection.retryBackoffSec",
"downloads.speedLimitBps", "downloads.speedLimitEnabled", "downloads.virusScanCommand",
"downloads.postDownloadCommand", "downloads.duplicatePolicy", "downloads.verifyChecksums",
"proxy.mode", "proxy.host", "proxy.port", "proxy.username", "proxy.bypassHosts", "proxy.pacUrl",
"sounds.enabled", "sounds.onComplete", "sounds.onQueueComplete", "sounds.onError"
]
}
+196
View File
@@ -0,0 +1,196 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/Settings.schema.json",
"title": "Settings",
"description": "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 \u2014 proxy and site-login credentials go to the Secret Service, never to SQLite and never over the wire.",
"type": "object",
"additionalProperties": false,
"properties": {
"general.launchOnLogin": {
"type": "boolean"
},
"general.minimizeToTray": {
"type": "boolean"
},
"general.showDropTarget": {
"type": "boolean"
},
"general.confirmOnExit": {
"type": "boolean"
},
"general.language": {
"type": "string",
"description": "BCP 47, or 'system'."
},
"general.checkForUpdates": {
"type": "boolean"
},
"capture.enabled": {
"type": "boolean"
},
"capture.monitoredExtensions": {
"type": "array",
"items": {
"type": "string"
}
},
"capture.monitoredMimeTypes": {
"type": "array",
"items": {
"type": "string"
}
},
"capture.minSizeBytes": {
"type": "integer",
"minimum": 0
},
"capture.excludedHosts": {
"type": "array",
"items": {
"type": "string"
}
},
"capture.bypassModifier": {
"$ref": "https://velox.dev/schema/types/BypassModifier.schema.json"
},
"capture.autoStartTypes": {
"type": "array",
"items": {
"type": "string"
},
"description": "Extensions that skip the File Info dialog and start immediately."
},
"saveTo.defaultDir": {
"type": "string"
},
"saveTo.tempDir": {
"type": "string"
},
"saveTo.allowedRoots": {
"type": "array",
"items": {
"type": "string"
},
"description": "Every write target is canonicalized and must resolve inside one of these. Read-only over the WebSocket transport."
},
"saveTo.fileExistsPolicy": {
"type": "string",
"enum": [
"ask",
"rename",
"overwrite",
"resume"
]
},
"saveTo.createSubfolderPerSite": {
"type": "boolean"
},
"connection.preset": {
"type": "string",
"enum": [
"auto",
"lan",
"broadband",
"slow"
]
},
"connection.maxSegmentsPerDownload": {
"type": "integer",
"minimum": 1,
"maximum": 32
},
"connection.bufferBytes": {
"type": "integer",
"minimum": 4096,
"maximum": 8388608
},
"connection.maxConcurrentDownloads": {
"type": "integer",
"minimum": 1,
"maximum": 64
},
"connection.timeoutSec": {
"type": "integer",
"minimum": 1,
"maximum": 3600
},
"connection.maxRetries": {
"type": "integer",
"minimum": 0,
"maximum": 100
},
"connection.retryBackoffSec": {
"type": "integer",
"minimum": 0,
"maximum": 3600
},
"downloads.speedLimitBps": {
"type": "integer",
"minimum": 0
},
"downloads.speedLimitEnabled": {
"type": "boolean"
},
"downloads.virusScanCommand": {
"type": "string"
},
"downloads.postDownloadCommand": {
"type": "string"
},
"downloads.duplicatePolicy": {
"type": "string",
"enum": [
"ask",
"skip",
"rename",
"redownload"
]
},
"downloads.verifyChecksums": {
"type": "boolean"
},
"proxy.mode": {
"type": "string",
"enum": [
"system",
"none",
"http",
"https",
"socks5",
"pac"
]
},
"proxy.host": {
"type": "string"
},
"proxy.port": {
"type": "integer",
"minimum": 1,
"maximum": 65535
},
"proxy.username": {
"type": "string"
},
"proxy.bypassHosts": {
"type": "array",
"items": {
"type": "string"
}
},
"proxy.pacUrl": {
"type": "string"
},
"sounds.enabled": {
"type": "boolean"
},
"sounds.onComplete": {
"type": "string"
},
"sounds.onQueueComplete": {
"type": "string"
},
"sounds.onError": {
"type": "string"
}
}
}
@@ -0,0 +1,8 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/StartMode.schema.json",
"title": "StartMode",
"description": "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.",
"type": "string",
"enum": ["now", "later", "queue"]
}
@@ -0,0 +1,27 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/TaskDetail.schema.json",
"title": "TaskDetail",
"description": "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.",
"type": "object",
"additionalProperties": false,
"required": ["summary", "segmentDetail"],
"properties": {
"summary": { "$ref": "https://velox.dev/schema/types/TaskSummary.schema.json" },
"segmentDetail": {
"type": "array",
"maxItems": 32,
"items": { "$ref": "https://velox.dev/schema/types/Segment.schema.json" }
},
"headers": { "oneOf": [{ "$ref": "https://velox.dev/schema/types/Headers.schema.json" }, { "type": "null" }] },
"referrer": { "type": ["string", "null"] },
"userAgent": { "type": ["string", "null"] },
"mime": { "type": ["string", "null"] },
"bufferBytes": { "type": ["integer", "null"], "minimum": 4096, "maximum": 8388608 },
"partPath": { "type": ["string", "null"], "description": "Absolute path of the .veloxpart file while the task is unfinished." },
"checksum": { "oneOf": [{ "$ref": "https://velox.dev/schema/types/Checksum.schema.json" }, { "type": "null" }] },
"checksumVerified": { "type": ["boolean", "null"], "description": "null until the verifying state has run." },
"averageSpeedBps": { "type": ["integer", "null"], "minimum": 0 },
"retryCount": { "type": "integer", "minimum": 0 }
}
}
@@ -0,0 +1,17 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/TaskError.schema.json",
"title": "TaskError",
"description": "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.",
"type": "object",
"additionalProperties": false,
"required": ["code", "message", "retryable"],
"properties": {
"code": { "type": "integer" },
"message": { "type": "string" },
"httpStatus": { "type": ["integer", "null"] },
"retryable": { "type": "boolean" },
"attempt": { "type": ["integer", "null"], "minimum": 0, "description": "How many attempts have been made so far." },
"nextRetryAt":{ "type": ["string", "null"], "format": "date-time" }
}
}
@@ -0,0 +1,16 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/TaskFilter.schema.json",
"title": "TaskFilter",
"description": "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.",
"type": "object",
"additionalProperties": false,
"properties": {
"states": { "type": ["array", "null"], "items": { "$ref": "https://velox.dev/schema/types/TaskState.schema.json" } },
"categoryId": { "type": ["string", "null"] },
"queueId": { "type": ["string", "null"] },
"query": { "type": ["string", "null"], "maxLength": 256, "description": "Case-insensitive substring of filename or url." },
"addedAfter": { "type": ["string", "null"], "format": "date-time" },
"addedBefore":{ "type": ["string", "null"], "format": "date-time" }
}
}
@@ -0,0 +1,13 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/TaskSort.schema.json",
"title": "TaskSort",
"description": "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.",
"type": "object",
"additionalProperties": false,
"required": ["field", "direction"],
"properties": {
"field": { "type": "string", "enum": ["filename", "sizeBytes", "state", "etaSeconds", "speedBps", "lastTryAt", "createdAt", "queuePosition", "description"] },
"direction": { "type": "string", "enum": ["asc", "desc"] }
}
}
@@ -0,0 +1,9 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/TaskState.schema.json",
"title": "TaskState",
"description": "Lifecycle of one download. The daemon is the only writer; clients render it and nothing more. Terminal states are complete, failed and cancelled.",
"type": "string",
"enum": ["new", "probing", "queued", "connecting", "downloading", "paused",
"retry_wait", "assembling", "verifying", "complete", "failed", "cancelled"]
}
+13 -25
View File
@@ -2,43 +2,31 @@
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://velox.dev/schema/types/TaskSummary.schema.json",
"title": "TaskSummary",
"description": "One row of the main download list. Everything the GUI table needs, and nothing more. TEMPLATE — lane PROTO owns the final shape.",
"description": "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.",
"type": "object",
"additionalProperties": false,
"required": ["taskId", "filename", "state", "createdAt"],
"required": ["taskId", "filename", "saveDir", "url", "state", "downloadedBytes",
"speedBps", "resumable", "segments", "createdAt"],
"properties": {
"taskId": { "type": "string", "format": "uuid" },
"filename": { "type": "string", "maxLength": 255 },
"saveDir": { "type": "string" },
"url": { "type": "string", "format": "uri" },
"effectiveUrl": { "type": "string", "format": "uri" },
"sizeBytes": { "type": ["integer", "null"], "minimum": 0, "description": "null when the server did not report a length" },
"saveDir": { "type": "string", "description": "Absolute, canonicalized, inside an allowed root." },
"url": { "type": "string", "format": "uri", "description": "The URL as the user or the extension supplied it." },
"effectiveUrl": { "type": ["string", "null"], "format": "uri", "description": "After redirects. null until the first probe succeeds." },
"sizeBytes": { "type": ["integer", "null"], "minimum": 0, "description": "null when the server did not report a length." },
"downloadedBytes": { "type": "integer", "minimum": 0 },
"state": {
"type": "string",
"enum": ["new", "probing", "queued", "connecting", "downloading", "paused",
"retry_wait", "assembling", "verifying", "complete", "failed", "cancelled"]
},
"state": { "$ref": "https://velox.dev/schema/types/TaskState.schema.json" },
"speedBps": { "type": "integer", "minimum": 0 },
"etaSeconds": { "type": ["integer", "null"], "minimum": 0 },
"etaSeconds": { "type": ["integer", "null"], "minimum": 0, "description": "null when the size or the speed is unknown." },
"resumable": { "type": "boolean" },
"segments": { "type": "integer", "minimum": 1, "maximum": 32 },
"segments": { "type": "integer", "minimum": 1, "maximum": 32, "description": "Connection count. Per-segment detail lives in TaskDetail." },
"categoryId": { "type": ["string", "null"] },
"queueId": { "type": ["string", "null"] },
"queuePosition":{ "type": ["integer", "null"], "minimum": 0, "description": "the Q column" },
"description": { "type": "string", "maxLength": 1024 },
"queuePosition":{ "type": ["integer", "null"], "minimum": 0, "description": "The Q column." },
"description": { "type": ["string", "null"], "maxLength": 1024 },
"createdAt": { "type": "string", "format": "date-time" },
"lastTryAt": { "type": ["string", "null"], "format": "date-time" },
"completedAt": { "type": ["string", "null"], "format": "date-time" },
"error": {
"type": ["object", "null"],
"additionalProperties": false,
"properties": {
"code": { "type": "integer" },
"message": { "type": "string" },
"httpStatus": { "type": ["integer", "null"] },
"retryable": { "type": "boolean" }
}
}
"error": { "oneOf": [{ "$ref": "https://velox.dev/schema/types/TaskError.schema.json" }, { "type": "null" }] }
}
}