Browse documentation

This page describes the implementation of @motherbase/cli. For installation and user workflows, see CLI Overview, CLI Getting Started, and CLI Commands.

The package is dependency-free CommonJS and requires Node.js 18 or newer. packages/cli/bin/motherbase.js passes process.argv.slice(2) to main() and maps its return value or thrown error to the process exit code.

Dispatch pipeline

packages/cli/src/main.js owns tokenization, help, command traversal, context construction, and top-level error handling.

  1. tokenize(argv) separates positionals and flags.
  2. main() handles version/help and finds the first positional in the registry.
  3. It descends through nested subcommands while names match.
  4. It creates context only for a runnable command.
  5. It calls run(ctx, remainingArgs, flags).
  6. CliError messages are printed directly; unexpected failures receive an Error: prefix.

Long flags accept --name=value or, unless known boolean, consume the next non-flag token. Repeated flags become arrays. -- stops flag parsing. Short aliases are -y, -h, -v, -p, -u, -e, and -f. The parser's known booleans include JSON/help/version, confirmations, codegen check/local, and RLS replace.

The registry in packages/cli/src/commands/index.js is the single source for dispatch and generated help text.

Command tree

Each leaf is a module object with summary, usage, and async run. Group nodes expose subcommands.

connection
├─ login
├─ logout
├─ whoami
├─ link
├─ unlink
└─ status
 
projects
├─ list
├─ create
├─ start
├─ stop
├─ restart
├─ update
├─ rebuild
├─ delete
├─ logs
├─ info
└─ limits
 
local self-host
├─ init
├─ start
├─ stop
├─ deploy
└─ logs
 
database
├─ db
│  ├─ tables
│  ├─ sql
│  ├─ dump
│  ├─ restore
│  └─ backups
├─ migrations
│  ├─ list
│  ├─ diff
│  ├─ get
│  ├─ create
│  ├─ apply
│  └─ rollback
├─ migration
│  └─ new
└─ import
 
resources
├─ rls
│  ├─ list
│  ├─ enable
│  ├─ disable
│  ├─ policy
│  │  ├─ create
│  │  └─ delete
│  ├─ preset
│  ├─ simulate
│  └─ lint
├─ users
│  ├─ list
│  ├─ get
│  ├─ create
│  ├─ update
│  ├─ delete
│  ├─ reset-password
│  ├─ promote
│  ├─ demote
│  └─ revoke-sessions
├─ storage
│  ├─ buckets
│  │  ├─ list
│  │  ├─ create
│  │  └─ delete
│  ├─ ls
│  ├─ cp
│  └─ rm
├─ functions
│  ├─ list
│  ├─ get
│  ├─ new
│  ├─ deploy
│  ├─ pull
│  ├─ serve
│  ├─ delete
│  ├─ invoke
│  └─ logs
├─ secrets
│  ├─ list
│  ├─ set
│  └─ unset
├─ realtime
│  ├─ config
│  └─ channels
├─ webhooks
│  ├─ list
│  ├─ create
│  ├─ delete
│  └─ test
├─ cron
│  ├─ list
│  ├─ create
│  ├─ delete
│  └─ toggle
├─ config
│  ├─ list
│  ├─ get
│  └─ set
└─ inspect
   ├─ logs
   ├─ stats
   ├─ performance
   ├─ requests
   ├─ advisor
   └─ shield
 
codegen
└─ gen
   └─ types
      └─ typescript

db backups config is handled as an argument inside the backups leaf rather than as a registry subcommand. Project lifecycle leaves are generated by one lifecycleCmd(action) helper in packages/cli/src/commands/projects.js.

Command module boundaries

AreaSourceInternal responsibility
Connectionpackages/cli/src/commands/login.js, packages/cli/src/commands/logout.js, packages/cli/src/commands/whoami.js, packages/cli/src/commands/link.js, packages/cli/src/commands/unlink.js, packages/cli/src/commands/status.jsCredentials, directory link, and health view
Platform projectspackages/cli/src/commands/projects.jsConsole lifecycle API and lifecycle command factory
Local stackpackages/cli/src/commands/local.jsdocker compose child processes and local initialization
Databasepackages/cli/src/commands/db.js, packages/cli/src/commands/db-catalog.js, packages/cli/src/commands/migration.js, packages/cli/src/commands/import.js, packages/cli/src/commands/migrate.jsSQL and files; the db indexes|types|roles|publications catalog views; the server migration lifecycle plus the local new scaffold (migrations is an alias of migration); Supabase import; and migrate codemod, which ports a Supabase codebase's imports and reports what this SDK does not implement
Security/authpackages/cli/src/commands/rls.js, packages/cli/src/commands/users.jsRLS policy/preset/simulation/advisor calls and user administration
Storage/functionspackages/cli/src/commands/storage.js, packages/cli/src/commands/functions.jsMultipart/stream I/O and repository-backed edge-function deployment
Operationspackages/cli/src/commands/secrets.js, packages/cli/src/commands/realtime.js, packages/cli/src/commands/webhooks.js, packages/cli/src/commands/cron.js, packages/cli/src/commands/config.js, packages/cli/src/commands/inspect.jsThin admin-resource transports and human renderers
Code generationpackages/cli/src/commands/gen.jsFetches server-rendered database types, writes/checks files, or emits stdout

The commands deliberately delegate HTTP mechanics to packages/cli/src/api.js and shared parsing/rendering to packages/cli/src/commands/_util.js. Edge-function type generation is server-owned; gen transports the rendered file instead of duplicating the emitter.

Configuration files

packages/cli/src/config.js separates secrets from project routing.

Global credentials:

{
  "consoles": {
    "https://console.example.com": {
      "token": "...",
      "email": "operator@example.com"
    }
  },
  "current": "https://console.example.com"
}

This lives at ~/.motherbase/config.json. writeGlobal() creates it with mode 0600 and makes a best-effort chmod(0600) for an existing file.

The directory link:

{
  "consoleUrl": "https://console.example.com",
  "projectId": "todo",
  "projectName": "todo"
}

This lives at ./.motherbase/config.json and contains no token. URL normalization only removes trailing slashes. Invalid or unreadable JSON is treated as missing configuration.

Lazy context resolution

createContext(flags, env, cwd) in packages/cli/src/context.js never requires a connection immediately. This lets local commands and login run without a linked project. Values are resolved with the following precedence:

ValueResolution order
Console URL--url → MOTHERBASE_CONSOLE_URL → local link.consoleUrl → global current
Console token--token → MOTHERBASE_TOKEN → token stored for the resolved Console
Project id--project → MOTHERBASE_PROJECT → local link.projectId
Project namelocal link.projectName → project id
Direct URLMOTHERBASE_URL
Direct tokenMOTHERBASE_ADMIN_TOKEN

Direct mode is selected only when both direct environment values exist and the context does not already have both a Console credential and a project link. Otherwise it stays in Console mode.

requireConsole() checks URL then token. requireProject() returns either a direct target or a Console proxy target and throws an actionable CliError only when the current command needs a missing value.

Note

Flags override environment/configuration for Console mode. Direct project mode is intentionally environment-driven; gen types typescript --local is the exception and constructs a temporary direct context from its --url/--token values.

Login and token lifecycle

packages/cli/src/commands/login.js supports two paths:

  • --token stores a supplied token directly for non-interactive use.
  • Email/password prompts call loginRequest().

loginRequest() in packages/cli/src/api.js posts JSON to /api/auth/sign-in/email, adds the target's own Origin header for Better Auth CSRF checks, and reads the session token from either body.token or the set-auth-token response header. The login command then stores it under the normalized Console URL and marks that Console current.

Password input in packages/cli/src/prompt.js uses raw TTY mode, masks printable characters, handles backspace and Ctrl-C, and ignores escape sequences. Non-TTY input falls back to a normal line prompt. Logout removes only the selected Console entry and clears current when it points to that URL.

HTTP routing

All network commands use packages/cli/src/api.js.

OperationURL shape
Console account/project<consoleUrl>/api/platform<path>
Project admin through Console<consoleUrl>/api/projects/<projectId>/admin<path>
Direct project admin<directUrl>/api/admin<path>
Edge invoke through Console<consoleUrl>/api/projects/<projectId>/edge/<route>
Direct edge invoke<directUrl>/api/edge/<route>

doFetch() adds the bearer token, adds the destination origin when no origin header exists, serializes JSON bodies, and maps network errors to CliError. parseResponse() parses JSON when possible, unwraps a top-level data property, and turns non-2xx responses into status-specific messages. A 401 message includes the login hint.

uploadFile() uses Node's native FormData/Blob and reads the source file before sending. downloadFile() bridges the web response body through Readable.fromWeb into a file stream. Both use the same target selection, auth, origin, and error mapping.

Output and prompts

packages/cli/src/ui.js provides dependency-free output helpers:

  • print and printErr;
  • semantic success, info, and warn;
  • pretty json;
  • aligned table and keyValue renderers;
  • a TTY spinner with a non-TTY status-line fallback.

ANSI color is enabled only when stdout is a TTY and NO_COLOR is not set. Tables print (no rows) for empty input.

output(ctx, data, humanFn) in packages/cli/src/commands/_util.js is the common output switch: --json bypasses the human renderer and prints the complete value. The same utility module centralizes required positional arguments, key=value coercion, and bucket:path splitting. Interactive confirmations and questions live in packages/cli/src/prompt.js.

Local and long-running commands

packages/cli/src/commands/local.js is the only Docker layer. It spawns docker compose with inherited stdio and a hidden Windows child window. init scaffolds a directory and copies .env.example when present.

Two command paths intentionally keep the process alive:

  • project log following polls Console snapshots every three seconds;
  • function serving watches function directories, coalesces editor save bursts for 150 ms, and redeploys to the real project runtime instead of implementing a second local one.

These behaviors live in packages/cli/src/commands/projects.js and packages/cli/src/commands/functions.js.