style(design): apply Minimal Dark Mode design system\n\n- Switched to complete Dark Mode (OLED-friendly)\n- Established standard color palette matching modern technical aesthetics\n- Changed font to Plus Jakarta Sans for increased legibility\n- Enforced dark mode as default in App.tsx\n- Fixed several React cascading render (setState in useEffect) warnings

This commit is contained in:
Gemini Nexus Bot committed 2026-04-28 03:49:34 -03:00
1 parent b80531f8a8
commit bac7b5591e
738 files changed
+138513 -138123

No files matched your search

+197 -197
View File
@@ -1,197 +1,197 @@
# Gemini Nexus CLI
A powerful command-line interface for managing multiple Google Cloud AI accounts in Gemini Nexus IDE. Supports account switching, quota monitoring, token validation, and more.
## Features
- 📊 **Quota Management**: Real-time quota tracking for Gemini and Claude models
- 🔄 **Account Switching**: Seamless switching between multiple Google accounts
- ✅ **Token Validation**: Automatic token refresh and expiry detection
- 🎯 **Smart Auto-Switch**: Automatically select the best account based on quotas
- 🏷️ **Aliases**: Create shortcuts for your accounts (e.g., `work`, `personal`)
- 💾 **Backup/Restore**: Export and import account configurations
- 🔍 **Database Sync**: View sync status between CLI and IDE
- 🩺 **Diagnostics**: System health checks and troubleshooting
- 🎮 **Interactive Mode**: Beautiful keyboard-navigated menus
## Installation
1. **Install Python dependencies:**
```powershell
pip install -r requirements.txt
```
2. **(Optional) Add to PATH:**
```powershell
.\agm.bat setup-path
```
Restart your terminal after this.
## Quick Start
### Interactive Mode (Recommended for Beginners)
Simply run without arguments to get a menu:
```powershell
.\agm.bat
```
### Command Line (For Power Users)
**List all accounts:**
```powershell
agm list
```
**Switch accounts:**
```powershell
agm switch [email protected]
# or use partial match
agm switch aleks
```
**Refresh quotas:**
```powershell
agm refresh-all
# or single account
agm refresh [email protected]
```
**Validate tokens:**
```powershell
agm validate
```
**Quick status:**
```powershell
agm status
```
## Advanced Features
### Aliases
Create shortcuts for frequently used accounts:
```powershell
# Set alias
agm alias work [email protected]
# Use alias
agm switch work
agm refresh work
# View all aliases
agm alias
# Remove alias
agm unalias work
```
### Auto-Switch
Automatically switch to the account with the best quota:
```powershell
# Any model with at least 50% quota
agm auto-switch
# Prefer Claude models
agm auto-switch --model claude --min-quota 30
# Prefer Gemini models
agm auto-switch --model gemini --min-quota 60
```
### Backup & Restore
```powershell
# Export accounts
agm export my_accounts.json
# Import accounts
agm import-backup my_accounts.json
```
### Live Monitoring
Watch quota changes in real-time:
```powershell
# Refresh every 10 seconds (default)
agm watch
# Custom interval
agm watch --interval 30
```
### Diagnostics
Run system health checks:
```powershell
agm doctor
```
## All Commands
| Command | Description |
|---------|-------------|
| `agm list` | List all accounts with quota summary |
| `agm info <email>` | Show detailed quota for specific account |
| `agm switch <email>` | Switch to an account |
| `agm status` | Show active account and quotas |
| `agm refresh <email>` | Refresh quotas for one account |
| `agm refresh-all` | Refresh quotas for all accounts |
| `agm validate` | Check and refresh expired tokens |
| `agm sync` | Show CLI ↔ IDE sync status |
| `agm remove <email>` | Delete an account from database |
| `agm alias [name] [email]` | Manage account aliases |
| `agm unalias <name>` | Remove an alias |
| `agm export [file]` | Export accounts to JSON |
| `agm import-backup <file>` | Import accounts from backup |
| `agm auto-switch` | Switch to best account automatically |
| `agm watch [--interval N]` | Live quota monitoring |
| `agm doctor` | Run system diagnostics |
| `agm setup-path` | Add CLI to Windows PATH |
## How It Works
The CLI interacts directly with the Gemini Nexus's encrypted database:
1. **Database Location**: Auto-discovers the database in `%APPDATA%\GeminiNexus` or IDE installation directory
2. **Encryption**: Uses Windows DPAPI + AES-256-GCM to decrypt account tokens (same as the IDE)
3. **Quota API**: Fetches live quota data from Google Cloud AI REST API
4. **Process Control**: Can start/stop the IDE executable automatically
## Troubleshooting
**"Database not found" error:**
- Ensure Gemini Nexus has been run at least once
- Check if accounts exist in the GUI first
**"Failed to decrypt" error:**
- Run `agm doctor` to diagnose
- Ensure you're on the same Windows user account
**"Token expired" warnings:**
- Run `agm validate` to auto-refresh all tokens
**Quota not updating:**
- Run `agm refresh-all` to force update
- Check network connection
## Requirements
- Windows 10/11
- Python 3.8+
- Gemini Nexus (for database access)
## Development
The CLI is split into two main modules:
- **`core.py`**: Core functionality (database access, API calls, encryption)
- **`main.py`**: Command definitions and interactive UI
- **`proto_utils.py`**: Protobuf encoding helpers
## License
Same as parent project.
## Contributing
Pull requests welcome! Please ensure:
- Code follows existing style
- All commands have help text
- No hardcoded paths or credentials
# Gemini Nexus CLI
A powerful command-line interface for managing multiple Google Cloud AI accounts in Gemini Nexus IDE. Supports account switching, quota monitoring, token validation, and more.
## Features
- 📊 **Quota Management**: Real-time quota tracking for Gemini and Claude models
- 🔄 **Account Switching**: Seamless switching between multiple Google accounts
- ✅ **Token Validation**: Automatic token refresh and expiry detection
- 🎯 **Smart Auto-Switch**: Automatically select the best account based on quotas
- 🏷️ **Aliases**: Create shortcuts for your accounts (e.g., `work`, `personal`)
- 💾 **Backup/Restore**: Export and import account configurations
- 🔍 **Database Sync**: View sync status between CLI and IDE
- 🩺 **Diagnostics**: System health checks and troubleshooting
- 🎮 **Interactive Mode**: Beautiful keyboard-navigated menus
## Installation
1. **Install Python dependencies:**
```powershell
pip install -r requirements.txt
```
2. **(Optional) Add to PATH:**
```powershell
.\agm.bat setup-path
```
Restart your terminal after this.
## Quick Start
### Interactive Mode (Recommended for Beginners)
Simply run without arguments to get a menu:
```powershell
.\agm.bat
```
### Command Line (For Power Users)
**List all accounts:**
```powershell
agm list
```
**Switch accounts:**
```powershell
agm switch [email protected]
# or use partial match
agm switch aleks
```
**Refresh quotas:**
```powershell
agm refresh-all
# or single account
agm refresh [email protected]
```
**Validate tokens:**
```powershell
agm validate
```
**Quick status:**
```powershell
agm status
```
## Advanced Features
### Aliases
Create shortcuts for frequently used accounts:
```powershell
# Set alias
agm alias work [email protected]
# Use alias
agm switch work
agm refresh work
# View all aliases
agm alias
# Remove alias
agm unalias work
```
### Auto-Switch
Automatically switch to the account with the best quota:
```powershell
# Any model with at least 50% quota
agm auto-switch
# Prefer Claude models
agm auto-switch --model claude --min-quota 30
# Prefer Gemini models
agm auto-switch --model gemini --min-quota 60
```
### Backup & Restore
```powershell
# Export accounts
agm export my_accounts.json
# Import accounts
agm import-backup my_accounts.json
```
### Live Monitoring
Watch quota changes in real-time:
```powershell
# Refresh every 10 seconds (default)
agm watch
# Custom interval
agm watch --interval 30
```
### Diagnostics
Run system health checks:
```powershell
agm doctor
```
## All Commands
| Command | Description |
|---------|-------------|
| `agm list` | List all accounts with quota summary |
| `agm info <email>` | Show detailed quota for specific account |
| `agm switch <email>` | Switch to an account |
| `agm status` | Show active account and quotas |
| `agm refresh <email>` | Refresh quotas for one account |
| `agm refresh-all` | Refresh quotas for all accounts |
| `agm validate` | Check and refresh expired tokens |
| `agm sync` | Show CLI ↔ IDE sync status |
| `agm remove <email>` | Delete an account from database |
| `agm alias [name] [email]` | Manage account aliases |
| `agm unalias <name>` | Remove an alias |
| `agm export [file]` | Export accounts to JSON |
| `agm import-backup <file>` | Import accounts from backup |
| `agm auto-switch` | Switch to best account automatically |
| `agm watch [--interval N]` | Live quota monitoring |
| `agm doctor` | Run system diagnostics |
| `agm setup-path` | Add CLI to Windows PATH |
## How It Works
The CLI interacts directly with the Gemini Nexus's encrypted database:
1. **Database Location**: Auto-discovers the database in `%APPDATA%\GeminiNexus` or IDE installation directory
2. **Encryption**: Uses Windows DPAPI + AES-256-GCM to decrypt account tokens (same as the IDE)
3. **Quota API**: Fetches live quota data from Google Cloud AI REST API
4. **Process Control**: Can start/stop the IDE executable automatically
## Troubleshooting
**"Database not found" error:**
- Ensure Gemini Nexus has been run at least once
- Check if accounts exist in the GUI first
**"Failed to decrypt" error:**
- Run `agm doctor` to diagnose
- Ensure you're on the same Windows user account
**"Token expired" warnings:**
- Run `agm validate` to auto-refresh all tokens
**Quota not updating:**
- Run `agm refresh-all` to force update
- Check network connection
## Requirements
- Windows 10/11
- Python 3.8+
- Gemini Nexus (for database access)
## Development
The CLI is split into two main modules:
- **`core.py`**: Core functionality (database access, API calls, encryption)
- **`main.py`**: Command definitions and interactive UI
- **`proto_utils.py`**: Protobuf encoding helpers
## License
Same as parent project.
## Contributing
Pull requests welcome! Please ensure:
- Code follows existing style
- All commands have help text
- No hardcoded paths or credentials
+930 -930
View File
File diff suppressed because it is too large. Load diff
+608 -608
View File
File diff suppressed because it is too large. Load diff
+126 -126
View File
@@ -1,126 +1,126 @@
# a:\UnityProjects\ManagerFork\AntigravityManager\cli\proto_utils.py
import struct
import base64
def encode_varint(value: int) -> bytes:
if value < 0:
raise ValueError("Varint encoding only supports non-negative integers. Use zigzag encoding for signed values.")
buf = []
val = value
while val >= 128:
buf.append((val & 0x7F) | 0x80)
val >>= 7
buf.append(val)
return bytes(buf)
def read_varint(data: bytes, offset: int) -> tuple[int, int]:
result = 0
shift = 0
pos = offset
length = len(data)
while pos < length:
byte = data[pos]
result |= (byte & 0x7F) << shift
pos += 1
if (byte & 0x80) == 0:
return result, pos
shift += 7
raise ValueError("Incomplete varint data")
def create_string_field(field_num: int, value: str) -> bytes:
tag = (field_num << 3) | 2
tag_bytes = encode_varint(tag)
value_bytes = value.encode('utf-8')
len_bytes = encode_varint(len(value_bytes))
return tag_bytes + len_bytes + value_bytes
def create_timestamp_field(field_num: int, seconds: int) -> bytes:
# Timestamp message: Field 1 (seconds) as varint.
# But usually Timestamp is int64, using varint here as per TS code logic.
inner_tag = (1 << 3) | 0
inner_tag_bytes = encode_varint(inner_tag)
seconds_bytes = encode_varint(seconds)
inner_msg = inner_tag_bytes + seconds_bytes
tag = (field_num << 3) | 2
tag_bytes = encode_varint(tag)
len_bytes = encode_varint(len(inner_msg))
return tag_bytes + len_bytes + inner_msg
def create_oauth_info(access_token: str, refresh_token: str, expiry: int) -> bytes:
# Field 1: Access Token (String)
field1 = create_string_field(1, access_token)
# Field 2: Type ("Bearer")
field2 = create_string_field(2, "Bearer")
# Field 3: Refresh Token (String)
field3 = create_string_field(3, refresh_token)
# Field 4: Expiry (Timestamp wrapped in len delim)
# The TS code creates a Timestamp message (field 1 -> seconds) wrapped as field 4 (len delim).
field4 = create_timestamp_field(4, expiry)
return field1 + field2 + field3 + field4
def create_unified_oauth_token(access_token: str, refresh_token: str, expiry: int) -> str:
oauth_info = create_oauth_info(access_token, refresh_token, expiry)
# Base64 encode the OAuth token info
oauth_info_b64 = base64.b64encode(oauth_info).decode('utf-8')
# Inner structure:
# Field 1: string "oauthTokenInfoSentinelKey"
# Field 2: string (base64 of oauth info) -> Wait, TS uses createStringField(1, oauthInfoB64) then createStringField(1, sentinel)??
# Let's re-read TS carefully:
# const inner1 = this.encodeStringField(1, 'oauthTokenInfoSentinelKey'); -> Field 1
# const inner2 = this.encodeStringField(1, oauthInfoB64); -> Actually createStringField(1, ...)
# WAIT! The TS code has:
# const inner2 = this.encodeStringField(1, oauthInfoB64);
# const inner1 = this.encodeStringField(1, 'oauthTokenInfoSentinelKey');
# const innerField2 = this.encodeLenDelimField(2, inner2); -> This seems wrong in my reading or TS variable naming.
# Ah, `inner2` is created as Field 1 string. Then it is wrapped in Field 2?
# Let's look at `createUnifiedOAuthToken` in TS again.
# TS Logic:
# 1. oauthInfo = createOAuthInfo(...)
# 2. oauthInfoB64 = base64(oauthInfo)
# 3. inner2 = encodeStringField(1, oauthInfoB64) -> This creates a Field 1 tag + len + string.
# 4. inner1 = encodeStringField(1, 'oauthTokenInfoSentinelKey') -> Field 1 tag + len + string.
# 5. innerField2 = encodeLenDelimField(2, inner2) -> Wraps `inner2` (which is already a field) into a Field 2?
# No, `encodeLenDelimField` takes `data` and wraps it. So `inner2` content becomes body of Field 2.
# So Field 2 contains (Field 1: oauthInfoB64).
# 6. inner = inner1 + innerField2
# So `inner` is:
# Field 1: "oauthTokenInfoSentinelKey"
# Field 2: { Field 1: "base64..." }
# 7. outer = encodeLenDelimField(1, inner)
# So `outer` is:
# Field 1: { ...inner... }
# 8. Return base64(outer).
# My implementation:
inner2_data = create_string_field(1, oauth_info_b64)
# Wrap inner2_data in Field 2
# encode_len_delim_field(2, inner2_data) logic:
tag2 = (2 << 3) | 2
tag2_bytes = encode_varint(tag2)
len2_bytes = encode_varint(len(inner2_data))
inner_field2 = tag2_bytes + len2_bytes + inner2_data
inner1 = create_string_field(1, 'oauthTokenInfoSentinelKey')
inner = inner1 + inner_field2
# Wrap inner in Field 1
tag1 = (1 << 3) | 2
tag1_bytes = encode_varint(tag1)
len1_bytes = encode_varint(len(inner))
outer = tag1_bytes + len1_bytes + inner
return base64.b64encode(outer).decode('utf-8')
# a:\UnityProjects\ManagerFork\AntigravityManager\cli\proto_utils.py
import struct
import base64
def encode_varint(value: int) -> bytes:
if value < 0:
raise ValueError("Varint encoding only supports non-negative integers. Use zigzag encoding for signed values.")
buf = []
val = value
while val >= 128:
buf.append((val & 0x7F) | 0x80)
val >>= 7
buf.append(val)
return bytes(buf)
def read_varint(data: bytes, offset: int) -> tuple[int, int]:
result = 0
shift = 0
pos = offset
length = len(data)
while pos < length:
byte = data[pos]
result |= (byte & 0x7F) << shift
pos += 1
if (byte & 0x80) == 0:
return result, pos
shift += 7
raise ValueError("Incomplete varint data")
def create_string_field(field_num: int, value: str) -> bytes:
tag = (field_num << 3) | 2
tag_bytes = encode_varint(tag)
value_bytes = value.encode('utf-8')
len_bytes = encode_varint(len(value_bytes))
return tag_bytes + len_bytes + value_bytes
def create_timestamp_field(field_num: int, seconds: int) -> bytes:
# Timestamp message: Field 1 (seconds) as varint.
# But usually Timestamp is int64, using varint here as per TS code logic.
inner_tag = (1 << 3) | 0
inner_tag_bytes = encode_varint(inner_tag)
seconds_bytes = encode_varint(seconds)
inner_msg = inner_tag_bytes + seconds_bytes
tag = (field_num << 3) | 2
tag_bytes = encode_varint(tag)
len_bytes = encode_varint(len(inner_msg))
return tag_bytes + len_bytes + inner_msg
def create_oauth_info(access_token: str, refresh_token: str, expiry: int) -> bytes:
# Field 1: Access Token (String)
field1 = create_string_field(1, access_token)
# Field 2: Type ("Bearer")
field2 = create_string_field(2, "Bearer")
# Field 3: Refresh Token (String)
field3 = create_string_field(3, refresh_token)
# Field 4: Expiry (Timestamp wrapped in len delim)
# The TS code creates a Timestamp message (field 1 -> seconds) wrapped as field 4 (len delim).
field4 = create_timestamp_field(4, expiry)
return field1 + field2 + field3 + field4
def create_unified_oauth_token(access_token: str, refresh_token: str, expiry: int) -> str:
oauth_info = create_oauth_info(access_token, refresh_token, expiry)
# Base64 encode the OAuth token info
oauth_info_b64 = base64.b64encode(oauth_info).decode('utf-8')
# Inner structure:
# Field 1: string "oauthTokenInfoSentinelKey"
# Field 2: string (base64 of oauth info) -> Wait, TS uses createStringField(1, oauthInfoB64) then createStringField(1, sentinel)??
# Let's re-read TS carefully:
# const inner1 = this.encodeStringField(1, 'oauthTokenInfoSentinelKey'); -> Field 1
# const inner2 = this.encodeStringField(1, oauthInfoB64); -> Actually createStringField(1, ...)
# WAIT! The TS code has:
# const inner2 = this.encodeStringField(1, oauthInfoB64);
# const inner1 = this.encodeStringField(1, 'oauthTokenInfoSentinelKey');
# const innerField2 = this.encodeLenDelimField(2, inner2); -> This seems wrong in my reading or TS variable naming.
# Ah, `inner2` is created as Field 1 string. Then it is wrapped in Field 2?
# Let's look at `createUnifiedOAuthToken` in TS again.
# TS Logic:
# 1. oauthInfo = createOAuthInfo(...)
# 2. oauthInfoB64 = base64(oauthInfo)
# 3. inner2 = encodeStringField(1, oauthInfoB64) -> This creates a Field 1 tag + len + string.
# 4. inner1 = encodeStringField(1, 'oauthTokenInfoSentinelKey') -> Field 1 tag + len + string.
# 5. innerField2 = encodeLenDelimField(2, inner2) -> Wraps `inner2` (which is already a field) into a Field 2?
# No, `encodeLenDelimField` takes `data` and wraps it. So `inner2` content becomes body of Field 2.
# So Field 2 contains (Field 1: oauthInfoB64).
# 6. inner = inner1 + innerField2
# So `inner` is:
# Field 1: "oauthTokenInfoSentinelKey"
# Field 2: { Field 1: "base64..." }
# 7. outer = encodeLenDelimField(1, inner)
# So `outer` is:
# Field 1: { ...inner... }
# 8. Return base64(outer).
# My implementation:
inner2_data = create_string_field(1, oauth_info_b64)
# Wrap inner2_data in Field 2
# encode_len_delim_field(2, inner2_data) logic:
tag2 = (2 << 3) | 2
tag2_bytes = encode_varint(tag2)
len2_bytes = encode_varint(len(inner2_data))
inner_field2 = tag2_bytes + len2_bytes + inner2_data
inner1 = create_string_field(1, 'oauthTokenInfoSentinelKey')
inner = inner1 + inner_field2
# Wrap inner in Field 1
tag1 = (1 << 3) | 2
tag1_bytes = encode_varint(tag1)
len1_bytes = encode_varint(len(inner))
outer = tag1_bytes + len1_bytes + inner
return base64.b64encode(outer).decode('utf-8')
+9 -9
View File
@@ -1,9 +1,9 @@
typer
rich
questionary
pywin32; sys_platform == "win32"
configparser
psutil
protobuf
cryptography
httpx
typer
rich
questionary
pywin32; sys_platform == "win32"
configparser
psutil
protobuf
cryptography
httpx