Skip to content

Commit 93a59e0

Browse files
lroolleclaude
andcommitted
feat: add grok agent (official xAI Grok CLI)
- agents/grok.sh: oauth default (mount ~/.grok; in-container login via grok login --device-auth) and api-key (XAI_API_KEY); appends --always-approve since the container is the sandbox - deva.sh: .grok canonical entry, autolink, XAI_API_KEY env filtering, blank auth.json overlay for api-key mode (grok prefers the session token over the env key; the overlay makes the key actually win) - install tooling: grok's npm bin is a trampoline that resolves ~/.grok/bin/grok first, and postinstall parks the ~125MB real binary there (self-update dir); move it to ~/.local/bin and drop ~/.grok/bin so a host-mounted ~/.grok (possibly a macOS binary) can never shadow the image CLI - pin GROK_CLI_VERSION=0.2.93 through versions.env, Makefile, Dockerfile(+rust), version scripts, ci/nightly/release workflows, install.sh agent list - tests: registry, version-upgrade, install-tooling, version-targets - docs: README, authentication, quick-start, how-it-works, changelog Close #403 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 99aa672 commit 93a59e0

29 files changed

Lines changed: 330 additions & 31 deletions

.deva.example

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,10 @@ EPHEMERAL=false
6161
# Hybrid Agent Setup:
6262
#
6363
# Hybrid is the DEFAULT. deva walks every populated subdir under
64-
# ~/.config/deva/ (claude, codex, gemini) and mounts each agent's
64+
# ~/.config/deva/ (claude, codex, gemini, grok) and mounts each agent's
6565
# canonical entries into the container. Populated = you either
6666
# hand-created the subdir or autolink symlinked it from legacy
67-
# ~/.claude, ~/.codex, ~/.gemini on first run.
67+
# ~/.claude, ~/.codex, ~/.gemini, ~/.grok on first run.
6868
#
6969
# No .deva entries required for the common case. To opt OUT of
7070
# hybrid for a single invocation, pass --config-home DIR to

.github/workflows/ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ jobs:
7979
--build-arg CCTRACE_VERSION="${{ steps.pins.outputs.cctrace_version }}" \
8080
--build-arg CODEX_VERSION="${{ steps.pins.outputs.codex_version }}" \
8181
--build-arg GEMINI_CLI_VERSION="${{ steps.pins.outputs.gemini_cli_version }}" \
82+
--build-arg GROK_CLI_VERSION="${{ steps.pins.outputs.grok_cli_version }}" \
8283
--build-arg CCX_VERSION="${{ steps.pins.outputs.ccx_version }}" \
8384
--build-arg COPILOT_API_VERSION="${{ steps.pins.outputs.copilot_api_version }}" \
8485
.
@@ -100,6 +101,7 @@ jobs:
100101
CCTRACE_VERSION="${{ steps.pins.outputs.cctrace_version }}" \
101102
CODEX_VERSION="${{ steps.pins.outputs.codex_version }}" \
102103
GEMINI_CLI_VERSION="${{ steps.pins.outputs.gemini_cli_version }}" \
104+
GROK_CLI_VERSION="${{ steps.pins.outputs.grok_cli_version }}" \
103105
CCX_VERSION="${{ steps.pins.outputs.ccx_version }}" \
104106
COPILOT_API_VERSION="${{ steps.pins.outputs.copilot_api_version }}" \
105107
PLAYWRIGHT_VERSION="${{ steps.pins.outputs.playwright_version }}" \
@@ -124,6 +126,7 @@ jobs:
124126
deva.sh claude -Q -- --version
125127
deva.sh codex -Q -- --version
126128
deva.sh gemini -Q -- --version
129+
deva.sh grok -Q -- --version
127130
128131
- name: Smoke Claude --chrome mount assembly
129132
shell: bash

.github/workflows/nightly-images.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ jobs:
5454
claude_code_version: ${{ steps.versions.outputs.claude_code_version }}
5555
codex_version: ${{ steps.versions.outputs.codex_version }}
5656
gemini_cli_version: ${{ steps.versions.outputs.gemini_cli_version }}
57+
grok_cli_version: ${{ steps.versions.outputs.grok_cli_version }}
5758
ccx_version: ${{ steps.versions.outputs.ccx_version }}
5859
copilot_api_version: ${{ steps.versions.outputs.copilot_api_version }}
5960
steps:
@@ -80,6 +81,7 @@ jobs:
8081
- Claude Code: \`${{ steps.versions.outputs.claude_code_version }}\`
8182
- Codex: \`${{ steps.versions.outputs.codex_version }}\`
8283
- Gemini CLI: \`${{ steps.versions.outputs.gemini_cli_version }}\`
84+
- Grok CLI: \`${{ steps.versions.outputs.grok_cli_version }}\`
8385
- ccx: \`${{ steps.versions.outputs.ccx_version }}\`
8486
- Copilot API: \`${{ steps.versions.outputs.copilot_api_version }}\`
8587
- Stamp: \`${{ steps.versions.outputs.stamp }}\`
@@ -140,6 +142,7 @@ jobs:
140142
CCTRACE_VERSION=${{ needs.load-version-pins.outputs.cctrace_version }}
141143
CODEX_VERSION=${{ needs.resolve-versions.outputs.codex_version }}
142144
GEMINI_CLI_VERSION=${{ needs.resolve-versions.outputs.gemini_cli_version }}
145+
GROK_CLI_VERSION=${{ needs.resolve-versions.outputs.grok_cli_version }}
143146
CCX_VERSION=${{ needs.resolve-versions.outputs.ccx_version }}
144147
COPILOT_API_VERSION=${{ needs.resolve-versions.outputs.copilot_api_version }}
145148
@@ -193,6 +196,7 @@ jobs:
193196
CCTRACE_VERSION=${{ needs.load-version-pins.outputs.cctrace_version }}
194197
CODEX_VERSION=${{ needs.resolve-versions.outputs.codex_version }}
195198
GEMINI_CLI_VERSION=${{ needs.resolve-versions.outputs.gemini_cli_version }}
199+
GROK_CLI_VERSION=${{ needs.resolve-versions.outputs.grok_cli_version }}
196200
CCX_VERSION=${{ needs.resolve-versions.outputs.ccx_version }}
197201
PLAYWRIGHT_VERSION=${{ needs.load-version-pins.outputs.playwright_version }}
198202
RUST_TOOLCHAINS=${{ needs.load-version-pins.outputs.rust_toolchains }}

.github/workflows/release.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ jobs:
5050
cctrace_version: ${{ steps.pins.outputs.cctrace_version }}
5151
codex_version: ${{ steps.pins.outputs.codex_version }}
5252
gemini_cli_version: ${{ steps.pins.outputs.gemini_cli_version }}
53+
grok_cli_version: ${{ steps.pins.outputs.grok_cli_version }}
5354
ccx_version: ${{ steps.pins.outputs.ccx_version }}
5455
copilot_api_version: ${{ steps.pins.outputs.copilot_api_version }}
5556
playwright_version: ${{ steps.pins.outputs.playwright_version }}
@@ -84,6 +85,7 @@ jobs:
8485
- cctrace: \`${{ steps.pins.outputs.cctrace_version }}\`
8586
- Codex: \`${{ steps.pins.outputs.codex_version }}\`
8687
- Gemini CLI: \`${{ steps.pins.outputs.gemini_cli_version }}\`
88+
- Grok CLI: \`${{ steps.pins.outputs.grok_cli_version }}\`
8789
- ccx: \`${{ steps.pins.outputs.ccx_version }}\`
8890
- Copilot API: \`${{ steps.pins.outputs.copilot_api_version }}\`
8991
- Playwright: \`${{ steps.pins.outputs.playwright_version }}\`
@@ -147,6 +149,7 @@ jobs:
147149
CCTRACE_VERSION=${{ needs.load-version-pins.outputs.cctrace_version }}
148150
CODEX_VERSION=${{ needs.load-version-pins.outputs.codex_version }}
149151
GEMINI_CLI_VERSION=${{ needs.load-version-pins.outputs.gemini_cli_version }}
152+
GROK_CLI_VERSION=${{ needs.load-version-pins.outputs.grok_cli_version }}
150153
CCX_VERSION=${{ needs.load-version-pins.outputs.ccx_version }}
151154
COPILOT_API_VERSION=${{ needs.load-version-pins.outputs.copilot_api_version }}
152155
@@ -202,6 +205,7 @@ jobs:
202205
CCTRACE_VERSION=${{ needs.load-version-pins.outputs.cctrace_version }}
203206
CODEX_VERSION=${{ needs.load-version-pins.outputs.codex_version }}
204207
GEMINI_CLI_VERSION=${{ needs.load-version-pins.outputs.gemini_cli_version }}
208+
GROK_CLI_VERSION=${{ needs.load-version-pins.outputs.grok_cli_version }}
205209
CCX_VERSION=${{ needs.load-version-pins.outputs.ccx_version }}
206210
PLAYWRIGHT_VERSION=${{ needs.load-version-pins.outputs.playwright_version }}
207211
RUST_TOOLCHAINS=${{ needs.load-version-pins.outputs.rust_toolchains }}

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1414
uncommitted work via live ls-remote + merge ancestry, and emits a
1515
tiered dry-run plan; SKILL.md layers the judgment calls and gates
1616
execution behind explicit approval (#401)
17+
- `grok` agent: xAI's official Grok CLI (npm `@xai-official/grok`) as the
18+
fourth first-party agent (#403). `deva.sh grok` runs it YOLO-style
19+
(`--always-approve`) in the standard container shape. Auth: `oauth`
20+
(default, mounts `~/.grok`; in-container first login via
21+
`grok login --device-auth`) or `api-key` (`XAI_API_KEY`). In api-key mode
22+
the default `auth.json` is masked with a blank overlay because grok
23+
prefers a session token over the env key. Grok's npm launcher resolves
24+
`~/.grok/bin/grok` (its self-update dir) before the packaged binary, so
25+
the image moves the real binary to `~/.local/bin` and drops `~/.grok/bin`
26+
— a host-mounted `~/.grok`, possibly holding a macOS binary, can never
27+
shadow the container CLI. Pin:
28+
`GROK_CLI_VERSION` in `versions.env`, label
29+
`org.opencontainers.image.grok_cli_version`, wired through make/CI/
30+
nightly/release version plumbing
1731

1832
## [0.13.0] - 2026-07-07
1933

DEV-LOGS.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,15 @@
1313
- Minimal markdown markers, no unnecessary formatting, minimal emojis.
1414
- Reference issue numbers in the format `#<issue-number>` for easy linking.
1515

16+
# [2026-07-09] Dev Log: add grok agent (official xAI Grok CLI) #403
17+
- Why: xAI shipped an official coding-agent CLI (x.ai/cli, npm @xai-official/grok); deva should launch it like claude/codex/gemini. Not to be confused with community superagent-ai/grok-cli (npm grok-dev) — different product, same bin name.
18+
- What:
19+
- agents/grok.sh: oauth default (mount ~/.grok) + api-key (XAI_API_KEY); appends --always-approve (grok's bypassPermissions alias) since the container is the sandbox
20+
- deva.sh: canonical entry .grok, autolink ~/.grok, XAI_API_KEY env handling, blank-overlay /home/deva/.grok/auth.json for api-key mode (grok prefers session token over env key — overlay makes the key actually win)
21+
- install-agent-tooling.sh pin_grok_platform_binary: grok's npm bin is a trampoline that resolves ~/.grok/bin/grok FIRST, and postinstall parks the ~125MB real binary there (self-update dir); a host mount would shadow it (macOS binary -> exec format error). Move the binary to ~/.local/bin, repoint the npm bin, rm ~/.grok/bin
22+
- GROK_CLI_VERSION pin wired through versions.env, Makefile, Dockerfile(+rust), version-pins/upgrade/report/resolve scripts, ci/nightly/release workflows; tests extended (release-utils registry, version-upgrade mock, install-tooling, version-targets)
23+
- Result: deva.sh grok works with both auth modes (dry-run verified: mount + env + overlay wiring); grok pinned at 0.2.93; make versions-up picks up new upstream releases
24+
1625
# [2026-07-07] Dev Log: switch --trace default from claude-trace to cctrace
1726
- Why: claude-trace's node --require fetch hook only sees /v1/messages and dies on native Claude binaries; cctrace (thevibeworks/cctrace) MITM-captures everything (OAuth, usage/credits, MCP) and auto-detects npm vs native installs
1827
- What:

Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,12 +211,14 @@ ARG CLAUDE_CODE_VERSION=2.1.143
211211
ARG CCTRACE_VERSION=0.4.0
212212
ARG CODEX_VERSION=0.131.0
213213
ARG GEMINI_CLI_VERSION=0.42.0
214+
ARG GROK_CLI_VERSION=0.2.93
214215

215216
# Record key tool versions as labels for quick inspection
216217
LABEL org.opencontainers.image.claude_code_version=${CLAUDE_CODE_VERSION}
217218
LABEL org.opencontainers.image.cctrace_version=${CCTRACE_VERSION}
218219
LABEL org.opencontainers.image.codex_version=${CODEX_VERSION}
219220
LABEL org.opencontainers.image.gemini_cli_version=${GEMINI_CLI_VERSION}
221+
LABEL org.opencontainers.image.grok_cli_version=${GROK_CLI_VERSION}
220222

221223
ARG CCX_VERSION=v0.7.0
222224

Dockerfile.rust

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ ARG CLAUDE_CODE_VERSION=2.1.143
1212
ARG CCTRACE_VERSION=0.4.0
1313
ARG CODEX_VERSION=0.131.0
1414
ARG GEMINI_CLI_VERSION=0.42.0
15+
ARG GROK_CLI_VERSION=0.2.93
1516
ARG CCX_VERSION=v0.7.0
1617
ARG PLAYWRIGHT_VERSION=1.60.0
1718
ARG RUST_TOOLCHAINS="stable"
@@ -22,6 +23,7 @@ LABEL org.opencontainers.image.claude_code_version=${CLAUDE_CODE_VERSION}
2223
LABEL org.opencontainers.image.cctrace_version=${CCTRACE_VERSION}
2324
LABEL org.opencontainers.image.codex_version=${CODEX_VERSION}
2425
LABEL org.opencontainers.image.gemini_cli_version=${GEMINI_CLI_VERSION}
26+
LABEL org.opencontainers.image.grok_cli_version=${GROK_CLI_VERSION}
2527
LABEL org.opencontainers.image.ccx_version=${CCX_VERSION}
2628
LABEL org.opencontainers.image.playwright_version=${PLAYWRIGHT_VERSION}
2729

Makefile

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ CLAUDE_CODE_VERSION ?= 2.1.143
3434
CCTRACE_VERSION ?= 0.4.0
3535
CODEX_VERSION ?= 0.131.0
3636
GEMINI_CLI_VERSION ?= 0.42.0
37+
GROK_CLI_VERSION ?= 0.2.93
3738
CCX_VERSION ?= v0.1.4
3839
COPILOT_API_VERSION ?= 0ea08febdd7e3e055b03dd298bf57e669500b5c1
3940
PLAYWRIGHT_VERSION ?= 1.60.0
@@ -59,6 +60,7 @@ AGENT_BUILD_ARGS := \
5960
--build-arg CCTRACE_VERSION=$(CCTRACE_VERSION) \
6061
--build-arg CODEX_VERSION=$(CODEX_VERSION) \
6162
--build-arg GEMINI_CLI_VERSION=$(GEMINI_CLI_VERSION) \
63+
--build-arg GROK_CLI_VERSION=$(GROK_CLI_VERSION) \
6264
--build-arg CCX_VERSION=$(CCX_VERSION)
6365

6466
MAIN_BUILD_ARGS := $(TOOLCHAIN_BUILD_ARGS) $(AGENT_BUILD_ARGS) \
@@ -81,6 +83,7 @@ VERSION_QUERY_OVERRIDES := \
8183
$(if $(filter command line environment environment\ override override,$(origin CCTRACE_VERSION)),CCTRACE_VERSION=$(CCTRACE_VERSION)) \
8284
$(if $(filter command line environment environment\ override override,$(origin CODEX_VERSION)),CODEX_VERSION=$(CODEX_VERSION)) \
8385
$(if $(filter command line environment environment\ override override,$(origin GEMINI_CLI_VERSION)),GEMINI_CLI_VERSION=$(GEMINI_CLI_VERSION)) \
86+
$(if $(filter command line environment environment\ override override,$(origin GROK_CLI_VERSION)),GROK_CLI_VERSION=$(GROK_CLI_VERSION)) \
8487
$(if $(filter command line environment environment\ override override,$(origin CCX_VERSION)),CCX_VERSION=$(CCX_VERSION)) \
8588
$(if $(filter command line environment environment\ override override,$(origin COPILOT_API_VERSION)),COPILOT_API_VERSION=$(COPILOT_API_VERSION)) \
8689
$(if $(filter command line environment environment\ override override,$(origin PLAYWRIGHT_VERSION)),PLAYWRIGHT_VERSION=$(PLAYWRIGHT_VERSION)) \
@@ -123,13 +126,15 @@ build-main: build-network-check
123126
@prev_claude=$$(docker inspect --format='{{ index .Config.Labels "org.opencontainers.image.claude_code_version" }}' $(MAIN_IMAGE) 2>/dev/null || true); \
124127
prev_codex=$$(docker inspect --format='{{ index .Config.Labels "org.opencontainers.image.codex_version" }}' $(MAIN_IMAGE) 2>/dev/null || true); \
125128
prev_gemini=$$(docker inspect --format='{{ index .Config.Labels "org.opencontainers.image.gemini_cli_version" }}' $(MAIN_IMAGE) 2>/dev/null || true); \
129+
prev_grok=$$(docker inspect --format='{{ index .Config.Labels "org.opencontainers.image.grok_cli_version" }}' $(MAIN_IMAGE) 2>/dev/null || true); \
126130
fmt() { v="$$1"; if [ -z "$$v" ] || [ "$$v" = "<no value>" ]; then echo "-"; else case "$$v" in v*) echo "$$v";; *) echo "v$$v";; esac; fi; }; \
127-
curC=$$(fmt "$$prev_claude"); curX=$$(fmt "$$prev_codex"); curG=$$(fmt "$$prev_gemini"); \
128-
tgtC=$$(fmt "$(CLAUDE_CODE_VERSION)"); tgtX=$$(fmt "$(CODEX_VERSION)"); tgtG=$$(fmt "$(GEMINI_CLI_VERSION)"); \
129-
if [ "$$curC" = "$$tgtC" ] && [ "$$curX" = "$$tgtX" ] && [ "$$curG" = "$$tgtG" ]; then \
131+
curC=$$(fmt "$$prev_claude"); curX=$$(fmt "$$prev_codex"); curG=$$(fmt "$$prev_gemini"); curK=$$(fmt "$$prev_grok"); \
132+
tgtC=$$(fmt "$(CLAUDE_CODE_VERSION)"); tgtX=$$(fmt "$(CODEX_VERSION)"); tgtG=$$(fmt "$(GEMINI_CLI_VERSION)"); tgtK=$$(fmt "$(GROK_CLI_VERSION)"); \
133+
if [ "$$curC" = "$$tgtC" ] && [ "$$curX" = "$$tgtX" ] && [ "$$curG" = "$$tgtG" ] && [ "$$curK" = "$$tgtK" ]; then \
130134
echo "Claude: $$tgtC (no change)"; \
131135
echo "Codex: $$tgtX (no change)"; \
132136
echo "Gemini: $$tgtG (no change)"; \
137+
echo "Grok: $$tgtK (no change)"; \
133138
echo "Already up-to-date"; \
134139
else \
135140
if [ "$$curC" = "$$tgtC" ]; then \
@@ -147,6 +152,11 @@ build-main: build-network-check
147152
else \
148153
echo "Gemini: $$curG -> $$tgtG"; \
149154
fi; \
155+
if [ "$$curK" = "$$tgtK" ]; then \
156+
echo "Grok: $$tgtK (no change)"; \
157+
else \
158+
echo "Grok: $$curK -> $$tgtK"; \
159+
fi; \
150160
fi
151161
@echo "Hint: override via GO_VERSION=... CLAUDE_CODE_VERSION=... or run 'make versions-pin'"
152162
docker build $(DOCKER_BUILD_EXTRA_ARGS) -f $(DOCKERFILE) $(MAIN_BUILD_ARGS) -t $(MAIN_IMAGE) .
@@ -403,6 +413,7 @@ help:
403413
@echo " CCTRACE_VERSION cctrace version (default: $(CCTRACE_VERSION))"
404414
@echo " CODEX_VERSION Codex CLI version (default: $(CODEX_VERSION))"
405415
@echo " GEMINI_CLI_VERSION Gemini CLI version (default: $(GEMINI_CLI_VERSION))"
416+
@echo " GROK_CLI_VERSION Grok CLI version (default: $(GROK_CLI_VERSION))"
406417
@echo " CCX_VERSION Atlas CLI version (default: $(CCX_VERSION))"
407418
@echo " PLAYWRIGHT_VERSION Playwright version (default: $(PLAYWRIGHT_VERSION))"
408419
@echo " RUST_TOOLCHAINS Rust toolchains to install (default: $(RUST_TOOLCHAINS))"

README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,17 @@
66
[![Checks](https://img.shields.io/badge/checks-shellcheck%20%7C%20docs%20%7C%20smoke-222222)](#development)
77
[![License](https://img.shields.io/github/license/thevibeworks/deva)](LICENSE)
88
[![Container](https://img.shields.io/badge/ghcr.io-thevibeworks%2Fdeva-blue)](https://github.com/thevibeworks/deva/pkgs/container/deva)
9-
[![Agents](https://img.shields.io/badge/agents-codex%20%7C%20claude%20%7C%20gemini-222222)](#what-this-is)
9+
[![Agents](https://img.shields.io/badge/agents-codex%20%7C%20claude%20%7C%20gemini%20%7C%20grok-222222)](#what-this-is)
1010

11-
Run Codex, Claude Code, and Gemini inside Docker without pretending the agent's own sandbox is the thing keeping you safe.
11+
Run Codex, Claude Code, Gemini, and Grok inside Docker without pretending the agent's own sandbox is the thing keeping you safe.
1212

1313
The container is the sandbox. Explicit mounts are the contract. Persistent project containers keep the workflow fast instead of rebuilding the same state every run.
1414

1515
This repo is the source of truth for `deva.sh`.
1616

1717
## What This Is
1818

19-
- a Docker-based launcher for Codex, Claude, and Gemini
19+
- a Docker-based launcher for Codex, Claude, Gemini, and Grok
2020
- one warm default container shape per project by default
2121
- explicit mount and env wiring instead of mystery behavior
2222
- per-agent config homes under `~/.config/deva/`
@@ -54,7 +54,7 @@ deva.sh ps
5454
deva.sh stop
5555
```
5656

57-
If you already use Codex, Claude, or Gemini locally, deva will auto-link those auth homes into `~/.config/deva/` by default. If not, first run will ask you to authenticate inside the container.
57+
If you already use Codex, Claude, Gemini, or Grok locally, deva will auto-link those auth homes into `~/.config/deva/` by default. If not, first run will ask you to authenticate inside the container.
5858

5959
## Docs
6060

@@ -115,6 +115,7 @@ deva.sh
115115
# Same container, different agents
116116
deva.sh codex
117117
deva.sh gemini
118+
deva.sh grok
118119

119120
# Throwaway run
120121
deva.sh claude --rm

0 commit comments

Comments
 (0)