mirror of
https://github.com/nexu-io/open-design.git
synced 2026-10-27 09:51:12 +01:00
feat(daemon): add Grok Build (xAI's first-party CLI) as an agent runtime (#2200)
* feat(daemon): add Grok Build (xAI's first-party CLI) as agent runtime
xAI shipped Grok Build (https://x.ai/cli) on 2026-05-19 — a first-
party CLI agent that boxes their SuperGrok subscription into a
Claude-Code-shaped headless mode (`grok -p <prompt>`, optional
`--output-format streaming-json`, `--max-turns`, native subagent /
plan / mcp subcommands). The OAuth flow it ships uses the same
`auth.x.ai` issuer + `b1a00492-073a-47ea-816f-4c329264a828` client_id
+ `grok-cli:access api:access` scope that the existing OD xAI
Settings panel already speaks, so once a user runs the install
script and `grok login --oauth`, Grok Build is fully authenticated
without OD doing anything.
This runtime def lets OD detect a `grok` binary on PATH and surface
it in the agent picker the same way Hermes / Claude Code / Codex are
surfaced today. Headless invocations get:
- `grok -p` (prompt via stdin to avoid Windows ENAMETOOLONG / Linux
E2BIG on large composed prompts), with `--model` / `--effort`
pass-through when the user picks one in the picker.
- `grok models` for live model discovery (parsed line-by-line,
matching the existing `parseLineSeparatedModels` helper opencode /
cursor-agent already use). Falls back to a static list of
grok-build / grok-4.3 / grok-4.20-{reasoning,non-reasoning,
multi-agent} when probing fails (no Heavy entitlement, network
blip, etc.).
- `--effort` exposed as the standard reasoning-options dropdown.
streamFormat is intentionally `plain` for this first cut. Grok
Build's `streaming-json` output is a real NDJSON event stream, but
the schema is xAI-specific and OD does not yet have a parser for it
— shipping with `plain` now means the user can chat single-turn,
and a follow-up `grok-stream-json` adapter can light up tool_use /
plan-mode streaming once we've validated the shape against a live
Heavy account.
No test added for the def itself; the existing
`tests/runtimes/registry-and-args.test.ts` and `agent-args.test.ts`
iterate over `AGENT_DEFS` and now cover this entry by construction.
All 126 runtime tests pass; daemon tsc + tsconfig.tests.json clean;
pnpm guard clean.
* feat(web): add grok-build agent icon
Pairs with the new daemon runtime def in 35418fe so the Settings →
Agent Runtime row stops falling back to the "G" initial.
The SVG is a hand-drawn geometric "X" silhouette (Unicode 𝕏 / U+1D54F
shape), painted via AgentIcon's mono mask path so it picks up the
active theme color the way hermes / mimo / pi / kilo / cursor-agent
already do. xAI does not publish a downloadable brand SVG (favicon
paths on x.ai 404), so this is intentionally a placeholder rather
than a copy of any licensed mark — happy to swap for an official
asset if maintainers have one to hand.
Wires the id into both `ICON_EXT` and the `MONO_ICONS` set in
AgentIcon.tsx. No other behaviour changes.
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
import { parseLineSeparatedModels, DEFAULT_MODEL_OPTION } from './shared.js';
|
||||
import type { RuntimeAgentDef } from '../types.js';
|
||||
|
||||
// xAI's first-party CLI agent — https://x.ai/cli — distributed as the
|
||||
// `grok` binary. Installed via `curl -fsSL https://x.ai/cli/install.sh | bash`,
|
||||
// which symlinks `~/.grok/bin/grok` into PATH.
|
||||
//
|
||||
// `grok` ships its own SuperGrok OAuth dance (same `auth.x.ai` issuer +
|
||||
// loopback-redirect shape Open Design's xAI Settings panel uses), so it's
|
||||
// already authenticated by the time OD detects the binary; OD does not
|
||||
// need to inject credentials. Users authenticate once with `grok login
|
||||
// --oauth` and the resulting `~/.grok/auth.json` is what every spawned
|
||||
// invocation reads.
|
||||
//
|
||||
// Headless mode follows Claude Code's pattern (`-p <PROMPT>` for single-
|
||||
// turn, `--output-format streaming-json` for structured streaming), but
|
||||
// the streaming-json schema is xAI-specific and we do not yet have a
|
||||
// daemon-side parser for it. To ship the runtime now and let users at
|
||||
// least chat with grok inside OD, this defaults to `plain` streamFormat
|
||||
// (single-turn text reply, no tool_use streaming). Upgrading to a
|
||||
// `grok-stream-json` event parser is follow-up work once the format is
|
||||
// stable enough to lock in.
|
||||
export const grokBuildAgentDef = {
|
||||
id: 'grok-build',
|
||||
name: 'Grok Build',
|
||||
bin: 'grok',
|
||||
versionArgs: ['--version'],
|
||||
helpArgs: ['-p', '--help'],
|
||||
// `grok models` prints one model id per line, plus a `Default model:`
|
||||
// header line that parseLineSeparatedModels strips because it isn't
|
||||
// an id token. Falls back to the static list below when probing fails
|
||||
// (no SuperGrok Heavy entitlement on this machine, network blip, etc.).
|
||||
listModels: {
|
||||
args: ['models'],
|
||||
timeoutMs: 10_000,
|
||||
parse: parseLineSeparatedModels,
|
||||
},
|
||||
fallbackModels: [
|
||||
DEFAULT_MODEL_OPTION,
|
||||
{ id: 'grok-build', label: 'grok-build (xAI · default)' },
|
||||
{ id: 'grok-4.3', label: 'grok-4.3 (xAI)' },
|
||||
{ id: 'grok-4.20-reasoning', label: 'grok-4.20-reasoning (xAI · deep)' },
|
||||
{
|
||||
id: 'grok-4.20-non-reasoning',
|
||||
label: 'grok-4.20-non-reasoning (xAI · fast)',
|
||||
},
|
||||
{
|
||||
id: 'grok-4.20-multi-agent',
|
||||
label: 'grok-4.20-multi-agent (xAI · orchestration)',
|
||||
},
|
||||
],
|
||||
// Prompt delivered via stdin so Windows `spawn ENAMETOOLONG` and Linux
|
||||
// `spawn E2BIG` can't truncate large composed prompts. `grok -p` with
|
||||
// no positional argument reads from piped stdin.
|
||||
buildArgs: (_prompt, _imagePaths, _extra = [], options = {}) => {
|
||||
const args = ['-p'];
|
||||
if (options.model && options.model !== DEFAULT_MODEL_OPTION.id) {
|
||||
args.push('--model', options.model);
|
||||
}
|
||||
if (options.reasoning) {
|
||||
args.push('--effort', options.reasoning);
|
||||
}
|
||||
return args;
|
||||
},
|
||||
reasoningOptions: [
|
||||
{ id: 'low', label: 'low' },
|
||||
{ id: 'medium', label: 'medium' },
|
||||
{ id: 'high', label: 'high' },
|
||||
{ id: 'xhigh', label: 'xhigh' },
|
||||
{ id: 'max', label: 'max' },
|
||||
],
|
||||
promptViaStdin: true,
|
||||
streamFormat: 'plain',
|
||||
installUrl: 'https://x.ai/cli',
|
||||
docsUrl: 'https://x.ai/cli',
|
||||
} satisfies RuntimeAgentDef;
|
||||
@@ -4,6 +4,7 @@ import { devinAgentDef } from './defs/devin.js';
|
||||
import { geminiAgentDef } from './defs/gemini.js';
|
||||
import { opencodeAgentDef } from './defs/opencode.js';
|
||||
import { hermesAgentDef } from './defs/hermes.js';
|
||||
import { grokBuildAgentDef } from './defs/grok-build.js';
|
||||
import { kimiAgentDef } from './defs/kimi.js';
|
||||
import { cursorAgentDef } from './defs/cursor-agent.js';
|
||||
import { qwenAgentDef } from './defs/qwen.js';
|
||||
@@ -24,6 +25,7 @@ const BASE_AGENT_DEFS: RuntimeAgentDef[] = [
|
||||
geminiAgentDef,
|
||||
opencodeAgentDef,
|
||||
hermesAgentDef,
|
||||
grokBuildAgentDef,
|
||||
kimiAgentDef,
|
||||
cursorAgentDef,
|
||||
qwenAgentDef,
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
<!--
|
||||
Geometric "X" silhouette evoking xAI's mathematical-double-struck X
|
||||
brand mark (Unicode U+1D54F 𝕏). Drawn as simple straight strokes so
|
||||
AgentIcon's mono mask path can paint it in the active theme color
|
||||
alongside hermes / mimo / pi / kilo / cursor-agent. The fill is set
|
||||
to a baked dark color so any non-masked render path still produces
|
||||
a legible mark instead of black-on-black.
|
||||
-->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" fill="#1c1b1a">
|
||||
<path d="M 158 158 L 432 432 L 158 432 L 158 158 Z" />
|
||||
<path d="M 866 158 L 866 432 L 592 432 L 866 158 Z" />
|
||||
<path d="M 432 592 L 158 592 L 158 866 L 432 592 Z" />
|
||||
<path d="M 592 592 L 866 592 L 866 866 L 592 592 Z" />
|
||||
<path d="M 432 432 L 592 432 L 592 592 L 432 592 L 432 432 Z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 785 B |
@@ -23,6 +23,7 @@ const ICON_EXT: Record<string, 'svg' | 'png'> = {
|
||||
deepseek: 'svg',
|
||||
mimo: 'svg',
|
||||
hermes: 'svg',
|
||||
'grok-build': 'svg',
|
||||
kimi: 'svg',
|
||||
pi: 'svg',
|
||||
kiro: 'svg',
|
||||
@@ -46,6 +47,7 @@ const MONO_ICONS = new Set([
|
||||
'mimo',
|
||||
'pi',
|
||||
'kilo',
|
||||
'grok-build',
|
||||
]);
|
||||
|
||||
export function AgentIcon({ id, size = 36, className }: Props) {
|
||||
|
||||
Reference in New Issue
Block a user