mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-06 15:39:41 +02:00
feat(grok): add Grok Build (xAI) as a seventh CLI run mode
SessionMode gains 'grok', a first-class backend alongside Claude Code,
shell, OpenCode, Codex, Gemini, Antigravity and Pi: its own PTY, tmux
session, charcoal tab identity ('gk' badge), welcome button, run-mode
entry, cron agentType, Docker and remote-SSH command defaults, and
clone-repo Brain option. Flag surface verified live against grok 1.0.5.
Grok mixes two existing shapes and the wiring follows from that:
- Codex-shaped on permissions: the bypass switch is GrokConfig.alwaysApprove
(--always-approve, grok's bypassPermissions mode; config-level deny rules
still apply on top). The Run button sends it true, like runAntigravity(),
and clampExternalCliBypassForOwner() puts grok in the only-if-sent branch:
a bare grok spawn is grok's own ask-mode default, which is already safe,
so only a sent config needs the flag forced off. Cron needs nothing for
the same reason.
- OpenCode-shaped on rendering: grok is a fullscreen alternate-screen TUI
with mouse support, so it stays OUT of isAltScreenStripMode() and lands
on the narrow tmux-attach strip and the 'buffer' local-echo fallthrough
(unmeasured against an authenticated composer; documented fallback is the
'off' branch).
- Pi-shaped on resolution: 'grok' has npm squatters (@vibe-kit/grok-cli
also installs a grok bin), so grok-cli-resolver.ts version-probes every
candidate (grok --version, killSignal SIGKILL, VITEST-gated) and
GET /api/grok/status surfaces path AND version; GROK_VERSION_REGEX is
shared with the dependency registry so doctor and run mode cannot drift.
Env allowlist gains GROK_* plus the XAI_* vendor namespace (XAI_API_KEY is
grok's documented headless auth var), the same narrow-vendor reasoning as
GOOGLE_* for gemini. Resume is id-regexed on purpose: grok's own --resume
also matches session titles, which are arbitrary user strings that must
never reach the bash -c spawn line.
Docker: grok is not on npm, so the agent image installs it in its own step
(xAI's installer has no --dir override; the binary is copied to
/usr/local/bin and root's ~/.grok dropped in the same layer), and
credentials are seeded per-file (auth.json, config.toml, pager.toml; the
dir also holds sessions/, memory/ and the ~160MB binary). Remote SSH routes
through the login-shell wrapper like the other agent CLIs.
Verified end to end on an isolated CODEMAN_INSTANCE with grok 1.0.5
installed: /api/grok/status resolves and reports the probed version,
quick-start spawns a pane whose command line ends in 'grok
--always-approve', the real TUI renders (OAuth device screen on an
unauthenticated box), and grokConfig round-trips through state.json.
Docs: docs/grok-integration.md (user guide) + docs/grok-integration-plan.md
(decisions, verification record, follow-ups).
Tests: test/grok-mode.test.ts, test/grok-cli-resolver.test.ts, plus
extended clamp/system-routes/render-index-html/run-mode-ui/mobile-overview/
local-echo-gating coverage. npm test (the CI gate) green: 5910 tests.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -347,7 +347,8 @@ html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="cat
|
||||
.history-view-all-btn,
|
||||
.session-tab .tab-mode.gemini,
|
||||
.session-tab .tab-mode.antigravity,
|
||||
.session-tab .tab-mode.pi
|
||||
.session-tab .tab-mode.pi,
|
||||
.session-tab .tab-mode.grok
|
||||
) {
|
||||
color: var(--accent-d);
|
||||
}
|
||||
@@ -2246,6 +2247,11 @@ body.solo-mode .btn-lifecycle-log {
|
||||
color: #f472b6;
|
||||
}
|
||||
|
||||
.session-tab .tab-mode.grok {
|
||||
background: rgba(212, 212, 216, 0.18);
|
||||
color: #d4d4d8;
|
||||
}
|
||||
|
||||
/* Timer Banner - Compact */
|
||||
.timer-banner {
|
||||
display: flex;
|
||||
@@ -3610,6 +3616,23 @@ body.touch-device .terminal-container .xterm .xterm-helper-textarea {
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
/* Grok (xAI): monochrome charcoal identity, matching .btn-toolbar.btn-run.mode-grok
|
||||
and .run-mode-dot.grok so the welcome action reads as the same backend. */
|
||||
.welcome-btn-grok {
|
||||
background: linear-gradient(135deg, #131316 0%, #27272a 55%, #3f3f46 100%);
|
||||
border-color: rgba(212, 212, 216, 0.4);
|
||||
color: #f4f4f5;
|
||||
box-shadow: 0 2px 8px rgba(212, 212, 216, 0.12), inset 0 1px 0 rgba(255, 255, 255, 0.06);
|
||||
}
|
||||
|
||||
.welcome-btn-grok:hover {
|
||||
background: linear-gradient(135deg, #1f1f23 0%, #3f3f46 55%, #52525b 100%);
|
||||
box-shadow: 0 4px 20px rgba(212, 212, 216, 0.22), 0 0 40px rgba(161, 161, 170, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.08);
|
||||
border-color: rgba(228, 228, 231, 0.5);
|
||||
color: #fafafa;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.welcome-btn-gemini {
|
||||
background: linear-gradient(135deg, #10243f 0%, #174ea6 55%, #4f46e5 100%);
|
||||
border-color: rgba(96, 165, 250, 0.4);
|
||||
@@ -4684,6 +4707,25 @@ body.touch-device .terminal-container .xterm .xterm-helper-textarea {
|
||||
color: #fff1f7;
|
||||
}
|
||||
|
||||
/* Grok mode colors. Same cascade note as pi above: this base-sheet pair only
|
||||
renders on the `og` skin — the nested `html:not([data-skin="og"])` block
|
||||
re-declares `.btn-toolbar.btn-run` at a HIGHER specificity, so grok also
|
||||
carries a rule inside that block (search `.btn-toolbar.btn-run.mode-grok`). */
|
||||
.btn-toolbar.btn-run.mode-grok,
|
||||
.btn-toolbar.btn-run-gear.mode-grok {
|
||||
background: linear-gradient(135deg, #131316 0%, #27272a 55%, #3f3f46 100%);
|
||||
border-color: rgba(212, 212, 216, 0.5);
|
||||
color: #f4f4f5;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2), inset 0 1px 0 rgba(255, 255, 255, 0.06);
|
||||
}
|
||||
.btn-toolbar.btn-run.mode-grok:hover,
|
||||
.btn-toolbar.btn-run-gear.mode-grok:hover {
|
||||
background: linear-gradient(135deg, #1f1f23 0%, #3f3f46 55%, #52525b 100%);
|
||||
box-shadow: 0 0 12px rgba(212, 212, 216, 0.28), 0 2px 8px rgba(63, 63, 70, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.08);
|
||||
border-color: rgba(228, 228, 231, 0.6);
|
||||
color: #fafafa;
|
||||
}
|
||||
|
||||
/* Dropdown menu */
|
||||
.run-mode-menu {
|
||||
display: none;
|
||||
@@ -4767,6 +4809,7 @@ body.touch-device .terminal-container .xterm .xterm-helper-textarea {
|
||||
.run-mode-dot.gemini { background: #8ab4f8; }
|
||||
.run-mode-dot.antigravity { background: #22d3ee; }
|
||||
.run-mode-dot.pi { background: #f472b6; }
|
||||
.run-mode-dot.grok { background: #a1a1aa; }
|
||||
.run-mode-dot.shell { background: #94a3b8; }
|
||||
|
||||
/* Phone-only Enter button (see index.html). Hidden by default at every width;
|
||||
@@ -14102,6 +14145,15 @@ html:not([data-skin="og"]) {
|
||||
color: #fff1f7;
|
||||
}
|
||||
.btn-toolbar.btn-run.mode-pi:hover { box-shadow: 0 0 14px -2px rgba(244, 114, 182, 0.45); }
|
||||
/* Grok keeps its charcoal identity on the non-og skins. Same specificity trap
|
||||
as pi above: without this rule the generic `.btn-toolbar.btn-run` in this
|
||||
nested block wins and grok renders as generic claude blue. */
|
||||
.btn-toolbar.btn-run.mode-grok {
|
||||
background: linear-gradient(135deg, #27272a, #52525b);
|
||||
border-color: #18181b;
|
||||
color: #fafafa;
|
||||
}
|
||||
.btn-toolbar.btn-run.mode-grok:hover { box-shadow: 0 0 14px -2px rgba(161, 161, 170, 0.5); }
|
||||
.btn-toolbar.btn-run-gear {
|
||||
background: var(--accent-d);
|
||||
border-color: var(--accent);
|
||||
|
||||
Reference in New Issue
Block a user