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.
This commit is contained in:
parent
4df21f20a0
commit
88073c4bea
3 changed files with 45 additions and 6 deletions
|
|
@ -18,6 +18,7 @@ import datetime as dt
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
import urllib.error
|
import urllib.error
|
||||||
|
|
@ -89,6 +90,19 @@ def _iso_reset(value: Any) -> str | None:
|
||||||
return parsed.isoformat()
|
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]]:
|
def limits_from_billing(payload: Mapping[str, Any]) -> list[dict[str, Any]]:
|
||||||
config = payload.get("config") if isinstance(payload.get("config"), dict) else payload
|
config = payload.get("config") if isinstance(payload.get("config"), dict) else payload
|
||||||
if not isinstance(config, dict):
|
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
|
percent = (used / limit) * 100.0
|
||||||
if percent is None:
|
if percent is None:
|
||||||
return []
|
return []
|
||||||
percent = max(0.0, min(100.0, percent))
|
fraction = _fraction_used(percent)
|
||||||
|
|
||||||
period = config.get("currentPeriod")
|
period = config.get("currentPeriod")
|
||||||
end = None
|
end = None
|
||||||
|
|
@ -114,7 +128,8 @@ def limits_from_billing(payload: Mapping[str, Any]) -> list[dict[str, Any]]:
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
"label": "Weekly",
|
"label": "Weekly",
|
||||||
"percent": percent,
|
"title": "Weekly",
|
||||||
|
"percent": fraction,
|
||||||
"resetsAt": _iso_reset(end),
|
"resetsAt": _iso_reset(end),
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
@ -375,6 +390,23 @@ def default_usage_dir(env: Mapping[str, str], home: Path) -> Path:
|
||||||
return root / "omarchy" / "agents" / "usage"
|
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:
|
def main(argv: list[str] | None = None) -> int:
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description="Collect SuperGrok weekly usage for the Omarchy agents panel."
|
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:
|
if result.path is not None:
|
||||||
print(f"wrote {result.path}", file=sys.stderr)
|
print(f"wrote {result.path}", file=sys.stderr)
|
||||||
|
notify_panel()
|
||||||
return 0
|
return 0
|
||||||
json.dump(record, sys.stdout, separators=(",", ":"), sort_keys=True)
|
json.dump(record, sys.stdout, separators=(",", ":"), sort_keys=True)
|
||||||
sys.stdout.write("\n")
|
sys.stdout.write("\n")
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ Wants=network-online.target
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=oneshot
|
Type=oneshot
|
||||||
|
Environment=OMARCHY_PATH=/usr/share/omarchy
|
||||||
ExecStart=%h/.local/bin/omarchy-grok-usage --write
|
ExecStart=%h/.local/bin/omarchy-grok-usage --write
|
||||||
Nice=10
|
Nice=10
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ class TestBillingMap(unittest.TestCase):
|
||||||
limits = m.limits_from_billing(payload)
|
limits = m.limits_from_billing(payload)
|
||||||
self.assertEqual(len(limits), 1)
|
self.assertEqual(len(limits), 1)
|
||||||
self.assertEqual(limits[0]["label"], "Weekly")
|
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")
|
self.assertEqual(limits[0]["resetsAt"], "2026-08-27T00:00:00+00:00")
|
||||||
|
|
||||||
def test_falls_back_to_used_over_limit(self) -> None:
|
def test_falls_back_to_used_over_limit(self) -> None:
|
||||||
|
|
@ -79,7 +79,7 @@ class TestBillingMap(unittest.TestCase):
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
limits = m.limits_from_billing(payload)
|
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")
|
self.assertEqual(limits[0]["resetsAt"], "2026-09-01T00:00:00+00:00")
|
||||||
|
|
||||||
def test_empty_billing_yields_no_limits(self) -> None:
|
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:
|
def test_percent_clamped_to_100(self) -> None:
|
||||||
payload = {"config": {"creditUsagePercent": 140}}
|
payload = {"config": {"creditUsagePercent": 140}}
|
||||||
limits = m.limits_from_billing(payload)
|
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):
|
class TestTierLabel(unittest.TestCase):
|
||||||
|
|
@ -260,7 +265,7 @@ class TestCollect(unittest.TestCase):
|
||||||
)
|
)
|
||||||
blob = json.dumps(record)
|
blob = json.dumps(record)
|
||||||
self.assertNotIn(FAKE_TOKEN, blob)
|
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.assertEqual(record["tierLabel"], "SuperGrok")
|
||||||
self.assertTrue(record["ready"])
|
self.assertTrue(record["ready"])
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue