---
title: "CLI Tool"
description: "The Dub CLI tool (dub) is a command-line interface designed to streamline URL shortening and link management directly from the terminal using the Dub API. It serves developers and platform users by..."
last_updated: "2026-10-05T05:07:35.166923+00:00"
canonical_url: "https://www.doc0.dev/docs/934e554a-e6a1-476f-bb2f-23e62d86c3fd/technical/developer-tools/cli-tool"
---

<details>
<summary>Relevant source files</summary>

The following files were used as context for generating this wiki page:

- [packages/cli/src/index.ts](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/index.ts)
- [packages/cli/src/commands/links.ts](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/commands/links.ts)
- [packages/cli/package.json](https://github.com/blade47/dub/blob/HEAD/packages/cli/package.json)
- [packages/cli/src/commands/login.ts](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/commands/login.ts)
- [apps/web/app/api/links/route.ts](https://github.com/blade47/dub/blob/HEAD/apps/web/app/api/links/route.ts)
- [packages/cli/src/types/index.ts](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/types/index.ts)
- [apps/web/lib/integrations/slack/commands.ts](https://github.com/blade47/dub/blob/HEAD/apps/web/lib/integrations/slack/commands.ts)
- [packages/cli/src/api/links.ts](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/api/links.ts)
- [apps/web/app/app.dub.co/onboarding/workspaces/page.tsx](https://github.com/blade47/dub/blob/HEAD/apps/web/app/app.dub.co/(onboarding)/workspaces/page.tsx)
- [apps/web/app/app.dub.co/dashboard/slug/links/page.tsx](https://github.com/blade47/dub/blob/HEAD/apps/web/app/app.dub.co/(dashboard)/%5Bslug%5D/links/page.tsx)
- [apps/web/app/app.dub.co/dashboard/slug/links/page-client.tsx](https://github.com/blade47/dub/blob/HEAD/apps/web/app/app.dub.co/(dashboard)/%5Bslug%5D/links/page-client.tsx)
- [apps/web/middleware.ts](https://github.com/blade47/dub/blob/HEAD/apps/web/middleware.ts)
- [packages/cli/src/utils/get-package-info.ts](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/utils/get-package-info.ts)
- [packages/cli/src/utils/oauth.ts](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/utils/oauth.ts)
- [package.json](https://github.com/blade47/dub/blob/HEAD/package.json)
- [packages/cli/src/api/domains.ts](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/api/domains.ts)
- [apps/web/app/app.dub.co/dashboard/slug/links/...link/page.tsx](https://github.com/blade47/dub/blob/HEAD/apps/web/app/app.dub.co/(dashboard)/%5Bslug%5D/links/%5B...link%5D/page.tsx)
- [apps/web/app/ee/admin.dub.co/dashboard/links/page.tsx](https://github.com/blade47/dub/blob/HEAD/apps/web/app/(ee)/admin.dub.co/(dashboard)/links/page.tsx)
- [apps/web/app/ee/admin.dub.co/dashboard/links/components/disable-restore-workspace.tsx](https://github.com/blade47/dub/blob/HEAD/apps/web/app/(ee)/admin.dub.co/(dashboard)/links/components/disable-restore-workspace.tsx)
- [packages/ui/src/footer.tsx](https://github.com/blade47/dub/blob/HEAD/packages/ui/src/footer.tsx)
- [packages/cli/src/utils/config.ts](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/utils/config.ts)
- [packages/cli/src/commands/domains.ts](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/commands/domains.ts)
- [apps/web/app/app.dub.co/dashboard/slug/links/folders/page.tsx](https://github.com/blade47/dub/blob/HEAD/apps/web/app/app.dub.co/(dashboard)/%5Bslug%5D/links/folders/page.tsx)
- [apps/web/app/app.dub.co/dashboard/slug/ee/program/groups/groupSlug/links/page.tsx](https://github.com/blade47/dub/blob/HEAD/apps/web/app/app.dub.co/(dashboard)/%5Bslug%5D/(ee)/program/groups/%5BgroupSlug%5D/links/page.tsx)
- [apps/web/app/ee/admin.dub.co/dashboard/domains/page.tsx](https://github.com/blade47/dub/blob/HEAD/apps/web/app/(ee)/admin.dub.co/(dashboard)/domains/page.tsx)
- [apps/web/scripts/dub-wrapped.ts](https://github.com/blade47/dub/blob/HEAD/apps/web/scripts/dub-wrapped.ts)
- [apps/web/app/app.dub.co/dashboard/slug/links/...link/page-client.tsx](https://github.com/blade47/dub/blob/HEAD/apps/web/app/app.dub.co/(dashboard)/%5Bslug%5D/links/%5B...link%5D/page-client.tsx)
- [apps/web/lib/dub.ts](https://github.com/blade47/dub/blob/HEAD/apps/web/lib/dub.ts)
- [packages/ui/package.json](https://github.com/blade47/dub/blob/HEAD/packages/ui/package.json)
- [packages/cli/tsup.config.ts](https://github.com/blade47/dub/blob/HEAD/packages/cli/tsup.config.ts)
</details>

## Overview

The Dub CLI tool (`dub`) is a command-line interface designed to streamline URL shortening and link management directly from the terminal using the Dub API. It serves developers and platform users by offering native command execution, persistent local configuration storage, and secure authentication workflows that mirror core web capabilities without requiring a browser interface. Sources: [packages/cli/src/index.ts:17-19](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/index.ts#L17-L19), [packages/cli/package.json:2-4](https://github.com/blade47/dub/blob/HEAD/packages/cli/package.json#L2-L4)

## Entry Point and Command Registration

### Overview

The executable bootstrapping process initializes the `dub` command-line application, configuring signal handlers, loading package metadata, registering root command modules via `commander`, and executing argument parsing.

Sources: [packages/cli/src/index.ts:11-34](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/index.ts#L11-L34)

### Bootstrapping and Execution Flow

The entry point script `packages/cli/src/index.ts` begins with the Node.js environment hashbang `#!/usr/bin/env node` and registers global listeners for `SIGINT` and `SIGTERM` signals to ensure clean process termination via `process.exit(0)`.

Sources: [packages/cli/src/index.ts:1-12](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/index.ts#L1-L12)

The asynchronous `main()` function drives the root setup sequence:
1. `getPackageInfo()` is called to fetch remote package metadata for `dub-cli`.
2. A new `Command` instance is initialized with `.name("dub")`, `.description("A CLI for shortening links with the Dub API.")`, and `.version(packageInfo.version || "1.0.0", "-v, --version", "display the version number")`.
3. Subcommands are registered sequentially using `.addCommand()` for `login`, `config`, `domains`, `shorten`, and `links`.
4. `program.parse()` executes command resolution and argument evaluation.

Sources: [packages/cli/src/index.ts:14-34](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/index.ts#L14-L34), [packages/cli/src/utils/get-package-info.ts:4-7](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/utils/get-package-info.ts#L4-L7)

> [!NOTE]
> `getPackageInfo()` retrieves metadata dynamically by querying `package-json` for `"dub-cli"`, falling back to `"1.0.0"` if the version field is undefined.

Sources: [packages/cli/src/utils/get-package-info.ts:4-7](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/utils/get-package-info.ts#L4-L7), [packages/cli/src/index.ts:20-24](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/index.ts#L20-L24)

### Registered Commands Reference

| Command Variable | Source Path | Description / Purpose |
| :--- | :--- | :--- |
| `login` | `@/commands/login` | Authenticates user via OAuth and stores credentials. Sources: [packages/cli/src/index.ts:5-27](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/index.ts#L5-L27) |
| `config` | `@/commands/config` | Manages local CLI configuration settings. Sources: [packages/cli/src/index.ts:3-28](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/index.ts#L3-L28) |
| `domains` | `@/commands/domains` | Discovers and configures workspace domains. Sources: [packages/cli/src/index.ts:4-29](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/index.ts#L4-L29) |
| `shorten` | `@/commands/shorten` | Shortens URLs using the Dub API. Sources: [packages/cli/src/index.ts:6-30](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/index.ts#L6-L30) |
| `links` | `./commands/links` | Queries and manages shortened links. Sources: [packages/cli/src/index.ts:9-31](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/index.ts#L9-L31) |

Sources: [packages/cli/src/index.ts:3-31](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/index.ts#L3-L31)

### Build Configuration and Bundling

The package utilizes `tsup` for building the CLI bundle as defined in `packages/cli/tsup.config.ts`. The configuration specifies ECMAScript module output format (`esm`), target environment `esnext`, source maps enabled, code minification enabled, and declaration generation (`dts: true`) into the `dist` directory with `src/index.ts` as the primary entry point.

Sources: [packages/cli/tsup.config.ts:1-12](https://github.com/blade47/dub/blob/HEAD/packages/cli/tsup.config.ts#L1-L12)

## OAuth Authentication Flow

### Overview

The `login` command orchestrates a browser-based OAuth authentication flow using PKCE, spinning up a local callback server listener to acquire API tokens from the Dub platform. Sources: [packages/cli/src/commands/login.ts:9-38](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/commands/login.ts#L9-L38)

### OAuth Client Configuration and Flow Execution

The `oauthClient` instance is initialized via `@badgateway/oauth2-client` with a predefined client ID and explicit authorization and token endpoints on the Dub platform. Sources: [packages/cli/src/utils/oauth.ts:1-8](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/utils/oauth.ts#L1-L8)

| Parameter | Value / Source | Purpose |
| :--- | :--- | :--- |
| `clientId` | `"dub_app_39527dcc11b452f38bb54a3a1664fd044d7158dfea8abcde"` | Identifies the Dub CLI application. Sources: [packages/cli/src/utils/oauth.ts:5-5](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/utils/oauth.ts#L5-L5) |
| `authorizationEndpoint` | `"https://app.dub.co/oauth/authorize"` | Target URL for initiating user authorization. Sources: [packages/cli/src/utils/oauth.ts:6-6](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/utils/oauth.ts#L6-L6) |
| `tokenEndpoint` | `"https://api.dub.co/oauth/token"` | Target URL for exchanging authorization codes for tokens. Sources: [packages/cli/src/utils/oauth.ts:7-7](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/utils/oauth.ts#L7-L7) |

Sources: [packages/cli/src/utils/oauth.ts:3-8](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/utils/oauth.ts#L3-L8)

The call-chain execution proceeds through the `login` command action handler:
1. `getNanoid(64)` generates a 64-character cryptographic `codeVerifier`. Sources: [packages/cli/src/commands/login.ts:14-14](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/commands/login.ts#L14-L14)
2. `oauthClient.authorizationCode.getAuthorizeUri()` constructs the authorization URI using `redirectUri` (`http://localhost:4587/callback`), `codeVerifier`, and the required scopes. Sources: [packages/cli/src/commands/login.ts:15-21](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/commands/login.ts#L15-L21)
3. `open(authUrl)` opens the browser for authentication while an `ora` spinner displays status updates. Sources: [packages/cli/src/commands/login.ts:23-27](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/commands/login.ts#L23-L27)
4. `oauthCallbackServer()` spins up the local listener passing the client, redirect URI, code verifier, and spinner to finalize token acquisition. Sources: [packages/cli/src/commands/login.ts:29-34](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/commands/login.ts#L29-L34)

> [!NOTE]
> The OAuth flow requests three specific permission scopes: `links.read`, `links.write`, and `domains.read`. Sources: [packages/cli/src/commands/login.ts:20-20](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/commands/login.ts#L20-L20)

### OAuth Configuration Reference

| Option / Variable | Value / Setting | Description |
| :--- | :--- | :--- |
| `redirectUri` | `"http://localhost:4587/callback"` | Local redirect URI for capturing the OAuth callback code. Sources: [packages/cli/src/commands/login.ts:15-15](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/commands/login.ts#L15-L15) |
| `codeVerifier` | `getNanoid(64)` | PKCE code verifier generated using a 64-character nanoid. Sources: [packages/cli/src/commands/login.ts:14-14](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/commands/login.ts#L14-L14) |
| `scope` | `["links.read", "links.write", "domains.read"]` | Array of permission scopes requested during authorization. Sources: [packages/cli/src/commands/login.ts:20-20](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/commands/login.ts#L20-L20) |

Sources: [packages/cli/src/commands/login.ts:14-21](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/commands/login.ts#L14-L21)

## Configuration Persistence and State Management

### Overview

State management and credential persistence are handled through the `Configstore` library under the application identifier `"dub-cli"`. Active settings and tokens are structured according to the `DubConfig` interface and manipulated via dedicated retrieval and persistence utility functions. Sources: [packages/cli/src/types/index.ts:1-6](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/types/index.ts#L1-L6), [packages/cli/src/utils/config.ts:3-6](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/utils/config.ts#L3-L6)

### Schema Definition and Storage Format

The configuration schema defines authentication tokens, expiration metadata, and active workspace parameters. Sources: [packages/cli/src/types/index.ts:1-6](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/types/index.ts#L1-L6)

| Field | Type | Description |
| :--- | :--- | :--- |
| `access_token` | `string` | The active OAuth access token for authenticating API requests. Sources: [packages/cli/src/types/index.ts:2-2](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/types/index.ts#L2-L2) |
| `refresh_token` | `string \| null` | The OAuth refresh token used to obtain new access tokens upon expiration. Sources: [packages/cli/src/types/index.ts:3-3](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/types/index.ts#L3-L3) |
| `expires_at` | `number \| null` | Epoch timestamp indicating when the current access token expires. Sources: [packages/cli/src/types/index.ts:4-4](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/types/index.ts#L4-L4) |
| `domain` | `string` (optional) | The active custom domain configured for link operations. Sources: [packages/cli/src/types/index.ts:5-5](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/types/index.ts#L5-L5) |

Sources: [packages/cli/src/types/index.ts:1-6](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/types/index.ts#L1-L6)

### Configuration Retrieval and Token Refresh Walkthrough

The configuration retrieval mechanism validates stored credentials and automatically triggers a token refresh if the access token has expired. Sources: [packages/cli/src/utils/config.ts:5-32](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/utils/config.ts#L5-L32)

The call-chain execution proceeds through `getConfig()`:
1. `new Configstore("dub-cli")` instantiates the persistent store handler. Sources: [packages/cli/src/utils/config.ts:6-6](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/utils/config.ts#L6-L6)
2. `configStore.size` is checked; if empty, it throws an error instructing the user to run `dub login`. Sources: [packages/cli/src/utils/config.ts:8-12](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/utils/config.ts#L8-L12)
3. `configStore.all` is cast to `DubConfig`, and `config.expires_at` is evaluated against `Date.now()`. Sources: [packages/cli/src/utils/config.ts:14-16](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/utils/config.ts#L14-L16)
4. If expired, `oauthClient.refreshToken()` is called with existing tokens and expiration data. Sources: [packages/cli/src/utils/config.ts:17-22](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/utils/config.ts#L17-L22)
5. `setConfig()` updates the store with the newly acquired token set and returns the updated configuration object. Sources: [packages/cli/src/utils/config.ts:24-28](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/utils/config.ts#L24-L28)

> [!WARNING]
> If `configStore.size` evaluates to zero, `getConfig()` immediately throws an error requiring re-authentication rather than returning an empty configuration object. Sources: [packages/cli/src/utils/config.ts:8-12](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/utils/config.ts#L8-L12)

### State Mutation and Persistence Operations

The `setConfig()` function accepts partial configuration updates, merges them into the existing state store, and validates the disk path before returning the finalized configuration. Sources: [packages/cli/src/utils/config.ts:34-52](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/utils/config.ts#L34-L52)

```typescript
export async function setConfig(
  newConfig: Partial<DubConfig>,
): Promise<DubConfig> {
  const configStore = new Configstore("dub-cli");
  const existingConfig: DubConfig = configStore.all;

  const updatedConfig: DubConfig = {
    ...existingConfig,
    ...newConfig,
  };

  configStore.set(updatedConfig);

  if (!configStore.path) {
    throw new Error("Failed to create or update config file");
  }

  return updatedConfig;
}
```

Sources: [packages/cli/src/utils/config.ts:34-52](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/utils/config.ts#L34-L52)

> [!NOTE]
> Setting configuration values performs a shallow spread merge over existing configuration data, preserving unmentioned properties such as active tokens while updating specific fields like the active domain. Sources: [packages/cli/src/utils/config.ts:38-43](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/utils/config.ts#L38-L43)

## Link Management and Shortening

### Overview

The link management subsystem integrates Commander command definitions with the official Dub SDK to query workspaces and generate short links remotely. Operations retrieve local credentials via `getConfig()`, instantiate the `Dub` client with the stored access token, and format query results into a structured console table using `ora` spinners for terminal feedback.

Sources: [packages/cli/src/commands/links.ts:1-46](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/commands/links.ts#L1-L46), [packages/cli/src/api/links.ts:1-16](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/api/links.ts#L1-L16)

### Command Mechanics and SDK Integration

The `links` command exposes search and pagination options using Commander.

Sources: [packages/cli/src/commands/links.ts:7-12](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/commands/links.ts#L7-L12)

| Option Flag | Argument Type | Default / Fallback | Description | Sources |
| --- | --- | --- | --- | --- |
| `-s, --search` | `[search]` | `undefined` | Search term to filter links by | Sources: [packages/cli/src/commands/links.ts:10-10](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/commands/links.ts#L10-L10) |
| `-l, --limit` | `[limit]` | `10` (parsed integer) | Number of links to fetch | Sources: [packages/cli/src/commands/links.ts:11-11](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/commands/links.ts#L11-L11), [packages/cli/src/commands/links.ts:24-24](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/commands/links.ts#L24-L24) |

Sources: [packages/cli/src/commands/links.ts:10-24](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/commands/links.ts#L10-L24)

### Link Search Call-Chain Walkthrough

The link search execution path processes user input through configuration loading, SDK querying, and tabular formatting:

1. `getConfig()` retrieves the active OAuth access token from persistent storage.
2. `ora("Fetching links").start()` initiates a terminal spinner animation during network traversal.
3. `new Dub({ token })` instantiates the SDK client using the retrieved access token.
4. `dub.links.list({ search, pageSize })` queries remote workspace links, parsing `limit` via `parseInt` or falling back to `10`.
5. `spinner.stop()` halts the CLI loading animation upon response receipt.
6. `links.result.map()` transforms raw link objects into structured key-value pairs (`Short Link`, `Destination URL`, `Clicks`, and localized `Created At` dates), which are rendered via `console.table()`.

Sources: [packages/cli/src/commands/links.ts:13-42](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/commands/links.ts#L13-L42)

> [!WARNING]
> Any uncaught errors during configuration retrieval, Dub client initialization, or remote API execution are intercepted by the catch block and piped directly into `handleError(error)`.

Sources: [packages/cli/src/commands/links.ts:43-45](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/commands/links.ts#L43-L45)

### Remote Link Generation API

The `createLink` helper function provisions new short links against the configured workspace domain.

Sources: [packages/cli/src/api/links.ts:4-16](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/api/links.ts#L4-L16)

```typescript
export async function createLink({ url, key }: { url: string; key: string }) {
  const config = await getConfig();

  const dub = new Dub({
    token: config.access_token,
  });

  return await dub.links.create({
    domain: config.domain,
    url: url,
    key: key,
  });
}
```

Sources: [packages/cli/src/api/links.ts:4-16](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/api/links.ts#L4-L16)

> [!TIP]
> The `createLink` function automatically binds the generated short link to `config.domain` retrieved from the active workspace settings alongside the provided destination `url` and custom slug `key`.

Sources: [packages/cli/src/api/links.ts:5-15](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/api/links.ts#L5-L15)

## Domain Operations and Workspace Context

### Overview

The `domains` command manages workspace domain configuration by retrieving available custom and default domains, presenting an interactive selection menu via `prompts`, and saving the chosen domain to local storage.

Sources: [packages/cli/src/commands/domains.ts:17-69](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/commands/domains.ts#L17-L69)

### Domain Discovery Call-Chain Walkthrough

The domain discovery workflow aggregates available domain slugs from both the Dub SDK and direct platform APIs:

1. `getConfig()` reads the local access token from disk configuration.
2. `new Dub({ token })` instantiates the Dub client.
3. `Promise.all()` executes concurrent requests to fetch custom domains via `dub.domains.list()` and default domains via a `fetch` call to `https://api.dub.co/domains/default`.
4. `parseApiResponse<string[]>(defaultDomainsResponse)` parses the raw HTTP response body into a string array.
5. Array mapping extracts custom domain `.slug` properties, combines them with default domains, and passes the concatenated list into `Array.from(new Set(allSlugs))` to eliminate duplicate entries before returning the unique slugs.

Sources: [packages/cli/src/api/domains.ts:7-33](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/api/domains.ts#L7-L33)

> [!NOTE]
> `getDomains` performs concurrent retrieval against both the SDK domain listing endpoint and the default domains route, unifying custom workspace domains and fallback options into a single deduplicated array.

Sources: [packages/cli/src/api/domains.ts:13-33](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/api/domains.ts#L13-L33)

### Interactive Selection and Configuration Persistence

The `domains` command presents an interactive terminal prompt using `ora` and `prompts`, validating user selection against a Zod schema before persisting the result.

Sources: [packages/cli/src/commands/domains.ts:17-69](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/commands/domains.ts#L17-L69)

| Component / Function | Type / Library | Purpose / Validation Behavior | Sources |
| --- | --- | --- | --- |
| `domainOptionsSchema` | Zod Object (`z/v4`) | Validates that the selected domain slug has a minimum length of 3 characters with message `"Please provide a valid slug"`. | Sources: [packages/cli/src/commands/domains.ts:11-15](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/commands/domains.ts#L11-L15) |
| Spinner (`ora`) | `ora` | Displays an active `"Fetching domains"` loader while `getDomains()` resolves. | Sources: [packages/cli/src/commands/domains.ts:21-26](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/commands/domains.ts#L21-L26) |
| Prompt (`prompts`) | `prompts` | Renders a select dropdown using the fetched choices and executes `domainOptionsSchema.shape.slug.safeParse(value)`. | Sources: [packages/cli/src/commands/domains.ts:36-47](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/commands/domains.ts#L36-L47) |
| Cancellation Handler | `onCancel` callback | Intercepts prompt cancellation, logs a warning via `logger.warn("You canceled the prompt.")`, and terminates the process with `process.exit(0)`. | Sources: [packages/cli/src/commands/domains.ts:49-56](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/commands/domains.ts#L49-L56) |
| Persistence | `setConfig` | Saves the validated selection via `setConfig({ domain: options.domain })` and outputs success logs using `chalk.green`. | Sources: [packages/cli/src/commands/domains.ts:59-64](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/commands/domains.ts#L59-L64) |

Sources: [packages/cli/src/commands/domains.ts:11-64](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/commands/domains.ts#L11-L64)

> [!CAUTION]
> If a user cancels the interactive domain prompt via keyboard interruption or escape, the `onCancel` handler forces an immediate process termination (`process.exit(0)`), preventing subsequent configuration writes.

Sources: [packages/cli/src/commands/domains.ts:49-56](https://github.com/blade47/dub/blob/HEAD/packages/cli/src/commands/domains.ts#L49-L56)

## Related

- [OpenAPI and Public REST API](https://www.doc0.dev/docs/934e554a-e6a1-476f-bb2f-23e62d86c3fd/technical/developer-tools/openapi-and-public-rest-api)


## Sitemap

See the full [sitemap](https://www.doc0.dev/docs/934e554a-e6a1-476f-bb2f-23e62d86c3fd/llms.txt) for all pages in this wiki.
