Professional local web workspace for the grok CLI and Grok Build workflows.
Website | xAI | Agent Client Protocol
This project is funded by Bags.
Grok Build Web runs on your machine and turns grok agent stdio into a browser-based workspace for coding sessions, tool calls, traces, project navigation, settings, routines, uploads, memory, and session management. It uses plain Node.js, static HTML, CSS, and browser JavaScript, with Mermaid for diagram previews. There is no frontend framework or build step.
Created by @lamps_apple, creator of grokify.ai and AppleLamps/grokify.
- Local browser UI for
grok agent stdio - One Grok agent process per browser tab session
- Per-tab sessions with
?session=URLs and local resume state - Project sidebar grouped by workspace, including local display aliases
- Chat, thinking traces, final answers, todos, plans, permissions, elicitations, and tool activity
- Tool renderers for terminal commands, edits, browser actions, multimodal
read_file, web and X search, images, videos, scheduler events, subagents, and plan cards - Manual approval or auto-approval mode for agent tool requests
- Settings for model, agent profile, subagent definitions, effort, sandbox behavior, rules, tool allow-lists, and display name
- Searchable, grouped sidebar tools for running tasks, managing resources, configuring the CLI, and diagnostics
- Structured plugin and worktree managers with contextual actions and guarded destructive operations
- Built-in panels for inspect, MCP servers, models, CLI version details, memory, hooks, traces, imports, routines, and headless runs
- File attachment, drag-and-drop, clipboard paste, upload previews, and generated media previews
- Share link support through
grok share - Update notice from
grok update --check --json - Mobile sidebar drawer, labeled workspace control, topbar overflow menu, and readable model selector for narrow viewports
- Node.js 24 (the currently tested runtime)
grokCLI installed and available onPATH- A cached Grok login from
grok login, or API-key billing enabled through configuration - Windows, macOS, or Linux
Clone and run the local server:
git clone https://github.com/AppleLamps/grok-build-web.git
cd grok-build-web
npm install
npm startThe server prints a one-time local URL:
http://127.0.0.1:58991/?token=...
Open that URL in your browser. The token is used once to set a local HttpOnly session cookie, then the app redirects to a clean URL.
The official Grok CLI does not currently provide a -web flag. You can add an
opt-in shell function that sends grok -web and grok --web to this checkout
while forwarding every other command to the normal Grok CLI.
On PowerShell, open your profile:
if (!(Test-Path $PROFILE)) { New-Item -ItemType File -Force $PROFILE }
notepad $PROFILEAdd the following, replacing the example path with the absolute path to your clone:
$GrokWebRoot = 'C:\path\to\grok-build-web'
function grok {
$webFlags = @('-web', '--web')
if ($args | Where-Object { $_ -in $webFlags }) {
$rest = $args | Where-Object { $_ -notin $webFlags }
$env:GROK_CWD = (Get-Location).Path
node (Join-Path $GrokWebRoot 'server.mjs') @rest
return
}
$grokCli = Get-Command grok -CommandType Application -ErrorAction Stop
& $grokCli.Source @args
}Reload the profile, then start the web app from the workspace you want to use:
. $PROFILE
grok -webOn bash or zsh, add this to ~/.bashrc or ~/.zshrc, replacing the example
path with the absolute path to your clone:
export GROK_WEB_ROOT="/path/to/grok-build-web"
grok() {
case "${1-}" in
-web|--web)
shift
GROK_CWD="$PWD" node "$GROK_WEB_ROOT/server.mjs" "$@"
;;
*)
command grok "$@"
;;
esac
}Restart the shell, or source its configuration file, before running
grok -web. This shortcut is local to the configured shell; cloning the
repository does not modify the Grok CLI or install it automatically.
-
Install Node.js 24.
node --version -
Install and authenticate the Grok CLI.
grok login
-
Install dependencies and start Grok Build Web.
npm install npm start
-
Choose a workspace from the folder button in the topbar. New sessions start in the selected workspace.
-
Open Settings to choose model, agent profile, subagent definitions, effort, sandbox behavior, rules, approval mode, tool allow-lists, and sidebar display name.
The sidebar groups sessions by workspace. Expand a project to resume a recent session, or use New Chat to start a fresh session in the current workspace.
Each browser tab has its own ACP session ID. Prompts from different tabs can run in parallel, while prompts in the same tab share that tab's agent process and prompt queue.
In a new session, the composer sits below the welcome shortcuts. After the first
message, it moves to the standard bottom chat position. Enter sends, and
Shift+Enter inserts a newline.
The send mode selector is capability-aware:
Interactive: normal ACP session+ self-check: shown only when the installed CLI accepts--checkBest of 3/Best of 5: shown only when the installed CLI accepts--best-of-n
Retired modes disappear automatically instead of dispatching unsupported flags. Headless modes do not append to the interactive ACP session history.
For more control, Tools -> Run -> Headless opens a runner with plain, json, streaming-json, or streaming-messages-json output; text, file, or ACP JSON prompts; JSON Schema output; new, named, resume-by-ID/title, continue, and forked-session modes. Collapsible launch settings expose permission and sandbox modes, allow/deny rules, tool and agent configuration, prompt overrides, memory/plan/subagent/web-search switches, verbatim prompts, and worktree name/ref options.
The attach button inserts text-like files into the prompt as fenced code blocks. Other files upload into .grok-web-uploads inside the current session workspace and are sent as attached file paths so Grok can read them with its native tools. Drag-and-drop and clipboard paste use the same flow.
Each attach action is capped at 5 files. Text files are capped at 256 KB, and binary uploads default to 25 MB.
Supported text extensions include .txt, .md, .js, .mjs, .ts, .tsx, .json, .css, .html, .py, .sh, .ps1, .yml, .yaml, .toml, .csv, .xml, and .log.
Images support .png, .jpg, .jpeg, .gif, .webp, .bmp, and .svg. PDFs support .pdf. Audio, video, archives, and other binary formats upload by path; images, PDFs, audio, and video receive browser preview links when their type is recognized.
Use the composer approval pill:
Auto-approve: the bridge accepts allowed tool requests automaticallyManual approval: tool requests render as permission cards
The Tools section is searchable and grouped by intent:
- Run: headless prompts and routines
- Manage: worktrees, plugins, memory, and capability-gated session import
- Configure: MCP servers, models, and hooks
- Diagnose: inspect, version information, session information, and traces
Plugin and worktree lists render as structured records rather than raw CLI JSON. Each record exposes relevant contextual actions. Plugin uninstall and worktree removal open their existing confirmation forms; worktree removal and garbage collection remain dry-run-first.
The Plugins manager supports install, update, enable, disable, details, uninstall, and workspace-confined manifest validation. The Worktrees manager supports named/ref-based creation, details, guarded removal and garbage collection, and worktree database statistics, path lookup, or confirmed rebuilds.
Type / in the composer to open slash-command autocomplete. Commands streamed
by the installed Grok CLI are normalized before display, and compatibility
fallbacks keep native commands such as /export, /config-agents, and
/code-review reachable when older or partial command streams omit them.
Use the pencil button on a project row in the sidebar to set a local display alias. Aliases are stored in browser localStorage and only change what the sidebar shows. They do not rename folders, Grok sessions, or summary.json values.
Biome recommended lint rules apply repository-wide. The only project-specific exceptions are noSvgWithoutTitle, because inline SVGs are decorative inside already-labelled controls, noControlCharactersInRegex, because the terminal renderer intentionally parses ANSI escape sequences, and noDescendingSpecificity, because the layered responsive stylesheet intentionally places state overrides before some base component rules.
Environment variables read at startup:
| Variable | Default | Purpose |
|---|---|---|
PORT |
0 |
HTTP port. 0 selects a random free port. |
GROK_BIN |
grok |
Path to the Grok CLI binary. |
GROK_BIN_ARGS |
[] |
Extra Grok CLI prefix args as a JSON array of strings. |
GROK_CWD |
process.cwd() |
Initial workspace directory. |
GROK_WEB_USER |
OS username | Default sidebar display name. |
GROK_WEB_NO_OPEN |
unset | Set to skip opening the browser automatically. |
GROK_WEB_USE_API_KEY |
unset | Set to 1 to keep XAI_API_KEY in spawned agent and one-shot CLI environments. |
GROK_WEB_SESSIONS_ROOT |
~/.grok/sessions |
Session directory used for sidebar history and plan hydration. |
GROK_MEMORY_ROOT |
~/.grok/memory |
Memory directory used by the memory panel. |
GROK_WEB_MAX_UPLOAD_BYTES |
26214400 |
Maximum binary upload size. |
GROK_WEB_MAX_REQUEST_BODY_BYTES |
67108864 |
Maximum JSON request body size. |
GROK_WEB_MAX_CLI_STDOUT_BYTES |
33554432 |
Maximum retained CLI stdout bytes; larger output keeps diagnostic head and tail content. |
GROK_WEB_MAX_CLI_STDERR_BYTES |
4194304 |
Maximum retained CLI stderr bytes; larger output keeps diagnostic head and tail content. |
GROK_WEB_MAX_AGENT_LINE_BYTES |
67108864 |
Maximum ACP stdout frame size before the agent is terminated as a protocol violation. |
GROK_WEB_RPC_TIMEOUT_MS |
120000 |
JSON-RPC timeout for non-prompt ACP calls. |
GROK_WEB_PROMPT_TIMEOUT_MS |
1800000 |
JSON-RPC timeout for session/prompt. |
GROK_WEB_PERMISSION_TIMEOUT_MS |
300000 |
Timeout for pending permission cards. |
GROK_WEB_ELICITATION_TIMEOUT_MS |
300000 |
Timeout for pending elicitation cards. |
GROK_WEB_MAX_ACTIVE_AGENTS |
4 |
Maximum active per-tab agent processes. |
GROK_WEB_AGENT_IDLE_MS |
1800000 |
Idle time before an agent process can be evicted. |
GROK_WEB_AGENT_IDLE_SWEEP_MS |
60000 |
Agent idle sweep interval. |
GROK_WEB_GRACEFUL_SHUTDOWN_MS |
3000 |
Maximum time allowed for graceful server shutdown. |
GROK_WEB_SESSIONS_CACHE_TTL_MS |
2000 |
In-memory session-list cache lifetime. |
GROK_WEB_SESSIONS_WATCH_DEBOUNCE_MS |
1500 |
Filesystem session-change debounce interval. |
GROK_WEB_SESSIONS_WATCH_POLL_MS |
5000 |
Poll interval used by the session watcher fallback. |
GROK_WEB_SESSIONS_WATCH_RETRY_MS |
5000 |
Delay before retrying a failed session watcher. |
GROK_WEB_DISABLE_FS_WATCH |
unset | Set to 1 to disable filesystem watching for session changes. |
GROK_OTEL_ENABLED |
unset | Passed through to spawned Grok CLI processes. Set when your Grok build uses this switch for OpenTelemetry export. |
OTEL_EXPORTER_OTLP_ENDPOINT |
unset | Passed through to spawned Grok CLI processes as the OpenTelemetry collector endpoint. |
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT |
unset | Passed through to spawned Grok CLI processes as the trace-specific OpenTelemetry collector endpoint. |
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT |
unset | Passed through to spawned Grok CLI processes as the metrics-specific OpenTelemetry collector endpoint. |
OTEL_SERVICE_NAME |
unset | Passed through to spawned Grok CLI processes for OpenTelemetry service labeling. |
By default, Grok Build Web strips XAI_API_KEY and GROK_API_KEY from spawned agent processes and one-shot CLI runs so the CLI uses the cached grok.com login from ~/.grok/auth.json. Set GROK_WEB_USE_API_KEY=1 to use API key billing instead.
OpenTelemetry variables are inherited by the Grok child process unchanged. Tools -> Diagnose -> Session info shows whether telemetry-related variables are present without displaying their values.
browser -> POST /prompt -> server.mjs -> stdin -> grok agent stdio
browser <- SSE /stream <- server.mjs <- stdout <- grok agent stdio
The Grok CLI exposes Agent Client Protocol over grok agent stdio. server.mjs starts a local HTTP server, creates a one-time bootstrap token, and lazily spawns Grok agent children through the bridge.
Each browser tab receives its own ACP sessionId, stored in the URL and browser state. The bridge tags events with sessionId, so each tab receives only its own stream. Agent processes are lazy-spawned on /tab/new or /tab/load, reused for prompts in that tab, and evicted after the configured idle period.
The server also shells out to the Grok CLI for non-ACP actions such as inspect, models, MCP servers, worktrees, sharing, traces, capability-gated imports, update checks, login, and headless prompts. Managed ACP and headless runs pass --no-auto-update when supported so the web app remains in control of update notices and process restarts.
grok-web/
|-- server.mjs Thin boot + HTTP listener
|-- lib/ Bridge modules (ACP, routes, sessions, CLI)
| |-- grok-bridge.mjs Multi-agent pool (one stdio child per tab session)
| |-- agent-connection.mjs Single ACP stdio child + prompt queue
| |-- sessions-store.mjs Session list + plan.json reads
| |-- cli-runner.mjs One-shot grok CLI shell-outs
| `-- http/
| |-- router.mjs Request dispatch
| `-- routes/ Per-domain HTTP handlers
|-- public/
| |-- index.html Page shell
| |-- styles/
| | |-- main.css Layout, sidebar, composer, settings, modals
| | `-- cards.css Tool, plan, permission, and elicitation cards
| `-- js/
| |-- main.js Browser entry point
| |-- api.js Fetch wrappers
| |-- state.js Shared client state and DOM refs
| |-- dispatch.js SSE event dispatch
| |-- chat.js Turn rendering and assistant output
| |-- tools.js Barrel re-export for tool rendering
| |-- tools/ Per-tool renderers + details registry
| |-- settings.js Settings panel
| |-- identity.js Sidebar identity display
| |-- sidebar.js Project drawer and recents
| |-- composer.js Input, send, stop, approval mode
| |-- topbar.js Workspace picker, mobile actions, share, update notice
| `-- tools-menu.js Grouped/searchable sidebar tool wiring
|-- package.json
|-- test/ Fake ACP, renderer, UI-state, bridge, and live integration tests
`-- probe/ Protocol discovery scripts
| Route | Method | Purpose |
|---|---|---|
/ |
GET | Token-gated app shell |
/static/* |
GET | Static CSS and JS |
/vendor/mermaid.min.js |
GET | Locally installed Mermaid browser bundle |
/session-media |
GET | Authenticated previews for generated media under Grok session storage |
/upload-media |
GET | Authenticated previews for uploaded media under the session workspace |
/stream |
GET | SSE stream of agent events |
/prompt |
POST | Send an ACP prompt |
/cancel |
POST | Cancel the running turn |
/upload |
POST | Upload a file into the current session workspace |
/settings |
GET, POST | Bridge settings, including auto approve and display name |
/identity |
GET | Current sidebar identity |
/spawn-opts |
GET | Current launch flags and detected Grok agent capabilities |
/sessions |
GET | Recent sessions from the configured sessions root |
/session/plan |
GET | Persisted plan.json state for a session |
/session/new |
POST | Start a new session in a workspace |
/session/load |
POST | Load an existing session |
/session/respawn |
POST | Restart the agent with new launch flags |
/tab/new |
POST | Create a per-tab ACP session |
/tab/load |
POST | Load a per-tab ACP session |
/permission |
POST | Answer a pending permission request |
/elicitation |
POST | Answer a pending elicitation request |
/cli/inspect |
GET | grok inspect --json |
/cli/update-check |
GET | grok update --check --json |
/cli/version |
GET | Normalized grok version --json details |
/cli/models |
GET | grok models |
/cli/mcp |
GET | grok mcp list |
/cli/worktree |
GET | Legacy plain-text grok worktree list output |
/cli/plugins |
GET | Installed plugins from grok plugin list --json |
/cli/plugins/action |
POST | Validated plugin lifecycle actions |
/cli/worktrees |
GET | Tracked worktrees from grok worktree list --json --all |
/cli/worktrees/action |
POST | Validated create/show/remove/GC/database actions |
/cli/login/status |
GET | Whether ~/.grok/auth.json exists after device auth |
/cli/memory/list |
GET | List Grok memory files under the configured memory root |
/cli/memory/read |
GET | Read one Grok memory file, confined to the memory root |
/cli/share |
POST | grok share <sessionId> |
/cli/trace |
POST | grok trace --local --json |
/cli/login |
POST | grok login --device-auth |
/cli/oneshot |
POST | Capability-gated legacy check and best-of-N modes |
/cli/headless |
POST | Configurable headless grok -p runner |
/cli/sessions/search |
POST | Search local Grok sessions |
/cli/import |
POST | grok import --json -- <targets> when supported; otherwise HTTP 501 |
Run syntax, lint, and format checks:
npm run checkRun the fast account-free development suite:
npm testThis runs the focused CLI, API, rendering, streaming, security, permission, and utility checks used during normal development. Run the exhaustive account-free suite before publishing:
npm run test:fullThe full suite adds fake ACP bridge/SSE integration, per-session cwd isolation,
sidebar/settings behavior, bootstrap recovery, session edge cases, uploads,
oversized replay pruning, fork-session isolation, large output, cancellation,
and complete tool-lifecycle coverage. npm run verify combines static checks,
the full account-free suite, and browser visual smoke coverage.
Run browser visual smoke checks:
npm run test:visualRun live integration checks against the installed grok CLI:
npm run test:liveLive tests start a real local server on an ephemeral port, bootstrap the one-time token, connect SSE, call /sessions, /spawn-opts, /cli/models, /cli/mcp, trigger real web search, read generated PNG/JPG/PDF/PPTX fixtures through read_file, and verify cancellation recovery. X search and plugin MCP auth are account-dependent opt-in checks:
$env:GROK_WEB_LIVE_X_SEARCH='1'; npm run test:live
$env:GROK_WEB_LIVE_PLUGIN_MCP_NAME='<server-name>'; npm run test:liveThe current audit baseline is Grok CLI 0.2.118 (1e1687c1cf) stable. On August
1, 2026, grok update --check --json reported the installation up to date. The
app detects
optional commands and launch flags at runtime, so unsupported import and legacy
headless dispatch modes are hidden instead of being sent to the CLI.
After a CLI upgrade, run npm run verify, then use
the live verification checklist for
account-dependent and platform-specific behavior. Update FUTURE.md
when the CLI adds a web-relevant command or flag.
Feature touch points:
- New tool renderer: add a file under
public/js/tools/and register it inpublic/js/tools/details-registry.mjs. - New event type: update
public/js/dispatch.js. - New permission UI: update
public/js/permissions.js. - New elicitation UI: update
public/js/elicitation.js. - New launch flag: update
public/js/settings.jsandlib/grok-bridge.mjs. - New HTTP route: add a handler in
lib/http/routes/and register it inlib/http/router.mjs. - New CLI panel: update
lib/http/routes/cli.mjs,public/js/api.js, andpublic/js/panels.js. - Sidebar or layout polish: update
public/styles/main.css. - Tool card polish: update
public/styles/cards.css.
Grok Build Web is designed for local use. The launch URL includes a one-time token that sets an HttpOnly cookie. API and SSE requests require that cookie. Static assets are public because they contain no secrets.
HTTP responses include a local-app security baseline: CSP with frame-ancestors 'none', X-Frame-Options: DENY, X-Content-Type-Options: nosniff, and Referrer-Policy: no-referrer. The server only accepts Host values for 127.0.0.1, localhost, or ::1 on its active port. Mutating browser requests with an Origin header must come from the same local origin.
The bridge can read and write files only through ACP requests from the agent, and those filesystem handlers are confined to the request's session workspace. Generated media previews are served only through the authenticated /session-media endpoint, which is confined to Grok session storage and does not expose arbitrary local files. Uploaded media previews are served only from .grok-web-uploads inside the active session workspace.
Per-tab session APIs keep workspace cwd in per-session state so one tab cannot silently change another tab's fallback cwd. Windows extended-length path prefixes are stripped from model-facing and rendered display text while internal filesystem paths remain confined by workspace checks. Agent restarts and session loads are serialized per agent and through a small bridge operation queue. Permission and elicitation timeout handles are cleared across respawns, and SSE reconnect replay is delivered with response backpressure, history pruning, and listener cleanup.