# Gspot: Gcloud auth monitoring for long-running coding agents

> Source: <https://github.com/Somnora/GSPOT>
> Published: 2026-08-23 00:28:44+00:00

"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 displays`G-Spot: Active`

,`G-Spot: Partial Auth`

,`G-Spot: Expired`

, or`G-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 action`Test 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 run`gcloud auth login && gcloud auth application-default login`

interactively.**Instant Project Switcher:** View recent projects and switch your active`gcloud`

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 named`gcloud`

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 (via`gcloud 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 is**not** a local setting — session length for Google Cloud (which covers the`gcloud`

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 (](https://cloud.google.com/sdk/docs/install)installed and initialized.`gcloud`

)

- Download
from the`GSPOT.exe`

[latest GitHub Release](https://github.com/Somnora/GSPOT/releases)or 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** and**right-click** open the status menu and quick switcher.

- Download
`G-Spot-1.0.0.dmg`

from the[latest GitHub Release](https://github.com/Somnora/GSPOT/releases). - Open the
`.dmg`

and drag**G-Spot.app** into your**Applications** 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+**.

```
# 1. Clone the repository
git clone https://github.com/Somnora/GSPOT.git && cd GSPOT

# 2. Create and activate a virtual environment
python3 -m venv .venv
source .venv/bin/activate    # On Windows: .venv\Scripts\activate

# 3. Install dependencies
pip install -r requirements.txt

# 4. Launch G-Spot
python main.py
pip install pyinstaller
.\windows\build_windows.bat
# Output: dist\GSPOT.exe and windows\GSPOT.exe
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
# Options:
#   -i, --interval SECONDS   Polling check interval in seconds (default: 300 / 5 minutes)
#   --debug                  Enable verbose debug logging
#   -v, --version            Display version information
#
# Headless mode (no tray, no display required):
#   --status                 Print current status and exit 0
#   --json                   With --status, print JSON instead of one line
#   --check                  Exit 0 if at least --min-minutes remain, else 1
#   --renew                  Force a non-interactive refresh; exit 0 on success
#   --min-minutes N          Minutes required (--check: 30, --renew: 55)
```

`--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.

``` bash
$ 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`

, type`shell:startup`

, and hit**Enter**. - 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"
```

*(Using*`pythonw.exe`

runs Python without opening a Command Prompt window). - Click
**Next**, name it`G-Spot`

, and click**Finish**.

- 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 loading 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.
