@displaydev/cli
v0.11.0
Published
CLI for [display.dev](https://display.dev) — publish HTML and Markdown behind company auth.
Readme
@displaydev/cli
CLI for display.dev — publish HTML and Markdown behind company auth.
Install
npm install -g @displaydev/cliAuthenticate
# Interactive login via email OTP
dsp login
# Or set an API key (from the dashboard)
export DISPLAYDEV_API_KEY=sk_live_...Agent-native login
dsp login is designed to be driven entirely by an AI agent that has inbox
access (Gmail/IMAP via MCP, etc.) — no human-in-the-loop required.
The command adapts to its environment:
| Invocation | Behavior |
|---|---|
| dsp login --email [email protected] (TTY) | Sends OTP, prompts for the code in the terminal. |
| dsp login --email [email protected] (non-TTY) | Sends OTP and exits 0. Caller reads the code (e.g. from inbox) and re-invokes. |
| dsp login --email [email protected] --code 123456 | Verifies the code, persists the token, exits. |
For structured output that doesn't rely on prose parsing:
dsp login --email [email protected] --json
# → {"status":"otp_sent","email":"[email protected]",
# "next":{"command":"dsp login","args":["--email","[email protected]","--code","<code>"]}}
dsp login --email [email protected] --code 123456 --json
# → {"status":"authenticated","method":"otp","email":"[email protected]"}OTP email format is stable and regex-parseable:
- Subject:
123456 is your display.dev sign-in code - Body:
Your verification code is: 123456
Extraction pattern: /Your verification code is: (\d{6})/.
Usage
# Publish a file (defaults to company auth)
dsp publish ./report.html --name "Q1 Report"
# Publish as public (no login required to view)
dsp publish ./report.html --name "Q1 Report" --public
# Share with external recipients (repeatable)
dsp publish ./proposal.html --name "Q1 Proposal" \
--share [email protected] --share [email protected]
# Update an existing artifact (keeps current visibility/shares if not specified)
dsp publish ./report.html --id abc123
# Change an existing artifact back to company auth
dsp publish ./report.html --id abc123 --company
# Remove all guest shares from an existing artifact
dsp publish ./report.html --id abc123 --clear-shares
# Search artifacts
dsp find
dsp find "quarterly" --by [email protected]
# Get artifact details
dsp get abc123
# Delete an artifact
dsp delete abc123 --confirm
# Start MCP server (for Claude Desktop / Claude Code)
dsp mcpPublish flags
| Flag | Effect |
|---|---|
| --name <name> | Artifact display name. Required for new artifacts. |
| --id <shortId> | Update an existing artifact (publishes a new version). |
| --public | Set visibility to public (no auth required to view). |
| --company | Set visibility to company auth (default for new artifacts). |
| --share <email> | Grant guest access to an external email (repeatable). |
| --clear-shares | Remove all guest shares. Only valid with --id. |
| --theme <theme> | Markdown rendering theme. |
Visibility and share flags default to "keep current" on update: omitting --public/--company leaves visibility unchanged, and omitting --share/--clear-shares leaves the guest list unchanged. --public/--company are mutually exclusive; --share/--clear-shares are mutually exclusive.
Testing
The CLI suite uses three distinct fetch-faking strategies, each pinned to a layer of the stack. New contributors pick the one that matches the layer they're testing — adding a fourth is almost always wrong.
- Unit-level fetch fake (
stubFetchJson(...)) — for exercisingApiClientand anything that composes it purely in-process. Low ceremony, full control over responses, no cross-process boundary. Use thestubFetchJson(body, init?)helper fromcli/src/test-helpers.ts— it stubsglobal.fetchwith a fresh JSONResponseper call and returns thevi.fn()so tests can still inspectfetchMock.mock.calls[...]. Fall back to rawvi.stubGlobal('fetch', vi.fn().mockRejectedValue(...))only for network- failure cases. Representative example:cli/src/api-client.spec.tsand the integration wire-up assertion incli/src/mcp-mode-selection.spec.ts. - Hand-rolled
createMockApiClient()— for MCP tool specs that verify argument-routing from an MCP tool handler to the rightApiClientmethod. No HTTP in the loop at all; the fake is a plain object ofvi.fn()s. Representative example:cli/src/mcp-server.spec.tsandcli/src/mcp-publish-file-read.spec.ts. - Real localhost HTTP fixture (
createHttpFixture+spawnCli) — forprogram.actionE2E specs that need to exercise commander argv parsing, the stdout/stderr split, exit codes, and the full HTTP round-trip. Drives the CLI as a real child process. Representative examples:cli/src/cli-mode-selection.spec.ts,cli/src/cli-subcommands.spec.ts,cli/src/login-otp.spec.ts,cli/src/login-otp-send.spec.ts.
MCP
The CLI doubles as an MCP server over stdio. Add to your MCP client config:
{
"mcpServers": {
"display": {
"command": "dsp",
"args": ["mcp"],
"env": {
"DISPLAYDEV_API_KEY": "sk_live_..."
}
}
}
}