@dashnex/cli
Command-line interface for the DashNex framework. This CLI tool provides essential commands for managing DashNex projects, including module building, database operations, and migrations. It searches for all @dashnex/* modules in node_modules folder, and loads commands from them.
Each module can add a command to CLI. Read more here
Installation
npm install -g @dashnex/cli
Update to the latest stable version later with dashnex update — it reinstalls the CLI with the same package manager (npm or pnpm) and package (dashnex or @dashnex/cli) it was installed with.
Usage
dashnex <command> [options]
Commands
| Command | Description |
|---|---|
version | Display the version of the CLI |
update | Update the CLI to the latest stable version, using the npm or pnpm global install it came from |
login | Log in to your DashNex account |
logout | Log out and remove local credentials |
switch | Switch to another business |
whoami | Show the logged-in business (with its ID), user and session file |
dev | Run the development server (run in DashNex application folder); -p/--port, -H/--host, --https |
check | Run build-time validations (run in DashNex application folder) |
install | Install dependencies using pnpm or npm (run in DashNex application folder) |
app create | Create a new DashNex application |
app pull | Pull existing application to specified folder |
app push | Push local application to DashNex |
app delete | Delete application from DashNex |
app deploy | Deploy application to DashNex |
app status | Show the business, application ID, status, URL and session file |
app clear | Delete local dependencies, caches and build output |
secrets list | List all secret names bound to the worker |
secrets set | Create or update a secret |
secrets delete | Delete a secret |
module init | Create a reserved marketplace module in packages/ from the module template (run in DashNex application folder) |
module submit <package> | Build, publish and submit a marketplace module for review (run in DashNex application folder) |
Sessions
The session lives in .dashnex/settings.json. Commands use the nearest one,
looking in the current folder first and then in each parent folder; files are
never merged. An application folder with its own .dashnex/settings.json
therefore has its own session, independent of the one in your home folder.
login, logout, switch, whoami and app status all act on that same
file and print its path.
The CLI keeps its session under two keys and leaves every other key in the file alone:
{
"business": { "token": "…", "refreshToken": "…", "id": "103602" },
"id": { "token": "…", "refreshToken": "…", "expiresAt": 1793270400000 }
}
business is the token for the selected business; id is the DashNex ID
session switch uses. Files written by CLI 0.7.x and older (flat token,
businessId, idToken… keys) are still read and are moved into this layout
the next time the CLI saves the file.
Inside an application, the CLI checks the session against the BUSINESS_ID
(and, where it matters, APPLICATION_ID) of the application. Each ID is read
from the first of .env.production, .env.development and .env that names
it.
app push, app deploy, app delete, app pull, app tail, secrets
and module commands that use the session (such as db migrate --remote)
refuse to run for another business. whoami and app status show a warning
instead.
Options
login:
--business <id>— Business ID to select--no-browser— Print the sign-in URL instead of opening a browser
Sign-in goes through DashNex ID in your browser: approve the request, copy the code you are shown, and paste it back into the CLI. Codes are valid for 10 minutes and can only be used once.
Inside an application, login selects the business named by BUSINESS_ID
(unless --business is given; see Sessions), and switches to it when the current
session is for another business.
logout:
Removes the session from the settings file the other commands use (see Sessions) and prints its path.
switch:
[businessId]— Business to switch to; without it, pick from the list of your businesses
Re-uses the DashNex ID session saved by dashnex login, so no new sign-in is needed.
dev:
-p, --port <port>— Port for the dev server (falls back to thePORTenv var, then 3000)-H, --host <host>— Host/interface to bind, e.g.0.0.0.0when the app runs inside Docker (falls back to theHOSTenv var, then localhost). With--httpsthe certificate also covers this host.--https— Serve over HTTPS with a locally trusted certificate. Usesmkcertfrom your PATH, or downloads it once into~/.dashnex/mkcert, installs the local CA (may prompt for your password) and writeslocalhost.pem+localhost-key.peminto the app's.dashnex/certificates/folder (never committed). Falls back to HTTP if that fails.
app create:
--subdomain <subdomain>— Subdomain-t, --template <template>— Template, optional (name@branch-or-tag)
app pull:
-y, --yes— Skip prompts and child commands
app push:
--deploy/--no-deploy— Auto-deploy or skip deploy after push--no-check— Skip pre-push checks
app delete:
-y, --yes— Skip confirmation prompt
app clear:
-y, --yes— Skip all prompts
Removes node_modules (including the vite cache), the generated src/dashnex registry and dist.
Interactively it then asks separately whether to delete the local database (.wrangler), since that
is the one target holding data a rebuild cannot restore. With -y/--ci there is nobody to ask, so
the database is cleared along with everything else.
secrets set:
--name <name>— Secret name (prompted if omitted)--value <value>— Secret value (prompted with masked input if omitted)
secrets delete:
--name <name>— Secret name (prompted if omitted)
module init:
[address]— Module to create, e.g.crmor@acme/crm. Without it, a single missing reserved module is created directly; with several, you pick one of them and only that module is created. If your business holds the same address in two namespaces (an older@dashnex/crmand a newer@acme/crm), pass the full package name--all— Create every reserved module that is not inpackages/yet--ci— No prompts. With several missing modules, pass an address or--all
module submit:
<address>— Module to submit, e.g.crmor@acme/crm. Pass the full package name when your business holds the address in two namespaces--ci— Disable spinner (CI mode)
Developing a marketplace module
Marketplace modules are npm packages that extend your DashNex application. A module is published under your business's own namespace, e.g. @acme/crm (acme is the namespace, crm the address). Modules reserved before namespaces existed keep their @dashnex/<address> name. To build one:
- Join the Developers program and reserve a module name on business.dashnex.com.
- Deploy your application once. This writes the DashNex registry
.npmrc. - Run
dashnex app pullin the application folder. - Run
dashnex module init. It createspackages/<address>from the module template, sets the package name to the reserved one (@<namespace>/<address>), points the template's translation namespace and/templatepage and API paths at the module, and links the module into your application'spackage.json. - Build and develop:
cd packages/<address> && pnpm install && pnpm build, thendashnex install && dashnex devin the application folder. - When the module is ready, run
dashnex module submit @<namespace>/<address>(a bare<address>also works when you hold it in one namespace). The package name inpackages/<address>/package.jsonmust match the reservation. It builds and publishes the module to the DashNex registry under thereviewdist-tag, then submits the version for review (latestmoves only once the version is approved). The local version must be higher than the last submitted one, and no other version may be in review.
The pulled application's .npmrc is copied into the module folder for publishing. It maps your namespace (@<namespace>:registry=) to the DashNex registry; the CLI publishes to that registry, falling back to the @dashnex:registry line. The file is gitignored in the module folder, and the CLI never puts the registry token on a command line.
Creating your own command
The CLI auto-discovers commands from every installed @dashnex/* module (and your app). To add a command, a module exports a default array of CliCommandConfig from src/commands/index.ts. On startup the CLI looks for the command file in each module, in this order:
dist/commands/index.js— the built package (published modules)src/commands/index.ts— linked / in-development modulessrc/commands/index.js
It imports the file and reads its default export (an array), or a named commands export as a fallback. Discovered commands are registered at the top level, so a command named report is invoked as dashnex report.
1. Write a handler
A handler is a CliCommand — any object with an async execute(options) method. options is a single object merging the parsed flags (by their long name), positional arguments (by name), and inherited parent options.
// src/commands/index.ts
import type { CliCommand, CliCommandConfig } from '@dashnex/types'
class ReportCommand implements CliCommand {
async execute(options: { format: string; out?: string; name?: string; ci?: boolean }) {
console.log(`Generating ${options.format} report for ${options.name ?? 'all'}…`)
// ...do the work
}
}
const commands: CliCommandConfig[] = [
{
name: 'report',
description: 'Generate a report',
arguments: [
{ name: 'name', description: 'Target name', required: false },
],
options: [
{ flags: '-f, --format <format>', description: 'Output format', defaultValue: 'json' },
{ flags: '-o, --out <path>', description: 'Write to a file' },
],
handler: new ReportCommand(),
},
]
export default commands
Now dashnex report acme --format csv -o ./acme.csv calls execute({ name: 'acme', format: 'csv', out: './acme.csv', ci: false }).
2. CliCommandConfig shape
| Field | Type | Description |
|---|---|---|
name | string | The command word (dashnex <name>). Required. |
description | string | Shown in --help. Required. |
handler | CliCommand | Object with execute(options): Promise<void>. Required (use a no-op when the command only groups subcommands). |
arguments | CliArgument[] | Positional args: { name, description, required?, defaultValue? }. Rendered as <name> (required) or [name] (optional). |
options | CliOption[] | Flags: { flags, description, defaultValue? } — flags uses commander syntax, e.g. -f, --format <format>. |
subcommands | CliCommandConfig[] | Nested commands (e.g. dashnex mymodule sync). A parent with subcommands prints help when run alone. |
Notes:
- A
--ciflag is added to every command automatically (disables spinners / interactive prompts); read it asoptions.ci. - Errors thrown from
executeare reported and exit with code1; throwing a user-interrupt is handled gracefully. - Publish step: make sure your build emits
dist/commands/index.jsand thatdistis in the package'sfiles, so the command ships with the module.