Skip to main content

Cabinet

Cabinet is a lightweight, flexible data organization tool that lets you manage your data with the simplicity of a JSON file or the power of MongoDB - your choice.

Breaking changes in 3.0.0

This release changes how logging works. Plan upgrades accordingly.

  • **logdb() removed.** Use cab.log(...) instead. There is no separate database-only logging API.

  • **--logdb / -ldb removed.** Use cabinet -l / --log with --level as needed; the CLI follows the same rules as the Python API.

  • File-first logging: cab.log() / cabinet --log always write to local-only daily files under path_dir_log on each machine. Logging does not use MongoDB. Optional **logging.loki_enabled** appends one structured JSON line per event to a sibling ***.jsonl** file so Promtail on that same machine can ship to a central Loki (see Logging and Loki (optional)).

  • **cab.log_query() and cabinet --query:** Search log files under path_dir_log only. MongoDB is for Cabinet’s configuration/data collection, not logs. Optional **since** filters by UTC cutoff or timedelta (compared using each line’s local timestamp).

  • **cab.log_query_issues():** Returns WARNING, ERROR, and CRITICAL lines for the last 24 hours by default by scanning today’s and yesterday’s daily files under path_dir_log.

  • **cab.log_query_documents() removed:** Use Loki/Grafana for centralized structured log views, or read **.jsonl** / **.log** files directly.

  • cab.remove() / cabinet --remove: Only applied when MongoDB is enabled. With local JSON storage (mongodb_enabled: false), Cabinet does not change ~/.cabinet/data.json; it prints that removal is MongoDB-only—use cabinet -e / --edit-file or edit the file directly.

  • cab.export() / cabinet --export: Writes a timestamped JSON file under ~/.cabinet/export. With MongoDB, that snapshot matches the cached collection export; with local storage, it writes a pretty-printed copy of ~/.cabinet/data.json.

  • Breaking change in 2.0.0

  • Cabinet

✨ Features

  • Access your data across multiple projects
  • Log messages to local files under path_dir_log (and optional local *.jsonl for that host’s Promtail). **cab.log_query()** / **cab.log_query_issues()** read files; **cab.log_query*_loki()** reads Loki when **logging.loki_url** is set. MongoDB is only for Cabinet data, not logs.
  • Store Cabinet data in MongoDB or ~/.cabinet/data.json; edit it like a JSON document (-e / cab.edit_cabinet()), export snapshots (--export), and use --remove only with MongoDB enabled
  • Send mail from the terminal (SMTP email.port accepts a number or numeric string; email.to may be a string, comma-separated addresses, or a JSON array—same idea as --to on the CLI). Transient SMTP failures are retried with exponential backoff and logged to Cabinet.
  • Send Telegram messages from Python (cabinet.telegram('message')) or the terminal (cabinet --telegram "message") via OpenClaw using telegram.target (optional telegram.bot_token uses the Bot API instead)
  • Library for interactive command-line interface components using prompt_toolkit

Breaking change in 2.0.0

  • mongodb_connection_string replaces mongodb_username and mongodb_password.

Installation and Setup

CLI and Python Library (Recommended)

  • Install pipx if you don't have it already
  • Install cabinet:
pipx install cabinet
cabinet --configure

Requires Python 3.10+ (see python_requires in setup.cfg).

CLI Only

curl -s https://api.github.com/repos/tylerjwoodfin/cabinet/releases/latest \
| grep "browser_download_url" \
| cut -d '"' -f 4 \
| xargs curl -L -o cabinet.pex

sudo mv cabinet.pex /usr/local/bin/cabinet

Dependencies

Outside of the standard Python library, the following packages are declared as dependencies (setup.cfg / package metadata):

  • pymongo: MongoDB client and related errors.
  • prompt_toolkit: Interactive CLI helpers (cabinet.ui).

For development, install optional test dependencies and run the suite:

pip install -e ".[dev]"
pytest

Structure

  • Data is stored in ~/.cabinet/data.json or MongoDB
    • Data from MongoDB is interacted with as if it were a JSON file.
    • With MongoDB enabled, Cabinet keeps ~/.config/cabinet/cache.json: it is refreshed when missing, when you use --force-cache-update / force_cache_update=True, or when the cache file is older than one hour; otherwise reads use the cache by default.
  • Logs: **cab.log()** / **cabinet --log** append to **path_dir_log/<YYYY-MM-DD>/** on that machine only. Optional JSONL for **logging.loki_enabled** (scraped by Promtail on the same host). **cab.log_query()** / **cab.log_query_issues()** scan local **.log** files. **cab.log_query_loki()** / **cab.log_query_documents_loki()** / **cab.log_query_issues_loki()** query Loki via **logging.loki_url**. MongoDB stores Cabinet variables only, not logs.

CLI usage

Run cabinet --help for the full list (wording matches your installed version). Summary:

Flag Role
--configure / -config Setup; writes ~/.config/cabinet/config.json
--edit / -e Edit Cabinet data as JSON (MongoDB via cache file, or ~/.cabinet/data.json locally)
--edit-file / -ef Edit a path or shortcut under path.edit.*
--force-cache-update Refresh MongoDB cache before reads
--no-create With -ef, do not create a missing file
--get / -g Read nested keys from Cabinet data
--put / -p Write nested keys to Cabinet data
--append / -a Append to a string or list value
--remove / -rm Remove nested keys (MongoDB only; with local JSON, Cabinet prints guidance and does not modify the file)
--get-file Read a file under ~/.cabinet (or configured notes path)
--export Write timestamped JSON to ~/.cabinet/export/ (Mongo snapshot or local data.json)
--strip With --get-file, do not strip whitespace (default strips)
--log / -l, --level, --tags Append to local log files (optional JSONL when logging.loki_enabled)
--editor Override editor for --edit / --edit-file
--query / -q Search log files under path_dir_log (optional log name; default today’s file)
--query-tags, --query-path, --query-hostname, --query-level, --query-date, --query-message Filters for --query
--mail, --subject / -s, --body / -b, --to / -t Send mail; --to may be comma-separated addresses
--telegram Send a Telegram message (uses telegram.target; OpenClaw by default)
--version / -v Print package version

--query matches Python Cabinet.log_query() (file-based logs only, not the Cabinet data store).

Configuration

  • Configuration data is stored in ~/.config/cabinet/config.json.
  • Upon first launch, the tool will walk you through each option.
    • path_dir_log is the local directory where logs are stored on this machine by default. If this is a multi-machine setup, do not put this under Syncthing, NFS, or other shared folders (see Logging and Loki). A practical default is ~/.local/share/cabinet/log, which stays off most Syncthing profiles even if you sync ~/.cabinet.
    • mongodb_enabled is a boolean that determines whether MongoDB is used.
    • mongodb_db_name is the name of the database you want to use by default.
    • mongodb_connection_string is the connection string for MongoDB.
    • editor is the default editor that will be used when editing files.
    • You will be prompted to enter your MongoDB credentials (optional).
    • If you choose not to use MongoDB, data will be stored in ~/.cabinet/data.json.
  • Follow these instructions to find your MongoDB connection string: MongoDB Atlas or MongoDB (for local MongoDB, untested).
  • You will be asked to configure your default editor from the list of available editors on your system. If this step is skipped, or an error occurs, nano will be used. You can change this with cabinet --configure and modifying the editor attribute.

Your config.json should look something like this:

{
    "path_dir_log": "/home/you/.local/share/cabinet/log",
    "mongodb_db_name": "cabinet (or other name of your choice)",
    "editor": "nvim",
    "mongodb_enabled": true,
    "mongodb_connection_string": "<your connection string>",
    "logging": {
        "loki_enabled": true,
        "log_dir": "/home/you/.local/share/cabinet/log",
        "loki_url": "http://loki.example.com:3100"
    }
}

Use a directory that exists only on this machine. If you previously used something like ~/syncthing/... for logs, move path_dir_log (and logging.log_dir if set) to a non-synced path such as ~/.local/share/cabinet/log or ~/.cache/cabinet/log.

The **logging** object is optional. loki_enabled turns on parallel JSON Lines files (*.jsonl) next to each *.log on that machine’s local disk for local Promtail to scrape. **loki_url** enables **cab.log_query*_loki(...)** helpers against a Loki server (central or tunneled). **log_dir**, when set, overrides the on-disk log directory for cab.log() (if omitted, top-level **path_dir_log** is used).

Logging and Loki (optional)

Architecture (multi-machine):

log() → local *.log + *.jsonl (this host only) → Promtail (this host) → Loki (one central server) → Grafana
  • File-first: Every cab.log() / cabinet -l line goes to the classic **.log** format on local disk.
  • Do not share log directories: Set path_dir_log / logging.log_dir to a local-only directory on each machine. Do not use Syncthing, NFS, or other shared folders for Cabinet logs: multiple writers to the same files cause sync conflicts, torn lines, duplicate or missing ingestion, and undefined ordering. That pattern is unsupported.
    • Syncthing: Even if Cabinet’s config lives under a synced tree, point path_dir_log at something outside your Syncthing folders (for example ~/.local/share/cabinet/log or ~/.cache/cabinet/log). mkdir -p ~/.local/share/cabinet/log on each host before logging.
  • No log push HTTP: cab.log() never POSTs to Loki; Promtail on each machine tails local *.jsonl and pushes to Loki. Reads from Loki (cab.log_query*_loki) use HTTP only when logging.loki_url is set.
  • Optional JSONL: With logging.loki_enabled, each event also appends one JSON object per line to **LOG_DAILY_<date>.jsonl** in the same date folder (fields include timestamp, level, message, tags, source, hostname from socket.gethostname() on that host).
  • Install Loki, Grafana, and Promtail (this repo’s Docker stack): You need Docker and the Docker Compose plugin on any machine that will run containers. Upstream docs: Grafana Loki, Promtail, source github.com/grafana/loki.
    • Central server (Loki + Grafana): One host runs the stack so all Promtail agents can push to it.
      cd /path/to/your/checkout/docker/loki
      cp .env.example .env    # optional: set GRAFANA_PORT
      docker compose up -d
      
      This starts Loki (port 3100 by default) and Grafana (host port from .env; login admin / admin). It does not start Promtail.
    • Each Cabinet host (Promtail only): In the same docker/loki directory, copy promtail.env.examplepromtail.env, set LOKI_URL (reachable from that host) and CABINET_LOG_DIR (same absolute path as path_dir_log / logging.log_dir in that machine’s Cabinet config), then:
      cd /path/to/your/checkout/docker/loki
      docker compose --env-file promtail.env -f docker-compose.promtail.yml up -d
      
      More detail, including host.docker.internal vs LAN IP for LOKI_URL, is in ../docker/loki/README.md (sibling of this cabinet repo when both live under the same parent directory).
  • Multi-machine example: Host cloud runs Cabinet → local JSONL → Promtail → Loki; host rainbow same; host ice same. In Grafana Explore, filter by source host with LogQL labels such as {job="cabinet", hostname="rainbow"} (hostname comes from the JSON line).
  • Turn off Loki shipping: Set loki_enabled to false, or stop Promtail on that host. Cabinet does not require Docker.
  • Explore logs in Grafana: Explore → datasource Loki → e.g. {job="cabinet"} or {job="cabinet", hostname="cloud"}.
  • Query from Python (Loki): Set logging.loki_url to your reachable Loki base URL—often Tailscale, LAN, or VPN (e.g. http://central:3100, http://cloud.tail….ts.net:3100); avoid a public hostname unless you intentionally expose Loki. Use **cab.log_query_loki(...)**, **cab.log_query_documents_loki(...)**, **cab.log_query_issues_loki(...)**. Optional **logging.loki_job** (default cabinet, must match Promtail’s job label) and **logging.loki_query_timeout** (seconds, default 30).
  • Config example (logging + Loki reads):
    "logging": {
        "loki_enabled": true,
        "log_dir": "/home/you/.local/share/cabinet/log",
        "loki_url": "http://loki.example.com:3100",
        "loki_job": "cabinet",
        "loki_query_timeout": 30
    }
    

edit_file() shortcuts

  • see example below to enable something like
    • cabinet -ef shopping from the terminal
      • rather than cabinet -ef "~/path/to/shopping_list.md"
    • or cabinet.Cabinet().edit("shopping")
      • rather than cabinet.Cabinet().edit("~/path/to/whatever.md")

Shortcut mapping example:

{
  "path": {
    "edit": {
      "shopping": {
        "value": "~/path/to/whatever.md"
      },
      "todo": {
        "value": "~/path/to/whatever.md"
      }
    }
  }
}

set from terminal:

cabinet -p edit shopping value "~/path/to/whatever.md"
cabinet -p edit todo value "~/path/to/whatever.md"

mail (configuration)

  • It is NEVER a good idea to store your password openly either locally or in MongoDB; for this reason, I recommend a "throwaway" account that is only used for sending emails, such as a custom domain email.
  • Gmail and most other mainstream email providers won't work with this; for support, search for sending mail from your email provider with smtplib.
  • email.port: May be a JSON number or a string (e.g. "587"). Cabinet normalizes it to an integer in the valid TCP range.
  • email.to: May be a single address string, comma-separated addresses in one string, or a JSON array of strings (or mix)—same rules as the CLI --to / Mail.send(..., to_addr=...).
  • email.smtp_max_retries (optional): Number of retries after a transient SMTP failure. Default 3 (4 attempts total including the first).
  • email.smtp_retry_base_delay (optional): Base delay in seconds for exponential backoff between retries. Default 120 (2 minutes). Delays double each retry: 2 min, 4 min, 8 min, …
  • Transient failures (timeouts, disconnects, connection errors) are retried automatically; authentication and configuration errors are not. Each attempt is logged via cab.log() (info on attempt start, warning before a retry, error when exhausted).
  • In Cabinet (cabinet -e), add the email object to make your settings file look like this example:

file:

{
    "email": {
        "from": "throwaway@example.com",
        "from_pw": "example",
        "from_name": "Cabinet (or other name of your choice)",
        "to": "destination@protonmail.com",
        "smtp_server": "example.com",
        "imap_server": "example.com",
        "port": 587,
        "smtp_max_retries": 3,
        "smtp_retry_base_delay": 120
    }
}

set from terminal:

cabinet -p email from throwaway@example.com
cabinet -p email from_pw example
...

telegram (configuration)

  • By default Cabinet sends through OpenClaw (openclaw message send), the same path diary-llm uses. You only need a chat id.
  • telegram.target: Chat id (string or number). telegram.chat_id is accepted as an alias.
  • telegram.channel (optional): OpenClaw channel. Default telegram.
  • telegram.openclaw_bin (optional): OpenClaw executable. Defaults to diary-llm's Gateway wrapper when that script exists, otherwise openclaw.
  • telegram.bot_token (optional): If set, send via the Telegram Bot API instead of OpenClaw. telegram.token is accepted as an alias.

In Cabinet (cabinet -e), add the telegram object:

{
    "telegram": {
        "target": "1234567890"
    }
}

set from terminal:

cabinet -p telegram target 1234567890

Examples

put

python:

from cabinet import Cabinet

cab = Cabinet()

cab.put("employee", "Tyler", "salary", 7.25)

or terminal:

cabinet -p employee Tyler salary 7.25

results in this structure in MongoDB:

{
    "employee": {
        "Tyler": {
            "salary": 7.25 # or "7.25" if done from terminal
        }
    }
}

get

python:

from cabinet import Cabinet

cab = Cabinet()

print(cab.get("employee", "Tyler", "salary"))

# or cab.get("employee", "Tyler", "salary", is_print = True)

or terminal:

cabinet -g employee Tyler salary
# Dotted paths work too (expanded to nested keys):
cabinet -g employee.Tyler.salary
  • optional: --force-cache-update to force a cache update

results in:

7.25

remove

python:

from cabinet import Cabinet

cab = Cabinet()

cab.remove("employee", "Tyler", "salary")

or terminal:

cabinet -rm employee Tyler salary

results in this structure in MongoDB (local data.json follows the same shape):

{
    "employee": {
        "Tyler": {}
    }
}

When mongodb_enabled is false, cab.remove() / cabinet --remove do not edit ~/.cabinet/data.json; Cabinet prints that removal requires MongoDB. Use cabinet -e, cabinet --edit-file, or edit the JSON file directly.

export

Writes a timestamped JSON file under ~/.cabinet/export/.

  • MongoDB: Exports the same JSON snapshot used for the editor cache (collection dump).
  • Local: Reads ~/.cabinet/data.json and writes pretty-printed JSON.

python:

from cabinet import Cabinet

Cabinet().export()

terminal:

cabinet --export

Appends a value to an existing string (concatenation) or array. For other types (bool, int, float, etc.), prints a graceful error. Prints the updated value.

python:

from cabinet import Cabinet

cab = Cabinet()

# Append to array: ['apple'] -> ['apple', 'banana']
cab.put("fruits", ["apple"])
cab.append("fruits", "banana", is_print=True)

# Append to string: 'apple' -> 'applebanana'
cab.put("fruits", "apple")
cab.append("fruits", "banana", is_print=True)

or terminal:

# Append to array: ['apple'] -> ['apple', 'banana']
cabinet -p fruits '["apple"]'
cabinet -a fruits banana

# Append to string: 'apple' -> 'applebanana'
cabinet -p fruits apple
cabinet -a fruits banana

# Nested paths
cabinet -a person tyler fruits banana
  • Arrays: Adds the value as a new element. ['apple']['apple', 'banana']
  • Strings: Concatenates. 'apple''applebanana'
  • Other types: Not allowed.
  • Missing key: Not allowed.

edit

  • With MongoDB, opens the JSON cache file that mirrors the cabinet collection; on save, changes are written back to MongoDB.
  • With local storage, opens ~/.cabinet/data.json.

terminal:

# opens file in the default editor (`cabinet --configure` -> 'editor'), saves upon exit
cabinet -e

# or

cabinet --edit

# you can add an 'editor':

cabinet -e --editor=code

edit_file

python:

from cabinet import Cabinet

cab = Cabinet()

# After `path.edit.shopping.value` is set (e.g. `cabinet -p edit shopping value "~/path/to/shopping.md"`), this edits that file:

# opens file in the default editor (`cabinet --configure` -> 'editor'), saves upon exit
cab.edit("shopping")

# or you can edit a file directly...
cab.edit("/path/to/shopping.md")

# you can pass an 'editor' to override the default:
cab.edit("/path/to/shopping.md", editor="nvim")

terminal:

# assumes path -> edit -> shopping -> path/to/shopping.md has been set
cabinet -ef shopping

# or

cabinet -ef "/path/to/shopping.md"

Sending mail

from cabinet import Mail

mail = Mail()

# Returns True on success, False on failure (e.g. SMTP exhausted retries)
mail.send('Test Subject', 'Test Body')

# Optional per-call overrides for retry behavior
mail.send(
    'Test Subject',
    'Test Body',
    max_retries=2,
    retry_base_delay=60,
)

terminal:

cabinet --mail --subject "Test Subject" --body "Test Body"

# or

cabinet --mail -s "Test Subject" -b "Test Body"

# optional recipients (comma-separated); otherwise `email.to` from Cabinet is used

cabinet --mail -s "Hi" -b "Hello" -t "one@example.com, two@example.com"

If --to / -t is omitted, email.to from Cabinet data is used (see mail configuration).

Sending Telegram

import cabinet

# Returns True on success, False on failure (missing config, HTTP error, …)
cabinet.telegram("Dinner is ready")

from cabinet import Telegram

Telegram().send("Dinner is ready")

# Optional per-call chat override
Telegram().send("Just you", target="1234567890")

terminal:

cabinet --telegram "Dinner is ready"

If target is omitted, telegram.target from Cabinet data is used (see telegram configuration).

**cab.log()** / **cabinet --log** write only to local files (classic **.log** lines). There are no network calls. If **logging.loki_enabled** is true in config.json, each event also appends a structured line to a matching local .jsonl file for Promtail on that host to ship to central Loki. Failures to write are printed to stderr and do not propagate as exceptions from log().

MongoDB is only for Cabinet’s **get / put** data. Logs are files (and optional Loki).

from cabinet import Cabinet

cab = Cabinet()

# Always: daily file under path_dir_log, e.g. ~/.cabinet/log/<YYYY-MM-DD>/LOG_DAILY_<date>.log
# If logging.loki_enabled: also ~/.cabinet/log/<YYYY-MM-DD>/LOG_DAILY_<date>.jsonl
cab.log("Connection timed out")  # defaults to info if no level is set
cab.log("Debug breakpoint", level="debug")

cab.log("This function hit a breakpoint", level="debug")
cab.log("Looks like the server is on fire", level="critical")
cab.log("This is fine", level="info")

# Log with tags (optional list of strings)
cab.log("Checked weather successfully", tags=["weather"])
cab.log("Starting Borg Backup...", tags=["backup", "start"])
cab.log("Pruning repository", tags=["backup", "prune"])
cab.log("Compacting repository", tags=["backup", "compact"])

# Custom daily log name / folder (file mode)
cab.log("30", log_name="LOG_TEMPERATURE")
cab.log("30", log_name="LOG_TEMPERATURE", log_folder_path="~/weather")

    # file format (without tags)
    # 2025-10-28 17:01:01,858 — INFO -> tools/weather.py:34@{hostname} -> Checking weather

    # file format (with tags)
    # 2025-09-27 02:01:09,012 — INFO [weather] -> tools/weather.py:116@cloud -> Checked weather successfully

terminal:

# Same file routing as Python (optional JSONL if logging.loki_enabled in config)
cabinet -l "Connection timed out"
cabinet --log "Connection timed out"
cabinet --log "Server is on fire" --level "critical"

cabinet --log "Checked weather successfully" --tags "weather"
cabinet --log "Starting Borg Backup..." --tags "backup,start"
cabinet --log "Pruning repository" --level "info" --tags "backup,prune"

log_query

Query log files under **path_dir_log** by tags, path, hostname, level, date, and message. Optional **since** (datetime or timedelta) restricts to entries on or after that UTC cutoff (using each line’s parsed local time).

from datetime import timedelta
from cabinet import Cabinet

cab = Cabinet()

# Optional time window
results = cab.log_query(since=timedelta(days=7), level="ERROR")

results = cab.log_query(tags=["weather"], level="INFO")

# Query today's file (log_file defaults to today's LOG_DAILY_*.log)
results = cab.log_query(tags=["weather"])

# File mode: query a specific file name
results = cab.log_query("LOG_DAILY_2025-09-27.log", tags=["weather"])

# Query by log level (in today's log)
results = cab.log_query(level="ERROR")

# Query by message content (case-insensitive fuzzy search)
results = cab.log_query(message="repository")

# Query by path (case-insensitive fuzzy search on file path after arrow)
results = cab.log_query(path="cabinet")

# Query by hostname
results = cab.log_query(hostname="cloud")

# Query by date (filters by timestamp in the log entry)
results = cab.log_query(date_filter="2025-09-27")

# Combine multiple filters on today's log
results = cab.log_query(
    tags=["backup"],
    level="INFO",
    message="repository"
)

# Combine multiple filters on specific log file
results = cab.log_query(
    "LOG_DAILY_2025-09-27.log",
    tags=["backup"],
    level="INFO",
    message="repository"
)

terminal:

# Query today's log (defaults to today if no log file specified)
cabinet --query --query-tags "weather"

# Short form
cabinet -q --query-tags "backup"

# Query by level
cabinet --query --query-level "ERROR"

# Query by message content
cabinet --query --query-message "repository"

# Query by path (fuzzy search)
cabinet --query --query-path "tools"

# Query by hostname
cabinet --query --query-hostname "cloud"

# Query by date
cabinet --query --query-date "2025-10-28"

# Combine multiple filters
cabinet --query --query-tags "backup" --query-level "INFO"

# Query specific log file
cabinet --query "LOG_DAILY_2025-09-27.log" --query-tags "weather"

# Complex query with multiple filters
cabinet -q "LOG_DAILY_2025-09-27.log" --query-tags "backup,weather" --query-level "INFO" --query-message "repository"

log_query_issues

Returns formatted log lines at WARNING, ERROR, or CRITICAL within since (timedelta or UTC-aware datetime; default: last 24 hours). Scans today’s and yesterday’s daily **.log** files under **path_dir_log** (same line format as **log_query**).

from datetime import timedelta
from cabinet import Cabinet

cab = Cabinet()

for line in cab.log_query_issues():
    print(line)

# Custom window
lines = cab.log_query_issues(since=timedelta(days=2))

log_query_loki, log_query_documents_loki, log_query_issues_loki

Loki-backed queries (HTTP to **logging.loki_url**). Same filter ideas as **log_query** and **log_query_issues**: since / end, level, hostname, tags, path (source file substring), message, date_filter, log_file (matches Promtail’s **filename** label when present), limit. Returns formatted lines or **dict** rows with **timestamp** as **datetime** (UTC) plus **_loki** metadata.

from datetime import timedelta
from cabinet import Cabinet

cab = Cabinet()

lines = cab.log_query_loki(level="error", since=timedelta(days=7), limit=100)
rows = cab.log_query_documents_loki(since=timedelta(hours=24), limit=50)
issues = cab.log_query_issues_loki(since=timedelta(hours=24))

logdb

Removed in 3.0.0 — see Breaking changes in 3.0.0. Use **cab.log(...)**, **cab.log_query()** / **cab.log_query_issues()**, and the **cab.log_query*_loki()** helpers instead.

UI Module

The cabinet.ui submodule (src/cabinet/ui.py) provides interactive command-line components built on prompt_toolkit:

from cabinet.ui import list_selection, render_html, confirmation

# List selection
items = ["Option 1", "Option 2", "Option 3"]
selected_index = list_selection(items, "Choose an option:")

# HTML rendering
render_html("<b>Bold text</b> and <i>italic text</i>")

# Confirmation dialog
result = confirmation("Do you want to proceed?", "Confirmation")

Disclaimers

  • Although I've done quite a bit of testing, I can't guarantee everything that works on my machine will work on yours. Always back up your data to multiple places to avoid data loss.
  • If you find any issues, please contact me... or get your hands dirty and raise a PR!

Author

Release files for cabinet 1!3.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for cabinet 1!3.1.0
File Size Uploaded
cabinet-1!3.1.0.tar.gz 68.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for cabinet 1!3.1.0
File Interpreter ABI Platform
cabinet-1!3.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 113.4 kB

Release files / cabinet-1!3.1.0.tar.gz

Download URL cabinet-1!3.1.0.tar.gz
Size 68.3 kB
Tags Source
SHA-256 checksum
How to use checksums
b08c88ea726b141201153290670bec3c4cac3a7ed1048361ef04b980de220be6
BLAKE2b-256 checksum
How to use checksums
46d5f8cdd49327ae03dc665573fd667b573c1b5cbcc7bfc71133284f969918c8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.14

Release files / cabinet-1!3.1.0-py3-none-any.whl

Download URL cabinet-1!3.1.0-py3-none-any.whl
Size 45.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
51bd1e74e6959be4f92e99c74d8eb65f7ebc4c1a5600facb86237613c3e1a42c
BLAKE2b-256 checksum
How to use checksums
f3a245f8b30d9478a653798b57144c453e452bff03881840a6e2bdaea14d16ed
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.14

Release history Release notifications | RSS feed

This release

1!3.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page