@dropthis/cli
v0.26.0
Published
Official CLI for Dropthis.
Readme
@dropthis/cli
Official CLI for dropthis -- the publish layer between AI and the internet. Built for humans, AI agents, and CI/CD pipelines.
Install
npm install -g @dropthis/cliRequires Node.js >= 20.
Quick start
dropthis login
dropthis ./index.html # publish is the default commandAuthentication
Log in with email OTP:
dropthis loginThe CLI prompts for your email and a one-time code. For non-interactive environments (agents, CI), use the two-step flow:
# Step 1: Request OTP
dropthis login request --email [email protected]
# Step 2: Verify OTP
dropthis login verify --email [email protected] --otp 123456A failed verify prints
Your code has expired(otp_expired) orThat code is incorrect(otp_invalid) and exits non-zero.
Or pass both flags directly:
dropthis login --email [email protected] --otp 123456Credentials resolve in this order:
--api-key sk_...flagDROPTHIS_API_KEYenvironment variable- Stored credential from
dropthis login
If you run dropthis publish without credentials in an interactive terminal, the CLI prompts you to log in inline — no separate dropthis login step needed. Disable with --no-interactive.
dropthis whoami # Check current auth status
dropthis logout # Remove stored credentials
dropthis logout --revoke # Remove and revoke the key on the serverPublish
publish is the default command — you can omit it and pass files directly:
# These are equivalent:
dropthis ./report.html
dropthis publish ./report.html# HTML file
dropthis ./report.html
# Directory (static site)
dropthis ./dist
# Stdin
echo "<h1>Hello</h1>" | dropthis publish - --content-type text/html --path index.html
# Multiple files bundled into one drop
dropthis index.html styles.css app.js
# Print only the URL (recommended for agents)
dropthis ./dist --urlPublish options
dropthis publish ./dist \
--title "Launch page" \
--visibility unlisted \
--entry index.html \
--expires-at 2026-12-31T00:00:00Zpublish always creates a NEW drop with a new URL. To change a drop you already published, use update-content or update-settings with its drop_… id — never publish again.
Canonical vs raw URLs
Every drop has a canonical url — an always-branded human view (badge guaranteed). For a single non-HTML file (.pdf, .png, .json, a URL you asked the server to fetch, etc.), publish also prints a Raw: line: the file's exact bytes at a natural path, no wrapper. Hand the canonical url to people; hand the raw URL to agents or downstream tooling that needs the real bytes.
Publishing a bundle with remote assets (--manifest)
When an AI agent generates an HTML page that references external images or other files, use --manifest to publish everything as one drop without base64-encoding the assets. The server fetches remote files during publish so bytes never pass through your process.
dropthis publish --manifest bundle.jsonbundle.json shape:
{
"files": [
{
"path": "index.html",
"content": "<html>...</html>"
},
{
"path": "hero.jpg",
"source_url": "https://cdn.example.com/hero.jpg"
},
{
"path": "data.json",
"content_base64": "eyJrZXkiOiJ2YWx1ZSJ9"
}
]
}Each file entry must have a path and exactly one content key:
| Key | Description |
|-----|-------------|
| content | UTF-8 text content (HTML, CSS, JSON, markdown, …) |
| source_url | Public https:// URL — the server fetches the bytes server-side. Use this for images and other binary assets instead of base64-encoding them. |
| content_base64 | Base64-encoded bytes (for binary files you already have in memory) |
Optional per-file keys: content_type (MIME type override).
The --manifest flag also works on update-content:
dropthis update-content drop_abc123 --manifest bundle-v2.jsonCannot be combined with a positional file/folder/URL argument.
All publish flags:
| Flag | Description |
|------|-------------|
| --title <title> | Drop title |
| --visibility <public\|unlisted> | Drop visibility |
| --password <password> | Require a password to view the drop (Pro plan) |
| --noindex | Prevent search indexing |
| --entry <path> | Entry file for directories |
| --content-type <mime> | MIME type (required for stdin) |
| --path <path> | Filename (required for stdin) |
| --manifest <file> | Multi-file bundle JSON (see above) |
| --expires-at <datetime> | Expiration datetime |
| --metadata <json> | Metadata as JSON string |
| --metadata-file <path> | Metadata from a JSON file |
| --idempotency-key <key> | Idempotency key |
| --url | Print only the URL, nothing else |
| --json | Output full JSON response |
| --dry-run | Validate without publishing |
Global flags
These flags are inherited by all commands:
| Flag | Description |
|------|-------------|
| --api-key <key> | Override API key for this invocation |
| --api-url <url> | Override API base URL |
| --json | Force JSON output |
| -q, --quiet | Suppress status output and imply JSON |
| --no-interactive | Disable interactive prompts (inline auth, confirmations) |
Update
Change an existing drop without creating a new URL. Content and settings are separate operations — pass the full drop_… id (not the slug):
# Replace the content at the same URL (ships a new deployment)
dropthis update-content drop_abc123 ./dist-v2
# Update from a manifest bundle (same --manifest format as publish)
dropthis update-content drop_abc123 --manifest bundle-v2.json
# Change settings only — title, visibility, password, expiry, metadata
dropthis update-settings drop_abc123 --title "New title"
# Optimistic concurrency (both accept --if-revision)
dropthis update-content drop_abc123 ./dist-v2 --if-revision 1update-content replaces the files at the URL; update-settings changes title, visibility, password, noindex, expiry, or metadata. Creating a new drop is always publish — neither update command makes a new URL.
With --if-revision, a concurrent edit fails with a 409 instead of clobbering. The error shows the drop's current_revision and how to retry:
✗ Revision mismatch
The drop changed since it was last read. Re-read it with GET /v1/drops/{drop_id} and retry with if_revision: 2 (sent as the If-Revision header).
Current revision: 2 — retry with --if-revision 2In JSON mode the same error carries current_revision and a next_action field.
Pull (read back)
Download what a drop is serving — by drop_… id, drop URL, or slug. URLs and slugs are resolved to your own drops; the local copy is also the rollback path (pull an old state, then update-content it back):
# By id, into ./drop_abc123/
dropthis pull drop_abc123
# By URL — resolves the slug to your drop, writes into ./abc123/
dropthis pull https://abc123.dropthis.app
# Choose the output directory
dropthis pull drop_abc123 -o ./sitePull fetches the current deployment's file manifest and writes every file into the output directory. It is owner-side read-back via the API — it works regardless of any viewer password. Custom-domain URLs are not resolvable yet; use the drop_… id instead.
Commands
dropthis [input...] # Publish content (default command)
dropthis publish [input...] # Same as above, explicit form
dropthis update-content <drop-id> [input] # Replace a drop's content (same URL)
dropthis update-settings <drop-id> # Change title/visibility/password/expiry/metadata
dropthis pull <id|url|slug> [-o <dir>] # Download a drop's files to a local directory
dropthis get <id|url|slug> # Show drop details
dropthis list # List your drops
dropthis list --domain reports.example.com # Only drops on a custom domain
dropthis delete <id|url|slug> # Delete a drop (--yes to confirm)
dropthis deployments # View deployment history
dropthis login # Authenticate with email OTP
dropthis login request --email <email> # Request OTP (non-interactive)
dropthis login verify --email <email> --otp <code> # Verify OTP
dropthis logout # Remove stored credentials
dropthis whoami # Show current auth status
dropthis account # Show account details
dropthis account update --display-name <name> # Update your display name
dropthis api-keys create # Create an API key
dropthis api-keys list # List API keys
dropthis api-keys delete <key-id> # Delete an API key (--yes to confirm)
dropthis upgrade # Update the CLI to the latest version
dropthis doctor # CLI diagnostics
dropthis commands # Machine-readable command metadataAgent & CI/CD usage
The CLI is designed for non-interactive use. In non-TTY environments (pipes, CI, agents), output defaults to JSON automatically.
Environment variables:
export DROPTHIS_API_KEY=sk_live_... # API key (same as --api-key)
export DROPTHIS_API_URL=https://... # Override API base URL (same as --api-url)
export DROPTHIS_NON_INTERACTIVE=1 # Disable interactive prompts (same as --no-interactive)
export DROPTHIS_NO_UPDATE_NOTIFIER=1 # Disable the startup update notice (also auto-disabled in CI, non-TTY, --json/--quiet)dropthis publish ./dist --urlAgent protocol:
- Use
--urlto get only the published URL (cleanest for agents) - Use
--jsonfor the full structured response - Use
--yesfor destructive commands (delete,api-keys delete,account delete) - Use
--no-interactiveto disable inline auth prompts - All errors write to stderr as JSON with a
next_actionfield
Exit codes:
| Code | Meaning |
|------|---------|
| 0 | Success |
| 1 | API or generic error |
| 2 | Invalid usage |
| 3 | Auth required (no credential, or the API returned 401 — re-authenticate) |
| 4 | Local input error (file or directory not found, too many files) |
| 5 | Network error (could not reach the API) |
| 6 | Domain verification pending (domains verify one-shot, not live yet) |
| 7 | Domain verification timeout (domains verify --wait exceeded --timeout) |
JSON output shape:
{"ok":true,"drop":{"url":"https://dropthis.app/abc123","id":"drop_abc123"}}Error shape (stderr):
{"ok":false,"error":{"code":"auth_error","message":"No API key found.","next_action":"Set DROPTHIS_API_KEY or run dropthis login."}}Doctor
Run diagnostics to verify CLI health:
dropthis doctorReports CLI version, auth source (env, flag, storage, or missing), and credential storage backend (secure, insecure, or none).
{"ok":true,"version":"0.4.1","auth":{"source":"env"},"storage":{"backend":"secure"}}Pricing tiers
- Free — drops expire after 7 days, 5 MB per drop, 500 MB active storage, dropthis badge.
- Pro — drops never expire, 100 MB per drop, 10 GB storage, no badge, 1 custom domain, password-protected drops. Pro is currently invite-only. Learn more at https://dropthis.app/pricing.
Connecting a custom domain (dropthis domains connect) requires the Pro plan (1 hostname). Domains already connected keep working regardless of plan.
dropthis account shows your active tier, its limits, and the workspace your key is bound to. If the workspace kind is team, your publishes land under the team's shared custom domain automatically — no extra flags needed.
Agent skills
For AI coding agents (Cursor, Claude Code, Windsurf, etc.), install the dropthis-skills package:
npx skills add dropthis-dev/dropthis-skills