"Hard to find, but keeps everything running smoothly."
G-Spot is a lightweight, cross-platform system tray application for macOS, Windows, and Linux that continuously monitors Google Cloud SDK authentication, tracks active GCP project configurations, inspects billing, and prevents AI coding agents (Gemini, Claude Code, Cursor, Copilot, LangChain, etc.) from timing out due to expired OAuth or Application Default Credentials (ADC).
Dynamic GCP-Inspired Generic Cloud Icon System (Pillow ImageDraw):- Programmatically rendered in Python using Pillow without using official trademarked logos.
- Dynamically colored based on credential status using Google's signature color palette:
Solid Green ( Both User (
#34A853
):gcloud auth
) and ADC (application-default
) tokens are active.Solid Yellow ( Partial authentication (only one set of tokens is active).#FBBC05
):Solid Red ( Both tokens are expired, missing, or disconnected.#EA4335
):Solid Blue ( Initializing / actively refreshing.#4285F4
):Solid Gray ( Offline β no network reachable, so credential status is unknown. Distinguished from Red deliberately: a red icon on a plane is a false alarm, and false alarms train you to ignore the icon.#80868B
):
Dynamic Tooltips: Hover displaysG-Spot: Active
,G-Spot: Partial Auth
,G-Spot: Expired
, orG-Spot: Offline (no network)
alongside the active project ID (e.g.G-Spot: Active (my-project-id)
).Identity & Token Expiry Header: Shows the authenticated Google account email and remaining token lifetime (e.g.,Account: dev@company.com (~48m left)
).Proactive Desktop Notifications & Interactive Modal Popups:- Fires native OS desktop notification banners whenever authentication drops from Green to Yellow/Red β and on the first check after launch, since opening your laptop to a session that died overnight is the most common way this bites you. - Stays quiet when the cause is simply no network: G-Spot will not ask you to re-authenticate against a server it can't reach.
- Automatically pops up an
Interactive System Modal Alert Dialog with a 1-click
[Re-Authenticate Now]
action button when authentication is lost, immediately opening the terminal login flow. - Toggleable via
Popup on Auth Loss: [ON/OFF]
with an instant preview actionTest Auth Loss Popup Now
.
- Fires native OS desktop notification banners whenever authentication drops from Green to Yellow/Red β
One-Click Re-Authentication:"Connect All (User + ADC)" launches your native terminal emulator (
Terminal.app
on macOS, Command Prompt / PowerShell on Windows, or standard Linux emulators) to rungcloud auth login && gcloud auth application-default login
interactively.Instant Project Switcher: View recent projects and switch your activegcloud
project directly from the tray context menu without opening a shell.Cloud Console Launcher: Quick-jump directly to the Cloud Console dashboard for the currently active project.
To keep the primary tray menu lightweight and clutter-free, pro-developer utilities are housed in the Developer & Agent Tools submenu:
Copy Agent Env Vars (Export): Copies standard shell export statements directly to the system clipboard:
export GOOGLE_CLOUD_PROJECT="active-project-id"
export CLOUDSDK_CORE_PROJECT="active-project-id"
export GOOGLE_APPLICATION_CREDENTIALS="/path/to/application_default_credentials.json"
export VERTEX_AI_PROJECT="active-project-id"
Run Agent Health Doctor: Runs a diagnostic scan verifying physical ADC file paths, token expiry, network reachability, and detecting any conflicting environment variables (GOOGLE_APPLICATION_CREDENTIALS
,CLOUDSDK_AUTH_ACCESS_TOKEN
). The full report is copied to your clipboard β a nine-field diagnostic doesn't fit in a notification banner.Switch SDK Profile / Config: Submenu to list and activate namedgcloud
configuration profiles (default
,staging
,production
).Billing & Spend Snapshot: Real-time visibility into project billing account linkage and status, with automatic fallback when missing billing IAM permissions.Auto-Renew Tokens: Toggleable background watcher that performs a real non-interactive refresh (viagcloud config config-helper --min-expiry
) when fewer than 10 minutes remain. If the refresh does not extend the token, the underlying session has expired and G-Spot says so rather than reporting a success it didn't achieve.Session Policy: Direct links to the settings that actually govern how long your credentials live. Token lifetime isnot a local setting β session length for Google Cloud (which covers thegcloud
CLI and ADC, not just the web console) is a Google Workspace admin policy, configurable between 1 and 24 hours by someone holding the Security Settings privilege. G-Spot detects whether your active account belongs to a Workspace domain and either links you to the right admin page or explains why no such policy exists for a personal account.
Google Cloud SDK (installed and initialized.gcloud
)
- Download
from the
GSPOT.exe
latest GitHub Releaseor find it directly in.windows/GSPOT.exe
-
Double-click to launch.
GSPOT.exe -
G-Spot will appear in your Windows notification area / taskbar tray (bottom-right near the clock, or inside the
^
overflow menu). - Both left-click andright-click open the status menu and quick switcher.
- Download
G-Spot-1.0.0.dmg
from thelatest GitHub Release. - Open the
.dmg
and dragG-Spot.app into yourApplications folder. - Launch G-Spot from Launchpad or Spotlight.
Note:On first launch macOS may show a Gatekeeper warning since the app is not notarized. Right-click the app βOpenβOpento bypass.
Requires Python 3.9+.
git clone https://github.com/Somnora/GSPOT.git && cd GSPOT
python3 -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
pip install -r requirements.txt
python main.py
pip install pyinstaller
.\windows\build_windows.bat
pip install pyinstaller
python macos/generate_icns.py # Generate macOS .icns app icon
pyinstaller macos/G-Spot.spec --noconfirm --clean
bash macos/build_dmg.sh # Output: dist/G-Spot-1.0.0.dmg
python main.py --help
#
--status
, --check
and --renew
run without a tray and without a display. They
never import pystray
, so they work over SSH, in a container, or in CI where importing a GUI toolkit would raise.
The failure this tool exists to prevent is starting a long agent run on a token that dies halfway through. Gate the run instead of being notified about it:
python main.py --check --min-minutes 30 || python main.py --renew
Exit 0 means you have at least 30 minutes and can safely start. Exit 1 means the
credential could not be brought up to that mark and only an interactive
gcloud auth login
will help.
$ python main.py --status
GREEN | you@example.com | my-project | ~42m left | user=ok adc=ok
$ python main.py --status --json
{
"state": "green",
"account": "you@example.com",
"project": "my-project",
"token_expiry_minutes": 42,
"adc_valid": true,
"user_valid": true,
"network_reachable": true
}
state
is one of green
, yellow
, red
, offline
, searching
. JSON goes to
stdout and diagnostics to stderr, so --status --json 2>/dev/null
is safe to
pipe straight into jq
. --status
always exits 0 β it reports, it does not gate.
network_reachable
is only actively probed when every auth check has already
failed, so true
means "not proven unreachable" rather than "just pinged".
| Command | 0 | 1 |
|---|---|---|
--status |
||
| always | β | |
--check |
||
at least --min-minutes remain |
||
| too little time, expired, offline, unknown expiry, or no gcloud | ||
--renew |
||
refreshed, or already above --min-minutes |
||
| refresh could not extend the credential |
--check
fails closed: anything it cannot positively verify is a failure, since a false pass costs you a dead agent run. Partial auth (one of user/ADC missing) still exits 0 if there is enough time, but prints a warning to stderr.
--renew
exits 0 when the token was already comfortable and gcloud declined to refresh it β that is the normal case in the one-liner above, not an error.
To have G-Spot run automatically in the background on system boot without showing a terminal window:
- Create a LaunchAgent plist file at
~/Library/LaunchAgents/com.user.gspot.plist
:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.user.gspot</string>
<key>ProgramArguments</key>
<array>
<string>/Users/YOUR_USERNAME/Desktop/G_Spot/.venv/bin/python</string>
<string>/Users/YOUR_USERNAME/Desktop/G_Spot/main.py</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/tmp/gspot.log</string>
<key>StandardErrorPath</key>
<string>/tmp/gspot.err</string>
</dict>
</plist>
- Load and start the service:
launchctl load ~/Library/LaunchAgents/com.user.gspot.plist
- Press
Win + R
, typeshell:startup
, and hitEnter. - Right-click in the folder -> New->** Shortcut**. - For location, enter:
"C:\path\to\G_Spot\.venv\Scripts\pythonw.exe" "C:\path\to\G_Spot\main.py"
(Usingpythonw.exe
runs Python without opening a Command Prompt window). - Click
Next, name itG-Spot
, and clickFinish.
-
Open Task Scheduler->** Create Basic Task**. - Trigger: When I log on. - Action: Start a program. - Program:
C:\path\to\G_Spot\.venv\Scripts\pythonw.exe -
Arguments:
main.py -
Start in:
C:\path\to\G_Spot
Create ~/.config/autostart/gspot.desktop
:
[Desktop Entry]
Type=Application
Name=G-Spot
Comment=Google Status, Project, & Optimization Tool
Exec=/path/to/G_Spot/.venv/bin/python /path/to/G_Spot/main.py
Hidden=false
NoDisplay=false
X-GNOME-Autostart-enabled=true
- Create
~/.config/systemd/user/gspot.service
:
[Unit]
Description=G-Spot Background Tray Service
After=graphical-session.target
[Service]
Type=simple
WorkingDirectory=/path/to/G_Spot
ExecStart=/path/to/G_Spot/.venv/bin/python main.py
Restart=always
RestartSec=5
[Install]
WantedBy=default.target
- Enable and start:
systemctl --user daemon-reload
systemctl --user enable --now gspot.service
G-Spot/
βββ .github/
β βββ workflows/
β βββ release.yml # CI: Build .app, package .dmg, publish GitHub Release on tag
βββ assets/
β βββ icons/ # Monotone SVG icons (active, degraded, expired, actions)
β βββ preview/ # Pre-rendered PNG previews of each icon state
β βββ icon.icns # macOS app icon bundle (generated by scripts/generate_icns.py)
βββ gspot/
β βββ __init__.py # Package metadata & tagline
β βββ app.py # Pystray tray application loop & streamlined menu hierarchy
β βββ clipboard.py # Cross-platform clipboard copy helper (pbcopy / clip / xclip)
β βββ config.py # State enums (GREEN/YELLOW/RED), polling constants, status dataclass
β βββ gcloud.py # Subprocess wrappers for auth, ADC, projects, configs, diagnostics
β βββ headless.py # --status / --check / --renew CLI; imports no GUI dependencies
β βββ icons.py # High-DPI 8x supersampled Pillow dynamic tray icon generator
β βββ notifications.py # Desktop alert dispatcher + interactive modal popups
β βββ settings.py # Persisted user preferences (~/.config/gspot/config.json)
β βββ terminal.py # Native interactive terminal launcher for re-authentication
βββ scripts/
β βββ generate_icns.py # Renders app icon into macOS .iconset β .icns
β βββ build_dmg.sh # Packages G-Spot.app into a distributable .dmg installer
βββ tests/
β βββ __init__.py
β βββ test_auth_lifecycle.py # 40 tests for state transitions, offline handling, auto-renew
β βββ test_gcloud.py # 25 tests for gcloud wrappers, billing, and the health doctor
β βββ test_headless.py # 22 tests for headless exit codes and display independence
β βββ test_settings.py # 12 tests for preference persistence and corrupt-file fallback
βββ main.py # CLI entrypoint; dispatches headless mode before the tray
βββ G-Spot.spec # PyInstaller build specification
βββ requirements.txt # Dependencies (pystray, Pillow, plyer)
βββ README.md
Preferences you set from the tray menu (auto-renew, popup alerts, check interval) persist to:
| Platform | Path |
|---|---|
| macOS / Linux | ~/.config/gspot/config.json (respects XDG_CONFIG_HOME ) |
| Windows | %APPDATA%\gspot\config.json |
The file is written atomically and is safe to edit by hand; out-of-range or malformed values fall
back to defaults rather than preventing startup. --interval
overrides the saved value for a single run without rewriting it.
G-Spot cannot extend how long your credentials last, and neither can any other local tool. Access
tokens live about an hour and refresh transparently; what actually kills a long agent run is the
session behind them expiring. That session length is set by a Google Workspace administrator
(Admin console β Security β Access and data control β Google Cloud session control), ranges from
1 to 24 hours, and applies to the gcloud
CLI and ADC just as much as to the web console. There is no gcloud flag or config property that changes it.
What G-Spot does instead: refresh while refreshing still works, tell you clearly when it stops
working, and put the relevant admin page one click away under Developer & Agent Tools β Session
Policy. Personal @gmail.com
accounts have no Workspace org and therefore no such policy β the menu says so rather than linking you to a page you'd get denied on.
source .venv/bin/activate
python -m unittest discover -s tests -v
The suite mocks every gcloud
invocation and network call, so it runs without a Google Cloud SDK installation and without touching your real credentials.
MIT License. Built to keep AI agents authenticated and uninterrupted.