lr-serial
CLI + Python client for interacting with the LumenRadio Serial Number Generation Service.
Install
pip install lr-serial
AI assistant integration
lr-serial ships a bundled skill/instruction file that teaches AI coding assistants
about the CLI commands, Python API, data models, and common workflows.
Run this once from the root of your project:
serial --install-skill
This does three things:
- Claude Code — copies the skill to
~/.claude/skills/lr-serial/(global, available in all projects) - GitHub Copilot — writes
.github/instructions/lr-serial.instructions.mdin the current directory - VSCode — sets
github.copilot.chat.codeGeneration.useInstructionFiles: truein.vscode/settings.json(creates the file if it doesn't exist; merges if it does)
To inspect the skill content without installing:
serial --show-skill
Python API
lr-serial exposes a SerialClient class for programmatic use — no CLI parsing, no sys.exit().
All errors are raised as typed exceptions from lr_serial.exceptions.
Quick start
from lr_serial import SerialClient
# From stored credentials (after `serial login`)
client = SerialClient.from_config()
# Or from M2M credentials directly
client = SerialClient.from_m2m(
client_id="<CLIENT_ID>",
client_secret="<CLIENT_SECRET>",
token_url="https://login.microsoftonline.com/<TENANT_ID>/oauth2/v2.0/token",
)
# List articles
articles = client.list_articles() # -> list[ArticleListItem]
# Generate serial numbers
serials = client.generate("PROD-01", quantity=5, fields={"site": "2"}) # -> list[str]
# Look up a serial
info = client.lookup("PROD-STH-000001") # -> SerialInfo
# Full article details
article = client.article_get("PROD-01") # -> Article
Exception handling
from lr_serial.exceptions import AuthenticationError, NotFoundError, SerialError
try:
info = client.lookup("UNKNOWN-000")
except NotFoundError:
print("Serial not found")
except AuthenticationError:
print("Token expired — run `serial login` again")
except SerialError as exc:
print(f"API error: {exc}")
Reference
| Method | Returns | Description |
|---|---|---|
SerialClient.from_config(path?) |
SerialClient |
Load from ~/.config/serial/login.json |
SerialClient.from_m2m(client_id, client_secret, token_url, scope?) |
SerialClient |
OAuth2 client credentials |
client.list_articles() |
list[ArticleListItem] |
All articles |
client.article_get(article_no) |
Article |
Full article details |
client.article_create(article_no, description, prefix, schema, disabled?) |
dict |
Create article |
client.article_update(article_no, *, description?, prefix?, disabled?, schema?) |
dict |
Partial update |
client.article_delete(article_no) |
None |
Delete article |
client.article_set_sequence(article_no, sequence_no) |
dict |
Set serial counter |
client.generate(article_no, quantity?, fields?) |
list[str] |
Generate serials |
client.lookup(serial_no) |
SerialInfo |
Serial metadata |
client.site_list() |
list[Site] |
All sites |
client.site_create(site_no, name) |
dict |
Create site |
client.site_update(site_no, name, *, client_id?, contact_email?, disabled?) |
dict |
Update site |
client.site_delete(site_no) |
None |
Delete site |
client.audit_list(limit?) |
list[AuditEntry] |
Audit log (admin scope) |
client.audit_restore(audit_id) |
dict |
Undo audited change (admin scope) |
Models
| Model | Key fields |
|---|---|
ArticleListItem |
article, description |
Article |
article, description, prefix, disabled, schema_ |
ArticleSchema |
version, format, regexp, fields |
SerialInfo |
serial_no, article, created_by, created_at |
Site |
site_no, name, client_id, contact_email, disabled |
AuditEntry |
id, touched_table, touched_key, change_type, changed_by, changed_at |
CLI Usage
Login Command
The login command supports two authentication modes:
User login (default)
Opens the system browser for interactive sign-in via OAuth 2.0 Authorization Code + PKCE. No client secret required — suitable for human operators.
LumenRadio employees can log in with no arguments (client ID and tenant ID are baked in):
serial login
If the browser does not open automatically the auth URL is printed to the terminal so you can paste it manually.
Machine-to-machine (M2M) login
Uses the OAuth 2.0 client credentials grant — suitable for automated pipelines.
serial login --mode m2m \
--client-id <CLIENT_ID> \
--client-secret <CLIENT_SECRET> \
--tenant-id <TENANT_ID>
Common options
--mode [user|m2m]— authentication mode (default:user) [env: SERIAL_AUTH_MODE]--client-id— Azure AD application client ID [env: SERIAL_CLIENT_ID]--client-secret— client secret, required form2m[env: SERIAL_CLIENT_SECRET]--tenant-id— Azure AD tenant ID [env: SERIAL_TENANT_ID]--token-url— token endpoint URL override (m2m only) [env: SERIAL_TOKEN_URL]--scope— OAuth2 scope override [env: SERIAL_SCOPE]--config-path— override the login.json location
Stores the bearer token (and refresh token for user logins) in your config directory for subsequent CLI commands.
Status Command
Check backend reachability and token validity:
serial status
Reports:
- Backend reachable ✓ / unreachable ✗
- Token valid ✓ / invalid ✗
Global Options
Most commands support:
--output [rich|json]— output format (rich table default, json for scripting)--config-path PATH— override the login.json location
Article Commands
List Articles
serial list-articles
serial list-articles --output json
Get Article
serial article get ARTICLE_NO
serial article get 710-4130 --output json
Returns full article details: article number, description, prefix, schema (version, fields, format, regexp), disabled flag.
Create Article
serial article create \
--article PROD-01 \
--description "Production line 1" \
--prefix PROD \
--schema-file schema.json
The --schema-file must be a JSON file with the ArticleSchema object:
{
"version": 1,
"fields": { "site": "string" },
"format": "{prefix}-{site}-{counter:06d}",
"regexp": "^[A-Z0-9]+-[A-Z0-9]+-[0-9]{6}$"
}
Options:
--article TEXT— article number (1-32 chars, [A-Za-z0-9-]) [required]--description TEXT— human-readable description (1-100 chars) [required]--prefix TEXT— serial prefix (1-10 chars, [A-Za-z0-9]) [required]--schema-file PATH— path to JSON file containing the ArticleSchema [required]--disabled / --no-disabled— create as disabled (default: enabled)
Update Article
serial article update ARTICLE_NO --description "New description"
serial article update ARTICLE_NO --prefix NEWP --disabled
At least one option must be provided. Options: --description, --prefix, --disabled/--no-disabled, --schema-file.
Delete Article
serial article delete ARTICLE_NO
Idempotent — succeeds even if the article does not exist.
Set Sequence Counter
serial article set-sequence ARTICLE_NO SEQUENCE_NO
serial article set-sequence 710-4130 1000
Sets the current counter value (integer ≥ 0) for the article.
Serial Number Commands
Generate Serial Numbers
serial generate ARTICLE_NO
serial generate ARTICLE_NO --quantity 5
serial generate ARTICLE_NO --field site=2 --field year=2024
serial generate ARTICLE_NO --quantity 3 --output json
Options:
--quantity INT— number of serials to generate (1-1000, default 1)--field KEY=VALUE— extra field value consumed by the article schema (repeatable). The conventionalsitefield should be asite_nofrom the sites table, but the backend does not enforce this — any string value is accepted.
Lookup a Serial Number
serial lookup SERIAL_NO
serial lookup QWDNA010000001 --output json
Returns: serial value, article, created_by, created_at.
Note: The site field is not returned by lookup — it is embedded in the serial
string itself according to the article's schema format. To find the production site
of a serial: look up the serial to get its article, get the article schema to
understand the format, parse the site_no digits from the serial string, then
resolve it via serial site list.
Site Commands
List Sites
serial site list
serial site list --output json
Create Site
serial site create --site-no 5 --name "Helsinki"
Options:
--site-no INT— site number (1-99) [required]--name TEXT— site name [required]
Update Site
serial site update 5 --name "Helsinki" --contact-email ops@example.com
serial site update 5 --name "Helsinki" --disabled
Note: --name is required by the API even when only updating other fields.
Options:
--name TEXT— new site name [required]--client-id TEXT— associated client ID (or empty to clear)--contact-email TEXT— contact email address (or empty to clear)--disabled / --no-disabled— enable or disable the site
Delete Site
serial site delete 5
Idempotent — succeeds even if the site does not exist.
Audit Commands
These commands require the app.admin.api scope.
List Audit Entries
serial audit list
serial audit list --limit 50 --output json
Returns audit entries newest-first: id, touched_table, touched_key, change_type (create/update/delete), changed_by, changed_at.
Options:
--limit INT— max entries to return (1-500, default 100)
Restore an Audit Entry
serial audit restore AUDIT_ID
Undoes the audited change:
create→ deletes the created rowupdate→ restores previous valuesdelete→ re-inserts the deleted row
If the token lacks the admin scope, the API returns 403 and the CLI will display:
Access denied. Ensure your credentials include the admin scope (app.admin.api).
Release files for lr-serial 1.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 | |
|---|---|---|---|
| lr_serial-1.1.0.tar.gz | 43.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| lr_serial-1.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 77.0 kB
Release files / lr_serial-1.1.0.tar.gz
| Download URL | lr_serial-1.1.0.tar.gz |
|---|---|
| Size | 43.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
83ebd073eb86646e0f885a222a275ae41abd50ee1d2ab4a4b81f1bacfe881caf
|
|
BLAKE2b-256 checksum How to use checksums |
cb7814722381ef803ac3b251ec3b74a2130a1ad0ea53f8d115eb486fb19fd9b0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.14
|
Release files / lr_serial-1.1.0-py3-none-any.whl
| Download URL | lr_serial-1.1.0-py3-none-any.whl |
|---|---|
| Size | 33.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
494fde246f0524864582d2416332b751226d324c5d34537302e4fe9a19056de2
|
|
BLAKE2b-256 checksum How to use checksums |
cdffc8d5735396247bd5633ca016fce9be17c52d152f729693e172b5f213694b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.14
|