@fluid-app/fluid-cli
The Fluid Commerce CLI. Provides authentication, profile management, and a plugin system for extending with domain-specific commands.
Installation
npm install -g @fluid-app/fluid-cli
Sign up
fluid signup creates a company with Fluid's public signup, before any
sign-in:
fluid signup --email [email protected] --first-name Maya --last-name Lee \
--company-name "Acme" --subdomain acme --yes --json
It sends the subdomain in the form the server stores (lowercase letters,
digits and hyphens, at most 63), prints the company's fluidShop, and the
two fluid login steps below that sign in to it. The second step saves the
sign-in under a profile named after the subdomain (--name), so it never
silently replaces a profile you already have. Fluid emails the address a link to
confirm the signup; signing in with the emailed code confirms it too.
It sends one request and never retries it: every attempt, failed or not,
counts toward the signup limits (per IP address and per email), and a
failure can come after the company was saved. Errors say which case
applies: SUBDOMAIN_TAKEN, SIGNUP_MAY_EXIST (run the same command once
more; a taken subdomain then means it exists), RATE_LIMITED (with
retryAfter in seconds), SIGNUP_FORBIDDEN or SIGNUP_OUTCOME_UNKNOWN.
A second signup for the same email within an hour normally sends no new
email. Without --yes it asks at a terminal, and refuses when there is no
terminal or with --json, which prints one object (status is
signed_up or error).
--agent-access [name] also asks for a credential an administrator of the new
company approves for an agent; the command waits, then saves it as a profile.
It is available when enabled: see AGENT_ACCESS.md.
Authentication
# Email MFA flow
fluid login
# API token (avoids shell history exposure)
FLUID_TOKEN=your-token-here fluid login
# Manage profiles
fluid whoami # Show active profile
fluid switch # Switch between profiles
fluid logout # Remove stored authentication
Profiles are stored at ~/.fluid/config.json.
--shop <shop> signs in to one company (acme or acme.fluid.app). For an
admin, finishing the sign-in with a code sent for that shop also confirms a
new company's signup.
Without a terminal
fluid login asks for the email and code at a prompt, so a script or agent
with no terminal signs in in two steps instead:
# 1. Email a code for one company and save the sign-in (prints how to finish)
fluid login --send-code --email [email protected] --shop acme
# 2. Finish with the emailed code from FLUID_LOGIN_CODE
FLUID_LOGIN_CODE=123456 fluid login --code --email [email protected] --yes
# or read it from stdin with --code -
echo 123456 | fluid login --code - --email [email protected] --yes
Step 1 needs --shop: the code is tied to that company, and only such a
code confirms a new signup (fluid signup prints the shop as
fluidShop; the API's signup response names it fluid_shop). Step 2 signs in to the same company; a --shop given there
must match it.
The code expires after 30 minutes, and five wrong codes lock it. Step 1
saves the server's id for the sign-in under
~/.fluid/pending-logins/ until step 2 finishes it; the id alone cannot
sign in. A code given as --code 123456 works but is visible in shell
history.
Without a terminal (and with --code -, which takes stdin), step 2 needs
--yes, which replaces a profile named after the company, or --name <profile>, which is refused before the code is spent if that profile
exists. Either way the code is never used up on a profile it cannot write.
--json prints one JSON object (status is code_sent, signed_in or
error, with a code on errors).
With no terminal, fluid login fails at once rather than waiting at a
prompt, unless it has a token (--token or FLUID_TOKEN) or runs one of
the two steps (--send-code, --code).
Companies on Fluid's identity sign-in
Fluid decides per company whether it signs in through the identity service.
For such a company the email has a sign-in link instead of a code. Paste the
link at the code prompt, or, without a terminal, give it to --code the same
way as a code (in FLUID_LOGIN_CODE or on stdin). fluid login then opens
your browser to sign in and receives the result on a one-time server at
http://127.0.0.1:<port>/callback. The sign-in URL is printed too (on stderr
with --json), for a terminal with no browser to open.
In this mode no token is written to config.json. The refresh token is kept in
the operating system credential store (macOS Keychain, Windows Credential
Manager, or the Secret Service on Linux), and access tokens only in memory.
fluid switch moves company by token exchange, fluid whoami reads the
identity session, and fluid logout revokes the session. Every other company
signs in exactly as before.
Plugins
Install plugins alongside the core CLI to add commands:
| Package | Commands | Description |
|---|---|---|
@fluid-app/fluid-cli-theme-dev | fluid theme * | Theme dev server, push, pull, init |
@fluid-app/fluid-cli-portal | fluid portal * | Portal app build, deploy, push, pull |
@fluid-app/fluid-cli-themes | fluid themes * | Theme management API commands |
@fluid-app/fluid-cli-assets | fluid assets * | Digital asset management commands |
@fluid-app/fluid-cli-droplets | fluid droplets * | Droplet management commands, including install, uninstall and installed |
@fluid-app/fluid-cli-products | fluid products * | Create a priced catalog product |
Example:
npm install -g @fluid-app/fluid-cli @fluid-app/fluid-cli-theme-dev
fluid theme dev
Plugins are auto-discovered from:
node_modules/@fluid-app/in the current working directorynode_modules/@fluid-app/where the CLI itself is installed (global installs)- Sibling packages in a pnpm workspace (monorepo development)
Discovering commands
fluid commands # every installed command, one line each
fluid commands domains # only `fluid domains` and its subcommands
fluid commands --json # the full catalog, for agents and doc generators
The JSON catalog lists every command with its arguments, flags (and the
parent commands whose flags it also accepts), whether it changes data, and
the API endpoints it calls. writes is true or false when the plugin
declares it or the command confirms with --yes, and null when unknown.
endpoints is null until a plugin declares them: unknown, not "calls no
API". Plugins declare endpoints with describeCommand:
import { describeCommand } from "@fluid-app/fluid-cli";
describeCommand(parent.command("status <domain>"), {
endpoints: [{ method: "GET", path: "/api/domains/{id}" }],
writes: false,
});
Shell completion
echo 'eval "$(fluid completion bash)"' >> ~/.bashrc
fluid completion zsh > "${fpath[1]}/_fluid"
fluid completion fish > ~/.config/fish/completions/fluid.fish
Completion asks the installed CLI on each tab, so new plugins complete
without reinstalling the script. It completes commands, aliases, flags and
values that have a fixed set of choices. The scripts pipe the typed words to
the CLI on stdin rather than as arguments, so a typed --token never shows
in the process list.
Updates
Once a day an interactive run starts a detached background check of npm for
a newer @fluid-app/fluid-cli, and a newer @fluid-app/fluid-cli-member-site
(the Sites plugin); no command waits on it. When the last check found one, a
short notice prints on stderr after the command finishes. The Sites plugin's
notice is about the copy the command loaded, and names the command that
updates that copy.
A failed check is retried the next day. It never runs in CI, when output is piped, or for a
workspace build. Set FLUID_NO_UPDATE_CHECK=1 (or NO_UPDATE_NOTIFIER=1)
to turn it off.
Environment Variables
| Variable | Description |
|---|---|
FLUID_TOKEN | API token (alternative to fluid login) |
FLUID_LOGIN_CODE | Emailed code read by fluid login --code |
FLUID_API_BASE | API base URL (default: https://api.fluid.app) |
FLUID_CONFIG_DIR | Override config directory (default: ~/.fluid/) |
FLUID_IDENTITY_URL | Identity service origin (default: follows FLUID_API_BASE) |
FLUID_NO_BROWSER | Print the sign-in URL without opening a browser |
FLUID_NO_UPDATE_CHECK | Turn off the daily update notice |
Development
For contributors working in this monorepo:
pnpm --filter @fluid-app/fluid-cli build
node packages/cli/core/dist/bin/fluid.mjs --help