feat: rebrand to ZeroGravity, replace proxyctl with zg Rust binary
Phase 1 - Rename: - Crate: antigravity-proxy -> zerogravity - Env: ANTIGRAVITY_OAUTH_TOKEN -> ZEROGRAVITY_TOKEN - Paths: ~/.config/antigravity-proxy -> ~/.config/zerogravity - Paths: /tmp/antigravity-* -> /tmp/zerogravity-* - User: antigravity-ls -> zerogravity-ls - Service: antigravity-proxy -> zerogravity Phase 2 - zg daemon manager: - New Rust binary src/bin/zg.rs replaces scripts/proxyctl bash - Commands: start, stop, restart, rebuild, status, logs, test, health - Auto-resolves project dir from binary location - All commands exit immediately (safe for agent fast-bash)
This commit is contained in:
74
Cargo.lock
generated
74
Cargo.lock
generated
@@ -103,43 +103,6 @@ dependencies = [
|
|||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "antigravity-proxy"
|
|
||||||
version = "3.0.0"
|
|
||||||
dependencies = [
|
|
||||||
"async-stream",
|
|
||||||
"axum",
|
|
||||||
"base64",
|
|
||||||
"brotli 7.0.0",
|
|
||||||
"bytes",
|
|
||||||
"chrono",
|
|
||||||
"clap",
|
|
||||||
"flate2",
|
|
||||||
"http",
|
|
||||||
"http-body-util",
|
|
||||||
"httparse",
|
|
||||||
"hyper",
|
|
||||||
"hyper-util",
|
|
||||||
"rand",
|
|
||||||
"rcgen",
|
|
||||||
"regex",
|
|
||||||
"rustls",
|
|
||||||
"rustls-native-certs",
|
|
||||||
"rustls-pemfile",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"time",
|
|
||||||
"tokio",
|
|
||||||
"tokio-rustls",
|
|
||||||
"tokio-stream",
|
|
||||||
"tower-http",
|
|
||||||
"tracing",
|
|
||||||
"tracing-subscriber",
|
|
||||||
"uuid",
|
|
||||||
"wreq",
|
|
||||||
"wreq-util",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-stream"
|
name = "async-stream"
|
||||||
version = "0.3.6"
|
version = "0.3.6"
|
||||||
@@ -2396,6 +2359,43 @@ dependencies = [
|
|||||||
"synstructure",
|
"synstructure",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerogravity"
|
||||||
|
version = "3.0.0"
|
||||||
|
dependencies = [
|
||||||
|
"async-stream",
|
||||||
|
"axum",
|
||||||
|
"base64",
|
||||||
|
"brotli 7.0.0",
|
||||||
|
"bytes",
|
||||||
|
"chrono",
|
||||||
|
"clap",
|
||||||
|
"flate2",
|
||||||
|
"http",
|
||||||
|
"http-body-util",
|
||||||
|
"httparse",
|
||||||
|
"hyper",
|
||||||
|
"hyper-util",
|
||||||
|
"rand",
|
||||||
|
"rcgen",
|
||||||
|
"regex",
|
||||||
|
"rustls",
|
||||||
|
"rustls-native-certs",
|
||||||
|
"rustls-pemfile",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"time",
|
||||||
|
"tokio",
|
||||||
|
"tokio-rustls",
|
||||||
|
"tokio-stream",
|
||||||
|
"tower-http",
|
||||||
|
"tracing",
|
||||||
|
"tracing-subscriber",
|
||||||
|
"uuid",
|
||||||
|
"wreq",
|
||||||
|
"wreq-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zeroize"
|
name = "zeroize"
|
||||||
version = "1.8.2"
|
version = "1.8.2"
|
||||||
|
|||||||
10
Cargo.toml
10
Cargo.toml
@@ -1,8 +1,16 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "antigravity-proxy"
|
name = "zerogravity"
|
||||||
version = "3.0.0"
|
version = "3.0.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "zerogravity"
|
||||||
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "zg"
|
||||||
|
path = "src/bin/zg.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
axum = { version = "0.8", features = ["json"] }
|
axum = { version = "0.8", features = ["json"] }
|
||||||
tokio = { version = "1", features = ["full"] }
|
tokio = { version = "1", features = ["full"] }
|
||||||
|
|||||||
38
GEMINI.md
38
GEMINI.md
@@ -27,41 +27,41 @@ This "LS as dumb relay" pattern keeps the LS interactions minimal and predictabl
|
|||||||
|
|
||||||
## Agent Quick Reference
|
## Agent Quick Reference
|
||||||
|
|
||||||
### `proxyctl` — Daemon Manager
|
### `zg` — Daemon Manager
|
||||||
|
|
||||||
`proxyctl` commands exit immediately (not foreground) — safe for agent use via fast-bash MCP.
|
`zg` commands exit immediately (not foreground) — safe for agent use via fast-bash MCP.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Rebuild and restart after code changes
|
# Rebuild and restart after code changes
|
||||||
proxyctl restart
|
zg restart
|
||||||
|
|
||||||
# Quick test
|
# Quick test
|
||||||
proxyctl test "say hi in 3 words"
|
zg test "say hi in 3 words"
|
||||||
|
|
||||||
# Check status
|
# Check status
|
||||||
proxyctl status
|
zg status
|
||||||
|
|
||||||
# Check health
|
# Check health
|
||||||
proxyctl health
|
zg health
|
||||||
```
|
```
|
||||||
|
|
||||||
| Command | Description |
|
| Command | Description |
|
||||||
| --------------------- | ----------------------------------- |
|
| --------------------- | ----------------------------------- |
|
||||||
| `proxyctl start` | Start the proxy daemon |
|
| `zg start` | Start the proxy daemon |
|
||||||
| `proxyctl stop` | Stop the proxy daemon |
|
| `zg stop` | Stop the proxy daemon |
|
||||||
| `proxyctl restart` | Rebuild + restart |
|
| `zg restart` | Rebuild + restart |
|
||||||
| `proxyctl rebuild` | Build release binary only |
|
| `zg rebuild` | Build release binary only |
|
||||||
| `proxyctl status` | Service status + quota + usage |
|
| `zg status` | Service status + quota + usage |
|
||||||
| `proxyctl logs [N]` | Tail last N lines + follow |
|
| `zg logs [N]` | Tail last N lines + follow |
|
||||||
| `proxyctl logs-all` | Full log dump (no follow) |
|
| `zg logs-all` | Full log dump (no follow) |
|
||||||
| `proxyctl test [msg]` | Quick test request (gemini-3-flash) |
|
| `zg test [msg]` | Quick test request (gemini-3-flash) |
|
||||||
| `proxyctl health` | Health check |
|
| `zg health` | Health check |
|
||||||
|
|
||||||
### Testing After Changes
|
### Testing After Changes
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1. Rebuild + restart
|
# 1. Rebuild + restart
|
||||||
proxyctl restart
|
zg restart
|
||||||
|
|
||||||
# 2. Test an endpoint
|
# 2. Test an endpoint
|
||||||
curl -s http://localhost:8741/v1/chat/completions \
|
curl -s http://localhost:8741/v1/chat/completions \
|
||||||
@@ -69,7 +69,7 @@ curl -s http://localhost:8741/v1/chat/completions \
|
|||||||
-d '{"model": "gemini-3-flash", "messages": [{"role": "user", "content": "Say hi"}]}' | jq .
|
-d '{"model": "gemini-3-flash", "messages": [{"role": "user", "content": "Say hi"}]}' | jq .
|
||||||
|
|
||||||
# 3. Inspect latest trace
|
# 3. Inspect latest trace
|
||||||
TRACE_DIR=~/.config/antigravity-proxy/traces/$(date +%Y-%m-%d)
|
TRACE_DIR=~/.config/zerogravity/traces/$(date +%Y-%m-%d)
|
||||||
cat "$TRACE_DIR/$(ls -t "$TRACE_DIR" | head -1)/summary.md"
|
cat "$TRACE_DIR/$(ls -t "$TRACE_DIR" | head -1)/summary.md"
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -99,8 +99,8 @@ cat "$TRACE_DIR/$(ls -t "$TRACE_DIR" | head -1)/summary.md"
|
|||||||
|
|
||||||
The proxy needs an OAuth token:
|
The proxy needs an OAuth token:
|
||||||
|
|
||||||
1. **Env var**: `ANTIGRAVITY_OAUTH_TOKEN=ya29.xxx`
|
1. **Env var**: `ZEROGRAVITY_TOKEN=ya29.xxx`
|
||||||
2. **Token file**: `~/.config/antigravity-proxy-token`
|
2. **Token file**: `~/.config/zerogravity-token`
|
||||||
3. **Runtime**: `curl -X POST http://localhost:8741/v1/token -d '{"token":"ya29.xxx"}'`
|
3. **Runtime**: `curl -X POST http://localhost:8741/v1/token -d '{"token":"ya29.xxx"}'`
|
||||||
|
|
||||||
## CLI Flags
|
## CLI Flags
|
||||||
|
|||||||
26
README.md
26
README.md
@@ -23,10 +23,10 @@ graph LR
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Headless mode (no running Antigravity app needed)
|
# Headless mode (no running Antigravity app needed)
|
||||||
RUST_LOG=info ./target/release/antigravity-proxy --headless
|
RUST_LOG=info ./target/release/zerogravity --headless
|
||||||
|
|
||||||
# Or use the daemon manager
|
# Or use the daemon manager
|
||||||
proxyctl start
|
zg start
|
||||||
```
|
```
|
||||||
|
|
||||||
## Endpoints
|
## Endpoints
|
||||||
@@ -50,22 +50,22 @@ proxyctl start
|
|||||||
|
|
||||||
The proxy needs an OAuth token:
|
The proxy needs an OAuth token:
|
||||||
|
|
||||||
1. **Env var**: `ANTIGRAVITY_OAUTH_TOKEN=ya29.xxx`
|
1. **Env var**: `ZEROGRAVITY_TOKEN=ya29.xxx`
|
||||||
2. **Token file**: `~/.config/antigravity-proxy-token`
|
2. **Token file**: `~/.config/zerogravity-token`
|
||||||
3. **Runtime**: `curl -X POST http://localhost:8741/v1/token -d '{"token":"ya29.xxx"}'`
|
3. **Runtime**: `curl -X POST http://localhost:8741/v1/token -d '{"token":"ya29.xxx"}'`
|
||||||
|
|
||||||
## `proxyctl` Commands
|
## `zg` Commands
|
||||||
|
|
||||||
| Command | Description |
|
| Command | Description |
|
||||||
| --------------------- | ------------------------------ |
|
| --------------------- | ------------------------------ |
|
||||||
| `proxyctl start` | Start the proxy daemon |
|
| `zg start` | Start the proxy daemon |
|
||||||
| `proxyctl stop` | Stop the proxy daemon |
|
| `zg stop` | Stop the proxy daemon |
|
||||||
| `proxyctl restart` | Rebuild + restart |
|
| `zg restart` | Rebuild + restart |
|
||||||
| `proxyctl rebuild` | Build release binary only |
|
| `zg rebuild` | Build release binary only |
|
||||||
| `proxyctl status` | Service status + quota + usage |
|
| `zg status` | Service status + quota + usage |
|
||||||
| `proxyctl logs [N]` | Tail last N lines + follow |
|
| `zg logs [N]` | Tail last N lines + follow |
|
||||||
| `proxyctl test [msg]` | Quick test request |
|
| `zg test [msg]` | Quick test request |
|
||||||
| `proxyctl health` | Health check |
|
| `zg health` | Health check |
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
|
|||||||
@@ -132,11 +132,11 @@ Events dispatched through `tokio::sync::mpsc` channels from MITM → API handler
|
|||||||
### UID-Scoped iptables (Classic Mode)
|
### UID-Scoped iptables (Classic Mode)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# One-time setup — creates antigravity-ls user + iptables rule
|
# One-time setup — creates zerogravity-ls user + iptables rule
|
||||||
sudo ./scripts/mitm-redirect.sh install
|
sudo ./scripts/mitm-redirect.sh install
|
||||||
|
|
||||||
# Run proxy (standalone LS + MITM both enabled by default)
|
# Run proxy (standalone LS + MITM both enabled by default)
|
||||||
RUST_LOG=info ./target/release/antigravity-proxy
|
RUST_LOG=info ./target/release/zerogravity
|
||||||
|
|
||||||
# Check intercepted usage
|
# Check intercepted usage
|
||||||
curl -s http://localhost:8741/v1/usage | jq .
|
curl -s http://localhost:8741/v1/usage | jq .
|
||||||
@@ -150,7 +150,7 @@ sudo ./scripts/mitm-redirect.sh uninstall
|
|||||||
No iptables or sudo needed. The LS connects through `HTTPS_PROXY` instead:
|
No iptables or sudo needed. The LS connects through `HTTPS_PROXY` instead:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
RUST_LOG=info ./target/release/antigravity-proxy --headless
|
RUST_LOG=info ./target/release/zerogravity --headless
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ Per-call debug traces for inspecting request/response flow. Every API call write
|
|||||||
## Location
|
## Location
|
||||||
|
|
||||||
```
|
```
|
||||||
~/.config/antigravity-proxy/traces/{YYYY-MM-DD}/{HH-MM-SS.sss}_{cascade_short}/
|
~/.config/zerogravity/traces/{YYYY-MM-DD}/{HH-MM-SS.sss}_{cascade_short}/
|
||||||
```
|
```
|
||||||
|
|
||||||
Disable with `--no-trace`.
|
Disable with `--no-trace`.
|
||||||
@@ -108,11 +108,11 @@ Traces are designed for LLM consumption. To inspect the last trace:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Find latest trace
|
# Find latest trace
|
||||||
ls -t ~/.config/antigravity-proxy/traces/$(date +%Y-%m-%d)/ | head -1
|
ls -t ~/.config/zerogravity/traces/$(date +%Y-%m-%d)/ | head -1
|
||||||
|
|
||||||
# Read the summary
|
# Read the summary
|
||||||
cat ~/.config/antigravity-proxy/traces/$(date +%Y-%m-%d)/$(ls -t ~/.config/antigravity-proxy/traces/$(date +%Y-%m-%d)/ | head -1)/summary.md
|
cat ~/.config/zerogravity/traces/$(date +%Y-%m-%d)/$(ls -t ~/.config/zerogravity/traces/$(date +%Y-%m-%d)/ | head -1)/summary.md
|
||||||
|
|
||||||
# Grep for failures
|
# Grep for failures
|
||||||
grep 'outcome=.*error\|outcome=.*timeout' ~/.config/antigravity-proxy/traces/$(date +%Y-%m-%d)/*/meta.txt
|
grep 'outcome=.*error\|outcome=.*timeout' ~/.config/zerogravity/traces/$(date +%Y-%m-%d)/*/meta.txt
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
# modification, no system-wide changes.
|
# modification, no system-wide changes.
|
||||||
#
|
#
|
||||||
# Flow:
|
# Flow:
|
||||||
# 1. Standalone LS runs as 'antigravity-ls' user (via sudo -u)
|
# 1. Standalone LS runs as 'zerogravity-ls' user (via sudo -u)
|
||||||
# 2. iptables catches :443 traffic from that UID only → REDIRECT to MITM port
|
# 2. iptables catches :443 traffic from that UID only → REDIRECT to MITM port
|
||||||
# 3. MITM terminates TLS (Go client trusts our CA via SSL_CERT_FILE)
|
# 3. MITM terminates TLS (Go client trusts our CA via SSL_CERT_FILE)
|
||||||
# 4. MITM forwards upstream, captures usage
|
# 4. MITM forwards upstream, captures usage
|
||||||
@@ -24,10 +24,10 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
MITM_PORT="${2:-8742}"
|
MITM_PORT="${2:-8742}"
|
||||||
LS_USER="antigravity-ls"
|
LS_USER="zerogravity-ls"
|
||||||
DATA_DIR="/tmp/antigravity-standalone"
|
DATA_DIR="/tmp/antigravity-standalone"
|
||||||
LS_BINARY="/usr/share/antigravity/resources/app/extensions/antigravity/bin/language_server_linux_x64"
|
LS_BINARY="/usr/share/antigravity/resources/app/extensions/antigravity/bin/language_server_linux_x64"
|
||||||
SUDOERS_FILE="/etc/sudoers.d/antigravity-ls"
|
SUDOERS_FILE="/etc/sudoers.d/zerogravity-ls"
|
||||||
|
|
||||||
install() {
|
install() {
|
||||||
if [[ $EUID -ne 0 ]]; then
|
if [[ $EUID -ne 0 ]]; then
|
||||||
@@ -54,7 +54,7 @@ install() {
|
|||||||
echo " + data dir: $DATA_DIR (mode 1777, writable by all)"
|
echo " + data dir: $DATA_DIR (mode 1777, writable by all)"
|
||||||
|
|
||||||
# ── 3. Sudoers entry ────────────────────────────────────────────────
|
# ── 3. Sudoers entry ────────────────────────────────────────────────
|
||||||
# Allow the invoking user (SUDO_USER) to run ANY command as antigravity-ls.
|
# Allow the invoking user (SUDO_USER) to run ANY command as zerogravity-ls.
|
||||||
# This is needed for the proxy to spawn the LS binary.
|
# This is needed for the proxy to spawn the LS binary.
|
||||||
local REAL_USER="${SUDO_USER:-$(logname 2>/dev/null || whoami)}"
|
local REAL_USER="${SUDO_USER:-$(logname 2>/dev/null || whoami)}"
|
||||||
cat > "$SUDOERS_FILE" <<EOF
|
cat > "$SUDOERS_FILE" <<EOF
|
||||||
@@ -78,7 +78,7 @@ EOF
|
|||||||
echo
|
echo
|
||||||
echo "[mitm-redirect] ✓ Installed (only affects uid=$LS_UID)"
|
echo "[mitm-redirect] ✓ Installed (only affects uid=$LS_UID)"
|
||||||
echo " Restart the proxy to take effect:"
|
echo " Restart the proxy to take effect:"
|
||||||
echo " RUST_LOG=info ./target/release/antigravity-proxy --standalone"
|
echo " RUST_LOG=info ./target/release/zerogravity --standalone"
|
||||||
}
|
}
|
||||||
|
|
||||||
uninstall() {
|
uninstall() {
|
||||||
|
|||||||
@@ -390,7 +390,7 @@ pub(crate) async fn handle_completions(
|
|||||||
if token.is_empty() {
|
if token.is_empty() {
|
||||||
return err_response(
|
return err_response(
|
||||||
StatusCode::UNAUTHORIZED,
|
StatusCode::UNAUTHORIZED,
|
||||||
"No OAuth token. POST to /v1/token or set ANTIGRAVITY_OAUTH_TOKEN env var.".into(),
|
"No OAuth token. POST to /v1/token or set ZEROGRAVITY_TOKEN env var.".into(),
|
||||||
"authentication_error",
|
"authentication_error",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ async fn handle_gemini_inner(
|
|||||||
if token.is_empty() {
|
if token.is_empty() {
|
||||||
return err_response(
|
return err_response(
|
||||||
StatusCode::UNAUTHORIZED,
|
StatusCode::UNAUTHORIZED,
|
||||||
"No OAuth token. POST to /v1/token or set ANTIGRAVITY_OAUTH_TOKEN env var.".into(),
|
"No OAuth token. POST to /v1/token or set ZEROGRAVITY_TOKEN env var.".into(),
|
||||||
"authentication_error",
|
"authentication_error",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -226,7 +226,7 @@ pub(crate) async fn handle_responses(
|
|||||||
if token.is_empty() {
|
if token.is_empty() {
|
||||||
return err_response(
|
return err_response(
|
||||||
StatusCode::UNAUTHORIZED,
|
StatusCode::UNAUTHORIZED,
|
||||||
"No OAuth token. POST to /v1/token or set ANTIGRAVITY_OAUTH_TOKEN env var.".into(),
|
"No OAuth token. POST to /v1/token or set ZEROGRAVITY_TOKEN env var.".into(),
|
||||||
"authentication_error",
|
"authentication_error",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ impl Backend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Then env var
|
// Then env var
|
||||||
if let Ok(env_token) = std::env::var("ANTIGRAVITY_OAUTH_TOKEN") {
|
if let Ok(env_token) = std::env::var("ZEROGRAVITY_TOKEN") {
|
||||||
if !env_token.is_empty() {
|
if !env_token.is_empty() {
|
||||||
let mut guard = self.inner.write().await;
|
let mut guard = self.inner.write().await;
|
||||||
if guard.oauth_token != env_token {
|
if guard.oauth_token != env_token {
|
||||||
@@ -607,12 +607,12 @@ fn discover() -> Result<BackendInner, String> {
|
|||||||
https_port = "3100".to_string();
|
https_port = "3100".to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
let oauth_token = std::env::var("ANTIGRAVITY_OAUTH_TOKEN")
|
let oauth_token = std::env::var("ZEROGRAVITY_TOKEN")
|
||||||
.ok()
|
.ok()
|
||||||
.filter(|s| !s.is_empty())
|
.filter(|s| !s.is_empty())
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
let home = std::env::var("HOME").unwrap_or_default();
|
let home = std::env::var("HOME").unwrap_or_default();
|
||||||
let path = format!("{home}/.config/antigravity-proxy-token");
|
let path = format!("{home}/.config/zerogravity/token");
|
||||||
fs::read_to_string(&path)
|
fs::read_to_string(&path)
|
||||||
.ok()
|
.ok()
|
||||||
.map(|s| s.trim().to_string())
|
.map(|s| s.trim().to_string())
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
fn main() {
|
fn main() {
|
||||||
antigravity_proxy::snapshot::run_cli();
|
zerogravity::snapshot::run_cli();
|
||||||
}
|
}
|
||||||
|
|||||||
273
src/bin/zg.rs
Normal file
273
src/bin/zg.rs
Normal file
@@ -0,0 +1,273 @@
|
|||||||
|
//! `zg` — ZeroGravity daemon manager.
|
||||||
|
//!
|
||||||
|
//! All commands exit immediately (safe for agent use via fast-bash MCP).
|
||||||
|
|
||||||
|
use std::process::{Command, Stdio};
|
||||||
|
|
||||||
|
const SERVICE: &str = "zerogravity";
|
||||||
|
const PORT: u16 = 8741;
|
||||||
|
|
||||||
|
// ANSI colors
|
||||||
|
const RED: &str = "\x1b[0;31m";
|
||||||
|
const GREEN: &str = "\x1b[0;32m";
|
||||||
|
const YELLOW: &str = "\x1b[1;33m";
|
||||||
|
const CYAN: &str = "\x1b[0;36m";
|
||||||
|
const BOLD: &str = "\x1b[1m";
|
||||||
|
const DIM: &str = "\x1b[2m";
|
||||||
|
const NC: &str = "\x1b[0m";
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let args: Vec<String> = std::env::args().collect();
|
||||||
|
let cmd = args.get(1).map(|s| s.as_str()).unwrap_or("");
|
||||||
|
let arg = args.get(2).map(|s| s.as_str());
|
||||||
|
|
||||||
|
match cmd {
|
||||||
|
"start" => do_start(),
|
||||||
|
"stop" => do_stop(),
|
||||||
|
"restart" => do_restart(),
|
||||||
|
"rebuild" => do_build(),
|
||||||
|
"status" => do_status(),
|
||||||
|
"logs" => do_logs(arg.unwrap_or("30"), false),
|
||||||
|
"logs-follow" => do_logs(arg.unwrap_or("30"), true),
|
||||||
|
"logs-all" => do_logs_all(),
|
||||||
|
"test" => do_test(arg.unwrap_or("Say hello in exactly 3 words")),
|
||||||
|
"health" => do_health(),
|
||||||
|
_ => usage(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage() {
|
||||||
|
println!("{BOLD}zg{NC} — ZeroGravity daemon manager\n");
|
||||||
|
println!(" {CYAN}start{NC} Start the proxy daemon");
|
||||||
|
println!(" {CYAN}stop{NC} Stop the proxy daemon");
|
||||||
|
println!(" {CYAN}restart{NC} Rebuild + restart");
|
||||||
|
println!(" {CYAN}rebuild{NC} Build release binary only");
|
||||||
|
println!(" {CYAN}status{NC} Service status + quota + usage");
|
||||||
|
println!(" {CYAN}logs{NC} [N] Show last N lines (default 30)");
|
||||||
|
println!(" {CYAN}logs-follow{NC} [N] Tail last N lines + follow");
|
||||||
|
println!(" {CYAN}logs-all{NC} Full log dump");
|
||||||
|
println!(" {CYAN}test{NC} [msg] Quick test request (gemini-3-flash)");
|
||||||
|
println!(" {CYAN}health{NC} Health check");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn project_dir() -> String {
|
||||||
|
// Resolve project dir from binary location:
|
||||||
|
// binary is at <project>/target/release/zg or <project>/target/debug/zg
|
||||||
|
let exe = std::env::current_exe().expect("cannot resolve exe path");
|
||||||
|
let exe_dir = exe.parent().expect("no parent dir");
|
||||||
|
|
||||||
|
// Walk up from target/release/ or target/debug/
|
||||||
|
if let Some(target_dir) = exe_dir.parent() {
|
||||||
|
if let Some(project) = target_dir.parent() {
|
||||||
|
return project.to_string_lossy().to_string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: use current dir
|
||||||
|
std::env::current_dir()
|
||||||
|
.expect("no cwd")
|
||||||
|
.to_string_lossy()
|
||||||
|
.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn base_url() -> String {
|
||||||
|
let port = std::env::var("PROXY_PORT")
|
||||||
|
.ok()
|
||||||
|
.and_then(|p| p.parse::<u16>().ok())
|
||||||
|
.unwrap_or(PORT);
|
||||||
|
format!("http://localhost:{port}")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn systemctl(args: &[&str]) -> bool {
|
||||||
|
Command::new("systemctl")
|
||||||
|
.arg("--user")
|
||||||
|
.args(args)
|
||||||
|
.status()
|
||||||
|
.map(|s| s.success())
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn curl_get(path: &str) -> Option<String> {
|
||||||
|
let url = format!("{}{}", base_url(), path);
|
||||||
|
Command::new("curl")
|
||||||
|
.args(["-sf", &url])
|
||||||
|
.output()
|
||||||
|
.ok()
|
||||||
|
.filter(|o| o.status.success())
|
||||||
|
.map(|o| String::from_utf8_lossy(&o.stdout).to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn curl_post(path: &str, body: &str) -> Option<String> {
|
||||||
|
let url = format!("{}{}", base_url(), path);
|
||||||
|
Command::new("curl")
|
||||||
|
.args(["-sf", &url, "-H", "Content-Type: application/json", "-d", body])
|
||||||
|
.output()
|
||||||
|
.ok()
|
||||||
|
.filter(|o| o.status.success())
|
||||||
|
.map(|o| String::from_utf8_lossy(&o.stdout).to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn health_ok() -> bool {
|
||||||
|
curl_get("/health").is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn jq_print(json: &str) {
|
||||||
|
let mut child = Command::new("jq")
|
||||||
|
.arg(".")
|
||||||
|
.stdin(Stdio::piped())
|
||||||
|
.spawn()
|
||||||
|
.expect("jq not found");
|
||||||
|
if let Some(stdin) = child.stdin.as_mut() {
|
||||||
|
use std::io::Write;
|
||||||
|
let _ = stdin.write_all(json.as_bytes());
|
||||||
|
}
|
||||||
|
let _ = child.wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Commands ──
|
||||||
|
|
||||||
|
fn do_build() {
|
||||||
|
let dir = project_dir();
|
||||||
|
println!("{YELLOW}Building release binary...{NC}");
|
||||||
|
let status = Command::new("cargo")
|
||||||
|
.args(["build", "--release"])
|
||||||
|
.current_dir(&dir)
|
||||||
|
.stdin(Stdio::inherit())
|
||||||
|
.stdout(Stdio::inherit())
|
||||||
|
.stderr(Stdio::inherit())
|
||||||
|
.status()
|
||||||
|
.expect("failed to run cargo");
|
||||||
|
|
||||||
|
if status.success() {
|
||||||
|
println!("{GREEN}Build complete.{NC}");
|
||||||
|
} else {
|
||||||
|
eprintln!("{RED}Build failed.{NC}");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_start() {
|
||||||
|
systemctl(&["daemon-reload"]);
|
||||||
|
systemctl(&["start", SERVICE]);
|
||||||
|
println!("{GREEN}Started.{NC} Waiting for ready...");
|
||||||
|
|
||||||
|
for _ in 0..20 {
|
||||||
|
if health_ok() {
|
||||||
|
println!("{GREEN}ZeroGravity is up on port {PORT}.{NC}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(500));
|
||||||
|
}
|
||||||
|
|
||||||
|
eprintln!("{RED}Proxy didn't become healthy in 10s. Check logs:{NC}");
|
||||||
|
let _ = Command::new("journalctl")
|
||||||
|
.args(["--user", "-u", SERVICE, "--no-pager", "-n", "20"])
|
||||||
|
.status();
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_stop() {
|
||||||
|
let _ = systemctl(&["stop", SERVICE]);
|
||||||
|
println!("{YELLOW}Stopped.{NC}");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_restart() {
|
||||||
|
println!("{YELLOW}Stopping...{NC}");
|
||||||
|
do_stop();
|
||||||
|
do_build();
|
||||||
|
do_start();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_status() {
|
||||||
|
println!("{BOLD}── Service ──{NC}");
|
||||||
|
let output = Command::new("systemctl")
|
||||||
|
.args(["--user", "status", SERVICE, "--no-pager"])
|
||||||
|
.output();
|
||||||
|
match output {
|
||||||
|
Ok(o) => {
|
||||||
|
let text = String::from_utf8_lossy(&o.stdout);
|
||||||
|
// Print first 6 lines
|
||||||
|
for (i, line) in text.lines().enumerate() {
|
||||||
|
if i >= 6 { break; }
|
||||||
|
println!("{line}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => println!("{RED}Not running{NC}"),
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
|
||||||
|
if !health_ok() {
|
||||||
|
println!("{RED}Proxy is not responding on port {PORT}.{NC}");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("{BOLD}── Quota ──{NC}");
|
||||||
|
match curl_get("/v1/quota") {
|
||||||
|
Some(json) => jq_print(&json),
|
||||||
|
None => println!("{DIM}(no quota data){NC}"),
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
|
||||||
|
println!("{BOLD}── Usage ──{NC}");
|
||||||
|
match curl_get("/v1/usage") {
|
||||||
|
Some(json) => jq_print(&json),
|
||||||
|
None => println!("{DIM}(no usage data){NC}"),
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
|
||||||
|
println!("{BOLD}── Sessions ──{NC}");
|
||||||
|
match curl_get("/v1/sessions") {
|
||||||
|
Some(json) => jq_print(&json),
|
||||||
|
None => println!("{DIM}(no sessions){NC}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_logs(n: &str, follow: bool) {
|
||||||
|
let mut args = vec!["--user", "-u", SERVICE, "--no-pager", "-n", n];
|
||||||
|
if follow {
|
||||||
|
args.push("-f");
|
||||||
|
}
|
||||||
|
let _ = Command::new("journalctl")
|
||||||
|
.args(&args)
|
||||||
|
.stdin(Stdio::inherit())
|
||||||
|
.stdout(Stdio::inherit())
|
||||||
|
.stderr(Stdio::inherit())
|
||||||
|
.status();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_logs_all() {
|
||||||
|
let _ = Command::new("journalctl")
|
||||||
|
.args(["--user", "-u", SERVICE, "--no-pager"])
|
||||||
|
.stdin(Stdio::inherit())
|
||||||
|
.stdout(Stdio::inherit())
|
||||||
|
.stderr(Stdio::inherit())
|
||||||
|
.status();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_test(msg: &str) {
|
||||||
|
println!("{CYAN}Testing:{NC} {msg}");
|
||||||
|
let body = format!(
|
||||||
|
r#"{{"model":"gemini-3-flash","input":"{}","stream":false,"timeout":30}}"#,
|
||||||
|
msg.replace('"', r#"\""#)
|
||||||
|
);
|
||||||
|
match curl_post("/v1/responses", &body) {
|
||||||
|
Some(json) => jq_print(&json),
|
||||||
|
None => {
|
||||||
|
eprintln!("{RED}Request failed. Is the proxy running?{NC}");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_health() {
|
||||||
|
match curl_get("/health") {
|
||||||
|
Some(json) => {
|
||||||
|
jq_print(&json);
|
||||||
|
println!("{GREEN}Healthy{NC}");
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
println!("{RED}Not responding{NC}");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -185,7 +185,7 @@ pub fn log_base() -> String {
|
|||||||
/// Token file path.
|
/// Token file path.
|
||||||
pub fn token_file_path() -> String {
|
pub fn token_file_path() -> String {
|
||||||
let home = std::env::var("HOME").unwrap_or_else(|_| "/root".to_string());
|
let home = std::env::var("HOME").unwrap_or_else(|_| "/root".to_string());
|
||||||
format!("{home}/.config/antigravity-proxy-token")
|
format!("{home}/.config/zerogravity/token")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// User-Agent string matching the Electron webview — computed once.
|
/// User-Agent string matching the Electron webview — computed once.
|
||||||
|
|||||||
22
src/main.rs
22
src/main.rs
@@ -1,4 +1,4 @@
|
|||||||
//! Antigravity OpenAI Proxy — Rust edition v3 (stealth hardened).
|
//! ZeroGravity — Rust edition v3 (stealth hardened).
|
||||||
//!
|
//!
|
||||||
//! Single-binary replacement for server.py. BoringSSL TLS impersonation,
|
//! Single-binary replacement for server.py. BoringSSL TLS impersonation,
|
||||||
//! byte-exact protobuf encoding, Chrome header fingerprinting, cascade
|
//! byte-exact protobuf encoding, Chrome header fingerprinting, cascade
|
||||||
@@ -26,8 +26,8 @@ use mitm::store::MitmStore;
|
|||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[command(
|
#[command(
|
||||||
name = "antigravity-proxy",
|
name = "zerogravity",
|
||||||
about = "Antigravity OpenAI Proxy (stealth)"
|
about = "ZeroGravity — stealth LLM proxy"
|
||||||
)]
|
)]
|
||||||
struct Cli {
|
struct Cli {
|
||||||
/// Port to listen on
|
/// Port to listen on
|
||||||
@@ -64,7 +64,7 @@ struct Cli {
|
|||||||
#[arg(long, conflicts_with = "headless")]
|
#[arg(long, conflicts_with = "headless")]
|
||||||
classic: bool,
|
classic: bool,
|
||||||
|
|
||||||
/// Disable per-call debug traces (on by default, writes JSON to ~/.config/antigravity-proxy/traces/)
|
/// Disable per-call debug traces (on by default, writes JSON to ~/.config/zerogravity/traces/)
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
no_trace: bool,
|
no_trace: bool,
|
||||||
}
|
}
|
||||||
@@ -236,12 +236,12 @@ async fn main() {
|
|||||||
|
|
||||||
let backend = Arc::new(if let Some((_, port, ref csrf)) = standalone_ls {
|
let backend = Arc::new(if let Some((_, port, ref csrf)) = standalone_ls {
|
||||||
// Build backend pointing at standalone LS
|
// Build backend pointing at standalone LS
|
||||||
let oauth = std::env::var("ANTIGRAVITY_OAUTH_TOKEN")
|
let oauth = std::env::var("ZEROGRAVITY_TOKEN")
|
||||||
.ok()
|
.ok()
|
||||||
.filter(|s| !s.is_empty())
|
.filter(|s| !s.is_empty())
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
let home = std::env::var("HOME").unwrap_or_default();
|
let home = std::env::var("HOME").unwrap_or_default();
|
||||||
let path = format!("{home}/.config/antigravity-proxy-token");
|
let path = format!("{home}/.config/zerogravity/token");
|
||||||
std::fs::read_to_string(&path)
|
std::fs::read_to_string(&path)
|
||||||
.ok()
|
.ok()
|
||||||
.map(|s| s.trim().to_string())
|
.map(|s| s.trim().to_string())
|
||||||
@@ -282,7 +282,7 @@ async fn main() {
|
|||||||
let trace_collector = trace::TraceCollector::new(trace_enabled);
|
let trace_collector = trace::TraceCollector::new(trace_enabled);
|
||||||
if trace_enabled {
|
if trace_enabled {
|
||||||
trace_collector.cleanup_old_traces(7);
|
trace_collector.cleanup_old_traces(7);
|
||||||
info!("Debug tracing enabled → ~/.config/antigravity-proxy/traces/");
|
info!("Debug tracing enabled → ~/.config/zerogravity/traces/");
|
||||||
}
|
}
|
||||||
|
|
||||||
let state = Arc::new(AppState {
|
let state = Arc::new(AppState {
|
||||||
@@ -391,7 +391,7 @@ fn print_banner(
|
|||||||
let ver = crate::constants::antigravity_version();
|
let ver = crate::constants::antigravity_version();
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
println!(" \x1b[1;35m>> antigravity-proxy\x1b[0m \x1b[2mv{ver}\x1b[0m");
|
println!(" \x1b[1;35m>> zerogravity\x1b[0m \x1b[2mv{ver}\x1b[0m");
|
||||||
println!(" \x1b[2m────────────────────────────────────────────────\x1b[0m");
|
println!(" \x1b[2m────────────────────────────────────────────────\x1b[0m");
|
||||||
println!();
|
println!();
|
||||||
println!(" \x1b[1mcore\x1b[0m");
|
println!(" \x1b[1mcore\x1b[0m");
|
||||||
@@ -462,11 +462,11 @@ fn print_banner(
|
|||||||
|
|
||||||
if token == "NOT SET" {
|
if token == "NOT SET" {
|
||||||
println!(" \x1b[1;33m[!]\x1b[0m no oauth token");
|
println!(" \x1b[1;33m[!]\x1b[0m no oauth token");
|
||||||
println!(" export ANTIGRAVITY_OAUTH_TOKEN=ya29.xxx");
|
println!(" export ZEROGRAVITY_TOKEN=ya29.xxx");
|
||||||
println!(
|
println!(
|
||||||
" curl -X POST http://127.0.0.1:{port}/v1/token -d '{{\"token\":\"ya29.xxx\"}}'"
|
" curl -X POST http://127.0.0.1:{port}/v1/token -d '{{\"token\":\"ya29.xxx\"}}'"
|
||||||
);
|
);
|
||||||
println!(" echo 'ya29.xxx' > ~/.config/antigravity-proxy-token");
|
println!(" echo 'ya29.xxx' > ~/.config/zerogravity/token");
|
||||||
println!();
|
println!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -522,5 +522,5 @@ fn dirs_data_dir() -> std::path::PathBuf {
|
|||||||
let home = std::env::var("HOME").unwrap_or_else(|_| "/tmp".to_string());
|
let home = std::env::var("HOME").unwrap_or_else(|_| "/tmp".to_string());
|
||||||
std::path::PathBuf::from(home)
|
std::path::PathBuf::from(home)
|
||||||
.join(".config")
|
.join(".config")
|
||||||
.join("antigravity-proxy")
|
.join("zerogravity")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,13 +24,13 @@ const LS_BINARY_PATH: &str =
|
|||||||
const APP_ROOT: &str = "/usr/share/antigravity/resources/app";
|
const APP_ROOT: &str = "/usr/share/antigravity/resources/app";
|
||||||
|
|
||||||
/// Data directory for the standalone LS.
|
/// Data directory for the standalone LS.
|
||||||
const DATA_DIR: &str = "/tmp/antigravity-standalone";
|
const DATA_DIR: &str = "/tmp/zerogravity-standalone";
|
||||||
|
|
||||||
/// System user for UID-scoped iptables isolation.
|
/// System user for UID-scoped iptables isolation.
|
||||||
const LS_USER: &str = "antigravity-ls";
|
const LS_USER: &str = "zerogravity-ls";
|
||||||
|
|
||||||
/// Path for the compiled dns_redirect.so preload library.
|
/// Path for the compiled dns_redirect.so preload library.
|
||||||
const DNS_REDIRECT_SO_PATH: &str = "/tmp/antigravity-dns-redirect.so";
|
const DNS_REDIRECT_SO_PATH: &str = "/tmp/zerogravity-dns-redirect.so";
|
||||||
|
|
||||||
/// Source file for the DNS redirect preload library (relative to binary).
|
/// Source file for the DNS redirect preload library (relative to binary).
|
||||||
const DNS_REDIRECT_C_SOURCE: &str = include_str!("../mitm/dns_redirect.c");
|
const DNS_REDIRECT_C_SOURCE: &str = include_str!("../mitm/dns_redirect.c");
|
||||||
|
|||||||
@@ -57,9 +57,9 @@ impl StandaloneLS {
|
|||||||
1, // DETECT_AND_USE_PROXY_ENABLED
|
1, // DETECT_AND_USE_PROXY_ENABLED
|
||||||
);
|
);
|
||||||
|
|
||||||
// Setup data dir (mode 1777 so both current user and antigravity-ls can write)
|
// Setup data dir (mode 1777 so both current user and zerogravity-ls can write)
|
||||||
let gemini_dir = format!("{DATA_DIR}/.gemini");
|
let gemini_dir = format!("{DATA_DIR}/.gemini");
|
||||||
let app_data_dir = format!("{DATA_DIR}/.gemini/antigravity-standalone");
|
let app_data_dir = format!("{DATA_DIR}/.gemini/zerogravity-standalone");
|
||||||
let annotations_dir = format!("{app_data_dir}/annotations");
|
let annotations_dir = format!("{app_data_dir}/annotations");
|
||||||
let brain_dir = format!("{app_data_dir}/brain");
|
let brain_dir = format!("{app_data_dir}/brain");
|
||||||
for dir in [
|
for dir in [
|
||||||
@@ -77,7 +77,7 @@ impl StandaloneLS {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Check if data dir is writable by writing a test file.
|
// Check if data dir is writable by writing a test file.
|
||||||
// Old runs as `antigravity-ls` user leave dirs owned by that user.
|
// Old runs as `zerogravity-ls` user leave dirs owned by that user.
|
||||||
let test_path = format!("{app_data_dir}/.write_test");
|
let test_path = format!("{app_data_dir}/.write_test");
|
||||||
if std::fs::write(&test_path, b"ok").is_err() {
|
if std::fs::write(&test_path, b"ok").is_err() {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
@@ -138,12 +138,12 @@ impl StandaloneLS {
|
|||||||
})
|
})
|
||||||
.unwrap_or_else(|| {
|
.unwrap_or_else(|| {
|
||||||
// Fall back to env var / token file
|
// Fall back to env var / token file
|
||||||
let token = std::env::var("ANTIGRAVITY_OAUTH_TOKEN")
|
let token = std::env::var("ZEROGRAVITY_TOKEN")
|
||||||
.ok()
|
.ok()
|
||||||
.filter(|s| !s.is_empty())
|
.filter(|s| !s.is_empty())
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
let home = std::env::var("HOME").unwrap_or_default();
|
let home = std::env::var("HOME").unwrap_or_default();
|
||||||
let path = format!("{home}/.config/antigravity-proxy-token");
|
let path = format!("{home}/.config/zerogravity/token");
|
||||||
std::fs::read_to_string(&path)
|
std::fs::read_to_string(&path)
|
||||||
.ok()
|
.ok()
|
||||||
.map(|s| s.trim().to_string())
|
.map(|s| s.trim().to_string())
|
||||||
@@ -153,7 +153,7 @@ impl StandaloneLS {
|
|||||||
if !token.is_empty() {
|
if !token.is_empty() {
|
||||||
info!("Loaded OAuth token from env/file (no refresh token — manual refresh needed)");
|
info!("Loaded OAuth token from env/file (no refresh token — manual refresh needed)");
|
||||||
} else {
|
} else {
|
||||||
eprintln!("[headless] ⚠ No OAuth token found. Login to Antigravity first, or set ANTIGRAVITY_OAUTH_TOKEN");
|
eprintln!("[headless] ⚠ No OAuth token found. Login to Antigravity first, or set ZEROGRAVITY_TOKEN");
|
||||||
}
|
}
|
||||||
(token, None)
|
(token, None)
|
||||||
});
|
});
|
||||||
@@ -224,7 +224,7 @@ impl StandaloneLS {
|
|||||||
"https://daily-cloudcode-pa.googleapis.com".to_string()
|
"https://daily-cloudcode-pa.googleapis.com".to_string()
|
||||||
},
|
},
|
||||||
"-app_data_dir".to_string(),
|
"-app_data_dir".to_string(),
|
||||||
"antigravity-standalone".to_string(),
|
"zerogravity-standalone".to_string(),
|
||||||
"-gemini_dir".to_string(),
|
"-gemini_dir".to_string(),
|
||||||
gemini_dir,
|
gemini_dir,
|
||||||
];
|
];
|
||||||
@@ -240,16 +240,16 @@ impl StandaloneLS {
|
|||||||
if let Some(mitm) = mitm_config {
|
if let Some(mitm) = mitm_config {
|
||||||
// Go's SSL_CERT_FILE replaces the entire system cert pool, so we
|
// Go's SSL_CERT_FILE replaces the entire system cert pool, so we
|
||||||
// need a combined bundle: system CAs + our MITM CA
|
// need a combined bundle: system CAs + our MITM CA
|
||||||
// Write to /tmp — accessible by antigravity-ls user
|
// Write to /tmp — accessible by zerogravity-ls user
|
||||||
// (user's ~/.config/ is not traversable by other UIDs)
|
// (user's ~/.config/ is not traversable by other UIDs)
|
||||||
let combined_ca_path = "/tmp/antigravity-mitm-combined-ca.pem".to_string();
|
let combined_ca_path = "/tmp/zerogravity-mitm-ca.pem".to_string();
|
||||||
let system_ca =
|
let system_ca =
|
||||||
std::fs::read_to_string("/etc/ssl/certs/ca-certificates.crt").unwrap_or_default();
|
std::fs::read_to_string("/etc/ssl/certs/ca-certificates.crt").unwrap_or_default();
|
||||||
let mitm_ca = std::fs::read_to_string(&mitm.ca_cert_path)
|
let mitm_ca = std::fs::read_to_string(&mitm.ca_cert_path)
|
||||||
.map_err(|e| format!("Failed to read MITM CA cert: {e}"))?;
|
.map_err(|e| format!("Failed to read MITM CA cert: {e}"))?;
|
||||||
std::fs::write(&combined_ca_path, format!("{system_ca}\n{mitm_ca}"))
|
std::fs::write(&combined_ca_path, format!("{system_ca}\n{mitm_ca}"))
|
||||||
.map_err(|e| format!("Failed to write combined CA bundle: {e}"))?;
|
.map_err(|e| format!("Failed to write combined CA bundle: {e}"))?;
|
||||||
// Make readable by antigravity-ls user
|
// Make readable by zerogravity-ls user
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
{
|
{
|
||||||
use std::os::unix::fs::PermissionsExt;
|
use std::os::unix::fs::PermissionsExt;
|
||||||
@@ -286,18 +286,18 @@ impl StandaloneLS {
|
|||||||
env_vars.push(("LD_PRELOAD".into(), so));
|
env_vars.push(("LD_PRELOAD".into(), so));
|
||||||
env_vars.push((
|
env_vars.push((
|
||||||
"DNS_REDIRECT_LOG".into(),
|
"DNS_REDIRECT_LOG".into(),
|
||||||
"/tmp/antigravity-dns-redirect.log".into(),
|
"/tmp/zerogravity-dns-redirect.log".into(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// In headless mode, never use sudo — run as current user
|
// In headless mode, never use sudo — run as current user
|
||||||
// In normal mode, use sudo if 'antigravity-ls' user exists
|
// In normal mode, use sudo if 'zerogravity-ls' user exists
|
||||||
let use_sudo = !headless && has_ls_user();
|
let use_sudo = !headless && has_ls_user();
|
||||||
|
|
||||||
let mut cmd = if use_sudo {
|
let mut cmd = if use_sudo {
|
||||||
info!("Using UID isolation: spawning LS as 'antigravity-ls' user");
|
info!("Using UID isolation: spawning LS as 'zerogravity-ls' user");
|
||||||
let mut c = Command::new("sudo");
|
let mut c = Command::new("sudo");
|
||||||
c.args(["-n", "-u", LS_USER, "--", "/usr/bin/env"]);
|
c.args(["-n", "-u", LS_USER, "--", "/usr/bin/env"]);
|
||||||
for (k, v) in &env_vars {
|
for (k, v) in &env_vars {
|
||||||
@@ -317,7 +317,7 @@ impl StandaloneLS {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Capture stderr for debugging — logs to /tmp so we can diagnose LS failures
|
// Capture stderr for debugging — logs to /tmp so we can diagnose LS failures
|
||||||
let stderr_file = std::fs::File::create("/tmp/antigravity-ls-debug.log")
|
let stderr_file = std::fs::File::create("/tmp/zerogravity-ls-debug.log")
|
||||||
.map_err(|e| format!("Failed to create LS debug log: {e}"))?;
|
.map_err(|e| format!("Failed to create LS debug log: {e}"))?;
|
||||||
cmd.stdin(Stdio::piped())
|
cmd.stdin(Stdio::piped())
|
||||||
.stdout(Stdio::null())
|
.stdout(Stdio::null())
|
||||||
@@ -342,7 +342,7 @@ impl StandaloneLS {
|
|||||||
let ls_pid = if use_sudo {
|
let ls_pid = if use_sudo {
|
||||||
// Give sudo a moment to spawn the real process
|
// Give sudo a moment to spawn the real process
|
||||||
std::thread::sleep(std::time::Duration::from_millis(500));
|
std::thread::sleep(std::time::Duration::from_millis(500));
|
||||||
// Find the LS process owned by antigravity-ls user
|
// Find the LS process owned by zerogravity-ls user
|
||||||
find_ls_pid_for_user(LS_USER).ok()
|
find_ls_pid_for_user(LS_USER).ok()
|
||||||
} else {
|
} else {
|
||||||
Some(child.id())
|
Some(child.id())
|
||||||
@@ -424,7 +424,7 @@ impl StandaloneLS {
|
|||||||
// The child is sudo which already exited. Kill the actual LS.
|
// The child is sudo which already exited. Kill the actual LS.
|
||||||
if let Some(pid) = self.ls_pid {
|
if let Some(pid) = self.ls_pid {
|
||||||
info!(pid, "Killing LS process via sudo -u {}", LS_USER);
|
info!(pid, "Killing LS process via sudo -u {}", LS_USER);
|
||||||
// Run kill AS the antigravity-ls user (same UID can signal)
|
// Run kill AS the zerogravity-ls user (same UID can signal)
|
||||||
let ok = std::process::Command::new("sudo")
|
let ok = std::process::Command::new("sudo")
|
||||||
.args(["-n", "-u", LS_USER, "kill", "-TERM", &pid.to_string()])
|
.args(["-n", "-u", LS_USER, "kill", "-TERM", &pid.to_string()])
|
||||||
.stdout(Stdio::null())
|
.stdout(Stdio::null())
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
//! Per-call debug trace system.
|
//! Per-call debug trace system.
|
||||||
//!
|
//!
|
||||||
//! Every API call gets a structured JSON trace file written to
|
//! Every API call gets a structured JSON trace file written to
|
||||||
//! `~/.config/antigravity-proxy/traces/{YYYY-MM-DD}/{HH-MM-SS}_{cascade_short}.json`.
|
//! `~/.config/zerogravity/traces/{YYYY-MM-DD}/{HH-MM-SS}_{cascade_short}.json`.
|
||||||
//!
|
//!
|
||||||
//! Designed for LLM consumption: compact, structured, no raw bodies.
|
//! Designed for LLM consumption: compact, structured, no raw bodies.
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ impl TraceCollector {
|
|||||||
let home = std::env::var("HOME").unwrap_or_else(|_| "/tmp".to_string());
|
let home = std::env::var("HOME").unwrap_or_else(|_| "/tmp".to_string());
|
||||||
let traces_dir = PathBuf::from(home)
|
let traces_dir = PathBuf::from(home)
|
||||||
.join(".config")
|
.join(".config")
|
||||||
.join("antigravity-proxy")
|
.join("zerogravity")
|
||||||
.join("traces");
|
.join("traces");
|
||||||
Self {
|
Self {
|
||||||
enabled,
|
enabled,
|
||||||
|
|||||||
Reference in New Issue
Block a user