CLI Reference
Complete reference for all DevOur CLI commands.
For installation and initial setup, see the CLI Setup guide.
How Commands Find Your Project
Commands that take an optional [projectId] use, in order:
- The project ID you pass
- The
idin your configuration file: the--configpath, ordevour.json,devour.yml, ordevour.yamlin the current directory - A list of your projects to pick from
devour setup also accepts a project name in place of the ID.
Non-Interactive Use
When an AI agent or a CI pipeline runs the CLI, set DEVOUR_NON_INTERACTIVE=1.
Setting CI has the same effect. With it set:
- Commands that are missing a project, and
form editorform deletewhen the form isn't found, fail with an error instead of showing a list devour setupskips deletions unless you pass--yes
These still ask a question, so give them what they need up front:
devour setup-key, and any command run before a key is saveddevour syncfor each setting that differs, unless you pass--overwrite,--local, or--newdevour setup --configwith a file that has noidand the name of an existing projectdevour fetchfor its warnings (see fetch)devour version bundle,version delete, andversion resetwithout--version, andversion deleteandversion resetfor their confirmationdevour setupwithout--configor--key,devour form create, anddevour form edit, which are interactive by designdevour theme createwithout a name
Authentication Commands
setup-key
Save your DevOur API key.
devour setup-key
The CLI prompts for your API key, checks that it is 64 characters long, and
stores it in your system's user configuration directory. Every command uses the
saved key and asks for one when none is saved. devour theme upload also
accepts --api-key or the DEVOUR_API_KEY environment variable.
logout
Remove the saved API key.
devour logout
Project Commands
ls
List your projects, oldest first, as ID - name.
devour ls
create
Create a new project.
devour create [name...]
Arguments:
| Argument | Description |
|---|---|
| name | Project name (supports spaces) |
Options:
| Option | Description |
|---|---|
| --config <path> | Take the project name from this configuration file |
| --name <name> | Project name |
The name comes from --config, then --name, then the arguments, then the
name in a configuration file in the current directory. Otherwise the CLI asks
for it. create prints the new project's ID and sets only its name. To create a
project and apply a whole configuration file in one step, run
devour setup --config with a file that has no id.
Examples:
devour create
devour create "My Project"
devour create --name "My Project"
setup
Apply a configuration file to a project, or update its settings one at a time.
devour setup [projectId]
Arguments:
| Argument | Description |
|---|---|
| projectId | Target project ID or name (optional) |
Options:
| Option | Description |
|---|---|
| --config <path> | Apply this configuration file |
| --ignore-config | Ignore the configuration file in the current directory |
| --key <key> | Update a single setting |
| --value <value> | New value for --key (the CLI asks for it when omitted) |
| --yes | Apply deletions without asking |
| --allow-data-loss | Also allow deleting collections that still hold entities |
setup runs in the first mode that matches:
- With
--config, it applies that file. - With
--key, it updates that one setting. - When a configuration file is in the current directory and you didn't pass
--ignore-config, it applies that file. - Otherwise, it opens an interactive menu of the project's settings.
Applying a Configuration File
When the file has an id, setup updates that project: its settings, logo,
Firebase service account, collections, forms, plans (products), pages,
navigation, and locales. A form in the file updates the server form with the
same id, or with the same name when it has no id. Any other form is created.
- Fresh file required.
setuprefuses to run if the project changed on the server since your lastdevour sync. Rundevour syncfirst, or rundevour stash,devour sync, anddevour stash popto keep your local edits. - Preview. Before changing anything,
setupprints what it will create, update, and delete. - Deletions. Anything on the server that the file no longer has is deleted.
setupasks first, and--yesskips the question. Deleting a collection that still holds entities also requires--allow-data-loss. The default locale is never deleted.
When the file has no id, setup looks for a project with the same name. If
one exists, it asks whether to update it. Otherwise, or if you decline, it
creates a new project. It then writes the project's id into the file. Run
devour sync afterwards so the next setup has a synced base to compare
against.
Examples:
devour setup
devour setup --config devour.json
devour setup --config devour.json --yes
devour setup --key colorMain --value "#3B82F6"
devour setup my-project-id --ignore-config
sync
Pull a project from the server into a local configuration file.
devour sync
Options:
| Option | Description |
|---|---|
| --config <path> | Path to configuration file |
| --overwrite | Use the remote value wherever local and remote differ |
| --new | Replace the local settings with the remote ones |
| --local | Keep local values and fill in what's missing from remote |
| --json | Write the file as JSON (devour.json) |
| --yaml | Write the file as YAML (devour.yml) |
sync writes:
- The project's settings, collections (with their properties and entities), forms, plans, pages, and navigation into the configuration file
- Its locales into a
locales/folder next to the file, one file per language - Its logo into an
assets/folder next to the file - A record of what it synced, which
devour setupchecks before applying
Without --overwrite, --local, or --new, sync asks you to choose the
local or remote value for each setting that differs.
sync only reads from the server. To send your edits, run devour setup.
With --json or --yaml, when your current file is in the other format, sync
writes a new file and asks whether to delete the old one.
Examples:
devour sync
devour sync --overwrite
devour sync --local
devour sync --yaml
devour sync --config custom-config.json
Config Stash Commands
Stashes keep your local edits while you refresh a configuration file that has fallen behind the server:
devour stash
devour sync
devour stash pop
devour setup --config devour.json
stash
Save the current configuration file's contents.
devour stash [-m <message>]
| Option | Description |
|---|---|
| -m, --message <text> | Description for this stash |
| --config <path> | Path to configuration file |
Stashes are stored in .devour-stash.json next to the configuration file.
stash pop
Re-apply the most recent stash onto the configuration file and remove it from the stash list.
devour stash pop
Stashed values replace current ones. Nested objects are merged key by key, and
lists such as collections or pages are replaced as a whole.
stash list
List saved stashes as stash@{n}: message (date).
devour stash list
Version Commands
version create
Create a new version of a project. A version is required before you can build and download a bundle.
devour version create [projectId]
Options:
| Option | Description |
|---|---|
| -v, --version <v> | Version to create (defaults to the current version's next patch) |
| --config <path> | Path to configuration file |
Versions follow semantic versioning: MAJOR.MINOR.PATCH. Without --version,
the patch number goes up by one, so 1.0.0 becomes 1.0.1.
Examples:
devour version create
devour version create my-project-id
devour version create --version 2.0.0
version bundle
Build the downloadable bundle for a version.
devour version bundle [projectId]
| Option | Description |
|---|---|
| -v, --version <v> | Version to bundle |
| --config <path> | Path to configuration file |
Without --version, the CLI lists the versions and marks the ones that already
have a bundle. If the bundle already exists, the command says so and changes
nothing.
version delete
Delete a version.
devour version delete [projectId]
| Option | Description |
|---|---|
| -v, --version <v> | Version to delete |
| --config <path> | Path to configuration file |
Without --version, the CLI lists the versions to pick from. It always asks for
confirmation.
version reset
Reset the project to a version, discarding the changes made since.
devour version reset [projectId]
| Option | Description |
|---|---|
| -v, --version <v> | Version to reset to |
| --config <path> | Path to configuration file |
Without --version, the CLI lists the versions to pick from. It always asks for
confirmation.
Download Commands
fetch
Download a project bundle and extract it into the current directory.
devour fetch [projectId]
Options:
| Option | Description |
|---|---|
| -v, --version <v> | Version to download (defaults to the version in your configuration file, then the latest) |
| --config <path> | Path to configuration file |
Behavior:
- When no version comes from
--versionor the configuration file,fetchwarns if the project has changes since its latest version and asks whether to continue - Asks before extracting into a directory that holds anything other than configuration files
- Offers to build the bundle when the version doesn't have one yet
Examples:
devour fetch
devour fetch my-project-id
devour fetch --version 1.0.0
After Downloading:
docker compose up -d --force-recreate --build
Form Commands
form create
Create a form. The CLI asks for its name and submit button label, then lets you add sections and fields.
devour form create [projectId]
form edit
Open a form's layout to add, change, or remove its sections and fields.
devour form edit [formId]
formId accepts a form ID or name. Without it, the CLI lists the project's
forms to pick from.
form list
List the project's forms.
devour form list
form delete
Delete a form after confirmation.
devour form delete [formId]
Theme Commands
theme create
Create a new theme from the starter template.
devour theme create [name]
The name is converted to kebab-case, and the CLI asks for it when omitted. The
theme is created in the current directory. Inside a DevOur project directory, it
is created next to the project instead, and the project's configuration file
gets its theme set to the new theme. The CLI then prints the next steps.
theme validate
Check a built theme against the component contract.
devour theme validate [path]
path defaults to the current directory. The theme needs a package.json with
a name, a version, and an author (the package scope or the author field),
@devourapp/components installed, and a build in dist/. The command reports
how many required components the theme implements and lists the ones that fall
back to the default theme.
theme upload
Upload a theme to DevOur.
devour theme upload <path>
<path> is a prebuilt .zip or a theme project directory. Given a directory,
the CLI packages the built files from dist/ with the name, version, and author
from package.json. Build the theme first.
| Option | Description |
|---|---|
| --description <text> | Theme description |
| --visibility <private|public> | Theme visibility (default private) |
| --forkable | Allow other users to fork this theme |
| --price-cents <int> | Price in cents, 0 for free (default 0) |
| --api-url <url> | Override the DevOur server URL |
| --api-key <key> | API key (defaults to the DEVOUR_API_KEY variable) |
Configuration Files
The CLI supports JSON and YAML configuration files. By default, it searches for:
devour.jsondevour.ymldevour.yaml
Besides the settings below, a synced file holds usageLimits, collections,
forms, products, pages, and navSections, and locales live in a
locales/ folder next to it. See
Collections & Data Modeling,
Pages & Navigation, and
Locales & Translations.
Usage Limits and Plans
usageLimits names what your plans limit, such as ["Guides", "Pieces"], up
to 10. A collection links to one with "usageLimit": "Guides", or "" to
unlink it.
products lists the subscription plans your platform sells, each created in
your own Stripe account:
{ "name", "description", "features", "active", "limits", "amount", "currency", "interval" }.
limits sets the plan's number for each usage limit by name, such as
{ "Guides": 5 }. amount is in the currency's smallest unit, and interval
is day, week, month, or year.
The generated platform enforces the limits: a customer creates records in a
linked collection until their plan's number, and without a plan can only read.
See
Usage Limits and What They Cap.
A usage limit missing from the file is removed after setup asks, or right away
with --yes.
Collection Record Access
Each collection has a recordAccess of private or shared. With private,
each signed-in customer of the generated platform sees and edits only the
records they created, and admins see every record. With shared, every
signed-in customer sees and edits every record. A collection created without
recordAccess is private. Leaving it out for an existing collection keeps its
current value.
{ "name": "Pieces", "recordAccess": "private", "properties": [] }
Property and Field Types
Collection properties and form fields take one of these type values:
text-input, text-area, number, toggle, checkbox, single-select,
multi-select, date, email, phone, address, address-country, color,
image, file
single-select and multi-select also need a linkedCollectionId. Use image
for a photo and file for any other upload, such as audio.
Page Blocks
A page holds sections, each with its own blocks, and can also hold blocks
directly. devour sync writes each section's id, so applying the file updates
that section. A section without an id is created. Every block is
{ "type", "displayOrder", "config" }, and type must be one of the following.
The value is case-sensitive.
type | config |
|---|---|
heading | text, and level: 1, 2 or 3 |
richText | content: HTML |
entityList | collectionId, enableCreate, enableEdit, enableDelete |
form | formId |
button | label and linkType: internal with linkPageId, or external with linkUrl |
collectionId, formId and linkPageId are the id of that collection, form
or page, as devour sync writes it.
{
"type": "entityList",
"displayOrder": 0,
"config": {
"collectionId": "bafc6c63-a4ed-44af-b1f4-b78e15fb773b",
"enableCreate": true,
"enableEdit": true,
"enableDelete": false
}
}
collectionId, formId, and label are required, and so is linkType with
its matching linkPageId or linkUrl. The others are optional. A left-out
enableCreate, enableEdit, or enableDelete is off, and a heading without a
level renders at level 3. The builder app starts new blocks with
enableCreate and enableEdit on and level at 2.
What the two data blocks do in the generated platform:
entityListlists the collection's records by name, a page at a time.enableEditopens a record's page,enableCreateadds a button for a new record, andenableDeletelets each record be deleted. The record page is the same for creating and editing, and shows every property of the collection in the order the properties were created.formshows one of the project's forms. A submission is stored as a form submission and never creates or edits a collection record.
Applying a file fails, and the error says why, when it has an unknown block,
property, or field type, or when a block's config has a key its type doesn't
take, is missing a required key, has a value of the wrong kind, or points at a
collection, form, or page that isn't in the project.
JSON Example
{
"id": "your-project-id",
"name": "My Project",
"description": "Customer portal for my store",
"theme": "default-theme@latest",
"logoUrl": "assets/logo.png",
"stripePublicKey": "pk_test_...",
"stripeSecretKey": "sk_test_...",
"firebaseApiKey": "AIzaSy...",
"firebaseAuthDomain": "project.firebaseapp.com",
"firebaseProjectId": "project-id",
"firebaseServiceAccount": "secrets/service-account.json",
"passwordlessSignIn": true,
"colorMain": "#3B82F6",
"colorSecondary": "#10B981"
}
YAML Example
id: your-project-id
name: My Project
description: Customer portal for my store
theme: default-theme@latest
logoUrl: assets/logo.png
stripePublicKey: pk_test_...
stripeSecretKey: sk_test_...
firebaseApiKey: AIzaSy...
firebaseAuthDomain: project.firebaseapp.com
firebaseProjectId: project-id
firebaseServiceAccount: secrets/service-account.json
passwordlessSignIn: true
colorMain: '#3B82F6'
colorSecondary: '#10B981'
Available Settings
| Key | Description | Value |
|---|---|---|
| name | Project name | text |
| description | Project description | text |
| theme | Theme of the generated platform | name@version, such as default-theme@latest |
| logoUrl | Logo | path or URL of an SVG, PNG, JPG, GIF, or WebP up to 5 MB |
| stripeSecretKey | Stripe secret key | text |
| stripePublicKey | Stripe public key | text |
| firebaseApiKey | Firebase API key | text |
| firebaseAuthDomain | Firebase auth domain | text |
| firebaseProjectId | Firebase project ID | text |
| firebaseStorageBucket | Firebase storage bucket | text |
| firebaseMessagingSenderId | Firebase messaging sender ID | text |
| firebaseAppId | Firebase app ID | text |
| firebaseMeasurementId | Firebase measurement ID | text |
| firebaseServiceAccount | Firebase service account | path to the JSON file |
| passwordlessSignIn | Passwordless sign-in in the generated platform | true or false |
| resendApiKey | Resend API key | text |
| resendFromEmail | Address emails are sent from | email address |
| databaseType | Database type | sql or firestore |
| dbHost | Database host | text |
| dbPort | Database port | text |
| dbUser | Database user | text |
| dbName | Database name | text |
| dbPassword | Database password | text |
| colorMain | Main color | hex color, such as #3B82F6 |
| colorSecondary | Secondary color | hex color |
| colorAlert | Alert color | hex color |
| colorError | Error color | hex color |
| colorSuccess | Success color | hex color |
| colorBackground | Background color | hex color |
| colorInk | Ink (text) color | hex color |
| colorDisabled | Disabled color | hex color |
| version | Current project version | semantic version, such as 1.0.0 |
| autoIncrementVersion | Auto-increment the version | true or false |
| autoDeleteOldVersions | Delete old versions automatically | true or false |
To upload a logo or a service account with setup, set logoUrl or
firebaseServiceAccount to a URL or a file path. A URL or a file outside the
project directory is uploaded, copied into assets/ or secrets/ next to the
configuration file, and replaced by that path. A file inside the project
directory, such as one you put next to the configuration file, is uploaded when
it's new or has changed since the last devour sync or devour setup, and
skipped otherwise. setup uploads files before it sends the settings that need
them. logoUrl and autoDeleteOldVersions can only be set from a configuration
file, not with --key.
Colors must be 6-digit hex values such as #3B82F6. The bundle replaces any
other format with the default color.
The database settings only reach the bundle once Use default Dockerized database is turned off in the web app. That switch can't be changed from the CLI. See Database Setup.
Help
devour --help
devour <command> --help
devour config
devour --version
Each command's help ends with an example and a link to its section here.
devour config prints everything a configuration file can hold: every setting
with its value format, the shape of collections, forms, pages, and navigation,
and the property, field, and block types. devour --version prints the CLI's
own version.
Exit Codes
| Code | Description |
|---|---|
| 0 | Success |
| 1 | No valid API key was saved, theme upload failed, or theme validate found a problem |
Other commands print their errors and exit with code 0, so check their output
when an agent or a script runs them.