CLI Reference
Purpose and Scope
The Dub CLI is the terminal entry point for working with Dub links and workspace settings without opening the dashboard. It is packaged as dub-cli, installs a public executable named dub, and is described by the package itself as a CLI for easily shortening URLs with the Dub API. In practice, it gives operators a small workflow surface for authenticating, inspecting local credentials, configuring domains, creating short links, and searching existing links. This page documents that public binary, the command registry, the OAuth login flow, and the local configuration behavior that other commands depend on.
Sources: packages/cli/README.md, packages/cli/package.json, packages/cli/src/index.ts
The CLI should be understood as a convenience layer over Dub’s API-oriented product model. Dub’s broader platform centers on short links, attribution, conversion tracking, and affiliate programs, but this package focuses on everyday link-management tasks that fit naturally in a shell. The README positions shortening as the core use case, while the command table expands the scope to include login, config inspection, domain configuration, link creation, and link search. Those commands map to the capabilities granted during authentication, especially reading links, writing links, and reading domains.
Sources: packages/cli/README.md, packages/cli/src/commands/login.ts
Relevant Source Files
packages/cli/README.md- User-facing package README that lists available commands and local development commands for the CLI package.packages/cli/package.json- Package manifest fordub-cli, including the published binary name, module entry points, package scripts, runtime dependencies, and publish metadata.packages/cli/src/index.ts- Executable entry point that constructs the Commander program, sets thedubname and description, wires the version flag, and registers subcommands.packages/cli/src/commands/login.ts- Login command implementation, including OAuth authorization URL creation, browser opening, localhost callback settings, and requested scopes.packages/cli/src/commands/config.ts- Config command implementation that loads stored credentials and prints the resulting JSON with a spinner and colorized output.packages/cli/src/utils/config.ts- Configuration storage utility that reads and updatesconfigstore, refreshes expired OAuth credentials, and raises the login-required error.
Installation, Binary, and Package Contract
The package manifest is the best reference for how the CLI is published and executed. The package name is dub-cli, versioned in the manifest, marked with public publish access, and configured as an ECMAScript module package. Its executable contract is the bin mapping from dub to ./dist/index.js, so users run commands with the dub binary even though the npm package name includes the -cli suffix. The package also declares ./dist/index.js as the main and export entry point and ./dist/index.d.ts as the type declaration output, reflecting that the source is built before it is used as a global command.
Sources: packages/cli/package.json
The manifest shows the package is intended to be used globally: it sets preferGlobal to true and publishes only the built dist directory. Build and development are handled by tsup, with scripts for dev, build, typecheck, clean, and start. The package depends on Commander for command parsing, open for browser-based login, ora for terminal spinners, configstore for persistent local state, json-colorizer for displaying configuration, and the dub SDK package for API interaction. That dependency list is useful when debugging behavior because the CLI is intentionally thin: command orchestration happens locally, while persistent API work belongs to authenticated Dub calls.
Sources: packages/cli/package.json
For local development, the README documents a two-terminal flow. First, navigate to packages/cli and run pnpm dev so tsup builds in watch mode. In a second terminal, run pnpm start [command], which executes node dist/index.js against the current build output. For a production-like check, build once with pnpm build, link the package globally with npm link, verify with dub -v, and then run dub [command]. The README warns that an existing global install of dub-cli should be removed first to avoid conflicts with the linked development build.
Sources: packages/cli/README.md, packages/cli/package.json
Command Reference
The executable entry point constructs a Commander program named dub, describes it as a CLI for shortening links with the Dub API, and registers -v, --version as the version flag. It also installs SIGINT and SIGTERM handlers that exit the process cleanly, which keeps interrupted terminal sessions from producing unnecessary stack traces. The command registry is explicit: login, config, domains, shorten, and links are added to the program before parsing user input. The README also lists dub help [command], which is Commander’s generated help behavior for discovering command-specific usage.
Sources: packages/cli/src/index.ts, packages/cli/README.md
| Command | Purpose | Notes |
|---|---|---|
dub login | Log into the Dub platform. | Opens a browser-based OAuth flow and stores credentials for later commands. |
dub config | See configured workspace credentials. | Loads the local config and prints formatted JSON. |
dub domains | Configure the workspace domain. | Listed by the README and registered by the entry point. |
dub shorten [url] [key] | Create a short link. | Accepts URL and generated key up front, or can prompt interactively. |
dub links [options] | Search links in the workspace. | Supports -s, --search <search> and -l, --limit <limit>, with a default limit of 10. |
dub help [command] | Display command help. | Provided through Commander’s help system. |
Two details matter when scripting with the CLI. First, the command names are stable at the public interface even when implementation modules evolve, because they are the names registered by the Commander entry point and documented in the README. Second, authentication is a prerequisite for commands that need workspace resources. If no local token exists, configuration loading fails with an explicit instruction to run dub login, and commands that call the config utility should surface that requirement rather than attempting anonymous API access.
Sources: packages/cli/README.md, packages/cli/src/index.ts, packages/cli/src/utils/config.ts
Authentication Flow
The dub login command uses an OAuth authorization-code flow tailored for a local CLI. When invoked, it generates a 64-character code verifier, defines http://localhost:4587/callback as the redirect URI, and asks the OAuth client to create an authorization URL. The requested scopes are links.read, links.write, and domains.read, which align with the command set: listing links, creating short links, and reading domain configuration. The command starts a spinner saying it is opening the browser for authentication, opens the generated URL in the user’s default browser, and then changes the spinner message while it waits for the callback.
Sources: packages/cli/src/commands/login.ts
After the browser opens, the command starts a local callback server by calling oauthCallbackServer with the OAuth client, redirect URI, code verifier, and spinner. The callback server is responsible for completing the OAuth exchange and persisting the resulting credentials through the shared configuration utilities. The login command itself is deliberately small: it defines the local OAuth parameters, delegates URL generation to the OAuth client, delegates browser launch to open, and delegates error handling to the shared handleError utility. If authorization fails before the callback server is established, the catch block sends the exception through that same error path.
Sources: packages/cli/src/commands/login.ts, packages/cli/src/utils/config.ts
This flow has operational implications for users and contributors. The local machine must be able to bind the callback port, the browser must be able to reach Dub’s authorization endpoint, and the final redirect must return to localhost:4587. Because the code verifier is generated per login, users should complete the same browser session that the CLI opened rather than copying an old authorization URL. The stored credentials are then reused by other commands through getConfig, so a successful login turns the CLI from an unauthenticated command shell into a workspace-aware Dub client.
Sources: packages/cli/src/commands/login.ts, packages/cli/src/utils/config.ts
Configuration Storage and Refresh Behavior
Configuration is stored with configstore under the application name dub-cli. The getConfig utility constructs that store on demand and first checks whether it has any contents. If the store is empty, it throws Access token not found. Please run dub login to authenticate with Dub., which is the main guardrail for commands that require authentication. This makes login state a local precondition rather than a command-line flag. Users do not pass tokens on every invocation; they authenticate once, and the CLI retrieves the stored credential object when needed.
Sources: packages/cli/src/utils/config.ts
The config utility also handles token expiry. It reads the stored object as a DubConfig, checks whether expires_at is present and in the past, and then calls oauthClient.refreshToken with the current access token, refresh token, and expiry timestamp. The refreshed accessToken, refreshToken, and expiresAt values are written back using setConfig, which merges new values into any existing configuration and persists the result. This design keeps commands simple: they can call getConfig and receive usable credentials, while refresh details stay centralized in one utility.
Sources: packages/cli/src/utils/config.ts
The dub config command is the public inspection tool for that local state. It starts an ora spinner with “Getting config”, calls getConfig, marks the spinner as successful, and prints the configuration object as indented JSON using json-colorizer. If loading or refreshing fails, the spinner is stopped and the error is passed to the shared error handler. Because dub config itself triggers getConfig, it can also refresh expired credentials as part of inspection. That behavior is useful for diagnosing login state, but it also means the command is not just a file viewer; it participates in the same credential lifecycle as API-facing commands.
Sources: packages/cli/src/commands/config.ts, packages/cli/src/utils/config.ts
Execution Flow for Contributors
When the dub binary starts, Node executes the built dist/index.js file generated from packages/cli/src/index.ts. The shebang in the source marks it as a Node executable, and the program’s asynchronous main function retrieves package information before constructing the Commander instance. The version displayed by dub -v comes from that package information when available, with a fallback value in the entry point. After the command modules are registered, Commander parses the process arguments and dispatches to the selected command’s action function.
Sources: packages/cli/src/index.ts, packages/cli/package.json
A typical contributor loop is therefore build, invoke, inspect, and repeat. Run the package in watch mode with pnpm dev, execute commands through pnpm start, and use pnpm start help to confirm the command surface after changes. For changes that affect authentication or configuration, test both an empty config store and an existing authenticated store, because the behavior differs sharply between “no token”, “valid token”, and “expired token with refresh token”. For release-like validation, use the README’s pnpm build plus npm link flow and verify that the global dub command reports a version and dispatches commands from the built output.
Sources: packages/cli/README.md, packages/cli/src/index.ts, packages/cli/src/utils/config.ts
Next Steps
Start with dub login, then run dub config to confirm that the CLI can read the stored credentials. From there, use dub shorten [url] [key] for direct short-link creation or dub links --search <search> --limit <limit> to inspect existing workspace links from the terminal. Contributors changing command behavior should update both the command implementation and the README command table so the public reference remains aligned with the registered Commander surface. For broader context, read the link-management and API reference pages next, because the CLI is intentionally a focused shell interface over Dub’s link and domain capabilities.
Sources: packages/cli/README.md, packages/cli/src/index.ts, packages/cli/src/commands/login.ts, packages/cli/src/commands/config.ts, packages/cli/src/utils/config.ts