> ## Documentation Index
> Fetch the complete documentation index at: https://docs.frogbot.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# CLI

> Every frogbot command, its arguments, and where each one is documented.

The `frogbot` package installs a `frogbot` command. Run it from your app's root directory through your package manager, for example `pnpm frogbot dev` or `npx frogbot dev`.

```bash theme={"theme":"tokyo-night"}
pnpm frogbot generate:types
```

Before any command runs, `frogbot` loads `.env*` files from the current directory with Next.js environment-loading rules: development files unless `NODE_ENV` is `production`. Commands that load your app find `frogbot.config.{ts,js,mjs}` in the current directory, its `src` folder, or a parent directory, or at the path in `FROGBOT_CONFIG_PATH`. Command names are not case-sensitive. A missing command prints the list of commands and exits with code `2`. An unknown command exits with code `2` too and points to [Plugin commands](#plugin-commands). A command that fails prints the error and exits with code `1`.

## Commands

| Command | Arguments | Description |
| - | - | - |
| `dev` | Next.js `dev` arguments | Generates the admin import map, then starts the Next.js development server. See [`dev` and `start`](#dev-and-start). |
| `start` | Next.js `start` arguments | Warns when the admin import map is stale, then starts the Next.js production server. See [`dev` and `start`](#dev-and-start). |
| `run` | `<file> [args...]` | Runs a TypeScript or JavaScript file with your app's environment and config alias. See [Outside Next.js](https://docs.frogbot.ai/local-api/outside-nextjs.md). |
| `generate:types` | — | Writes `frogbot-types.ts` from your config. See [Generating types](https://docs.frogbot.ai/typescript/generating-types.md). |
| `generate:importmap` | — | Writes the admin import map for the custom components your config references. See [Custom components](https://docs.frogbot.ai/custom-components/overview.md). |
| `generate:piece-types` | `<module> --export <factory> --output <file> [--check]` | Writes the TypeScript types for a piece's actions and triggers. See [Generate piece types](#generate-piece-types). |
| `pieces:port` | `<slug>` | Prepares a piece package in the FrogBot repository for porting. See [Port a piece](#port-a-piece). |
| `jobs:run` | `--cron`, `--limit`, `--queue`, `--all-queues`, `--handle-schedules` | Runs a long-lived jobs worker without Next.js. See [Workers](https://docs.frogbot.ai/jobs-queue/workers.md). |
| `channels:run` | — | Holds chat-channel gateway connections, such as Discord's, in their own process. See [Discord](https://docs.frogbot.ai/pieces/discord.md). |
| `migrate` | — | Applies pending database migrations. See [Migrations](https://docs.frogbot.ai/database/migrations.md). |
| `migrate:create` | `[name]`, `--name`, `--file`, `--skip-empty`, `--force-accept-warning` | Creates a migration from the current schema. See [Migrations](https://docs.frogbot.ai/database/migrations.md). |
| `migrate:status` | — | Lists applied and pending migrations. See [Migrations](https://docs.frogbot.ai/database/migrations.md). |
| `migrate:down` | — | Rolls back the last batch of migrations. See [Migrations](https://docs.frogbot.ai/database/migrations.md). |
| `migrate:refresh` | — | Rolls back every migration, then applies them all again. See [Migrations](https://docs.frogbot.ai/database/migrations.md). |
| `migrate:reset` | — | Rolls back every migration. See [Migrations](https://docs.frogbot.ai/database/migrations.md). |
| `migrate:fresh` | `--force-accept-warning` | Drops every table, then applies all migrations. See [Migrations](https://docs.frogbot.ai/database/migrations.md). |

## Plugin commands

Plugins and your config can add commands under `cli.commands`. Each key is a command name, written in lowercase, and each value is a function that runs when you call `frogbot <name>`.

```ts theme={"theme":"tokyo-night"}
import { buildConfig } from 'frogbot'

export default buildConfig({
  // ...
  cli: {
    commands: {
      'users:count': async ({ getFrogBot }) => {
        const frogbot = await getFrogBot()
        const { totalDocs } = await frogbot.count({ collection: 'users' })

        console.log(totalDocs)
      },
    },
  },
})
```

When your app has a config file, `frogbot` loads it before running any command, so it can find these. The handler gets one argument:

| Field | Type | Description |
| - | - | - |
| `args` | `string[]` | Everything after the command name. |
| `command` | `string` | The name the command was run as. |
| `getConfig` | `() => Promise<FrogBotSanitizedConfig>` | Resolves to the config `frogbot` already loaded. |
| `getFrogBot` | `(options?) => Promise<FrogBot>` | Boots a FrogBot instance the first time it's called and returns that same instance after. Takes the same options as `init`, without `config`. The instance is destroyed when the command ends. |

A command that throws prints the error and exits with code `1`. Otherwise `frogbot` exits when the command's promise resolves, with `process.exitCode` if the command set one or `0`, so await everything the command starts. A command with the same name as a built-in replaces it. If the config file fails to load, built-in commands still run, and any other command prints the load error and exits with code `1`. A plugin adds commands by spreading the ones already there, so it keeps yours: `cli: { ...config.cli, commands: { ...config.cli?.commands, 'my:command': handler } }`. A name with capitals, or a value that isn't a function, fails the config.

Running a name that's neither built in nor in `cli.commands` prints `[frogbot] "<name>" isn't a command in this app.` with a link here, and exits with code `2`. Check that the plugin that adds the command is in your config's `plugins`. When no config file is found, it prints `[frogbot] "<name>" isn't a built-in command, and there's no app here to add it.` with the same link instead.

These commands come from [`capturePlugin()`](https://docs.frogbot.ai/plugins/capture):

| Command | Arguments | Description |
| - | - | - |
| `export:training-data` | `--output`, `--where`, `--page-size` | Exports chats as JSONL training data. See [Training data](https://docs.frogbot.ai/chat/training-data.md). |
| `export:captures` | `--output`, `--user`, `--api-key`, `--from`, `--to`, `--operation` | Exports captured AI calls. See [Capture](https://docs.frogbot.ai/plugins/capture.md). |

These commands come from [`dojo()`](https://docs.frogbot.ai/plugins/dojo), which apps made with `create-frogbot-app` list. They also run without an app, or in one that doesn't list `dojo()`, as `npx @frogbotai/plugin-dojo <command>`: that is how you `pull` an app into an empty folder. Each command's usage line names the form you ran.

| Command | Arguments | Description |
| - | - | - |
| `share` | `--data`, `--no-data`, `--workspace`, `--name`, `--no-keys`, `--skip-key`, `--yes`, `--update-keys` | Uploads the app to Dojo, builds it and prints its live URL. See [`share`](#share). |
| `pull` | `<app>` | Downloads an app shared with you on Dojo into a new folder. See [`pull`](#pull). |
| `logs` | `<app>`, `--follow` | Prints an app's recent logs from Dojo, or follows them. See [`logs`](#logs). |
| `logout` | — | Signs the CLI out of Dojo. See [`logout`](#logout). |

### `share`

```bash theme={"theme":"tokyo-night"}
npx frogbot share --no-data
```

`frogbot share` takes the app in the current folder live on Dojo. In an app that doesn't list `dojo()`, run `npx @frogbotai/plugin-dojo share` instead. Run it anywhere inside the app: it works from the folder that holds `frogbot.config.ts`, or the parent of `src/` when the config is there. It checks the app, signs in, uploads the working files, streams the build as `[build]` lines, and prints a link to share the app, then its live URL as the last line.

| Argument | Type | Default | Description |
| - | - | - | - |
| `--data` | flag | — | On the first share, brings the local data along without asking. |
| `--no-data` | flag | — | On the first share, shares the code only, without asking. |
| `--workspace` | workspace slug | — | On the first share, the Dojo workspace to create the app in, when you belong to more than one. |
| `--name` | name | `package.json` `name` | On the first share, the app's name in its address, `https://<name>.frogbot.app`. Lowercased, with dashes for anything but letters and digits. |
| `--no-keys` | flag | — | Brings no keys. |
| `--skip-key` | key name, repeatable | — | Leaves this key out, such as `--skip-key STRIPE_KEY`. A name that isn't a key (names are case-sensitive) stops with exit 2. |
| `--yes` | flag | — | On the first share, brings every key without showing the list to untick. |
| `--update-keys` | flag | — | On a later share, sends local values for keys that differ from the app's live ones. |

Before anything is sent, `share` stops with one line when there's no `package.json`, the app uses Bun, or there's no `pnpm-lock.yaml`, `package-lock.json` or `yarn.lock`. Run `pnpm install` to make a lockfile.

**Signing in.** The first time, `share` prints a Dojo link and a code, each on its own line, and opens the link in your browser when it runs in a terminal. Confirm the code in Dojo and the share carries on. The token is stored in `~/.config/frogbotai/auth.json` (or under `$XDG_CONFIG_HOME`), readable only by you. When Dojo no longer accepts it, `share` signs in again.

**The first share** creates a Dojo app named from `package.json`'s `name` and writes its id to `.frogbot/app.json`, adding `/.frogbot` to `.gitignore`. Later shares from this folder ship a new version of that app. Delete `.frogbot/app.json` to share as a new app. With more than one workspace, `share` asks which one, or, with no terminal, stops and asks you to rerun with `--workspace`. When the name is taken on Dojo, `share` stops with `[frogbot] The name "deals" is taken on Dojo. Rerun with --name <another name>.`

**Your data.** The first share asks whether to bring your local data, as `Bring your data? 42 deals, 3 users, 12 files`: the records in each collection you can see in the admin, trash left out, and the files in your upload collections. With no terminal, it stops before sending anything and asks you to rerun with `--data` or `--no-data`. With nothing to bring there's no question. Only SQLite apps on a `file:` database can bring data; any other database shares the code and says `[frogbot] Your data stays on this computer: bringing data works only for SQLite apps for now.`

`share` reads a copy of the database, never `frogbot.db` itself, so it works while `frogbot dev` runs. The copy leaves out what belongs to your computer: queued jobs, locks, logins and trigger subscriptions. Your users come along with their passwords, so they sign in on Dojo as they do locally, and `FROGBOT_SECRET` comes along too, so stored connections still work; without a local `FROGBOT_SECRET`, `share` warns that they won't. The copy and the files in each upload collection's folder go straight to the app's storage after `[frogbot] Bringing data: 42 deals, 3 users, 12 files (13.4 GB)`, with a progress line, such as `[frogbot] Uploading data: 1.2 GB of 13.4 GB`. An upload folder must lie inside the app: one that is the app itself, holds it or sits outside it is left out with a line naming it. Inside a folder, `share` leaves out the database, `.env` files, `.git`, `node_modules` and half-finished uploads, and names any file whose name Dojo can't store (a `\` or a control character). There's no size limit. A file that fails to upload is retried 3 times; if it still fails, nothing is deployed, and running `share` again sends a fresh copy of the database and skips the files that already arrived. Answering No on that rerun removes the data the earlier attempt sent.

Once a version of the app has started, shares send code only and never ask; `--data` then prints `[frogbot] Data comes only with the first share; sharing code only.` Live data is never overwritten from your computer.

**Keys.** `share` brings the app's keys, such as `STRIPE_KEY`, from the same `.env` files `frogbot dev` loads; the first file to set a key wins, and empty values and variables set only in the shell stay behind. `DATABASE_URL`, `FROGBOT_SECRET`, `NODE_ENV`, `PORT` and `HOSTNAME` are never brought as keys: Dojo provides them, and `FROGBOT_SECRET` comes along only with your data. Values show masked, as `STRIPE_KEY ••••4242`, or `••••` under 12 characters, and are sealed to Dojo's key before they leave your machine.

On the first share, `share` prints `[frogbot] Bringing 2 keys: STRIPE_KEY ••••4242, OPENAI_API_KEY ••••x7Qa` and, in a terminal, lets you untick any. With no terminal it doesn't wait: it brings them all and says `[frogbot] No terminal: carrying all keys. Use --no-keys or --skip-key NAME to leave some out.`

Later shares compare your keys with the app's live ones without sending the values. New keys are brought and listed, as `[frogbot] Adding 1 new key: AIRTABLE_KEY ••••a91b`. A key whose local value differs from live keeps its live value and is named, as `[frogbot] 1 key differs from live, kept live: STRIPE_KEY. Use --update-keys to send local values.` A key you no longer have locally stays live. Change or remove live keys in Dojo.

An editor's share, such as from a folder made by [`pull`](#pull), brings no keys: only the app's owners manage them on Dojo. When there were local keys or `--update-keys` to leave out, it says so once, as `[frogbot] Keys left out: only the app's owners manage its keys on Dojo.`, and it ends with the live URL but no `Share it:` line.

**What's uploaded.** The files in the folder as they are now, committed or not. In a git repository that is every file git tracks or would track; outside one, `.gitignore` files are still followed. These are never uploaded, even when committed: `.git`, `.frogbot/`, any `.env*` file, `frogbot.db*`, `*.sqlite*`, the database file a local `file:` `DATABASE_URL` names, `node_modules`, `.next`, `files/` and every upload collection's folder. Neither is the CLI's login file. Symlinks, and files reached through a symlinked folder, are left out and named in one line, such as `[frogbot] Skipped 2 symlinks: data, shared.ts`. The upload is limited to 100 MB; over it, `share` stops before sending.

A failed build exits with code `1` and prints the error and the last lines of the build log. Usage errors, such as `--data` with `--no-data`, exit with code `2`.

Set `FROGBOT_DOJO_URL` to share to a different Dojo, such as a local one.

### `pull`

```bash theme={"theme":"tokyo-night"}
npx @frogbotai/plugin-dojo pull deals
```

`pull <app>` downloads the live version of an app you are an editor or owner of on Dojo into a new `./<app>` folder, named by the app's slug (or its id). It signs in as [`share`](#share) does. The folder gets the app's files, `.frogbot/app.json` linking it to the app, so `share` from there ships a new version of the same app, and a `.env` with `DATABASE_URL=file:./frogbot.db` and a fresh `FROGBOT_SECRET`. It starts with an empty database: live data and production keys stay on Dojo. Then run:

```bash theme={"theme":"tokyo-night"}
cd deals
pnpm install
pnpm dev
```

`pull` refuses a folder that already exists and leaves it untouched. It writes only files and folders from the download: no links, and nothing outside the new folder. When Dojo refuses, such as for an app you can only use, or an app with nothing live yet, `pull` prints Dojo's reason in one line, exits with code `1` and leaves no folder behind. With no app named, it prints its usage and exits with code `2`.

### `logs`

```bash theme={"theme":"tokyo-night"}
npx @frogbotai/plugin-dojo logs deals --follow
```

`logs <app>` prints the recent logs of an app on Dojo, oldest first, one `<time> <message>` line each. Logs reach back only 24 hours, so an app that broke days ago may show nothing. It takes the app's slug or id and signs in as [`share`](#share) does. Editors and owners can read an app's logs; anyone else gets Dojo's reason. Every stored key value of 8 or more characters, Dojo's own included, shows as `••••`. An app that has never started prints `[frogbot] No logs yet.`

With `--follow`, it keeps asking Dojo every 2 seconds and prints only the new lines, until you press Ctrl-C. When Dojo refuses or can't read the logs, at any point, `logs` prints Dojo's reason in one `[frogbot]` line and exits with code `1`. With no app named, it prints its usage and exits with code `2`.

### `logout`

```bash theme={"theme":"tokyo-night"}
npx @frogbotai/plugin-dojo logout
```

`logout` revokes the CLI's key in Dojo and removes it from `auth.json`. If Dojo can't be reached or refuses, the key is kept, so you can run `logout` again.

## `dev` and `start`

```bash theme={"theme":"tokyo-night"}
frogbot dev --port 4000
```

`frogbot dev` regenerates the admin import map, then runs `next dev`. `frogbot start` checks the import map without writing it, prints a warning to run `frogbot generate:importmap` when it's out of date, then runs `next start`. Set `admin.importMap.autoGenerate` to `false` to skip the import map step in both commands.

| Argument | Type | Default | Description |
| - | - | - | - |
| Any Next.js argument | Next.js `dev` or `start` arguments | — | Passed to Next.js unchanged, for example `--port 4000` or `--webpack`. |

Both commands need a `next.config.{ts,mjs,js,cjs}` file and the `next` package in the current directory. `start` with a `next.config.ts` also needs the `typescript` package installed, because Next.js compiles the file at startup. The command exits with Next.js's exit code and forwards `SIGINT` and `SIGTERM` to it.

## Generate piece types

```bash theme={"theme":"tokyo-night"}
frogbot generate:piece-types src/index.ts --export createResend --output src/piece-types.ts
```

`frogbot generate:piece-types` loads a piece factory and writes the input and output types of its actions and triggers, formatted with your Prettier config. Piece packages use it to type their own action code.

| Argument | Type | Default | Description |
| - | - | - | - |
| `<module>` | file path | Required | Module that exports the piece factory, resolved from the current directory. |
| `--export` | export name | Required | Name of the piece factory export, such as `createResend`. |
| `--output` | file path | Required | File to write. It's left untouched when the content hasn't changed. |
| `--check` | flag | off | Fails with `Piece types are stale` instead of writing when the file is out of date. Use it in CI. |

## Port a piece

```bash theme={"theme":"tokyo-night"}
frogbot pieces:port google-sheets
```

`frogbot pieces:port` is a tool for FrogBot contributors, run from the root of the FrogBot repository. It moves `packages/pieces/piece-<slug>` and its `test/unit/piece-<slug>` tests to `.legacy` folders, then writes a native piece package and test skeleton in their place for porting the upstream integration. It fails without changes when the piece package, its installed upstream source, or `packages/pieces/PORTING.md` is missing, or when a `.legacy` folder already exists.

| Argument | Type | Default | Description |
| - | - | - | - |
| `<slug>` | lowercase letters, numbers, and single dashes | Required | Piece slug, such as `google-sheets`. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.