mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-05 06:59:42 +02:00
feat: make Antigravity a first-class CLI across docs, installer and UI
Antigravity (agy) was wired into the session layer but never propagated to the surfaces around it, while Gemini CLI stayed documented as a consumer product despite being enterprise-only since Google's cutover. Gemini keeps full support; Antigravity now sits beside it everywhere. Functional fixes: - docker/agent.Dockerfile never installed agy, so a docker case with mode 'antigravity' died on command-not-found. agy is not on npm, so it gets its own installer step. --dir /usr/local/bin is load-bearing: the default $HOME/.local/bin resolves to root's home at build time and is unreachable by the `agent` user the container runs as. Verified inside codeman/agent:base (v1.1.10, reachable as `agent`). Note the binary is ~190MB, the largest layer in the image. - Welcome screen gained a Run Antigravity action, gated on agy being present like the other CLI buttons, with a cyan identity matching the toolbar run button and run-mode dot. - install.sh now detects agy (search paths mirroring the resolver), counts it as a satisfying AI CLI, and recommends it over Gemini in the install hints. Detection only, no new auto-install path. Docs corrected where they were factually wrong: - architecture-invariants documented isExternalCliMode() as opencode/codex/gemini when the code has included antigravity for a while, said "all three modes", and omitted ANTIGRAVITY_ from the env prefix allowlist row. - cron-guide's agentType enum, cron-discovery's SessionMode, and remote-sessions' RemoteCommandMode were all stale. Also: README + README.zh-CN (five CLIs, Gemini marked enterprise-only), package.json keyword, and comment drift in 8 places. test/run-mode-ui.test.ts now covers the new welcome button; verified it fails without the settings-ui wiring. Antigravity nests its whole state under ~/.gemini/antigravity-cli/, not ~/.antigravity, so the existing .gemini docker credential seed already covers it. Recorded as a comment so nobody adds dead config later. isAltScreenStripMode() deliberately still excludes antigravity: whether its TUI needs the alt-screen strip is a behavioural question that needs a real agy session, not a guess. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -596,6 +596,9 @@ interface CredStorePolicy {
|
||||
|
||||
const CRED_STORES: CredStorePolicy[] = [
|
||||
{ rel: '.codex', shareDirs: ['sessions'], shareFiles: ['history.jsonl'], seedFiles: ['auth.json', 'config.toml'] },
|
||||
// Also covers Antigravity: `agy` nests its whole state (auth `jetski_state.pbtxt`,
|
||||
// `conversations/`, `knowledge/`) under `~/.gemini/antigravity-cli/`, so it needs no
|
||||
// entry of its own. There is no `~/.antigravity` credential dir to add.
|
||||
{ rel: '.gemini', seedWhole: true },
|
||||
{ rel: '.config/gcloud', seedWhole: true },
|
||||
{ rel: '.config/opencode', seedWhole: true },
|
||||
|
||||
+1
-1
@@ -982,7 +982,7 @@ export function dockerTmuxSessionName(sessionId: string): string {
|
||||
const RESUME_ID_SAFE = /^[A-Za-z0-9._-]+$/;
|
||||
|
||||
/**
|
||||
* Append the CLI-specific resume flag to a pane command (codex/gemini). Only fires
|
||||
* Append the CLI-specific resume flag to a pane command (codex/gemini/antigravity). Only fires
|
||||
* when the in-container tmux is RE-CREATED (`new-session -A` makes the flag inert
|
||||
* on a live reattach), i.e. exactly when the previous live agent was lost and we
|
||||
* want to resume the conversation from the bind-mounted transcript. Claude mode
|
||||
|
||||
@@ -28,7 +28,7 @@ let _claudeDir: string | null = null;
|
||||
|
||||
/**
|
||||
* Returns true if the Claude CLI binary can be located (via `which` or one of
|
||||
* the common install directories). Mirrors `isGeminiAvailable`/`isOpenCodeAvailable`/
|
||||
* the common install directories). Mirrors `isGeminiAvailable`/`isAntigravityAvailable`/`isOpenCodeAvailable`/
|
||||
* `isCodexAvailable` in the sibling resolvers.
|
||||
*/
|
||||
export function isClaudeAvailable(): boolean {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @fileoverview Resolve the `cloudflared` binary across common install paths.
|
||||
*
|
||||
* Mirrors the CLI resolvers (gemini-cli-resolver.ts et al), for the same reason
|
||||
* Mirrors the CLI resolvers (antigravity-cli-resolver.ts et al), for the same reason
|
||||
* they exist: the welcome screen should not offer a button whose only possible
|
||||
* outcome is an error toast.
|
||||
*
|
||||
|
||||
@@ -1976,7 +1976,7 @@ class CodemanApp {
|
||||
// Source 2: Terminal buffer fallback — strip ANSI, drop Claude CLI chrome.
|
||||
// Claude + shell only: _cleanTerminalBuffer knows Claude CLI's output, and
|
||||
// shell sessions have no transcript source at all; for TUI modes
|
||||
// (codex/opencode/gemini) it yields repaint garbage, so a clear
|
||||
// (codex/opencode/gemini/antigravity) it yields repaint garbage, so a clear
|
||||
// placeholder beats a messy screen dump there.
|
||||
const sessionMode = this.sessions.get(this.activeSessionId)?.mode || 'claude';
|
||||
if (!lastResponse && (sessionMode === 'claude' || sessionMode === 'shell')) {
|
||||
@@ -4281,7 +4281,7 @@ class CodemanApp {
|
||||
// (viewport + scrollback + colors) for an instant first paint. For codex
|
||||
// this is also a correctness fix — its byte-stream replay shows only the
|
||||
// latest TUI frame (the idle welcome banner) because codex doesn't include
|
||||
// earlier conversation in its current redraw. For claude/opencode/gemini
|
||||
// earlier conversation in its current redraw. For claude/opencode/gemini/antigravity
|
||||
// the replay is already complete, so the snapshot is purely a faster,
|
||||
// scroll-preserving first paint before the canonical fetch reconciles.
|
||||
//
|
||||
|
||||
@@ -323,6 +323,10 @@
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="5 3 19 12 5 21 5 3"/></svg>
|
||||
Run OpenCode
|
||||
</button>
|
||||
<button class="welcome-btn welcome-btn-antigravity" id="welcomeAntigravityBtn" style="display: none;" onclick="app.setRunMode('antigravity'); app.runAntigravity()">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="5 3 19 12 5 21 5 3"/></svg>
|
||||
Run Antigravity
|
||||
</button>
|
||||
<button class="welcome-btn welcome-btn-gemini" id="welcomeGeminiBtn" style="display: none;" onclick="app.setRunMode('gemini'); app.runGemini()">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="5 3 19 12 5 21 5 3"/></svg>
|
||||
Run Gemini
|
||||
@@ -2192,7 +2196,7 @@
|
||||
<div class="form-row">
|
||||
<label>Image</label>
|
||||
<input type="text" id="dockerImage" placeholder="codeman/agent:base" autocomplete="off" autocapitalize="off" spellcheck="false">
|
||||
<span class="form-hint">Build it once with <code>node scripts/build-agent-image.mjs</code>. Contains node + claude/codex/gemini + tmux.</span>
|
||||
<span class="form-hint">Build it once with <code>node scripts/build-agent-image.mjs</code>. Contains node + claude/codex/gemini/opencode/agy + tmux.</span>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label>Network</label>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @fileoverview Quick start (case loading, session spawning for Claude/Shell/OpenCode/Codex/Gemini),
|
||||
* @fileoverview Quick start (case loading, session spawning for Claude/Shell/OpenCode/Codex/Gemini/Antigravity),
|
||||
* session options modal (per-session settings, color picker, rename),
|
||||
* session options tabs (Ralph config tab), case settings (CRUD, links),
|
||||
* create case modal, and mobile case picker.
|
||||
|
||||
@@ -748,6 +748,7 @@ Object.assign(CodemanApp.prototype, {
|
||||
const buttons = [
|
||||
['welcomeClaudeBtn', 'claude'],
|
||||
['welcomeOpencodeBtn', 'opencode'],
|
||||
['welcomeAntigravityBtn', 'antigravity'],
|
||||
['welcomeGeminiBtn', 'gemini'],
|
||||
// Not a run mode, same reasoning: offering a Cloudflare Tunnel on a box
|
||||
// without cloudflared can only ever produce "cloudflared not found".
|
||||
|
||||
@@ -3305,6 +3305,23 @@ body.touch-device .terminal-container .xterm .xterm-helper-textarea {
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
/* Antigravity: cyan identity, matching .btn-toolbar.btn-run.mode-antigravity and
|
||||
.run-mode-dot.antigravity so the welcome action reads as the same backend. */
|
||||
.welcome-btn-antigravity {
|
||||
background: linear-gradient(135deg, #0b2b33 0%, #0e7490 55%, #0891b2 100%);
|
||||
border-color: rgba(34, 211, 238, 0.4);
|
||||
color: #cffafe;
|
||||
box-shadow: 0 2px 8px rgba(34, 211, 238, 0.16), inset 0 1px 0 rgba(255, 255, 255, 0.06);
|
||||
}
|
||||
|
||||
.welcome-btn-antigravity:hover {
|
||||
background: linear-gradient(135deg, #124450 0%, #0891b2 55%, #06b6d4 100%);
|
||||
box-shadow: 0 4px 20px rgba(34, 211, 238, 0.3), 0 0 40px rgba(8, 145, 178, 0.12), inset 0 1px 0 rgba(255, 255, 255, 0.08);
|
||||
border-color: rgba(103, 232, 249, 0.5);
|
||||
color: #ecfeff;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.welcome-btn-gemini {
|
||||
background: linear-gradient(135deg, #10243f 0%, #174ea6 55%, #4f46e5 100%);
|
||||
border-color: rgba(96, 165, 250, 0.4);
|
||||
|
||||
@@ -1408,7 +1408,7 @@ Object.assign(CodemanApp.prototype, {
|
||||
}
|
||||
titleSpan.appendChild(document.createTextNode(s.name || s.firstPrompt || shortDir));
|
||||
|
||||
// Badge row: mode (claude/codex/opencode/gemini/shell) + a LIVE pill.
|
||||
// Badge row: mode (claude/codex/opencode/gemini/antigravity/shell) + a LIVE pill.
|
||||
const badgeRow = document.createElement('div');
|
||||
badgeRow.className = 'history-item-badges';
|
||||
if (s.mode) {
|
||||
|
||||
@@ -415,7 +415,7 @@ export function registerSessionRoutes(
|
||||
//
|
||||
// For keys the caller is actively setting, strip any stale disk entry a prior
|
||||
// Codeman version may have written. Scope limited to:
|
||||
// - Claude mode (OpenCode/Codex/Gemini don't read .claude/settings.local.json)
|
||||
// - Claude mode (OpenCode/Codex/Gemini/Antigravity don't read .claude/settings.local.json)
|
||||
// - workingDir inside CASES_DIR / the per-user case space (Codeman's managed
|
||||
// territory — we never mutate .claude/settings.local.json in arbitrary user
|
||||
// repos that POST /api/sessions can target, as those may have hand-authored
|
||||
@@ -2027,7 +2027,7 @@ export function registerSessionRoutes(
|
||||
if (!host) return createErrorResponse(ApiErrorCode.NOT_FOUND, 'Remote host not found');
|
||||
|
||||
// Per-session config that is applied to the LOCAL tmux/CLI wrapper (env vars via
|
||||
// tmux setenv, effort/model CLI args, codex/gemini/opencode config) does NOT
|
||||
// tmux setenv, effort/model CLI args, codex/gemini/antigravity/opencode config) does NOT
|
||||
// cross ssh, so it would silently no-op. Reject rather than pretend it worked —
|
||||
// remote command/env customization goes through the per-host command override.
|
||||
if (
|
||||
@@ -2364,7 +2364,7 @@ export function registerSessionRoutes(
|
||||
});
|
||||
ctx.broadcast(SseEvent.SessionInteractive, { id: session.id, mode: 'shell' });
|
||||
} else {
|
||||
// 'claude', 'opencode', 'codex', and 'gemini' modes use startInteractive()
|
||||
// 'claude', 'opencode', 'codex', 'gemini', and 'antigravity' modes use startInteractive()
|
||||
await session.startInteractive();
|
||||
getLifecycleLog().log({
|
||||
event: 'started',
|
||||
|
||||
+1
-1
@@ -516,7 +516,7 @@ export const DockerHostSchema = z.object({
|
||||
mountCredentials: z.boolean().optional(),
|
||||
hooksEnabled: z.boolean().optional(),
|
||||
resumeOnStart: z.boolean().optional(),
|
||||
commands: RemoteCommandOverridesSchema, // same shell/claude/opencode/codex/gemini shape
|
||||
commands: RemoteCommandOverridesSchema, // same shell/claude/opencode/codex/gemini/antigravity shape
|
||||
extraCreateArgs: z
|
||||
.array(
|
||||
z
|
||||
|
||||
Reference in New Issue
Block a user