No description
  • Python 44.8%
  • QML 41.1%
  • JavaScript 14.1%
Find a file
Luis Serralheiro 88ab5a169b Close remaining credential and rich-text trust boundaries
Security review of e1e6af0 flagged two trust-boundary gaps:

- fetch_usage() sent X-AFK-API-Key through urllib's default redirect
  handler, which replays custom request headers onto the follow-up
  request. A redirect from the usage endpoint could therefore disclose
  the AFK API key to another origin. Requests now go through an opener
  that follows only same-scheme/same-host/same-port redirects and
  raises HTTPError for anything else.
- Provider-controlled strings (subscription label, window name, error,
  credits/balance, bar labels) reached QML Text elements that default
  to AutoText, so a provider response could be interpreted as rich text
  and trigger embedded-resource requests. Every provider-controlled
  Text sink now sets textFormat: Text.PlainText.

Regression tests cover the cross-origin redirect, the scheme downgrade,
the allowed same-origin hop, and the oversized-response path.

Generated with AFK
Co-Authored-By: AFK <noreply@afk.local> using deepseek-flash
2026-09-16 10:59:39 +01:00
assets Add GitHub Copilot remaining-credit quota card 2026-09-02 03:33:46 +01:00
.gitignore Add AFK Monitor bar widget for omarchy 2026-09-01 22:41:57 +01:00
collector.py Close remaining credential and rich-text trust boundaries 2026-09-16 10:59:39 +01:00
LICENSE Adding the license 2026-09-03 23:15:43 +00:00
manifest.json Move plugin files to repo root for omarchy plugin add 2026-09-01 22:43:41 +01:00
Model.js Harden marketplace plugin input boundaries 2026-09-13 22:47:28 +01:00
Panel.qml Close remaining credential and rich-text trust boundaries 2026-09-16 10:59:39 +01:00
preview.png Add marketplace preview screenshot 2026-09-04 16:00:43 +01:00
README.md Harden marketplace plugin input boundaries 2026-09-13 22:47:28 +01:00
test_collector_security.py Close remaining credential and rich-text trust boundaries 2026-09-16 10:59:39 +01:00
test_model.js Harden marketplace plugin input boundaries 2026-09-13 22:47:28 +01:00

AFK Monitor (omarchy bar plugin)

Bar widget showing AFK subscription usage per connection: quota windows (percent used, time to reset) and prepaid credit balances.

AFK Monitor showing subscription quota windows and credit balances in the Omarchy bar

How it works

  • collector.py polls https://afk-server.mooglest.com/api/auth/<provider>/usage for every provider AFK supports (ChatGPT/Codex, GitHub Copilot, xAI, OpenCode Go, Kimi, OpenRouter, DeepSeek, Moonshot). Providers that are not connected return 401/403/404 and are skipped. Copilot remaining-credit quota needs AFK 0.13.7+ and a GitHub Copilot subscription connected under Account → LLM → + Copilot subscription (not an API key).

  • Every watched account must be configured explicitly — nothing is assumed from AFK's own config. Keys live in ~/.config/omarchy/afk-monitor.json (0600); array order is display order:

    { "keys": [ { "label": "Work org", "key": "afk-..." } ] }
    

    Manage them from the popup (add / reorder / remove / show-on-bar) or the add-key <label> --stdin; for example: printf '%s\n' "$AFK_API_KEY" | python3 collector.py add-key work --stdin. Remove or reorder entries with remove-key <label>, move-key <label> <up|down|position>, and list-keys.

  • Panel.qml (Quickshell bar widget) runs the collector on a 5-minute timer and on popup open. With no keys configured the bar shows the AFK mark and the popup offers the add-key form. Each subscription card has a switch to include or exclude it from the bar (default: on), persisted per account/provider in the widget's shell.json entry.

Claude subscriptions

Claude (anthropic-oauth) quota is not available over HTTP — AFK receives it as a live websocket subscription_quota message inside the agent process while a Claude session runs, and the hub treats it as transient (never persisted).

Optional mirror: run any Claude-backed AFK session with AFK_QUOTA_MIRROR=1 and an agent hook that writes the latest subscription_quota payload to ~/.cache/afk/claude-quota.json ({"provider":"anthropic-oauth","windows":{...},"status":"..."}). The collector picks it up and shows it first. Without the mirror, Claude subscriptions are simply absent from the monitor.

GitHub Copilot

Copilot quota is AFK's proxy of GitHub's unofficial user-quota snapshot (GET /api/auth/copilot/usage, AFK 0.13.7+). The widget shows remaining premium credits (253 / 1500 credits), not a dollar amount. Unlimited chat plans render as Unlimited with no percent bar. Hide-on-404 is the same as the other providers, so accounts without Copilot connected show nothing.

Install

omarchy plugin add https://github.com/lcavadas/omarchy-afk-monitor.git --enable
omarchy bar move lcavadas.afk-monitor --section center   # or any placement you like

--enable also asks where to place the widget on the bar. Enable later or change placement any time with omarchy plugin enable lcavadas.afk-monitor and omarchy bar move lcavadas.afk-monitor --section right.

Manual install (alternative)

git clone https://github.com/lcavadas/omarchy-afk-monitor.git \
  ~/.config/omarchy/plugins/lcavadas.afk-monitor
omarchy plugin enable lcavadas.afk-monitor

The shell hot-reloads plugin code on save; omarchy restart shell forces a full reload if needed.

Remove

omarchy plugin remove lcavadas.afk-monitor

Files

File Role
manifest.json Plugin manifest (bar-widget entry point, repo root)
collector.py Polls AFK hub usage endpoints, emits one JSON record
Model.js Record parsing + formatting helpers (window labels, reset countdown, icon mapping)
Panel.qml Bar widget (provider mark + value per subscription) and popup cards
assets/ Monochrome provider marks: <id>.svg (white) + <id>-light.svg (dark) for light bars

Provider marks follow the agents panel's convention: the widget picks the white or dark twin based on the bar's text colour, so marks stay readable on dark, light, and transparent bars. Sources: simpleicons.org (anthropic, githubcopilot, openrouter, deepseek, kimi, moonshotai, opencode), the omarchy agents plugin (claude, codex), worldvectorlogo (xai); the AFK mark is AFK's own brand glyph.