agent-usage-manager 0.2.2__tar.gz → 0.2.4__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.
Files changed (20) hide show
  1. {agent_usage_manager-0.2.2 → agent_usage_manager-0.2.4}/PKG-INFO +30 -4
  2. {agent_usage_manager-0.2.2 → agent_usage_manager-0.2.4}/README.md +29 -3
  3. {agent_usage_manager-0.2.2 → agent_usage_manager-0.2.4}/agent_usage_manager/agents.yaml +4 -0
  4. {agent_usage_manager-0.2.2 → agent_usage_manager-0.2.4}/agent_usage_manager/cli.py +22 -1
  5. {agent_usage_manager-0.2.2 → agent_usage_manager-0.2.4}/agent_usage_manager/static/index.html +1 -0
  6. {agent_usage_manager-0.2.2 → agent_usage_manager-0.2.4}/pyproject.toml +1 -1
  7. {agent_usage_manager-0.2.2 → agent_usage_manager-0.2.4}/uv.lock +1 -1
  8. {agent_usage_manager-0.2.2 → agent_usage_manager-0.2.4}/.github/workflows/ci.yml +0 -0
  9. {agent_usage_manager-0.2.2 → agent_usage_manager-0.2.4}/.gitignore +0 -0
  10. {agent_usage_manager-0.2.2 → agent_usage_manager-0.2.4}/LICENSE +0 -0
  11. {agent_usage_manager-0.2.2 → agent_usage_manager-0.2.4}/agent_usage_manager/__init__.py +0 -0
  12. {agent_usage_manager-0.2.2 → agent_usage_manager-0.2.4}/agent_usage_manager/app.py +0 -0
  13. {agent_usage_manager-0.2.2 → agent_usage_manager-0.2.4}/demo.tape +0 -0
  14. {agent_usage_manager-0.2.2 → agent_usage_manager-0.2.4}/docs/dashboard.png +0 -0
  15. {agent_usage_manager-0.2.2 → agent_usage_manager-0.2.4}/docs/design/HLD.md +0 -0
  16. {agent_usage_manager-0.2.2 → agent_usage_manager-0.2.4}/docs/design/LLD.md +0 -0
  17. {agent_usage_manager-0.2.2 → agent_usage_manager-0.2.4}/requirements.txt +0 -0
  18. {agent_usage_manager-0.2.2 → agent_usage_manager-0.2.4}/run.sh +0 -0
  19. {agent_usage_manager-0.2.2 → agent_usage_manager-0.2.4}/tests/test_smoke.py +0 -0
  20. {agent_usage_manager-0.2.2 → agent_usage_manager-0.2.4}/tests/test_synthetic.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agent-usage-manager
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Summary: htop for AI agents — liveness, CPU/mem/GPU usage, and a kill switch for headless agents (openclaw, hermes, ollama, vllm, claude-code).
5
5
  Project-URL: Homepage, https://github.com/minglong51/agent-usage-manager
6
6
  Project-URL: Repository, https://github.com/minglong51/agent-usage-manager
@@ -19,14 +19,14 @@ Description-Content-Type: text/markdown
19
19
 
20
20
  # agent-usage-manager
21
21
 
22
- A tiny, single-file web dashboard for **headless AI agents** running on a machine —
22
+ A small web dashboard for **headless AI agents** running on a machine —
23
23
  OpenClaw, Hermes, Claude Code, Ollama, vLLM, llama.cpp, or anything you name. It
24
24
  shows which agents are alive and what they're costing you (CPU, memory, GPU), and
25
25
  gives you a **kill button** per agent. Think `htop`, scoped to just your agents —
26
26
  the [screenshot below](docs/dashboard.png) is a real run on a fleet node.
27
27
 
28
28
  No database, no auth framework (one static token file gates the kill switch),
29
- no dependencies beyond FastAPI + psutil. Runs on macOS and Linux. It is a
29
+ four direct dependencies (FastAPI, uvicorn, psutil, PyYAML). Runs on macOS and Linux. It is a
30
30
  per-node monitor and guarded local control panel: fleet schedulers may consume
31
31
  its read-only telemetry, but should own their own scheduling and actuation.
32
32
 
@@ -161,6 +161,15 @@ This is the important part — a web page that can kill processes needs guardrai
161
161
 
162
162
  ## Limits & known issues
163
163
 
164
+ - **Matching is a heuristic and it over-matches.** An agent's identity usually
165
+ lives in its arguments (`node .../bin/codex`), and a GUI agent's real name is
166
+ its `.app` bundle (Kiro launches a binary called `Electron`) — so the match
167
+ target is the executable basename plus the first few args plus the bundle
168
+ name. The cost is that anything living inside an agent's `.app` (updaters,
169
+ crash handlers, helpers) and any process that names an agent in its first
170
+ args (`tmux attach -t workspace-claude`) can be misclassified as that agent.
171
+ `ignore:` is the escape hatch. The bundled list is not exhaustive and can't
172
+ be — every new agent ships new helpers under its own name.
164
173
  - **GPU column is NVIDIA-only.** Per-process GPU memory comes from
165
174
  `nvidia-smi --query-compute-apps` — NVIDIA compute processes (CUDA), in
166
175
  practice on Linux. AMD/Intel GPUs aren't read, graphics-only workloads don't
@@ -365,7 +374,24 @@ SIGTERM/SIGKILL on POSIX and TerminateProcess on Windows.
365
374
 
366
375
  ## Release notes
367
376
 
368
- ### 0.2.2unreleased
377
+ ### 0.2.4stop reporting non-agents
378
+
379
+ - `ignore:` now covers Sparkle's `Autoupdate`/`Updater` (Codex.app's equivalent of
380
+ Squirrel's ShipIt), the "Codex for Chrome" extension host, and `tmux attach`
381
+ clients — a session named `workspace-claude` matched the claude pattern via the
382
+ `-t` argument.
383
+ - Dogfooded against `ps` on a live 10-agent fleet: 22 rows → 17, with all six
384
+ removals confirmed as not agents and every real agent retained.
385
+
386
+ ### 0.2.3 — startup fixes
387
+
388
+ - The browser now opens once the port actually accepts, instead of on a fixed
389
+ 1s timer. A cold `uvx` run outran the timer and landed on connection-refused.
390
+ - No more `/favicon.ico` 404.
391
+ - Dropped two inaccurate README claims ("single-file"; "no dependencies beyond
392
+ FastAPI + psutil" — there are four).
393
+
394
+ ### 0.2.2 — telemetry identity and fleet labels
369
395
 
370
396
  - Added `api_version` and `aum_version` to `/api/agents` and `list --json`.
371
397
  - Added per-agent `create_time` so external telemetry consumers can pair it with
@@ -1,13 +1,13 @@
1
1
  # agent-usage-manager
2
2
 
3
- A tiny, single-file web dashboard for **headless AI agents** running on a machine —
3
+ A small web dashboard for **headless AI agents** running on a machine —
4
4
  OpenClaw, Hermes, Claude Code, Ollama, vLLM, llama.cpp, or anything you name. It
5
5
  shows which agents are alive and what they're costing you (CPU, memory, GPU), and
6
6
  gives you a **kill button** per agent. Think `htop`, scoped to just your agents —
7
7
  the [screenshot below](docs/dashboard.png) is a real run on a fleet node.
8
8
 
9
9
  No database, no auth framework (one static token file gates the kill switch),
10
- no dependencies beyond FastAPI + psutil. Runs on macOS and Linux. It is a
10
+ four direct dependencies (FastAPI, uvicorn, psutil, PyYAML). Runs on macOS and Linux. It is a
11
11
  per-node monitor and guarded local control panel: fleet schedulers may consume
12
12
  its read-only telemetry, but should own their own scheduling and actuation.
13
13
 
@@ -142,6 +142,15 @@ This is the important part — a web page that can kill processes needs guardrai
142
142
 
143
143
  ## Limits & known issues
144
144
 
145
+ - **Matching is a heuristic and it over-matches.** An agent's identity usually
146
+ lives in its arguments (`node .../bin/codex`), and a GUI agent's real name is
147
+ its `.app` bundle (Kiro launches a binary called `Electron`) — so the match
148
+ target is the executable basename plus the first few args plus the bundle
149
+ name. The cost is that anything living inside an agent's `.app` (updaters,
150
+ crash handlers, helpers) and any process that names an agent in its first
151
+ args (`tmux attach -t workspace-claude`) can be misclassified as that agent.
152
+ `ignore:` is the escape hatch. The bundled list is not exhaustive and can't
153
+ be — every new agent ships new helpers under its own name.
145
154
  - **GPU column is NVIDIA-only.** Per-process GPU memory comes from
146
155
  `nvidia-smi --query-compute-apps` — NVIDIA compute processes (CUDA), in
147
156
  practice on Linux. AMD/Intel GPUs aren't read, graphics-only workloads don't
@@ -346,7 +355,24 @@ SIGTERM/SIGKILL on POSIX and TerminateProcess on Windows.
346
355
 
347
356
  ## Release notes
348
357
 
349
- ### 0.2.2unreleased
358
+ ### 0.2.4stop reporting non-agents
359
+
360
+ - `ignore:` now covers Sparkle's `Autoupdate`/`Updater` (Codex.app's equivalent of
361
+ Squirrel's ShipIt), the "Codex for Chrome" extension host, and `tmux attach`
362
+ clients — a session named `workspace-claude` matched the claude pattern via the
363
+ `-t` argument.
364
+ - Dogfooded against `ps` on a live 10-agent fleet: 22 rows → 17, with all six
365
+ removals confirmed as not agents and every real agent retained.
366
+
367
+ ### 0.2.3 — startup fixes
368
+
369
+ - The browser now opens once the port actually accepts, instead of on a fixed
370
+ 1s timer. A cold `uvx` run outran the timer and landed on connection-refused.
371
+ - No more `/favicon.ico` 404.
372
+ - Dropped two inaccurate README claims ("single-file"; "no dependencies beyond
373
+ FastAPI + psutil" — there are four).
374
+
375
+ ### 0.2.2 — telemetry identity and fleet labels
350
376
 
351
377
  - Added `api_version` and `aum_version` to `/api/agents` and `list --json`.
352
378
  - Added per-agent `create_time` so external telemetry consumers can pair it with
@@ -57,6 +57,10 @@ ignore:
57
57
  - crashpad # chrome_crashpad_handler — the bundle's crash reporter
58
58
  - shipit # Squirrel.framework auto-updater
59
59
  - kiro-cli-term # Kiro's integrated-terminal shells (zsh), not the agent
60
+ - autoupdate # Sparkle's Autoupdate helper — Codex.app's ShipIt equivalent
61
+ - updater # Sparkle's Updater.app; argv[1] is the app path, so it matches
62
+ - for chrome # "Codex for Chrome" extension host, not the agent
63
+ - tmux attach # tmux clients: `attach -t <session>` carries the agent's name
60
64
 
61
65
  # Optional: per-instance labels from tmux session names. A fleet of identical
62
66
  # agents (e.g. several claude-code bots, one per tmux session bot-*) all hit
@@ -3,7 +3,9 @@ from __future__ import annotations
3
3
  import argparse
4
4
  import ipaddress
5
5
  import os
6
+ import socket
6
7
  import threading
8
+ import time
7
9
  import webbrowser
8
10
 
9
11
 
@@ -25,6 +27,23 @@ def _bind_allowed(host: str, unsafe_expose: bool) -> bool:
25
27
  return False
26
28
 
27
29
 
30
+ def _open_when_ready(url: str, host: str, port: int, timeout: float = 15.0) -> None:
31
+ """Open the browser only once the port accepts, never on a fixed delay.
32
+
33
+ A cold first run (uvx resolving the env, then importing fastapi/uvicorn)
34
+ outruns any guess, and the browser lands on connection-refused. On timeout
35
+ we stay silent: the URL is already on stdout.
36
+ """
37
+ deadline = time.monotonic() + timeout
38
+ while time.monotonic() < deadline:
39
+ try:
40
+ with socket.create_connection((host, port), timeout=0.25):
41
+ webbrowser.open(url)
42
+ return
43
+ except OSError:
44
+ time.sleep(0.1)
45
+
46
+
28
47
  def _dur(s: float) -> str:
29
48
  s = max(0, int(s))
30
49
  d, h, m = s // 86400, s % 86400 // 3600, s % 3600 // 60
@@ -128,7 +147,9 @@ def main() -> None:
128
147
  print(f"agent-usage-manager → {url}")
129
148
 
130
149
  if not args.no_browser:
131
- threading.Timer(1.0, lambda: webbrowser.open(url)).start()
150
+ threading.Thread(
151
+ target=_open_when_ready, args=(url, open_host, args.port), daemon=True
152
+ ).start()
132
153
 
133
154
  uvicorn.run("agent_usage_manager.app:app", host=args.host, port=args.port)
134
155
 
@@ -3,6 +3,7 @@
3
3
  <head>
4
4
  <meta charset="utf-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <link rel="icon" href="data:," />
6
7
  <title>agent usage manager</title>
7
8
  <style>
8
9
  :root { color-scheme: dark; }
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "agent-usage-manager"
7
- version = "0.2.2"
7
+ version = "0.2.4"
8
8
  description = "htop for AI agents — liveness, CPU/mem/GPU usage, and a kill switch for headless agents (openclaw, hermes, ollama, vllm, claude-code)."
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -8,7 +8,7 @@ resolution-markers = [
8
8
 
9
9
  [[package]]
10
10
  name = "agent-usage-manager"
11
- version = "0.2.2"
11
+ version = "0.2.4"
12
12
  source = { editable = "." }
13
13
  dependencies = [
14
14
  { name = "fastapi", version = "0.128.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },