cpms, the LeanCPMS command line
cpms manages an organization in a LeanCPMS instance from your own
machine: live state, sessions, the monthly statement, and the declared
state (organization, price list, sites) as YAML documents you can keep in
a git repository. It talks to the instance's JSON API over HTTPS with an
API key, and the same commands work for people and for AI agents.
This directory builds leancpms-cli, the command without the server. It
depends on httpx, pydantic and PyYAML only.
Install
With uv, from PyPI once it is published there:
uv tool install leancpms-cli
cpms --help
Until then, from a GitHub release (a tag cli-v<version>) or straight
from the repository:
gh release download cli-v0.1.0 --repo forsslund/leancpms --pattern '*.whl'
uv tool install ./leancpms_cli-0.1.0-py3-none-any.whl
# or
uv tool install "git+https://github.com/forsslund/leancpms#subdirectory=cli"
- The repository is private for now, so both need read access to it on
GitHub; over SSH the URL is
git+ssh://git@github.com/forsslund/leancpms#subdirectory=cli. - It needs Python 3.14. uv downloads it if your machine lacks it.
- To get a newer version, run
uv tool upgrade leancpms-cli. To pin a version, add@<tag or commit>after the repository URL. pipx install "git+…#subdirectory=cli"works too.- Do not install it into an environment that already has the server
(
leancpms). The server already includescpms, and both packages carry the same files, so uninstalling one breaks the other.
Configure
Log in with the same e-mail address you use for the instance's portal:
cpms login --url https://cpms.example.com
It prints a code and opens the portal's page for it in your browser (or
prints the address only: --no-browser, or no display). Log in there if
you are not, check that the page shows the same code, choose your
organization and whether the key may act (write) or only look (read),
and approve. Your role as a member decides which you may choose: members
are read unless the operator, or a colleague with a write key, made
them write (cpms members set-role). cpms login waits for that and then saves a
personal key, yours and that organization's, as the profile
[default] (--profile NAME for another) in ~/.config/cpms/config.toml,
readable only by you. Other profiles in the file stay as they were.
cpms whoami # which key is in use, whose, for which organization
cpms logout # revokes the key on the server and removes the profile
The key stops working when you are removed as a member, and so does
every key you issued with it. Approve a code only if you just ran
cpms login yourself: whoever started it gets the key.
Machine keys, for CI and agents
A pipeline or an agent of its own gets a machine key instead, which
records who issued it: cpms keys issue --name ci --scope read with your
personal write key. It is shown only once. An organization key sees only
its own organization. A key you issue lives only as long as your
membership: when you leave, it is revoked with your own. An integration
that must outlive you gets its key from the operator. Members you add do
stay when you leave; cpms members list shows who added whom.
Put the URL and the key in ~/.config/cpms/config.toml (or
$XDG_CONFIG_HOME/cpms/, or the path in CPMS_CONFIG), readable only by
you (chmod 600):
[default]
url = "https://cpms.example.com"
api_key = "lcp_…"
[test]
url = "http://localhost:8000"
api_key = "lcp_…"
Alternatively, set CPMS_URL and CPMS_API_KEY in the environment, which
suits CI and agents. --profile test (or CPMS_PROFILE=test) picks a
profile. Without a profile, the environment wins, then [default].
A networked command that has neither exits 2 and says what to set.
Use
cpms # live state: sites, fuse groups, outlets, current per phase
cpms sessions --since 2026-10-01
cpms report statement --month 2026-10 # receipts incl./excl. VAT, sessions without one
cpms manifest export kvartersel > all.yaml
cpms manifest export kvartersel --site odalvagen > sites/odalvagen.yaml
cpms manifest validate sites/odalvagen.yaml # offline: the schema
cpms manifest plan sites/odalvagen.yaml # the server: the diff, references, live warnings
cpms manifest apply sites/odalvagen.yaml -m "Fused up to 50 A"
cpms manifest history kvartersel --site odalvagen
cpms --help and cpms manifest --help list the rest.
A repository of manifests
One file per document works well:
organization.yaml
pricelist.yaml
sites/odalvagen.yaml
sites/brotorp.yaml
Edit a file, run validate and plan, commit, then run apply with the
commit's reason as -m. The server holds the truth and the files are a
way to say things: if someone changed a price in the portal, plan shows
that applying your file would undo it, and export brings the file up to
date. apply refuses a
document that changed on the server since your plan; plan again. Removing
anything needs --prune. validate needs no key, so CI can run it on
every pull request; plan needs one.
In your editor: the JSON Schema
The documents have a JSON Schema, so an editor can complete field names and flag mistakes as you type. That works in editors that use yaml-language-server: VS Code with Red Hat's YAML extension, Neovim, Helix, Zed. Add this line at the top of each file:
# yaml-language-server: $schema=https://raw.githubusercontent.com/forsslund/leancpms/main/schema/manifest.cpms-v1.schema.json
That URL works once the repository is public; replace main with a tag
to pin a version. Until then, use a local copy, written by the command
itself and kept in your manifest repository:
cpms manifest schema > cpms-v1.schema.json
# yaml-language-server: $schema=../cpms-v1.schema.json
The path is relative to the YAML file. export does not write the
line, so add it again after an export.
The schema checks types, required and unknown fields, allowed values,
key spelling, and that amounts are quoted ("4.70", not 4.70). It does
not check anything that spans several fields: a budget above the fuse,
duplicate keys or ocpp ids, a plan naming a missing tariff, or an unknown
time zone. validate and plan check those.
For agents
--jsonworks on every command, before or after the subcommand.- Exit codes: 0 ok, 1 the action failed (the server said no, the server could not be reached, a plan has errors), 2 usage or configuration.
cpms manifest applynever prompts without a TTY. It prints the plan and exits 2 unless you pass--yes. Show the plan to a person first, then apply with--yes -m "why".session … stop|closeacts at once, with no confirmation step.- Pass the key in the environment or the config file, never on the command line or in a transcript.
Releasing (maintainers)
Raise version in both cli/pyproject.toml and the root pyproject.toml
(a test keeps them equal), then tag and push:
git tag cli-v0.1.1 && git push origin cli-v0.1.1
.github/workflows/release-cli.yml builds the wheel and sdist, smoke-tests
the wheel, and attaches both to a GitHub release. It also publishes to PyPI
when the repository variable PYPI_PUBLISH is true. That needs, once:
a PyPI account with two-factor authentication; a pending trusted
publisher on PyPI for the project leancpms-cli (owner forsslund,
repository leancpms, workflow release-cli.yml, environment pypi);
and an environment pypi in the GitHub repository's settings. A pending
publisher does not reserve the name: if someone else registers
leancpms-cli first, it becomes invalid. The first release creates the
project and so reserves the name. Trusted publishing uses GitHub's OIDC
token, so no PyPI token is stored anywhere. A file published to PyPI cannot be replaced,
only yanked: a wrong release is fixed with a new version.
Metadata
Release files for leancpms-cli 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| leancpms_cli-0.1.0.tar.gz | 40.9 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| leancpms_cli-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 81.8 kB
Release files / leancpms_cli-0.1.0.tar.gz
| Download URL | leancpms_cli-0.1.0.tar.gz |
|---|---|
| Size | 40.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
27e608c8e1e1dde576bb2ccadc4a4b628f6455b96a60a57347bda5e88b97f007
|
|
BLAKE2b-256 checksum How to use checksums |
16bb88a641a6bd61cd23a84dbbdb1ccefa0e5558988ef9374d999f56ec60cfb8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.
Transparency logRelease files / leancpms_cli-0.1.0-py3-none-any.whl
| Download URL | leancpms_cli-0.1.0-py3-none-any.whl |
|---|---|
| Size | 40.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9baf4df29198c09ccd0cea091633ef774d93866415ea560ea44b0f3c59cc5f2a
|
|
BLAKE2b-256 checksum How to use checksums |
3aae7f80670a7a573c776912b74920395fc742a648dc7b24e82fdc807916d7c6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.
Transparency logSecurity for leancpms-cli 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for leancpms_cli-0.1.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.