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.
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. A command that fails prints the error and exits with code 1.
Commands
Plugin commands
Plugins and your config can add commands undercli.commands. Each key is a command name, written in lowercase, and each value is a function that runs when you call frogbot <name>.
frogbot loads it before running any command, so it can find these. The handler gets one argument:
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():
These commands come from
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.
share
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.
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, 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
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 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:
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
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 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
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
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.
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
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.
Port a piece
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.