Extension API
WarpDL extensions are JavaScript modules executed using the Goja runtime. Extensions transform URLs before downloads begin, enabling support for custom hosting services and URL schemes.
Extension Structure
Section titled “Extension Structure”Every extension requires two files:
- manifest.json - Extension metadata
- main.js (or custom entrypoint) - JavaScript code with
extract()function
Directory Layout
Section titled “Directory Layout”<platform-config-dir>/warpdl/extstore/└── <extension-hash>/ ├── manifest.json └── main.jsPlatform-specific paths:
- Linux:
~/.config/warpdl/extstore/ - macOS:
~/Library/Application Support/warpdl/extstore/ - Windows:
%AppData%/warpdl/extstore/(e.g.,C:\Users\<username>\AppData\Roaming\warpdl\extstore\)
manifest.json
Section titled “manifest.json”The manifest defines extension metadata and URL matching patterns.
Schema
Section titled “Schema”{ "name": "my-extension", "version": "1.0.0", "matches": ["^https://example\\.com/.*"], "description": "Transform example.com URLs", "entrypoint": "main.js"}Fields
Section titled “Fields”| Field | Required | Type | Description |
|---|---|---|---|
name |
Yes | string | Extension display name |
version |
Yes | string | Semantic version (e.g., “1.0.0”) |
matches |
Yes | string[] | Array of regex patterns for URL matching |
description |
No | string | Human-readable description |
entrypoint |
No | string | Main JavaScript file (default: “main.js”) |
assets |
No | string[] | Additional files to include (e.g., helper scripts) |
auth |
No | object | OAuth2 configuration for extensions that call an API — see Authenticated Extensions |
URL Matching
Section titled “URL Matching”The matches field contains JavaScript regex patterns. WarpDL tests each pattern against download URLs using RegExp.MatchString().
{ "matches": [ "^https://drive\\.google\\.com/.*", "^https://.*\\.googleapis\\.com/.*" ]}JavaScript API
Section titled “JavaScript API”Required Function: extract()
Section titled “Required Function: extract()”Every extension must define an extract(url) function. This is the only entry point WarpDL calls.
function extract(url) { // Transform the URL and return the direct download link return transformedUrl;}Parameters:
url(string): The original URL to transform
Returns:
string: Transformed URL for downloading- Return
"end"to signal interaction failure/cancellation
Example:
function extract(url) { // Force HTTPS if (url.startsWith("http://")) { return url.replace("http://", "https://"); }
// Extract direct link from hosting service let match = url.match(/example\.com\/file\/(\w+)/); if (match) { return "https://cdn.example.com/dl/" + match[1]; }
return url;}Built-in Functions
Section titled “Built-in Functions”WarpDL provides several built-in functions for extensions:
print(…args)
Section titled “print(…args)”Print values to stdout (for debugging).
print("Processing URL:", url);print("Match found:", fileId);input(prompt, [callback])
Section titled “input(prompt, [callback])”Read user input from stdin. Optionally process input with a callback function.
// Simple inputlet choice = input("Select quality (1-3): ");
// With callbackinput("Enter filename: ", function(filename) { print("You entered:", filename); return filename;});require(modulePath)
Section titled “require(modulePath)”Import additional JavaScript modules from the extension directory.
// Import helper.js from extension directorylet helper = require("./helper.js");Note: Modules use CommonJS format (module.exports).
request(options)
Section titled “request(options)”Make HTTP requests (limited to 1MB response size).
let response = request({ method: "GET", url: "https://api.example.com/file-info", headers: { "User-Agent": "WarpDL Extension", "Authorization": "Bearer token" }, body: "" // Optional request body});
print("Status:", response.status_code);print("Body:", response.body);print("Content-Length:", response.content_length);
// Access response headerslet contentType = response.headers.get("Content-Type");Request Options:
method(string): HTTP method (GET, POST, etc.)url(string): Request URLheaders(object): Request headers (key-value pairs)body(string): Request body
Response Object:
status_code(number): HTTP status codebody(string): Response body (max 1MB)content_length(number): Content-Length header valueheaders(Headers): Response headers object
Headers Object Methods:
get(name)- Get header valuehas(name)- Check if header existsset(name, value)- Set header valueappend(name, value)- Append to header valuedelete(name)- Remove headerkeys()- Get all header namesvalues()- Get all header valuesentries()- Get [name, value] pairsforEach(callback)- Iterate over headersgetSetCookies()- Get Set-Cookie header values
Authenticated Extensions (OAuth2)
Section titled “Authenticated Extensions (OAuth2)”Extensions that call an API on the user’s behalf declare an auth block in their manifest. WarpDL then drives the OAuth2 login, stores the credential, and exposes token access to the extension’s JavaScript.
Manifest auth Block
Section titled “Manifest auth Block”{ "name": "my-api-extension", "version": "1.0.0", "description": "Transform api.example.com URLs", "matches": ["^https://api\\.example\\.com/.*"], "entrypoint": "main.js", "auth": { "type": "oauth2", "client_id": "1234.apps.example.com", "scopes": ["files.read"], "authorize_url": "https://api.example.com/oauth/authorize", "token_url": "https://api.example.com/oauth/token", "device_url": "https://api.example.com/oauth/device/code", "revoke_url": "https://api.example.com/oauth/revoke", "pkce": "S256", "extra_auth_params": { "access_type": "offline" } }}| Field | Required | Description |
|---|---|---|
type |
yes | Must be "oauth2" |
client_id |
yes | OAuth2 client ID |
scopes |
yes | At least one scope; also the default requested at login |
authorize_url |
yes | Authorization endpoint (https:// only) |
token_url |
yes | Token endpoint (https:// only) |
device_url |
no | Enables the device-code flow |
revoke_url |
no | Used on logout to revoke the token server-side |
client_secret |
no | Only for providers that require it on /token for public clients (e.g. Google “Desktop app”); it ships inside the extension and is not secret |
pkce |
no | "S256" (default) or "plain" |
extra_auth_params |
no | Extra query parameters for the authorization URL |
A device_url lets users log in with --device (no browser callback). Without it, login uses a loopback PKCE redirect.
Auth Bindings
Section titled “Auth Bindings”These globals are only defined for extensions whose manifest declares auth:
getAccessToken(options)
Section titled “getAccessToken(options)”Returns a valid access token (string), triggering or refreshing the login flow when needed.
var token = getAccessToken(); // default account, manifest scopesvar token = getAccessToken({ scopes: ["files.read"] });var token = getAccessToken({ account: "work" });Requested scopes must be a subset of the manifest’s scopes. options.account selects a named credential; omit it for the default account.
fetchWithAuth(req, options)
Section titled “fetchWithAuth(req, options)”Calls request(req) with Authorization: Bearer <token> added to the headers, and retries once with a fresh token if the response is 401.
var response = fetchWithAuth({ method: "GET", url: "https://api.example.com/me"}, { scopes: ["files.read"] });invalidateToken(options)
Section titled “invalidateToken(options)”Drops the cached access token so the next getAccessToken refreshes or re-authenticates. The refresh credential is kept.
invalidateToken({ account: "work" });listAccounts()
Section titled “listAccounts()”Returns the account labels this extension has stored credentials for.
listAccounts(); // e.g. ["default", "work"]Logging In
Section titled “Logging In”Users authenticate once per plugin (and per account label) with the auth command:
warpdl auth login my-api-extension # loopback PKCE flow, opens a browserwarpdl auth login my-api-extension --as work # store under the "work" labelwarpdl auth login my-api-extension --device # device-code flow (requires device_url)warpdl auth login my-api-extension --no-browser # print the URL instead of opening a browserwarpdl auth login my-api-extension --scopes files.read
warpdl auth list # stored credentials, expiry includedwarpdl auth status my-api-extension --as work # exit 0 valid, 1 missing, 2 expiredwarpdl auth logout my-api-extension --as workCredentials are encrypted in tokens.gob in the WarpDL config directory (the encryption key lives in the OS keyring where available) and are separate from the extension code. If a download needs a token that isn’t stored yet, the daemon surfaces an authentication error and the CLI hints at warpdl auth login <plugin-id>. Login waits up to 5 minutes for the browser step to complete.
Complete Example
Section titled “Complete Example”This extension transforms Google Drive sharing URLs into direct download links:
manifest.json:
{ "name": "gdrive-extractor", "version": "1.0.0", "matches": ["^https://drive\\.google\\.com/file/d/.*"], "description": "Extract direct download links from Google Drive"}main.js:
function extract(url) { // Extract file ID from sharing URL let match = url.match(/\/file\/d\/([a-zA-Z0-9_-]+)/);
if (!match) { print("Error: Invalid Google Drive URL"); return "end"; // Signal failure }
let fileId = match[1];
// Construct direct download URL let directUrl = "https://drive.google.com/uc?export=download&id=" + fileId;
print("Extracted file ID:", fileId); return directUrl;}Extension Management
Section titled “Extension Management”Install Extension
Section titled “Install Extension”warpdl ext install /path/to/extension/List Extensions
Section titled “List Extensions”# Show active extensions onlywarpdl ext list
# Show all extensions (including inactive)warpdl ext list --show-all# orwarpdl ext list -aActivate/Deactivate
Section titled “Activate/Deactivate”warpdl ext activate <extension-id|name>warpdl ext deactivate <extension-id|name>Uninstall
Section titled “Uninstall”warpdl ext uninstall <extension-id|name>Limitations
Section titled “Limitations”- Execution: Synchronous only (no async/await, Promises)
- Network: HTTP requests limited to 1MB response size
- File System: No direct file system access (use
require()for local modules) - Isolation: Each extension runs in a separate JavaScript runtime
- URL Transformation: Extensions can only transform URLs via
extract()- no download hooks or progress callbacks
Error Handling
Section titled “Error Handling”If extract() returns "end", WarpDL treats it as an interaction failure and stops processing:
function extract(url) { let apiResponse = request({ method: "GET", url: "https://api.example.com/resolve?url=" + url });
if (apiResponse.status_code !== 200) { print("API error:", apiResponse.status_code); return "end"; // Stop processing }
return apiResponse.body;}Throwing errors or runtime exceptions will also terminate extension execution and display the error to the user.
