imbue-mngr-usage 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- imbue_mngr_usage-0.1.0/.gitignore +346 -0
- imbue_mngr_usage-0.1.0/CHANGELOG.md +26 -0
- imbue_mngr_usage-0.1.0/PKG-INFO +140 -0
- imbue_mngr_usage-0.1.0/README.md +129 -0
- imbue_mngr_usage-0.1.0/UNABRIDGED_CHANGELOG.md +149 -0
- imbue_mngr_usage-0.1.0/changelog/.gitkeep +0 -0
- imbue_mngr_usage-0.1.0/conftest.py +15 -0
- imbue_mngr_usage-0.1.0/docs/cron_recipes.md +222 -0
- imbue_mngr_usage-0.1.0/imbue/mngr_usage/__init__.py +0 -0
- imbue_mngr_usage-0.1.0/imbue/mngr_usage/api.py +876 -0
- imbue_mngr_usage-0.1.0/imbue/mngr_usage/api_test.py +402 -0
- imbue_mngr_usage-0.1.0/imbue/mngr_usage/cli.py +1097 -0
- imbue_mngr_usage-0.1.0/imbue/mngr_usage/cli_test.py +1934 -0
- imbue_mngr_usage-0.1.0/imbue/mngr_usage/conftest.py +26 -0
- imbue_mngr_usage-0.1.0/imbue/mngr_usage/data_types.py +322 -0
- imbue_mngr_usage-0.1.0/imbue/mngr_usage/plugin.py +64 -0
- imbue_mngr_usage-0.1.0/imbue/mngr_usage/plugin_test.py +24 -0
- imbue_mngr_usage-0.1.0/imbue/mngr_usage/resources/__init__.py +0 -0
- imbue_mngr_usage-0.1.0/imbue/mngr_usage/test_ratchets.py +278 -0
- imbue_mngr_usage-0.1.0/pyproject.toml +78 -0
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
**/__pycache__/
|
|
3
|
+
**/*.py[codz]
|
|
4
|
+
**/*$py.class
|
|
5
|
+
|
|
6
|
+
# macOS Finder metadata
|
|
7
|
+
**/.DS_Store
|
|
8
|
+
|
|
9
|
+
# C extensions
|
|
10
|
+
**/*.so
|
|
11
|
+
|
|
12
|
+
# Distribution / packaging
|
|
13
|
+
**/.Python
|
|
14
|
+
**/build/
|
|
15
|
+
**/develop-eggs/
|
|
16
|
+
**/dist/
|
|
17
|
+
**/downloads/
|
|
18
|
+
**/eggs/
|
|
19
|
+
**/.eggs/
|
|
20
|
+
**/lib/
|
|
21
|
+
**/lib64/
|
|
22
|
+
**/parts/
|
|
23
|
+
**/sdist/
|
|
24
|
+
**/var/
|
|
25
|
+
**/wheels/
|
|
26
|
+
**/share/python-wheels/
|
|
27
|
+
**/*.egg-info/
|
|
28
|
+
**/.installed.cfg
|
|
29
|
+
**/*.egg
|
|
30
|
+
**/MANIFEST
|
|
31
|
+
|
|
32
|
+
# PyInstaller
|
|
33
|
+
# Usually these files are written by a python script from a template
|
|
34
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
35
|
+
**/*.manifest
|
|
36
|
+
**/*.spec
|
|
37
|
+
|
|
38
|
+
# Installer logs
|
|
39
|
+
**/pip-log.txt
|
|
40
|
+
**/pip-delete-this-directory.txt
|
|
41
|
+
|
|
42
|
+
# Unit test / coverage reports
|
|
43
|
+
**/htmlcov/
|
|
44
|
+
**/.tox/
|
|
45
|
+
**/.nox/
|
|
46
|
+
**/.coverage
|
|
47
|
+
**/.coverage.*
|
|
48
|
+
**/.cache
|
|
49
|
+
**/nosetests.xml
|
|
50
|
+
**/coverage.xml
|
|
51
|
+
**/*.cover
|
|
52
|
+
**/*.py.cover
|
|
53
|
+
**/.hypothesis/
|
|
54
|
+
**/.pytest_cache/
|
|
55
|
+
**/cover/
|
|
56
|
+
|
|
57
|
+
# Translations
|
|
58
|
+
**/*.mo
|
|
59
|
+
**/*.pot
|
|
60
|
+
|
|
61
|
+
# Django stuff:
|
|
62
|
+
**/*.log
|
|
63
|
+
**/local_settings.py
|
|
64
|
+
**/db.sqlite3
|
|
65
|
+
**/db.sqlite3-journal
|
|
66
|
+
|
|
67
|
+
# Flask stuff:
|
|
68
|
+
**/instance/
|
|
69
|
+
**/.webassets-cache
|
|
70
|
+
|
|
71
|
+
# Scrapy stuff:
|
|
72
|
+
**/.scrapy
|
|
73
|
+
|
|
74
|
+
# Sphinx documentation
|
|
75
|
+
**/docs/_build/
|
|
76
|
+
|
|
77
|
+
# PyBuilder
|
|
78
|
+
**/.pybuilder/
|
|
79
|
+
**/target/
|
|
80
|
+
|
|
81
|
+
# Jupyter Notebook
|
|
82
|
+
**/.ipynb_checkpoints
|
|
83
|
+
|
|
84
|
+
# IPython
|
|
85
|
+
**/profile_default/
|
|
86
|
+
**/ipython_config.py
|
|
87
|
+
|
|
88
|
+
# pyenv
|
|
89
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
90
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
91
|
+
# .python-version
|
|
92
|
+
|
|
93
|
+
# pipenv
|
|
94
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
95
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
96
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
97
|
+
# install all needed dependencies.
|
|
98
|
+
#Pipfile.lock
|
|
99
|
+
|
|
100
|
+
# UV
|
|
101
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
102
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
103
|
+
# commonly ignored for libraries.
|
|
104
|
+
#uv.lock
|
|
105
|
+
|
|
106
|
+
# poetry
|
|
107
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
108
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
109
|
+
# commonly ignored for libraries.
|
|
110
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
111
|
+
#poetry.lock
|
|
112
|
+
#poetry.toml
|
|
113
|
+
|
|
114
|
+
# pdm
|
|
115
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
116
|
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
|
117
|
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
|
118
|
+
#pdm.lock
|
|
119
|
+
#pdm.toml
|
|
120
|
+
**/.pdm-python
|
|
121
|
+
**/.pdm-build/
|
|
122
|
+
|
|
123
|
+
# pixi
|
|
124
|
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
|
125
|
+
#pixi.lock
|
|
126
|
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
|
127
|
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
|
128
|
+
**/.pixi
|
|
129
|
+
|
|
130
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
131
|
+
**/__pypackages__/
|
|
132
|
+
|
|
133
|
+
# Celery stuff
|
|
134
|
+
**/celerybeat-schedule
|
|
135
|
+
**/celerybeat.pid
|
|
136
|
+
|
|
137
|
+
# SageMath parsed files
|
|
138
|
+
**/*.sage.py
|
|
139
|
+
|
|
140
|
+
# Environments
|
|
141
|
+
**/.env
|
|
142
|
+
**/.envrc
|
|
143
|
+
**/.venv
|
|
144
|
+
**/.venv-*
|
|
145
|
+
**/env/
|
|
146
|
+
**/venv/
|
|
147
|
+
**/ENV/
|
|
148
|
+
**/env.bak/
|
|
149
|
+
**/venv.bak/
|
|
150
|
+
|
|
151
|
+
# Spyder project settings
|
|
152
|
+
**/.spyderproject
|
|
153
|
+
**/.spyproject
|
|
154
|
+
|
|
155
|
+
# Rope project settings
|
|
156
|
+
**/.ropeproject
|
|
157
|
+
|
|
158
|
+
# mkdocs documentation
|
|
159
|
+
**/site
|
|
160
|
+
|
|
161
|
+
# mypy
|
|
162
|
+
**/.mypy_cache/
|
|
163
|
+
**/.dmypy.json
|
|
164
|
+
**/dmypy.json
|
|
165
|
+
|
|
166
|
+
# Pyre type checker
|
|
167
|
+
**/.pyre/
|
|
168
|
+
|
|
169
|
+
# pytype static type analyzer
|
|
170
|
+
**/.pytype/
|
|
171
|
+
|
|
172
|
+
# Cython debug symbols
|
|
173
|
+
**/cython_debug/
|
|
174
|
+
|
|
175
|
+
# PyCharm
|
|
176
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
177
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
178
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
179
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
180
|
+
**/.idea/
|
|
181
|
+
|
|
182
|
+
# Abstra
|
|
183
|
+
# Abstra is an AI-powered process automation framework.
|
|
184
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
185
|
+
# Learn more at https://abstra.io/docs
|
|
186
|
+
**/.abstra/
|
|
187
|
+
|
|
188
|
+
# Visual Studio Code
|
|
189
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
|
190
|
+
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
191
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
|
192
|
+
# you could uncomment the following to ignore the entire vscode folder
|
|
193
|
+
# .vscode/
|
|
194
|
+
|
|
195
|
+
# Ruff stuff:
|
|
196
|
+
**/.ruff_cache/
|
|
197
|
+
|
|
198
|
+
# PyPI configuration file
|
|
199
|
+
**/.pypirc
|
|
200
|
+
|
|
201
|
+
# Cursor
|
|
202
|
+
# Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
|
|
203
|
+
# exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
|
|
204
|
+
# refer to https://docs.cursor.com/context/ignore-files
|
|
205
|
+
**/.cursorignore
|
|
206
|
+
**/.cursorindexingignore
|
|
207
|
+
|
|
208
|
+
# Marimo
|
|
209
|
+
**/marimo/_static/
|
|
210
|
+
**/marimo/_lsp/
|
|
211
|
+
**/__marimo__/
|
|
212
|
+
|
|
213
|
+
# task folders for local custom claude workflow
|
|
214
|
+
*/*/_tasks/
|
|
215
|
+
**/.sesskey
|
|
216
|
+
|
|
217
|
+
# Node.js
|
|
218
|
+
**/node_modules/
|
|
219
|
+
|
|
220
|
+
# Frontend build artifacts
|
|
221
|
+
**/frontend-dist/
|
|
222
|
+
|
|
223
|
+
# Claude Code local settings (session-specific permissions)
|
|
224
|
+
**/.claude/settings.local.json
|
|
225
|
+
|
|
226
|
+
# Local Claude settings backup files
|
|
227
|
+
**/.claude/*.bak
|
|
228
|
+
|
|
229
|
+
# Claude worktress
|
|
230
|
+
**/.claude/worktrees
|
|
231
|
+
|
|
232
|
+
# Agent definitions deployed by mngr_subagent_proxy at session start
|
|
233
|
+
**/.claude/agents/
|
|
234
|
+
|
|
235
|
+
# Local mngr settings (not committed)
|
|
236
|
+
**/.mngr/settings.local.toml
|
|
237
|
+
|
|
238
|
+
# Local working / scratch markdown (not committed)
|
|
239
|
+
**/*.local.md
|
|
240
|
+
|
|
241
|
+
# Local working / scratch shell scripts (not committed)
|
|
242
|
+
# (covers user notification scripts like scripts/notify_user.local.sh, too)
|
|
243
|
+
**/*.local.sh
|
|
244
|
+
|
|
245
|
+
# Test output files (slow tests, coverage summaries)
|
|
246
|
+
**/.test_output/
|
|
247
|
+
|
|
248
|
+
# TMR output directories (reports + test outputs)
|
|
249
|
+
**/tmr_*/
|
|
250
|
+
|
|
251
|
+
# Active session marker file (used to detect interrupted sessions)
|
|
252
|
+
**/.claude/active
|
|
253
|
+
|
|
254
|
+
# we stick the image build artifacts here
|
|
255
|
+
**/.mngr/dev/build/
|
|
256
|
+
**/.mngr/dev/secrets/
|
|
257
|
+
|
|
258
|
+
# Offload caches and local files
|
|
259
|
+
**/.offload/**
|
|
260
|
+
**/test-results
|
|
261
|
+
|
|
262
|
+
# Minds deploy-time build artifacts.
|
|
263
|
+
# Ignore contents so we can un-ignore the committed `template/` subtree: git
|
|
264
|
+
# won't descend into a fully-ignored directory, so negations must live below
|
|
265
|
+
# the contents pattern rather than below the directory pattern.
|
|
266
|
+
**/.minds/*
|
|
267
|
+
# The template directory is committed: it defines the expected keys for each
|
|
268
|
+
# <service>.sh file, with empty values. `scripts/push_modal_secrets.py` uses
|
|
269
|
+
# it to check that per-env .sh files declare every template key.
|
|
270
|
+
!/.minds/template/
|
|
271
|
+
!/.minds/template/**
|
|
272
|
+
# Vault ACL policies for the minds tiers. The HCL is non-sensitive and is
|
|
273
|
+
# the source of truth for the policies pushed to Vault via
|
|
274
|
+
# `vault policy write <name> .minds/policies/<name>.hcl`.
|
|
275
|
+
!/.minds/policies/
|
|
276
|
+
!/.minds/policies/**
|
|
277
|
+
|
|
278
|
+
# Autofix session artifacts
|
|
279
|
+
**/.autofix/
|
|
280
|
+
**/.reviewer/logs/
|
|
281
|
+
**/.reviewer/outputs/
|
|
282
|
+
**/.reviewer/settings.local.json
|
|
283
|
+
**/.reviewer/.stop_hook_consecutive_blocks
|
|
284
|
+
**/.reviewer/conversation-issues.jsonl
|
|
285
|
+
**/.reviews/
|
|
286
|
+
|
|
287
|
+
# Stuck agent tracking (stop hook escape valve)
|
|
288
|
+
**/.claude/blocked_stop_commits
|
|
289
|
+
|
|
290
|
+
# Scheduled tasks lock file
|
|
291
|
+
**/.claude/scheduled_tasks.lock
|
|
292
|
+
|
|
293
|
+
# Demo recordings (asciinema .cast, .txt, and scripts)
|
|
294
|
+
**/.demos/
|
|
295
|
+
|
|
296
|
+
# for git worktrees from other repos
|
|
297
|
+
**/.external_worktrees/
|
|
298
|
+
|
|
299
|
+
# ignore coverage files generated within subprojects
|
|
300
|
+
**/cov.xml
|
|
301
|
+
|
|
302
|
+
# ignore demos
|
|
303
|
+
**/recordings
|
|
304
|
+
|
|
305
|
+
# Generated by the _generate-dockerignore justfile recipe before each
|
|
306
|
+
# test-offload* run. The recipe strips this self-reference so offload's
|
|
307
|
+
# copy_untracked_files ships the generated file into the build context.
|
|
308
|
+
/.dockerignore
|
|
309
|
+
|
|
310
|
+
# Transient offload image-cache marker written by test-offload runs; not
|
|
311
|
+
# checked in (unlike the committed .offload-*-cache-key files which pin CI
|
|
312
|
+
# image versions).
|
|
313
|
+
/.offload-image-cache
|
|
314
|
+
|
|
315
|
+
# Generated by _generate-release-dockerfile from Dockerfile + .release.extras.
|
|
316
|
+
/libs/mngr/imbue/mngr/resources/Dockerfile.release
|
|
317
|
+
**/.reviewer/autofix/issues/
|
|
318
|
+
.reviewer/logs/
|
|
319
|
+
|
|
320
|
+
# Tailwind Play CDN JS for the minds desktop client. Fetched once via
|
|
321
|
+
# `just minds-tailwind` (plain curl, no build) and shipped with the
|
|
322
|
+
# distributable. Not checked in because it's a ~300KB blob we can always
|
|
323
|
+
# re-fetch.
|
|
324
|
+
/apps/minds/imbue/minds/desktop_client/static/tailwind.js
|
|
325
|
+
**/.env.swp
|
|
326
|
+
# Vim swap files for any file (`.foo.swp`, `.foo.swo`, etc.) -- generated
|
|
327
|
+
# while a file is open in vim. Never checked in.
|
|
328
|
+
**/.*.sw[a-p]
|
|
329
|
+
|
|
330
|
+
# `minds env deploy` writes a recover-target file at the monorepo root
|
|
331
|
+
# on failed deploys; `minds env recover` deletes it after rolling back.
|
|
332
|
+
# Never checked in. Per-env naming
|
|
333
|
+
# (``.minds-deploy-recover-target-<env-name>.json``) means each in-flight
|
|
334
|
+
# deploy has its own file, so concurrent deploys against different envs
|
|
335
|
+
# don't trip over each other.
|
|
336
|
+
**/.minds-deploy-*.json
|
|
337
|
+
# Per-env deploy lock files (kernel-level ``flock`` targets). Created
|
|
338
|
+
# by ``hold_deploy_lock`` in ``apps/minds/imbue/minds/envs/recover.py``
|
|
339
|
+
# and held for the entire duration of ``minds env deploy`` /
|
|
340
|
+
# ``minds env recover`` so two concurrent invocations against the same
|
|
341
|
+
# env serialize. The file itself is empty and cheap to leave around.
|
|
342
|
+
**/.minds-deploy-lock-*.lock
|
|
343
|
+
**/.antigravitycli/
|
|
344
|
+
|
|
345
|
+
# Playwright MCP scratch artifacts (mock screenshots, page snapshots)
|
|
346
|
+
**/.playwright-mcp/
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Changelog - mngr_usage
|
|
2
|
+
|
|
3
|
+
A concise, human-friendly summary of changes for the `mngr_usage` library. Entries are categorized using the [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) categories: Added, Changed, Deprecated, Removed, Fixed, Security.
|
|
4
|
+
|
|
5
|
+
For the full, unedited changelog entries, see [UNABRIDGED_CHANGELOG.md](UNABRIDGED_CHANGELOG.md).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [v0.1.0] - 2026-06-05
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Added: `mngr usage` per-session cost aggregation with separate `subscription_cost` / `api_cost` aggregates, `--since`, `--detail`, and CEL/format-template surfaces; cost tracking now works for direct `ANTHROPIC_API_KEY` users.
|
|
14
|
+
- Added: New `docs/cron_recipes.md` "cron automation recipes" doc (linked from the README) with worked examples of driving `mngr` from cron using check mode (`mngr usage --format json`) rather than the blocking `mngr usage wait`, plus a shared `spare-capacity.sh` helper (exit 0 when the 5h window still has budget and the week is under pace). Worked examples cover: using up an about-to-expire 5h window via a dedicated agent's full lifecycle, warming a fresh 5h window early so it resets partway through your work, and dispatching a queue of task files capped by a shared `queue=live` label.
|
|
15
|
+
- Added: The usage plugin contributes its cron-recipes documentation as a `mngr help usage_cron_recipes` topic via mngr's new `register_help_topics` hook. The topic body is the plugin's `cron_recipes.md`, shipped inside the wheel via `force-include`, and its `DocFile` carries a GitHub `source_url` so relative links (e.g. `[Waiting on a predicate](../README.md#waiting-on-a-predicate)`) are rewritten to clickable absolute GitHub URLs in an interactive terminal.
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- Changed: Added to the release tooling's publish graph (`scripts/utils.py`); will be offered for first publication to PyPI on the next release. Stale `imbue-mngr==0.2.6` pin in `pyproject.toml` is realigned to the current `0.2.10`. No runtime change.
|
|
20
|
+
|
|
21
|
+
## [v0.2.8] - 2026-05-13
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- Added: New `mngr usage` command reporting Claude Code's rolling 5h / 7d / overage quota usage with `human`/`json`/`jsonl` formats and the standard agent-filter flags.
|
|
26
|
+
- Added: New `mngr usage wait --until <CEL>` command that blocks until a usage snapshot matches a predicate, with exit codes mirroring `mngr wait`.
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: imbue-mngr-usage
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: mngr usage subcommand: agent-agnostic rolling-window quota / usage CLI (provider plugins implement the data side via the current_usage_snapshot hook)
|
|
5
|
+
Requires-Python: >=3.12
|
|
6
|
+
Requires-Dist: click-option-group>=0.5.6
|
|
7
|
+
Requires-Dist: click>=8.0
|
|
8
|
+
Requires-Dist: imbue-mngr==0.2.11
|
|
9
|
+
Requires-Dist: pydantic>=2.0
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
|
|
12
|
+
# imbue-mngr-usage
|
|
13
|
+
|
|
14
|
+
`mngr usage` -- agent-agnostic CLI for rolling-window usage / quota data.
|
|
15
|
+
|
|
16
|
+
## What it does
|
|
17
|
+
|
|
18
|
+
Provides a single `mngr usage` command that surfaces rolling-window usage data
|
|
19
|
+
in human / json / jsonl / format-template output. The command itself knows
|
|
20
|
+
nothing about any specific agent type or provider; it walks events files on
|
|
21
|
+
disk and renders whatever it finds.
|
|
22
|
+
|
|
23
|
+
## Architecture
|
|
24
|
+
|
|
25
|
+
This package contains:
|
|
26
|
+
|
|
27
|
+
- The `mngr usage` CLI command and its rendering helpers.
|
|
28
|
+
- The `UsageSnapshot`, `WindowSnapshot`, and `CostSnapshot` data types.
|
|
29
|
+
|
|
30
|
+
Discovery is by path convention. The CLI walks
|
|
31
|
+
`<host_dir>/agents/*/events/<source>/usage/events.jsonl` (the same shape
|
|
32
|
+
`mngr transcript` uses for `events/<source>/common_transcript/...`), scans
|
|
33
|
+
every event line, and aggregates per `<source>`: rate-limit windows
|
|
34
|
+
collapse freshest-wins (the account-level counter), while cost is grouped
|
|
35
|
+
per `session_id` and filtered to a recency window (`--since`, default 24h).
|
|
36
|
+
The `<source>` segment is free-form -- whatever the writer plugin chose.
|
|
37
|
+
|
|
38
|
+
When multiple writers contribute, each renders as its own `[source]` section in
|
|
39
|
+
human output and as an entry in the JSON `sources` array.
|
|
40
|
+
|
|
41
|
+
## Output formats
|
|
42
|
+
|
|
43
|
+
- `mngr usage` (human summary: per-mode cost line(s) + window lines)
|
|
44
|
+
- `mngr usage --detail` (human + per-session breakdown lines)
|
|
45
|
+
- `mngr usage --format json` (summary JSON: per-mode aggregates `subscription_cost` and `api_cost`, `session_count` plus per-mode counts, windows)
|
|
46
|
+
- `mngr usage --format json --detail` (JSON with `sessions[]` per source; each session carries a `cost_mode` tag)
|
|
47
|
+
- `mngr usage --format jsonl`
|
|
48
|
+
- `mngr usage --format '5h:{five_hour.used_percentage}%/{seven_day.used_percentage}%'`
|
|
49
|
+
|
|
50
|
+
The `--detail` flag is independent of `--verbose` (which controls log
|
|
51
|
+
level). It toggles only the per-session breakdown surfaces.
|
|
52
|
+
|
|
53
|
+
## Waiting on a predicate
|
|
54
|
+
|
|
55
|
+
`mngr usage wait --until <CEL>` blocks until at least one source's CEL
|
|
56
|
+
context satisfies every `--until` expression, then exits 0. Composable with
|
|
57
|
+
shell:
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
mngr usage wait --until 'five_hour.elapsed_percentage > 75 && five_hour.used_percentage < 50' \
|
|
61
|
+
&& mngr message my-agent "ok, kick off the next batch"
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The CEL context per source mirrors one entry of `mngr usage --format json`'s
|
|
65
|
+
`sources` array. Each window exposes the writer-emitted fields
|
|
66
|
+
(`used_percentage`, `resets_at`, `window_seconds`, `label`, ...) plus the
|
|
67
|
+
reader-derived `seconds_until_reset`, `elapsed_seconds`, and
|
|
68
|
+
`elapsed_percentage` (the last two require `window_seconds` from the
|
|
69
|
+
writer; absent on variable-duration windows like Claude's overage).
|
|
70
|
+
Source-level fields are exposed too. Cost is split by auth mode and
|
|
71
|
+
**never lumped**: `subscription_cost.*` aggregates sessions whose Claude
|
|
72
|
+
Code process was on a Claude.ai Pro/Max subscription (cost is **imputed**
|
|
73
|
+
by Claude Code -- the user actually pays a flat subscription), and
|
|
74
|
+
`api_cost.*` aggregates sessions whose process was on a direct
|
|
75
|
+
`ANTHROPIC_API_KEY` (cost is **real** billable spend). Pick the mode you
|
|
76
|
+
care about, e.g. `api_cost.total_cost_usd > 20.0` means "I've spent more
|
|
77
|
+
than $20 of real API money across recent sessions". `session_count` is
|
|
78
|
+
the total across both modes; `subscription_session_count` and
|
|
79
|
+
`api_session_count` break that down per mode. `since_seconds` and
|
|
80
|
+
`sessions[]` (every session in the window, newest-first, each carrying
|
|
81
|
+
a `cost_mode` of `"SUBSCRIPTION"` or `"API_KEY"`) are available as well.
|
|
82
|
+
Use `--since` to tighten or widen the aggregation window from its
|
|
83
|
+
default (24h). For a per-session predicate, index `sessions[]` directly
|
|
84
|
+
(e.g. `sessions[0].cost.total_cost_usd > 5` for the most recent
|
|
85
|
+
session's own contribution).
|
|
86
|
+
|
|
87
|
+
Exit codes mirror `mngr wait`: 0 matched, 1 error, 2 timeout. Default poll
|
|
88
|
+
interval is 30s; use `--interval` for tighter cadence. To restrict matching
|
|
89
|
+
to a specific writer, use the top-level `source` field in CEL (e.g.
|
|
90
|
+
`--until 'source == "claude" && five_hour.used_percentage < 50'`).
|
|
91
|
+
|
|
92
|
+
## Polling from cron (check mode)
|
|
93
|
+
|
|
94
|
+
For recurring automation, let `cron` own the cadence: poll the plain
|
|
95
|
+
`mngr usage --format json` snapshot on a schedule and branch in the shell. See
|
|
96
|
+
[cron automation recipes](docs/cron_recipes.md) for worked examples: using up an
|
|
97
|
+
about-to-expire 5h window (with a weekly pace check), warming a fresh window the
|
|
98
|
+
moment the last one elapses, and working through a queue of task files a couple
|
|
99
|
+
at a time.
|
|
100
|
+
|
|
101
|
+
## Implementing a writer plugin
|
|
102
|
+
|
|
103
|
+
A writer plugin is responsible for producing `cost_snapshot` events at the
|
|
104
|
+
conventional path. The minimal contract is just the JSONL line shape:
|
|
105
|
+
|
|
106
|
+
```jsonl
|
|
107
|
+
{"source":"<your-source>/usage","type":"cost_snapshot","event_id":"evt-<hex>","timestamp":"<ISO 8601>","session_id":"<uuid>","cost":{"total_cost_usd":<float>},"rate_limits":{"<window-key>":{"used_percentage":<float>,"resets_at":<unix-ts>}}}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Every event must carry a `session_id` (non-empty string) plus at least one
|
|
111
|
+
of `rate_limits` or `cost`. Events missing `session_id` are dropped with a
|
|
112
|
+
WARNING log naming the source and event_id -- so if you're writing a usage
|
|
113
|
+
plugin and don't see your data in `mngr usage`, check the log first. Any
|
|
114
|
+
plausible statusline source has a session identifier handy, so this is
|
|
115
|
+
rarely a constraint; if a future tool doesn't, synthesize a stable
|
|
116
|
+
per-session id on the writer side. The `type` field is informational --
|
|
117
|
+
the reader doesn't validate it -- but `"cost_snapshot"` is the current
|
|
118
|
+
convention.
|
|
119
|
+
|
|
120
|
+
Append one line per refresh to:
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
<agent_state_dir>/events/<your-source>/usage/events.jsonl
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
`mngr usage` will pick it up automatically -- no plugin registration with this
|
|
127
|
+
package required.
|
|
128
|
+
|
|
129
|
+
The writer chooses both the window keys and (optionally) per-window
|
|
130
|
+
`label`s. Keys are used by format templates (`{<key>.used_percentage}`) and
|
|
131
|
+
should be identifier-safe if you want format-template support; the per-window
|
|
132
|
+
`label` controls human display (e.g. `"5h"` vs the literal key `"five_hour"`).
|
|
133
|
+
Render order is the writer's insertion order in the JSONL.
|
|
134
|
+
|
|
135
|
+
Writers may also include `"window_seconds": <int>` per window to declare a
|
|
136
|
+
fixed window duration. When present, `mngr usage` derives `elapsed_seconds`
|
|
137
|
+
and `elapsed_percentage`, which `mngr usage wait` CEL predicates can use to
|
|
138
|
+
express "75% of the window has elapsed" without callers hardcoding window
|
|
139
|
+
durations. Omit `window_seconds` for windows without a fixed length (e.g.
|
|
140
|
+
Claude's overage indicator).
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# imbue-mngr-usage
|
|
2
|
+
|
|
3
|
+
`mngr usage` -- agent-agnostic CLI for rolling-window usage / quota data.
|
|
4
|
+
|
|
5
|
+
## What it does
|
|
6
|
+
|
|
7
|
+
Provides a single `mngr usage` command that surfaces rolling-window usage data
|
|
8
|
+
in human / json / jsonl / format-template output. The command itself knows
|
|
9
|
+
nothing about any specific agent type or provider; it walks events files on
|
|
10
|
+
disk and renders whatever it finds.
|
|
11
|
+
|
|
12
|
+
## Architecture
|
|
13
|
+
|
|
14
|
+
This package contains:
|
|
15
|
+
|
|
16
|
+
- The `mngr usage` CLI command and its rendering helpers.
|
|
17
|
+
- The `UsageSnapshot`, `WindowSnapshot`, and `CostSnapshot` data types.
|
|
18
|
+
|
|
19
|
+
Discovery is by path convention. The CLI walks
|
|
20
|
+
`<host_dir>/agents/*/events/<source>/usage/events.jsonl` (the same shape
|
|
21
|
+
`mngr transcript` uses for `events/<source>/common_transcript/...`), scans
|
|
22
|
+
every event line, and aggregates per `<source>`: rate-limit windows
|
|
23
|
+
collapse freshest-wins (the account-level counter), while cost is grouped
|
|
24
|
+
per `session_id` and filtered to a recency window (`--since`, default 24h).
|
|
25
|
+
The `<source>` segment is free-form -- whatever the writer plugin chose.
|
|
26
|
+
|
|
27
|
+
When multiple writers contribute, each renders as its own `[source]` section in
|
|
28
|
+
human output and as an entry in the JSON `sources` array.
|
|
29
|
+
|
|
30
|
+
## Output formats
|
|
31
|
+
|
|
32
|
+
- `mngr usage` (human summary: per-mode cost line(s) + window lines)
|
|
33
|
+
- `mngr usage --detail` (human + per-session breakdown lines)
|
|
34
|
+
- `mngr usage --format json` (summary JSON: per-mode aggregates `subscription_cost` and `api_cost`, `session_count` plus per-mode counts, windows)
|
|
35
|
+
- `mngr usage --format json --detail` (JSON with `sessions[]` per source; each session carries a `cost_mode` tag)
|
|
36
|
+
- `mngr usage --format jsonl`
|
|
37
|
+
- `mngr usage --format '5h:{five_hour.used_percentage}%/{seven_day.used_percentage}%'`
|
|
38
|
+
|
|
39
|
+
The `--detail` flag is independent of `--verbose` (which controls log
|
|
40
|
+
level). It toggles only the per-session breakdown surfaces.
|
|
41
|
+
|
|
42
|
+
## Waiting on a predicate
|
|
43
|
+
|
|
44
|
+
`mngr usage wait --until <CEL>` blocks until at least one source's CEL
|
|
45
|
+
context satisfies every `--until` expression, then exits 0. Composable with
|
|
46
|
+
shell:
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
mngr usage wait --until 'five_hour.elapsed_percentage > 75 && five_hour.used_percentage < 50' \
|
|
50
|
+
&& mngr message my-agent "ok, kick off the next batch"
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The CEL context per source mirrors one entry of `mngr usage --format json`'s
|
|
54
|
+
`sources` array. Each window exposes the writer-emitted fields
|
|
55
|
+
(`used_percentage`, `resets_at`, `window_seconds`, `label`, ...) plus the
|
|
56
|
+
reader-derived `seconds_until_reset`, `elapsed_seconds`, and
|
|
57
|
+
`elapsed_percentage` (the last two require `window_seconds` from the
|
|
58
|
+
writer; absent on variable-duration windows like Claude's overage).
|
|
59
|
+
Source-level fields are exposed too. Cost is split by auth mode and
|
|
60
|
+
**never lumped**: `subscription_cost.*` aggregates sessions whose Claude
|
|
61
|
+
Code process was on a Claude.ai Pro/Max subscription (cost is **imputed**
|
|
62
|
+
by Claude Code -- the user actually pays a flat subscription), and
|
|
63
|
+
`api_cost.*` aggregates sessions whose process was on a direct
|
|
64
|
+
`ANTHROPIC_API_KEY` (cost is **real** billable spend). Pick the mode you
|
|
65
|
+
care about, e.g. `api_cost.total_cost_usd > 20.0` means "I've spent more
|
|
66
|
+
than $20 of real API money across recent sessions". `session_count` is
|
|
67
|
+
the total across both modes; `subscription_session_count` and
|
|
68
|
+
`api_session_count` break that down per mode. `since_seconds` and
|
|
69
|
+
`sessions[]` (every session in the window, newest-first, each carrying
|
|
70
|
+
a `cost_mode` of `"SUBSCRIPTION"` or `"API_KEY"`) are available as well.
|
|
71
|
+
Use `--since` to tighten or widen the aggregation window from its
|
|
72
|
+
default (24h). For a per-session predicate, index `sessions[]` directly
|
|
73
|
+
(e.g. `sessions[0].cost.total_cost_usd > 5` for the most recent
|
|
74
|
+
session's own contribution).
|
|
75
|
+
|
|
76
|
+
Exit codes mirror `mngr wait`: 0 matched, 1 error, 2 timeout. Default poll
|
|
77
|
+
interval is 30s; use `--interval` for tighter cadence. To restrict matching
|
|
78
|
+
to a specific writer, use the top-level `source` field in CEL (e.g.
|
|
79
|
+
`--until 'source == "claude" && five_hour.used_percentage < 50'`).
|
|
80
|
+
|
|
81
|
+
## Polling from cron (check mode)
|
|
82
|
+
|
|
83
|
+
For recurring automation, let `cron` own the cadence: poll the plain
|
|
84
|
+
`mngr usage --format json` snapshot on a schedule and branch in the shell. See
|
|
85
|
+
[cron automation recipes](docs/cron_recipes.md) for worked examples: using up an
|
|
86
|
+
about-to-expire 5h window (with a weekly pace check), warming a fresh window the
|
|
87
|
+
moment the last one elapses, and working through a queue of task files a couple
|
|
88
|
+
at a time.
|
|
89
|
+
|
|
90
|
+
## Implementing a writer plugin
|
|
91
|
+
|
|
92
|
+
A writer plugin is responsible for producing `cost_snapshot` events at the
|
|
93
|
+
conventional path. The minimal contract is just the JSONL line shape:
|
|
94
|
+
|
|
95
|
+
```jsonl
|
|
96
|
+
{"source":"<your-source>/usage","type":"cost_snapshot","event_id":"evt-<hex>","timestamp":"<ISO 8601>","session_id":"<uuid>","cost":{"total_cost_usd":<float>},"rate_limits":{"<window-key>":{"used_percentage":<float>,"resets_at":<unix-ts>}}}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Every event must carry a `session_id` (non-empty string) plus at least one
|
|
100
|
+
of `rate_limits` or `cost`. Events missing `session_id` are dropped with a
|
|
101
|
+
WARNING log naming the source and event_id -- so if you're writing a usage
|
|
102
|
+
plugin and don't see your data in `mngr usage`, check the log first. Any
|
|
103
|
+
plausible statusline source has a session identifier handy, so this is
|
|
104
|
+
rarely a constraint; if a future tool doesn't, synthesize a stable
|
|
105
|
+
per-session id on the writer side. The `type` field is informational --
|
|
106
|
+
the reader doesn't validate it -- but `"cost_snapshot"` is the current
|
|
107
|
+
convention.
|
|
108
|
+
|
|
109
|
+
Append one line per refresh to:
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
<agent_state_dir>/events/<your-source>/usage/events.jsonl
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
`mngr usage` will pick it up automatically -- no plugin registration with this
|
|
116
|
+
package required.
|
|
117
|
+
|
|
118
|
+
The writer chooses both the window keys and (optionally) per-window
|
|
119
|
+
`label`s. Keys are used by format templates (`{<key>.used_percentage}`) and
|
|
120
|
+
should be identifier-safe if you want format-template support; the per-window
|
|
121
|
+
`label` controls human display (e.g. `"5h"` vs the literal key `"five_hour"`).
|
|
122
|
+
Render order is the writer's insertion order in the JSONL.
|
|
123
|
+
|
|
124
|
+
Writers may also include `"window_seconds": <int>` per window to declare a
|
|
125
|
+
fixed window duration. When present, `mngr usage` derives `elapsed_seconds`
|
|
126
|
+
and `elapsed_percentage`, which `mngr usage wait` CEL predicates can use to
|
|
127
|
+
express "75% of the window has elapsed" without callers hardcoding window
|
|
128
|
+
durations. Omit `window_seconds` for windows without a fixed length (e.g.
|
|
129
|
+
Claude's overage indicator).
|