Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ Never leak `DISCORD_BOT_TOKEN` or other OAB credentials to the agent.

### 4. Dockerfile Discipline

There are 7 Dockerfiles: `Dockerfile`, `Dockerfile.claude`, `Dockerfile.codex`, `Dockerfile.copilot`, `Dockerfile.cursor`, `Dockerfile.gemini`, `Dockerfile.opencode`.
There are 10 Dockerfiles: `Dockerfile`, `Dockerfile.antigravity`, `Dockerfile.claude`, `Dockerfile.codex`, `Dockerfile.copilot`, `Dockerfile.cursor`, `Dockerfile.gemini`, `Dockerfile.grok`, `Dockerfile.hermes`, `Dockerfile.opencode`.

A change to one MUST be evaluated against ALL. Common layers (base image, openab binary, tini) are shared — update all or explain why not.

Expand Down
131 changes: 131 additions & 0 deletions Dockerfile.antigravity
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# --- Build stage ---
FROM rust:1-bookworm AS builder
WORKDIR /build
COPY Cargo.toml Cargo.lock ./
RUN mkdir src && echo 'fn main() {}' > src/main.rs && cargo build --release && rm -rf src
COPY src/ src/
RUN touch src/main.rs && cargo build --release

# --- Runtime stage ---
#
# Antigravity is Google's new agent platform. The official `agy` CLI
# (https://github.com/google-antigravity/antigravity-cli) shipped at v1.0.0
# but does NOT yet expose `--acp` (or any other stdio JSON-RPC) mode —
# verified by inspecting v1.0.0's `--help` output and binary `strings`.
# Its only non-interactive surface is `-p/--print`, which is one-shot and
# cannot drive OpenAB's streaming-message / tool-approval / session-cancel
# pipeline.
#
# Antigravity's backend, however, speaks the same wire protocol as
# gemini-cli's: `/v1internal:streamGenerateContent` over the same
# /v1internal envelope. Just on a different host
# (`daily-cloudcode-pa.googleapis.com`) and with a different OAuth client.
#
# So until `agy` adds ACP support, this image:
# 1. Installs the regular @google/gemini-cli (which already implements `--acp`).
# 2. At container startup, rewrites the OAuth client_id / client_secret /
# Code Assist endpoint constants in gemini-cli's bundle so requests go to
# Antigravity's backend with Antigravity OAuth credentials.
# 3. Then exec's `gemini --acp` exactly like Dockerfile.gemini.
#
# No literal Antigravity secrets are shipped in this image. The Antigravity
# OAuth client_id + secret are supplied at runtime via env vars:
# ANTIGRAVITY_OAUTH_CLIENT_ID
# ANTIGRAVITY_OAUTH_CLIENT_SECRET
# ANTIGRAVITY_API_URL (optional, default daily-cloudcode-pa.googleapis.com)
#
# See docs/antigravity.md for how to obtain those values and the rationale.
#
# Status: EXPERIMENTAL. This image will become a thin wrapper around the
# eventual official `@google/antigravity-cli` once Google ships it.
#
FROM node:22-bookworm-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates curl procps ripgrep tini && \
rm -rf /var/lib/apt/lists/*

# Install Gemini CLI — its --acp implementation is what we re-target.
ARG GEMINI_CLI_VERSION=0.42.0
RUN npm install -g @google/gemini-cli@${GEMINI_CLI_VERSION} --retry 3

# Install gh CLI (parity with Dockerfile.gemini)
RUN curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \
-o /usr/share/keyrings/githubcli-archive-keyring.gpg && \
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \
> /etc/apt/sources.list.d/github-cli.list && \
apt-get update && apt-get install -y --no-install-recommends gh && \
rm -rf /var/lib/apt/lists/*

# Make gemini-cli's bundle directory writable by the `node` user so the runtime
# patch script can modify it. (npm installs globally as root by default.)
RUN GEMINI_BUNDLE=$(npm root -g)/@google/gemini-cli/bundle && \
chown -R node:node "$GEMINI_BUNDLE"

# Antigravity runtime patch script. Runs once on first container start;
# leaves a stamp file so subsequent restarts are no-ops.
RUN cat > /usr/local/bin/antigravity-patch.sh <<'PATCH_SCRIPT' && \
chmod +x /usr/local/bin/antigravity-patch.sh
#!/bin/bash
set -e

: "${ANTIGRAVITY_OAUTH_CLIENT_ID:?required env var ANTIGRAVITY_OAUTH_CLIENT_ID — see docs/antigravity.md}"
: "${ANTIGRAVITY_OAUTH_CLIENT_SECRET:?required env var ANTIGRAVITY_OAUTH_CLIENT_SECRET — see docs/antigravity.md}"
: "${ANTIGRAVITY_API_URL:=https://daily-cloudcode-pa.googleapis.com}"

BUNDLE_DIR=$(npm root -g)/@google/gemini-cli/bundle
STAMP="$BUNDLE_DIR/.antigravity-patched"

if [ -f "$STAMP" ]; then
exit 0
fi

echo "[antigravity] patching gemini-cli bundle to target Antigravity backend..." >&2

# Auto-discover gemini-cli's default OAuth values from the bundle so we don't
# need to ship them as literals in this Dockerfile. These values are public
# (gemini-cli is Apache-2.0); we still avoid embedding them to keep secret
# scanners happy.
DEFAULT_CLIENT_ID=$(grep -hoE '[0-9]+-[a-z0-9]+\.apps\.googleusercontent\.com' "$BUNDLE_DIR"/chunk-*.js 2>/dev/null | head -1 || true)
DEFAULT_CLIENT_SECRET=$(grep -hoE 'GOCSPX-[A-Za-z0-9_-]+' "$BUNDLE_DIR"/chunk-*.js 2>/dev/null | head -1 || true)
DEFAULT_ENDPOINT="https://cloudcode-pa.googleapis.com"

if [ -z "$DEFAULT_CLIENT_ID" ] || [ -z "$DEFAULT_CLIENT_SECRET" ]; then
echo "[antigravity] FATAL: could not locate gemini-cli OAuth constants in bundle." >&2
echo "[antigravity] This usually means the gemini-cli bundle layout changed." >&2
echo "[antigravity] Try pinning GEMINI_CLI_VERSION to the version this image was tested against." >&2
exit 1
fi

for f in "$BUNDLE_DIR"/chunk-*.js; do
sed -i \
-e "s|${DEFAULT_CLIENT_ID}|${ANTIGRAVITY_OAUTH_CLIENT_ID}|g" \
-e "s|${DEFAULT_CLIENT_SECRET}|${ANTIGRAVITY_OAUTH_CLIENT_SECRET}|g" \
-e "s|${DEFAULT_ENDPOINT}|${ANTIGRAVITY_API_URL}|g" \
"$f"
done

touch "$STAMP"
echo "[antigravity] patch complete; endpoint=${ANTIGRAVITY_API_URL}" >&2
PATCH_SCRIPT

# Wrapper that runs the patch then exec's the command. This becomes the
# container's entrypoint so openab + gemini both see the patched bundle.
RUN cat > /usr/local/bin/antigravity-entrypoint.sh <<'ENTRYPOINT_SCRIPT' && \
chmod +x /usr/local/bin/antigravity-entrypoint.sh
#!/bin/sh
/usr/local/bin/antigravity-patch.sh
exec "$@"
ENTRYPOINT_SCRIPT

ENV HOME=/home/node
WORKDIR /home/node

COPY --from=builder --chown=node:node /build/target/release/openab /usr/local/bin/openab

RUN mkdir -p /home/node/.gemini && chown -R node:node /home/node/.gemini

USER node
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
CMD pgrep -x openab || exit 1
ENTRYPOINT ["tini", "--", "/usr/local/bin/antigravity-entrypoint.sh"]
CMD ["openab", "run", "-c", "/etc/openab/config.toml"]
32 changes: 32 additions & 0 deletions charts/openab/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,38 @@ agents:
# storageClass: ""
# size: 1Gi
# image: "ghcr.io/openabdev/openab-grok"
# antigravity:
# # EXPERIMENTAL: Bridges OpenAB to Google's Antigravity backend by
# # re-targeting gemini-cli's --acp implementation. See docs/antigravity.md.
# command: gemini
# args:
# - --acp
# discord:
# enabled: true
# allowedChannels:
# - "YOUR_CHANNEL_ID"
# allowedUsers: []
# allowBotMessages: "off"
# trustedBotIds: []
# workingDir: /home/node
# # ⚠️ Required env vars — see docs/antigravity.md for how to obtain values.
# # Do NOT commit these to git. Prefer `secretEnv` from a Kubernetes Secret.
# env: {}
# secretEnv:
# - name: ANTIGRAVITY_OAUTH_CLIENT_ID
# secretName: antigravity-oauth
# secretKey: client_id
# - name: ANTIGRAVITY_OAUTH_CLIENT_SECRET
# secretName: antigravity-oauth
# secretKey: client_secret
# pool:
# maxSessions: 10
# sessionTtlHours: 24
# persistence:
# enabled: true
# storageClass: ""
# size: 1Gi
# image: "ghcr.io/openabdev/openab-antigravity"
image: ""
command: kiro-cli
args:
Expand Down
141 changes: 141 additions & 0 deletions docs/antigravity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# Antigravity (Experimental)

> **Status:** EXPERIMENTAL. Antigravity is Google's new agent platform (announced at Google I/O 2026); the CLI (`agy`) shipped at v1.0.0 alongside the IDE. **However, the official `agy` CLI does not yet expose ACP / `--acp` mode** — its only non-interactive surface is `-p / --print` which is one-shot, no streaming, no tool-approval flow. Until Google ships ACP support in `agy`, this image bridges OpenAB to Antigravity's backend by re-targeting `@google/gemini-cli`'s ACP implementation (gemini-cli speaks identical wire protocol to Antigravity, just on a different host with a different OAuth client).
>
> **Tracking issue for ACP support in `agy`:** [google-antigravity/antigravity-cli — feature request: add `--acp` flag](https://github.com/google-antigravity/antigravity-cli/issues) (filed by the OpenAB community).

## Why this image exists when `agy` is already released

Google's [`agy`](https://antigravity.google/product/antigravity-cli) (v1.0.0, May 2026) ships these CLI modes:

| `agy` mode | Streams output | Tool approval | Cancel | Conversation state |
|---|---|---|---|---|
| Default TUI | ✅ | ✅ | ✅ | ✅ |
| `-i / --prompt-interactive` | ✅ | ✅ | ✅ | ✅ |
| `-p / --print` | ❌ (single block) | ❌ (auto with `--dangerously-skip-permissions`) | ❌ | New each invocation |

OpenAB needs **JSON-RPC over stdio** (the ACP protocol) so it can: stream `agent_message_chunk` events, surface `agent_thought_chunk`, pause for `session/set_config_option`, deliver `session/cancel`, etc. **None of `agy`'s shipped modes provide this.** Until they do, this image uses the patched-gemini-cli approach so OpenAB's existing ACP machinery continues to work end-to-end against Antigravity's backend.

## What this gives you

- Access to **Gemini 3.5 Flash / Gemini 3 Pro** plus **Claude 4.6** (and other Antigravity-hosted models) through your Google account's Antigravity subscription
- **Native ACP support** via `gemini --acp` — no protocol changes in OpenAB
- Same Discord / Slack / Custom Gateway integration as every other OpenAB agent

## How it works

Antigravity reuses the same wire protocol as Gemini CLI:

```text
POST https://daily-cloudcode-pa.googleapis.com/v1internal:streamGenerateContent?alt=sse
Authorization: Bearer <Antigravity OAuth token>
```

…on a different host (`daily-cloudcode-pa.googleapis.com` instead of `cloudcode-pa.googleapis.com`) with a different OAuth client.

`Dockerfile.antigravity` installs `@google/gemini-cli` (which already speaks ACP) and rewrites three constants in its bundle at container startup:

1. `OAUTH_CLIENT_ID` → Antigravity OAuth client
2. `OAUTH_CLIENT_SECRET` → Antigravity OAuth secret
3. `CODE_ASSIST_ENDPOINT` → `https://daily-cloudcode-pa.googleapis.com`

After patching, the standard `gemini --acp` entrypoint just works — OpenAB doesn't see any difference vs `Dockerfile.gemini`.

**No literal Antigravity secrets are shipped in the image.** All three values are supplied at container start via environment variables.

## Obtaining Antigravity OAuth credentials

You have two options:

### Option A — Create your own Google OAuth client (recommended for production)

1. Open the [Google Cloud Console → APIs & Services → Credentials](https://console.cloud.google.com/apis/credentials).
2. Create credentials → **OAuth client ID** → Application type **Desktop app**.
3. Enable the **Cloud Code (cloudcode-pa.googleapis.com)** API on the same project.
4. Copy the client ID and client secret into the env vars below.

### Option B — Reuse the published Antigravity desktop app credentials

The Antigravity desktop application uses a published OAuth client. The values can be found in community projects that integrate with Antigravity, e.g. [`su-kaka/gcli2api`](https://github.com/su-kaka/gcli2api/blob/master/src/utils.py). Google's policy ([OAuth installed-app guidelines](https://developers.google.com/identity/protocols/oauth2#installed)) explicitly states client_secret for installed apps is not treated as a secret.

⚠️ Using these shared values means your traffic shares quota / fingerprint with all other community users of the same OAuth client. For low-volume personal use it's usually fine; for any deployment serving multiple users, prefer Option A.

## Docker Image

```bash
docker build -f Dockerfile.antigravity -t openab-antigravity:latest .
```

The image installs `@google/gemini-cli@0.42.0` by default. Override with `--build-arg GEMINI_CLI_VERSION=...`.

## Helm Install

```bash
helm install openab openab/openab \
--set agents.kiro.enabled=false \
--set agents.antigravity.discord.enabled=true \
--set agents.antigravity.discord.botToken="$DISCORD_BOT_TOKEN" \
--set-string 'agents.antigravity.discord.allowedChannels[0]=YOUR_CHANNEL_ID' \
--set agents.antigravity.image=ghcr.io/openabdev/openab-antigravity:latest \
--set agents.antigravity.command=gemini \
--set agents.antigravity.args='{--acp}' \
--set agents.antigravity.workingDir=/home/node \
--set agents.antigravity.env.ANTIGRAVITY_OAUTH_CLIENT_ID="$ANTIGRAVITY_OAUTH_CLIENT_ID" \
--set agents.antigravity.env.ANTIGRAVITY_OAUTH_CLIENT_SECRET="$ANTIGRAVITY_OAUTH_CLIENT_SECRET"
```

> Set `agents.kiro.enabled=false` to disable the default Kiro agent.
>
> ⚠️ This integration reuses the `agents.gemini.*` Helm config schema for now since `gemini --acp` is the actual binary being invoked. A dedicated `agents.antigravity` schema can be added once the integration stabilizes.

## Manual config.toml

```toml
[agent]
command = "gemini"
args = ["--acp"]
working_dir = "/home/node"

[agent.env]
ANTIGRAVITY_OAUTH_CLIENT_ID = "${ANTIGRAVITY_OAUTH_CLIENT_ID}"
ANTIGRAVITY_OAUTH_CLIENT_SECRET = "${ANTIGRAVITY_OAUTH_CLIENT_SECRET}"
# Optional override (default: https://daily-cloudcode-pa.googleapis.com)
# ANTIGRAVITY_API_URL = "${ANTIGRAVITY_API_URL}"
```

## Authentication flow

On first launch, `gemini` (now repointed at Antigravity) opens a browser-based OAuth flow with Antigravity's OAuth client. After consent, tokens are persisted to `/home/node/.gemini/` and refreshed automatically. The patched constants flow through unchanged:

- gemini-cli emits requests to `daily-cloudcode-pa.googleapis.com`
- Refresh tokens are bound to Antigravity's OAuth client (so refresh works correctly)
- Account ID (extracted from `id_token`) routes the request to the user's Antigravity account

## Limitations and risks

| Item | Notes |
|------|------|
| **gemini-cli bundle layout** | The runtime patch grepps gemini-cli's chunked JS bundle for the OAuth constants. If a future gemini-cli release changes its bundle naming or constant patterns, the patch will fail loudly at container start (clear error message, no silent corruption). |
| **Antigravity API contract** | `daily-cloudcode-pa.googleapis.com /v1internal:streamGenerateContent` is an internal API. Wire format may change without notice. |
| **Quota / fingerprint** | Using Option B (shared OAuth client) means your traffic is indistinguishable from other community users of that client; Antigravity may rate-limit aggressively. |
| **No SLA** | This integration is unsupported by Google. For commercial workloads, use Vertex AI with a service account. |

## Future replacement

When Google's `agy` CLI adds an `--acp` (or equivalent stdio-JSON-RPC) mode, this image can be reduced to a thin wrapper installing the official binary via its bootstrap script:

```dockerfile
RUN curl -fsSL https://antigravity.google/cli/install.sh | bash
ENTRYPOINT ["tini", "--"]
CMD ["openab", "run", "-c", "/etc/openab/config.toml"]
# config.toml: command = "agy", args = ["--acp"]
```

The runtime patch script in this image is a workaround for the absence of `--acp` in `agy` v1.0.0 only. Track [this feature request](https://github.com/google-antigravity/antigravity-cli/issues) for the upstream status.

## See also

- [`Dockerfile.gemini`](../Dockerfile.gemini) — the standard Gemini CLI integration this image is based on.
- [`docs/gemini.md`](gemini.md) — Gemini CLI integration docs.
- [`@google/gemini-cli` source](https://github.com/google-gemini/gemini-cli) — upstream CLI.
- [`google-gemini/gemini-cli/.../oauth2.ts`](https://github.com/google-gemini/gemini-cli/blob/main/packages/core/src/code_assist/oauth2.ts) — published OAuth flow.
Loading