From 88073c4bea80a7eb65ea3085dfd6d46c6724ebfd Mon Sep 17 00:00:00 2001 From: Ebeneezer Date: Thu, 20 Aug 2026 15:39:06 -0700 Subject: [PATCH] fix: emit 0-1 limit fractions and refresh the agents panel Omarchy's panel treats percent as a fraction (1.0 = 100%). Vendor creditUsagePercent is 0-100, so 1.0 was rendering as a full bar. After writing grok.json, ping omarchy.agents refresh so the widget rescans without a shell restart. --- src/omarchy_grok_usage.py | 37 ++++++++++++++++++++++++++++-- systemd/omarchy-grok-usage.service | 1 + tests/test_collector.py | 13 +++++++---- 3 files changed, 45 insertions(+), 6 deletions(-) diff --git a/src/omarchy_grok_usage.py b/src/omarchy_grok_usage.py index 7e2cf93..02730ba 100755 --- a/src/omarchy_grok_usage.py +++ b/src/omarchy_grok_usage.py @@ -18,6 +18,7 @@ import datetime as dt import json import os import re +import subprocess import sys import tempfile import urllib.error @@ -89,6 +90,19 @@ def _iso_reset(value: Any) -> str | None: return parsed.isoformat() +def _fraction_used(raw: float) -> float: + """Map a vendor percent into the panel's 0..1 fraction. + + Same rule as Omarchy's Claude collector: any value >= 1 is a 0-100 + percentage, so 1.0 is 1% not 100%. + """ + if raw < 0: + return 0.0 + if raw >= 1: + return min(1.0, raw / 100.0) + return min(1.0, raw) + + def limits_from_billing(payload: Mapping[str, Any]) -> list[dict[str, Any]]: config = payload.get("config") if isinstance(payload.get("config"), dict) else payload if not isinstance(config, dict): @@ -102,7 +116,7 @@ def limits_from_billing(payload: Mapping[str, Any]) -> list[dict[str, Any]]: percent = (used / limit) * 100.0 if percent is None: return [] - percent = max(0.0, min(100.0, percent)) + fraction = _fraction_used(percent) period = config.get("currentPeriod") end = None @@ -114,7 +128,8 @@ def limits_from_billing(payload: Mapping[str, Any]) -> list[dict[str, Any]]: return [ { "label": "Weekly", - "percent": percent, + "title": "Weekly", + "percent": fraction, "resetsAt": _iso_reset(end), } ] @@ -375,6 +390,23 @@ def default_usage_dir(env: Mapping[str, str], home: Path) -> Path: return root / "omarchy" / "agents" / "usage" +def notify_panel() -> None: + """Ask the live Omarchy agents widget to rescan usage files.""" + env = os.environ.copy() + env.setdefault("OMARCHY_PATH", "/usr/share/omarchy") + try: + subprocess.run( + ["omarchy-shell", "-q", "omarchy.agents", "refresh"], + env=env, + timeout=8, + check=False, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + except (OSError, subprocess.SubprocessError): + return + + def main(argv: list[str] | None = None) -> int: parser = argparse.ArgumentParser( description="Collect SuperGrok weekly usage for the Omarchy agents panel." @@ -402,6 +434,7 @@ def main(argv: list[str] | None = None) -> int: ) if result.path is not None: print(f"wrote {result.path}", file=sys.stderr) + notify_panel() return 0 json.dump(record, sys.stdout, separators=(",", ":"), sort_keys=True) sys.stdout.write("\n") diff --git a/systemd/omarchy-grok-usage.service b/systemd/omarchy-grok-usage.service index 8e49064..dd1847a 100644 --- a/systemd/omarchy-grok-usage.service +++ b/systemd/omarchy-grok-usage.service @@ -5,6 +5,7 @@ Wants=network-online.target [Service] Type=oneshot +Environment=OMARCHY_PATH=/usr/share/omarchy ExecStart=%h/.local/bin/omarchy-grok-usage --write Nice=10 diff --git a/tests/test_collector.py b/tests/test_collector.py index 74312ab..f38c989 100644 --- a/tests/test_collector.py +++ b/tests/test_collector.py @@ -67,7 +67,7 @@ class TestBillingMap(unittest.TestCase): limits = m.limits_from_billing(payload) self.assertEqual(len(limits), 1) self.assertEqual(limits[0]["label"], "Weekly") - self.assertEqual(limits[0]["percent"], 31.5) + self.assertEqual(limits[0]["percent"], 0.315) self.assertEqual(limits[0]["resetsAt"], "2026-08-27T00:00:00+00:00") def test_falls_back_to_used_over_limit(self) -> None: @@ -79,7 +79,7 @@ class TestBillingMap(unittest.TestCase): } } limits = m.limits_from_billing(payload) - self.assertEqual(limits[0]["percent"], 40.0) + self.assertEqual(limits[0]["percent"], 0.4) self.assertEqual(limits[0]["resetsAt"], "2026-09-01T00:00:00+00:00") def test_empty_billing_yields_no_limits(self) -> None: @@ -88,7 +88,12 @@ class TestBillingMap(unittest.TestCase): def test_percent_clamped_to_100(self) -> None: payload = {"config": {"creditUsagePercent": 140}} limits = m.limits_from_billing(payload) - self.assertEqual(limits[0]["percent"], 100.0) + self.assertEqual(limits[0]["percent"], 1.0) + + def test_one_point_zero_is_one_percent_not_full(self) -> None: + payload = {"config": {"creditUsagePercent": 1.0}} + limits = m.limits_from_billing(payload) + self.assertEqual(limits[0]["percent"], 0.01) class TestTierLabel(unittest.TestCase): @@ -260,7 +265,7 @@ class TestCollect(unittest.TestCase): ) blob = json.dumps(record) self.assertNotIn(FAKE_TOKEN, blob) - self.assertEqual(record["limits"][0]["percent"], 12) + self.assertEqual(record["limits"][0]["percent"], 0.12) self.assertEqual(record["tierLabel"], "SuperGrok") self.assertTrue(record["ready"])