Compare commits
17 Commits
971f367df7
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 42d9758ecc | |||
| b34d5b1d97 | |||
| ff86eeff4b | |||
| ff30800337 | |||
| 319db74bce | |||
| f04e24b64d | |||
| 9005edd58c | |||
| 5abdee0d4b | |||
| ff670b78e6 | |||
| 573dbef686 | |||
| 9fa21f1a9e | |||
| 07840b0dd3 | |||
| 3ea2296eca | |||
| 07d22bb7d4 | |||
| 4103adeef7 | |||
| 374601cee5 | |||
| eecad6d4ee |
@@ -1,3 +1,4 @@
|
|||||||
*.swp
|
*.swp
|
||||||
.netrwhist
|
.netrwhist
|
||||||
*/.config/systemd/user/default.target.wants/
|
*/.config/systemd/user/default.target.wants/
|
||||||
|
.codex
|
||||||
|
|||||||
+49
-10
@@ -1,26 +1,65 @@
|
|||||||
# Beware! This file is rewritten by htop when settings are changed in the interface.
|
# Beware! This file is rewritten by htop when settings are changed in the interface.
|
||||||
# The parser is also very primitive, and not human-friendly.
|
# The parser is also very primitive, and not human-friendly.
|
||||||
|
htop_version=3.5.1
|
||||||
|
config_reader_min_version=3
|
||||||
fields=0 5 48 17 18 38 39 2 46 47 49 1
|
fields=0 5 48 17 18 38 39 2 46 47 49 1
|
||||||
sort_key=46
|
|
||||||
sort_direction=1
|
|
||||||
hide_threads=0
|
|
||||||
hide_kernel_threads=1
|
hide_kernel_threads=1
|
||||||
hide_userland_threads=1
|
hide_userland_threads=1
|
||||||
|
hide_running_in_container=0
|
||||||
shadow_other_users=1
|
shadow_other_users=1
|
||||||
show_thread_names=1
|
show_thread_names=1
|
||||||
show_program_path=1
|
show_program_path=1
|
||||||
highlight_base_name=0
|
highlight_base_name=1
|
||||||
|
highlight_deleted_exe=1
|
||||||
|
shadow_distribution_path_prefix=0
|
||||||
highlight_megabytes=1
|
highlight_megabytes=1
|
||||||
highlight_threads=1
|
highlight_threads=1
|
||||||
tree_view=1
|
highlight_changes=0
|
||||||
|
highlight_changes_delay_secs=5
|
||||||
|
find_comm_in_cmdline=1
|
||||||
|
strip_exe_from_cmdline=1
|
||||||
|
show_merged_command=0
|
||||||
header_margin=1
|
header_margin=1
|
||||||
|
screen_tabs=0
|
||||||
detailed_cpu_time=0
|
detailed_cpu_time=0
|
||||||
cpu_count_from_zero=0
|
cpu_count_from_one=1
|
||||||
|
show_cpu_smt_labels=0
|
||||||
|
show_cpu_usage=1
|
||||||
|
show_cpu_frequency=0
|
||||||
|
show_cpu_temperature=0
|
||||||
|
degree_fahrenheit=0
|
||||||
|
show_cached_memory=1
|
||||||
update_process_names=0
|
update_process_names=0
|
||||||
account_guest_in_cpu_meter=0
|
account_guest_in_cpu_meter=0
|
||||||
color_scheme=0
|
color_scheme=0
|
||||||
|
enable_mouse=1
|
||||||
delay=15
|
delay=15
|
||||||
left_meters=AllCPUs2 Memory Swap
|
hide_function_bar=0
|
||||||
left_meter_modes=1 1 1
|
header_layout=two_50_50
|
||||||
right_meters=Tasks LoadAverage Uptime
|
column_meters_0=AllCPUs2 Memory Swap
|
||||||
right_meter_modes=2 2 2
|
column_meter_modes_0=1 1 1
|
||||||
|
column_meters_1=Tasks LoadAverage Uptime
|
||||||
|
column_meter_modes_1=2 2 2
|
||||||
|
tree_view=1
|
||||||
|
sort_key=46
|
||||||
|
tree_sort_key=0
|
||||||
|
sort_direction=1
|
||||||
|
tree_sort_direction=1
|
||||||
|
tree_view_always_by_pid=0
|
||||||
|
all_branches_collapsed=0
|
||||||
|
screen:Main=PID SESSION USER PRIORITY NICE M_VIRT M_RESIDENT STATE PERCENT_CPU PERCENT_MEM TIME Command
|
||||||
|
.sort_key=PERCENT_CPU
|
||||||
|
.tree_sort_key=PID
|
||||||
|
.tree_view_always_by_pid=0
|
||||||
|
.tree_view=1
|
||||||
|
.sort_direction=1
|
||||||
|
.tree_sort_direction=1
|
||||||
|
.all_branches_collapsed=0
|
||||||
|
screen:I/O=PID USER IO_PRIORITY IO_RATE IO_READ_RATE IO_WRITE_RATE PERCENT_SWAP_DELAY PERCENT_IO_DELAY Command
|
||||||
|
.sort_key=IO_RATE
|
||||||
|
.tree_sort_key=PID
|
||||||
|
.tree_view_always_by_pid=0
|
||||||
|
.tree_view=0
|
||||||
|
.sort_direction=-1
|
||||||
|
.tree_sort_direction=1
|
||||||
|
.all_branches_collapsed=0
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
" Work around Vim 9.2.500 htmldjango syntax spillover from the May 2026
|
||||||
|
" djangoOperator change. The stock djangoOperator rule is enough here; the
|
||||||
|
" htmldjango-only djangoTagBlockNaive region can leak across following HTML.
|
||||||
|
silent! syntax clear djangoTagBlockNaive
|
||||||
|
silent! syntax clear djangoOperator
|
||||||
|
syntax match djangoOperator "==\|!=\|<=\|>=\|<\|>" contained containedin=djangoTagBlock
|
||||||
|
highlight default link djangoOperator Operator
|
||||||
+1
-1
Submodule base/.vim/bundle/ale updated: 6c337ad19c...2a3af30fb6
+1
-1
Submodule base/.vim/bundle/gruvbox updated: f1ecde848f...697c00291d
Submodule base/.vim/bundle/lightline.vim updated: e358557e1a...6c283f8df8
Submodule base/.vim/bundle/vim-gitgutter updated: 55b368d4c6...21c977e859
Submodule base/.vim/bundle/vim-javascript updated: c470ce1399...b26c9edb35
Submodule base/.vim/bundle/vim-terraform updated: 8912ca1be3...520498fab1
@@ -92,9 +92,6 @@ au BufNewFile,BufRead *.html,*.php
|
|||||||
\ set shiftwidth=2 |
|
\ set shiftwidth=2 |
|
||||||
\ set autoindent |
|
\ set autoindent |
|
||||||
\ set smartindent
|
\ set smartindent
|
||||||
" Treat PHP like HTML
|
|
||||||
au BufNewFile,BufRead *.php
|
|
||||||
\ set filetype=html
|
|
||||||
au FileType yaml
|
au FileType yaml
|
||||||
\ set tabstop=2 |
|
\ set tabstop=2 |
|
||||||
\ set softtabstop=2 |
|
\ set softtabstop=2 |
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ blink=yes
|
|||||||
[touch]
|
[touch]
|
||||||
# long-press-delay=400
|
# long-press-delay=400
|
||||||
|
|
||||||
[colors-dark]
|
[colors]
|
||||||
alpha=0.8
|
alpha=0.8
|
||||||
background=282828
|
background=282828
|
||||||
foreground=ebdbb2
|
foreground=ebdbb2
|
||||||
|
|||||||
@@ -47,7 +47,6 @@ animations {
|
|||||||
|
|
||||||
dwindle {
|
dwindle {
|
||||||
# https://wiki.hyprland.org/Configuring/Dwindle-Layout/
|
# https://wiki.hyprland.org/Configuring/Dwindle-Layout/
|
||||||
pseudotile = yes # Enables pseudotile functionality
|
|
||||||
preserve_split = yes # Remember splits
|
preserve_split = yes # Remember splits
|
||||||
force_split = 2 # Force splits to put the child on the right/down
|
force_split = 2 # Force splits to put the child on the right/down
|
||||||
default_split_ratio = 1.1
|
default_split_ratio = 1.1
|
||||||
@@ -71,7 +70,6 @@ misc {
|
|||||||
# o7 hyprland animu
|
# o7 hyprland animu
|
||||||
disable_hyprland_logo = true
|
disable_hyprland_logo = true
|
||||||
background_color = 0
|
background_color = 0
|
||||||
vfr = 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
xwayland {
|
xwayland {
|
||||||
|
|||||||
@@ -7,12 +7,11 @@ $mainMod = ALT
|
|||||||
bind = $mainMod, Return, exec, foot
|
bind = $mainMod, Return, exec, foot
|
||||||
bind = $mainMod, Q, killactive
|
bind = $mainMod, Q, killactive
|
||||||
bind = $mainMod, Space, exec, pgrep wofi || wofi --show drun
|
bind = $mainMod, Space, exec, pgrep wofi || wofi --show drun
|
||||||
#bind = $mainMod, M, exit,
|
|
||||||
bind = $mainMod, T, pseudo
|
bind = $mainMod, T, pseudo
|
||||||
bind = $mainMod, F, togglefloating
|
bind = $mainMod, F, togglefloating
|
||||||
bind = $mainMod Shift, F, fullscreen
|
bind = $mainMod Shift, F, fullscreen
|
||||||
bind = $mainMod, R, togglesplit
|
bind = $mainMod, R, layoutmsg, togglesplit
|
||||||
bind = $mainMod, G, swapsplit
|
bind = $mainMod, G, layoutmsg, swapsplit
|
||||||
|
|
||||||
# Dunst management
|
# Dunst management
|
||||||
bind = $mainMod Shift, Space, exec, dunstctl close
|
bind = $mainMod Shift, Space, exec, dunstctl close
|
||||||
@@ -76,7 +75,26 @@ bind = $mainMod SHIFT, 9, movetoworkspace, 9
|
|||||||
bind = $mainMod SHIFT, 0, movetoworkspace, 10
|
bind = $mainMod SHIFT, 0, movetoworkspace, 10
|
||||||
bind = $mainMod SHIFT, MINUS, movetoworkspace, 11
|
bind = $mainMod SHIFT, MINUS, movetoworkspace, 11
|
||||||
|
|
||||||
# Scroll through existing workspaces with mainMod + scroll
|
# Role workspaces
|
||||||
|
bind = $mainMod, E, workspace, name:mail
|
||||||
|
bind = $mainMod SHIFT, E, movetoworkspace, name:mail
|
||||||
|
bind = $mainMod, C, workspace, name:comms
|
||||||
|
bind = $mainMod SHIFT, C, movetoworkspace, name:comms
|
||||||
|
bind = $mainMod, M, workspace, name:media
|
||||||
|
bind = $mainMod SHIFT, M, movetoworkspace, name:media
|
||||||
|
bind = $mainMod, S, togglespecialworkspace, secrets
|
||||||
|
bind = $mainMod SHIFT, S, movetoworkspace, special:secrets
|
||||||
|
bind = $mainMod, N, togglespecialworkspace, scratch
|
||||||
|
bind = $mainMod SHIFT, N, movetoworkspace, special:scratch
|
||||||
|
|
||||||
|
# Monitor-local workspace movement
|
||||||
|
bind = $mainMod, bracketleft, workspace, r-1
|
||||||
|
bind = $mainMod, bracketright, workspace, r+1
|
||||||
|
bind = $mainMod SHIFT, bracketleft, movetoworkspace, r-1
|
||||||
|
bind = $mainMod SHIFT, bracketright, movetoworkspace, r+1
|
||||||
|
bind = $mainMod, BackSpace, workspace, previous_per_monitor
|
||||||
|
|
||||||
|
# Scroll through existing workspaces
|
||||||
bind = $mainMod, Period, workspace, e+1
|
bind = $mainMod, Period, workspace, e+1
|
||||||
bind = $mainMod, Comma, workspace, e-1
|
bind = $mainMod, Comma, workspace, e-1
|
||||||
|
|
||||||
|
|||||||
@@ -14,10 +14,10 @@ windowrule = pin on, match:class ^(.*pavucontrol.*)$
|
|||||||
|
|
||||||
# Assign specific windows to specific workspaces on launch
|
# Assign specific windows to specific workspaces on launch
|
||||||
windowrule = workspace 2 silent, match:class ^(steam)$
|
windowrule = workspace 2 silent, match:class ^(steam)$
|
||||||
windowrule = workspace 8 silent, match:class ^(org.keepassxc.KeePassXC)$
|
windowrule = workspace special:secrets silent, match:class ^(org\.keepassxc\.KeePassXC)$
|
||||||
windowrule = workspace 8 silent, match:class ^(org.mozilla.Thunderbird)$
|
windowrule = workspace name:mail silent, match:class ^(org\.mozilla\.Thunderbird|org\.mozilla\.thunderbird_esr)$
|
||||||
windowrule = workspace 9 silent, match:class ^(im.riot.Riot)$
|
windowrule = workspace name:comms silent, match:class ^(im\.riot\.Riot)$
|
||||||
windowrule = workspace 9 silent, match:class ^(.*vesktop.*)$
|
windowrule = workspace name:comms silent, match:class ^(.*vesktop.*)$
|
||||||
windowrule = workspace 10 silent, match:class ^(.*spotube.*)$
|
windowrule = workspace name:media silent, match:class ^(.*spotube.*)$
|
||||||
windowrule = workspace 10 silent, match:class ^(.*potify.*)$
|
windowrule = workspace name:media silent, match:class ^(.*potify.*)$
|
||||||
windowrule = workspace 10 silent, match:class ^(feishin)$
|
windowrule = workspace name:media silent, match:class ^(feishin)$
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
# vim: set ft=hyprlang:
|
# vim: set ft=hyprlang:
|
||||||
# Workspace-specific applications
|
# Workspace-specific applications
|
||||||
exec-once = [workspace 2 silent] flatpak run com.valvesoftware.Steam -silent
|
exec-once = [workspace 2 silent] flatpak run com.valvesoftware.Steam -silent
|
||||||
exec-once = [workspace 8 silent] flatpak run org.keepassxc.KeePassXC || keepassxc
|
exec-once = [workspace special:secrets silent] flatpak run org.keepassxc.KeePassXC || keepassxc
|
||||||
exec-once = [workspace 8 silent] flatpak run org.mozilla.Thunderbird || thunderbird
|
exec-once = [workspace name:mail silent] flatpak run org.mozilla.Thunderbird || thunderbird
|
||||||
exec-once = [workspace 9 silent] flatpak run im.riot.Riot || element-desktop
|
exec-once = [workspace name:comms silent] flatpak run im.riot.Riot || element-desktop
|
||||||
exec-once = [workspace 9 silent] flatpak run dev.vencord.Vesktop
|
exec-once = [workspace name:comms silent] flatpak run dev.vencord.Vesktop
|
||||||
#exec-once = [workspace 10 silent] flatpak run com.spotify.Client
|
#exec-once = [workspace 10 silent] flatpak run com.spotify.Client
|
||||||
exec-once = [workspace 10 silent] flatpak run org.jeffvli.feishin || ~/Programs/feishin
|
exec-once = [workspace name:media silent] flatpak run org.jeffvli.feishin || ~/Programs/feishin
|
||||||
|
|||||||
@@ -16,3 +16,9 @@ workspace = 8, monitor:DP-2
|
|||||||
workspace = 9, monitor:DP-2
|
workspace = 9, monitor:DP-2
|
||||||
workspace = 10, monitor:DP-2
|
workspace = 10, monitor:DP-2
|
||||||
workspace = 11, monitor:DP-2
|
workspace = 11, monitor:DP-2
|
||||||
|
|
||||||
|
workspace = name:mail, monitor:DP-2, persistent:true
|
||||||
|
workspace = name:comms, monitor:DP-2, persistent:true
|
||||||
|
workspace = name:media, monitor:DP-2, persistent:true
|
||||||
|
workspace = special:secrets, persistent:true
|
||||||
|
workspace = special:scratch, persistent:true
|
||||||
|
|||||||
@@ -3,3 +3,20 @@
|
|||||||
monitor=eDP-1,preferred,auto,1.00
|
monitor=eDP-1,preferred,auto,1.00
|
||||||
# Should be the Nreal Air
|
# Should be the Nreal Air
|
||||||
monitor=DP-1,preferred,auto,1.5
|
monitor=DP-1,preferred,auto,1.5
|
||||||
|
|
||||||
|
workspace = 1, monitor:eDP-1
|
||||||
|
workspace = 2, monitor:eDP-1
|
||||||
|
workspace = 3, monitor:eDP-1
|
||||||
|
workspace = 4, monitor:eDP-1
|
||||||
|
workspace = 5, monitor:eDP-1
|
||||||
|
workspace = 6, monitor:eDP-1
|
||||||
|
workspace = 7, monitor:eDP-1
|
||||||
|
workspace = 8, monitor:eDP-1
|
||||||
|
workspace = 9, monitor:eDP-1
|
||||||
|
workspace = 10, monitor:eDP-1
|
||||||
|
workspace = 11, monitor:eDP-1
|
||||||
|
workspace = name:mail, monitor:eDP-1, persistent:true
|
||||||
|
workspace = name:comms, monitor:eDP-1, persistent:true
|
||||||
|
workspace = name:media, monitor:eDP-1, persistent:true
|
||||||
|
workspace = special:secrets, persistent:true
|
||||||
|
workspace = special:scratch, persistent:true
|
||||||
|
|||||||
@@ -11,6 +11,29 @@
|
|||||||
"hyprland/workspaces": {
|
"hyprland/workspaces": {
|
||||||
"all-outputs": false,
|
"all-outputs": false,
|
||||||
"disable-scroll": true,
|
"disable-scroll": true,
|
||||||
|
"show-special": true,
|
||||||
|
"special-visible-only": false,
|
||||||
|
"format": "{icon}",
|
||||||
|
"format-icons": {
|
||||||
|
"1": "1",
|
||||||
|
"2": "2",
|
||||||
|
"3": "3",
|
||||||
|
"4": "4",
|
||||||
|
"5": "5",
|
||||||
|
"6": "6",
|
||||||
|
"7": "7",
|
||||||
|
"8": "8",
|
||||||
|
"9": "9",
|
||||||
|
"10": "10",
|
||||||
|
"11": "11",
|
||||||
|
"mail": "",
|
||||||
|
"comms": "",
|
||||||
|
"media": "",
|
||||||
|
"secrets": "S",
|
||||||
|
"scratch": "N",
|
||||||
|
"empty": "",
|
||||||
|
"default": ""
|
||||||
|
},
|
||||||
"persistent-workspaces": {
|
"persistent-workspaces": {
|
||||||
"eDP-1": [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ],
|
"eDP-1": [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ],
|
||||||
"DP-1": [ 1, 2, 3, 4, 5, 6, 7 ],
|
"DP-1": [ 1, 2, 3, 4, 5, 6, 7 ],
|
||||||
@@ -38,7 +61,7 @@
|
|||||||
"margin-bottom": 16,
|
"margin-bottom": 16,
|
||||||
"margin-left": 16,
|
"margin-left": 16,
|
||||||
"margin-right": 16,
|
"margin-right": 16,
|
||||||
"modules-left": ["gamemode", "battery", "temperature", "cpu", "memory", "network"],
|
"modules-left": ["gamemode", "battery", "temperature", "cpu", "memory", "custom/codex-primary", "custom/codex-secondary", "network"],
|
||||||
"modules-center": [],
|
"modules-center": [],
|
||||||
"modules-right": ["mpris", "pulseaudio", "custom/output-device", "backlight", "idle_inhibitor", "clock"],
|
"modules-right": ["mpris", "pulseaudio", "custom/output-device", "backlight", "idle_inhibitor", "clock"],
|
||||||
"clock": {
|
"clock": {
|
||||||
@@ -132,6 +155,22 @@
|
|||||||
"exec": "flatpak remote-ls --updates --app | wc -l",
|
"exec": "flatpak remote-ls --updates --app | wc -l",
|
||||||
"exec-if": "test $(flatpak remote-ls --updates --app | wc -l) -gt 0"
|
"exec-if": "test $(flatpak remote-ls --updates --app | wc -l) -gt 0"
|
||||||
},
|
},
|
||||||
|
"custom/codex-primary": {
|
||||||
|
"interval": 15,
|
||||||
|
"return-type": "json",
|
||||||
|
"exec": "$HOME/.config/waybar/scripts/openai-rate.py --window primary",
|
||||||
|
"exec-if": "test -r ~/.codex/auth.json",
|
||||||
|
"escape": false,
|
||||||
|
"format": " {}"
|
||||||
|
},
|
||||||
|
"custom/codex-secondary": {
|
||||||
|
"interval": 15,
|
||||||
|
"return-type": "json",
|
||||||
|
"exec": "$HOME/.config/waybar/scripts/openai-rate.py --window secondary",
|
||||||
|
"exec-if": "test -r ~/.codex/auth.json",
|
||||||
|
"escape": false,
|
||||||
|
"format": " {}"
|
||||||
|
},
|
||||||
"custom/backup": {
|
"custom/backup": {
|
||||||
"interval": 60,
|
"interval": 60,
|
||||||
"format": "",
|
"format": "",
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
__pycache__
|
||||||
+178
@@ -0,0 +1,178 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import json
|
||||||
|
import math
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import urllib.error
|
||||||
|
import urllib.request
|
||||||
|
from argparse import ArgumentParser
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
DEFAULT_BASE_URL = "https://chatgpt.com/backend-api"
|
||||||
|
|
||||||
|
|
||||||
|
def waybar(text, klass="good", tooltip=None):
|
||||||
|
print(json.dumps({"text": text, "class": klass, "tooltip": tooltip or text}))
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
def unknown(tooltip):
|
||||||
|
waybar('unk <span font-size="7pt">--</span>', "good", tooltip)
|
||||||
|
|
||||||
|
|
||||||
|
def load_auth():
|
||||||
|
auth_path = Path(os.environ.get("OPENAI_AUTH_FILE", "~/.codex/auth.json")).expanduser()
|
||||||
|
auth = {}
|
||||||
|
if auth_path.is_file():
|
||||||
|
try:
|
||||||
|
auth = json.loads(auth_path.read_text())
|
||||||
|
except (OSError, json.JSONDecodeError):
|
||||||
|
waybar("AI auth unreadable", "critical", f"Could not parse {auth_path}")
|
||||||
|
|
||||||
|
tokens = auth.get("tokens") or {}
|
||||||
|
access_token = tokens.get("access_token")
|
||||||
|
account_id = tokens.get("account_id")
|
||||||
|
if access_token:
|
||||||
|
return access_token, account_id, "~/.codex/auth.json:tokens.access_token"
|
||||||
|
|
||||||
|
api_key = auth.get("OPENAI_API_KEY") or os.environ.get("OPENAI_API_KEY")
|
||||||
|
if api_key:
|
||||||
|
source = "~/.codex/auth.json:OPENAI_API_KEY" if auth.get("OPENAI_API_KEY") else "env:OPENAI_API_KEY"
|
||||||
|
return api_key, account_id, source
|
||||||
|
|
||||||
|
waybar(
|
||||||
|
"AI auth missing",
|
||||||
|
"warning",
|
||||||
|
"No Codex access token found in ~/.codex/auth.json",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def usage_url():
|
||||||
|
base = os.environ.get("CODEX_RATE_BASE_URL") or os.environ.get("OPENAI_BASE_URL") or DEFAULT_BASE_URL
|
||||||
|
base = base.rstrip("/")
|
||||||
|
if base.endswith("/backend-api/codex"):
|
||||||
|
base = base.removesuffix("/codex")
|
||||||
|
if base.startswith(("https://chatgpt.com", "https://chat.openai.com")) and "/backend-api" not in base:
|
||||||
|
base += "/backend-api"
|
||||||
|
return f"{base}/wham/usage" if "/backend-api" in base else f"{base}/api/codex/usage"
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_usage(token, account_id):
|
||||||
|
headers = {
|
||||||
|
"Authorization": f"Bearer {token}",
|
||||||
|
"Accept": "application/json",
|
||||||
|
"User-Agent": "codex-waybar-rate",
|
||||||
|
}
|
||||||
|
if account_id:
|
||||||
|
headers["ChatGPT-Account-ID"] = account_id
|
||||||
|
|
||||||
|
req = urllib.request.Request(usage_url(), headers=headers)
|
||||||
|
try:
|
||||||
|
with urllib.request.urlopen(req, timeout=15) as res:
|
||||||
|
return json.load(res)
|
||||||
|
except urllib.error.HTTPError as exc:
|
||||||
|
detail = exc.read().decode("utf-8", "replace")[:240]
|
||||||
|
unknown(f"Usage endpoint returned HTTP {exc.code}\n{detail}")
|
||||||
|
except (OSError, TimeoutError) as exc:
|
||||||
|
unknown(f"Could not reach Codex usage endpoint\n{exc}")
|
||||||
|
except json.JSONDecodeError as exc:
|
||||||
|
unknown(f"Usage endpoint returned invalid JSON\n{exc}")
|
||||||
|
|
||||||
|
|
||||||
|
def window_label(seconds):
|
||||||
|
if not isinstance(seconds, int) or seconds <= 0:
|
||||||
|
return "usage"
|
||||||
|
|
||||||
|
minutes = math.ceil(seconds / 60)
|
||||||
|
if minutes < 90:
|
||||||
|
return f"{minutes}m"
|
||||||
|
|
||||||
|
hours = round(minutes / 60)
|
||||||
|
if hours < 36:
|
||||||
|
return f"{hours}h"
|
||||||
|
|
||||||
|
days = round(hours / 24)
|
||||||
|
if days < 365:
|
||||||
|
return f"{days}d"
|
||||||
|
|
||||||
|
return f"{round(days / 365)}y"
|
||||||
|
|
||||||
|
|
||||||
|
def reset_text(seconds):
|
||||||
|
if not isinstance(seconds, int) or seconds < 0:
|
||||||
|
return None
|
||||||
|
if seconds < 90:
|
||||||
|
return f"{seconds}s"
|
||||||
|
minutes = round(seconds / 60)
|
||||||
|
if minutes < 90:
|
||||||
|
return f"{minutes}m"
|
||||||
|
hours = round(minutes / 60)
|
||||||
|
if hours < 48:
|
||||||
|
return f"{hours}h"
|
||||||
|
return f"{round(hours / 24)}d"
|
||||||
|
|
||||||
|
|
||||||
|
def usage_class(used, limit_reached=False):
|
||||||
|
if limit_reached or used >= 95:
|
||||||
|
return "critical"
|
||||||
|
if used >= 90:
|
||||||
|
return "warning"
|
||||||
|
if used >= 75:
|
||||||
|
return "regular"
|
||||||
|
return "good"
|
||||||
|
|
||||||
|
|
||||||
|
def format_window(name, window):
|
||||||
|
if not isinstance(window, dict):
|
||||||
|
return None
|
||||||
|
|
||||||
|
used = window.get("used_percent")
|
||||||
|
if not isinstance(used, int):
|
||||||
|
return None
|
||||||
|
|
||||||
|
label = window_label(window.get("limit_window_seconds"))
|
||||||
|
left = max(0, 100 - used)
|
||||||
|
reset = reset_text(window.get("reset_after_seconds"))
|
||||||
|
tooltip = f"{name} {label}: {used}% used"
|
||||||
|
if reset:
|
||||||
|
tooltip += f", resets in {reset}"
|
||||||
|
return {
|
||||||
|
"label": label,
|
||||||
|
"left": left,
|
||||||
|
"used": used,
|
||||||
|
"tooltip": tooltip,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def output_window(data, source, selector):
|
||||||
|
rate_limit = data.get("rate_limit") or {}
|
||||||
|
window = format_window(selector, rate_limit.get(f"{selector}_window"))
|
||||||
|
if not window:
|
||||||
|
waybar("n/a", "warning", f"No {selector} usage window returned\nsource: {source}")
|
||||||
|
|
||||||
|
klass = usage_class(window["used"], rate_limit.get("limit_reached"))
|
||||||
|
tooltip = [window["tooltip"], f"{window['left']}% remaining", f"source: {source}"]
|
||||||
|
waybar(f"{window['left']}% <span font-size=\"7pt\">{window['label']}</span>", klass, "\n".join(tooltip))
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args():
|
||||||
|
parser = ArgumentParser(description="Waybar Codex rate-limit widget")
|
||||||
|
parser.add_argument(
|
||||||
|
"--window",
|
||||||
|
choices=("primary", "secondary"),
|
||||||
|
required=True,
|
||||||
|
help="usage window to display",
|
||||||
|
)
|
||||||
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
args = parse_args()
|
||||||
|
token, account_id, source = load_auth()
|
||||||
|
data = fetch_usage(token, account_id)
|
||||||
|
output_window(data, source, args.window)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -66,6 +66,18 @@ window#waybar {
|
|||||||
color: #fabd2f;
|
color: #fabd2f;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
#workspaces button.special {
|
||||||
|
color: rgba(235, 219, 178, 0.55);
|
||||||
|
}
|
||||||
|
#workspaces button.special.empty {
|
||||||
|
color: rgba(235, 219, 178, 0.2);
|
||||||
|
}
|
||||||
|
#workspaces button.special.visible,
|
||||||
|
#workspaces button.special.active {
|
||||||
|
color: rgba(40, 40, 40, 0.8);
|
||||||
|
background: #d3869b;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
/* Window and mode display */
|
/* Window and mode display */
|
||||||
#window {
|
#window {
|
||||||
color: rgba(235, 219, 178, 0.4);
|
color: rgba(235, 219, 178, 0.4);
|
||||||
@@ -226,3 +238,22 @@ window#waybar.fullscreen #window {
|
|||||||
color: #ebdbb2;
|
color: #ebdbb2;
|
||||||
padding: 0 1em;
|
padding: 0 1em;
|
||||||
}
|
}
|
||||||
|
#custom-codex-primary,
|
||||||
|
#custom-codex-secondary {
|
||||||
|
padding: 0 1em;
|
||||||
|
color: rgba(235, 219, 178, 0.2);
|
||||||
|
}
|
||||||
|
#custom-codex-primary.regular,
|
||||||
|
#custom-codex-secondary.regular {
|
||||||
|
color: #ebdbb2;
|
||||||
|
}
|
||||||
|
#custom-codex-primary.warning,
|
||||||
|
#custom-codex-secondary.warning {
|
||||||
|
color: #fabd2f;
|
||||||
|
}
|
||||||
|
#custom-codex-primary.critical,
|
||||||
|
#custom-codex-secondary.critical {
|
||||||
|
color: #282828;
|
||||||
|
background: #fb4934;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user