@lepsto/cli
v0.4.1
Published
Unified command-line facade over the Lepsto platform operations
Readme
@lepsto/cli
lepsto — a unified command-line facade over the Lessly platform operations.
It renders the same operation registry agents reach over MCP, so the CLI has
parity with MCP by construction.
Install
@lepsto/cli publishes to the public npm registry:
npm i -g @lepsto/cliOr run it without installing:
npx lepsto --help # unscoped launcher package, pins @lepsto/cli at the same version
npx -p @lepsto/cli lepsto --help # the same CLI, straight from the scoped packageIn short: install globally with @lepsto/cli, run with npx as lepsto. Don't
npm i -g lepsto — the launcher and @lepsto/cli both provide the lepsto command,
so installing both globally makes npm refuse with a bin conflict.
npx @lepsto/cli on its own does not work: the package has two bins (lepsto,
lessly) and neither is named after the package, so npx cannot pick one. Use one
of the two forms above.
Renamed from @lessly/cli
@lepsto/cli is the new name of @lessly/cli — same code, same API, same version
line. During the transition every release is published under both names from one
build, so npm i -g @lessly/cli keeps working at the same version. Both packages
install two bins: lepsto (primary) and lessly, an alias that prints one
deprecation line to stderr and otherwise behaves identically. LESSLY_*
environment variables are unchanged; the config directory moved to ~/.lepsto/
(see below).
Where the CLI keeps its files
Everything lives in ~/.lepsto/ (directory mode 0700):
config— default profile, output format, custom profilescredentials.json— tokens per profile (mode 0600)cache/— operation catalog cache per profile
Releases before 0.4.0 used ~/.lessly/. The first run of 0.4.0 or later moves it:
if ~/.lepsto does not exist and ~/.lessly does, the whole directory is renamed
(or, across filesystems, copied, verified and then the old one removed), file modes
included, and moved ~/.lessly to ~/.lepsto is printed once to stderr. If both exist,
~/.lepsto wins and ~/.lessly is not touched — delete it yourself when you no
longer need it. If a move fails, ~/.lessly is left intact and the next run retries.
Auth
Browser (device-code) login for humans:
lepsto auth login # prints a code + URL, opens your browser, polls
lepsto --profile prod auth login # against the prod auth serverNon-interactive (CI / agents) with a product-scoped service token:
lepsto auth login --token "$LESSLY_TOKEN"Both store the Bearer under ~/.lepsto/credentials.json (mode 0600), per profile.
Auth servers: prod auth.lessly.com (default), staging auth.lessly.dev
(opt-in via --profile staging).
Connection profile
A fresh install talks to production (mcp.lessly.com / auth.lessly.com).
Staging is an explicit opt-in.
Priority: --profile <name> > LESSLY_PROFILE > defaultProfile in ~/.lepsto/config > built-in prod.
LESSLY_PROFILE=staging lepsto organization product listUsage
lepsto --profile staging organization product list
lepsto organization product create --name "Acme" --yes
lepsto deployment cloud-sql-database attach --input @args.json
lepsto operations # raw op list (MCP/CLI parity debug)
lepsto config set product <id> # active productFlag placement (gcloud/gh convention)
- Connection flags go BEFORE the command:
--profile <staging|prod>,--refresh. Example:lepsto --profile staging organization product list. - Output / behavior flags go AFTER the operation:
-o table|json|yaml(--json),--input <json>|@file|-,--yes/--force. Example:lepsto organization product create --name Acme --yes.
A behavior flag placed before the command is rejected with an "unknown option"
error — move it after the operation. (Behavior flags are per-operation, so an
operation may even define its own --input/--force parameter; in that case the
flag is that operation's argument and --yes is the confirm bypass.)
Mutations
Mutating operations prompt for confirmation; pass --yes (or --force) to
bypass, and non-interactive sessions must pass --yes (otherwise the command
exits 2 rather than running a mutation unattended). A few operations define their
own --force argument (e.g. deployment environment reconcile); on those the
operation's --force is its parameter, so use --yes to bypass the confirm.
Until the gateway emits the MCP readOnlyHint annotation, mutation detection
uses a conservative verb heuristic on the operation's action (list, get,
show, describe, status, logs, export, … and list-*/get-* /
*-list/*-logs/*-status/*-info are treated as read-only; everything else
is treated as a mutation). When readOnlyHint is present it always wins. The
safe default is to treat the unknown as a mutation, so an occasional read may ask
for --yes — never the reverse.
Exit codes
| Code | Meaning | |------|---------| | 0 | success | | 1 | generic error | | 2 | validation | | 3 | auth / token | | 4 | not found / forbidden / no product selected |
Architecture
Commands depend only on the neutral Operation model produced by the operation
catalog (tools/list → Operation). The transport seam (src/transport) is the
only MCP-specific code; swapping it for a manifest endpoint in step 2 changes no
command.
