Skip to content

Commit b6be056

Browse files
nezharVibePod
authored andcommitted
Fixes gemini cli start issues
1 parent 80b8d44 commit b6be056

7 files changed

Lines changed: 161 additions & 14 deletions

File tree

docs/agents/index.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ VibePod manages each agent as a Docker container. Credentials and config are per
77
| Agent | Provider | Shortcut | Image |
88
|-------|----------|----------|-------|
99
| `claude` | Anthropic | `vp c` | `vibepod/claude:latest` |
10-
| `gemini` | Google | `vp g` | `nezhar/gemini-container:latest` |
11-
| `opencode` | OpenAI | `vp o` | `nezhar/opencode-cli:latest` |
12-
| `devstral` | Mistral | `vp d` | `nezhar/devstral-cli:latest` |
13-
| `auggie` | Augment Code | `vp a` | `nezhar/auggie-cli:latest` |
14-
| `copilot` | GitHub | `vp p` | `nezhar/copilot-cli:latest` |
10+
| `gemini` | Google | `vp g` | `vibepod/gemini:latest` |
11+
| `opencode` | OpenAI | `vp o` | `vibepod/opencode:latest` |
12+
| `devstral` | Mistral | `vp d` | `vibepod/devstral:latest` |
13+
| `auggie` | Augment Code | `vp a` | `vibepod/auggie:latest` |
14+
| `copilot` | GitHub | `vp p` | `vibepod/copilot:latest` |
1515
| `codex` | OpenAI | `vp x` | `vibepod/codex:latest` |
1616

1717
## First run & authentication

docs/configuration.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,35 +45,35 @@ agents:
4545

4646
gemini:
4747
enabled: true
48-
image: nezhar/gemini-container:latest
48+
image: vibepod/gemini:latest
4949
env: {}
5050
volumes: []
5151
init: []
5252

5353
opencode:
5454
enabled: true
55-
image: nezhar/opencode-cli:latest
55+
image: vibepod/opencode:latest
5656
env: {}
5757
volumes: []
5858
init: []
5959

6060
devstral:
6161
enabled: true
62-
image: nezhar/devstral-cli:latest
62+
image: vibepod/devstral:latest
6363
env: {}
6464
volumes: []
6565
init: []
6666

6767
auggie:
6868
enabled: true
69-
image: nezhar/auggie-cli:latest
69+
image: vibepod/auggie:latest
7070
env: {}
7171
volumes: []
7272
init: []
7373

7474
copilot:
7575
enabled: true
76-
image: nezhar/copilot-cli:latest
76+
image: vibepod/copilot:latest
7777
env: {}
7878
volumes: []
7979
init: []

src/vibepod/commands/run.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,14 @@ def _host_user() -> str | None:
165165
return f"{getuid()}:{getgid()}"
166166

167167

168+
def _terminal_env_defaults() -> dict[str, str]:
169+
"""Return host terminal-related env vars for interactive container apps."""
170+
keys = ("TERM", "COLORTERM", "TERM_PROGRAM", "TERM_PROGRAM_VERSION", "LANG")
171+
values = {key: value for key in keys if (value := os.environ.get(key))}
172+
values.setdefault("TERM", "xterm-256color")
173+
return values
174+
175+
168176
def _compose_file_present(workspace: Path) -> bool:
169177
return (workspace / "docker-compose.yml").exists() or (workspace / "compose.yml").exists()
170178

@@ -260,6 +268,7 @@ def run(
260268
merged_env = {
261269
"USER_UID": str(os.getuid()),
262270
"USER_GID": str(os.getgid()),
271+
**_terminal_env_defaults(),
263272
**spec.extra_env,
264273
**{str(k): str(v) for k, v in agent_cfg.get("env", {}).items()},
265274
**_parse_env_pairs(env or []),

src/vibepod/core/agents.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,9 @@ class AgentSpec:
5353
"google",
5454
DEFAULT_IMAGES["gemini"],
5555
"gemini",
56-
["gemini"],
56+
# Run via node to bypass shebang parsing in Alpine BusyBox (/usr/bin/env has no -S),
57+
# and force HOME to the mounted config path expected by VibePod.
58+
["env", "HOME=/config", "node", "/usr/local/bin/gemini"],
5759
"/config",
5860
{"HOME": "/config"},
5961
),

tests/test_agents.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ def test_codex_spec_has_ikwid_args() -> None:
6464
assert spec.ikwid_args == ["--dangerously-bypass-approvals-and-sandbox"]
6565

6666

67+
def test_gemini_spec_runs_via_node_wrapper() -> None:
68+
spec = get_agent_spec("gemini")
69+
assert spec.command == ["env", "HOME=/config", "node", "/usr/local/bin/gemini"]
70+
71+
6772
def test_unsupported_agents_have_no_ikwid_args() -> None:
6873
for agent in ("gemini", "opencode", "devstral", "auggie", "copilot"):
6974
spec = get_agent_spec(agent)

tests/test_constants.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
"""Constants and default-image mapping tests."""
2+
3+
from __future__ import annotations
4+
5+
from vibepod.constants import get_default_images
6+
7+
8+
def test_default_images_match_documented_registry_defaults(monkeypatch) -> None:
9+
for key in (
10+
"VP_IMAGE_NAMESPACE",
11+
"VP_IMAGE_CLAUDE",
12+
"VP_IMAGE_GEMINI",
13+
"VP_IMAGE_OPENCODE",
14+
"VP_IMAGE_DEVSTRAL",
15+
"VP_IMAGE_AUGGIE",
16+
"VP_IMAGE_COPILOT",
17+
"VP_IMAGE_CODEX",
18+
"VP_DATASETTE_IMAGE",
19+
"VP_PROXY_IMAGE",
20+
):
21+
monkeypatch.delenv(key, raising=False)
22+
23+
images = get_default_images()
24+
25+
assert images["claude"] == "vibepod/claude:latest"
26+
assert images["gemini"] == "vibepod/gemini:latest"
27+
assert images["opencode"] == "vibepod/opencode:latest"
28+
assert images["devstral"] == "vibepod/devstral:latest"
29+
assert images["auggie"] == "vibepod/auggie:latest"
30+
assert images["copilot"] == "vibepod/copilot:latest"
31+
assert images["codex"] == "vibepod/codex:latest"
32+
assert images["datasette"] == "vibepod/datasette:latest"
33+
assert images["proxy"] == "vibepod/proxy:latest"

tests/test_run.py

Lines changed: 101 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ def __init__(self) -> None:
6262

6363
manager.run_agent(
6464
agent="auggie",
65-
image="nezhar/auggie-cli:latest",
65+
image="vibepod/auggie:latest",
6666
workspace=workspace,
6767
config_dir=config_dir,
6868
config_mount_path="/config",
@@ -110,7 +110,7 @@ def __init__(self) -> None:
110110

111111
manager.run_agent(
112112
agent="devstral",
113-
image="nezhar/devstral-cli:latest",
113+
image="vibepod/devstral:latest",
114114
workspace=workspace,
115115
config_dir=config_dir,
116116
config_mount_path="/config",
@@ -663,7 +663,7 @@ def run_agent(self, **kwargs) -> object: # type: ignore[no-untyped-def]
663663
run_cmd.run(agent="gemini", workspace=tmp_path, detach=True, ikwid=True)
664664

665665
# Command should be unchanged (no ikwid args appended)
666-
assert captured["command"] == ["gemini"]
666+
assert captured["command"] == ["env", "HOME=/config", "node", "/usr/local/bin/gemini"]
667667

668668

669669
def test_ikwid_false_does_not_modify_command(monkeypatch, tmp_path: Path) -> None:
@@ -1047,3 +1047,101 @@ def __init__(self) -> None:
10471047
run_cmd.run(agent="c", workspace=tmp_path)
10481048

10491049
assert exc.value.exit_code == EXIT_DOCKER_NOT_RUNNING
1050+
1051+
1052+
def test_run_forwards_host_terminal_env(monkeypatch, tmp_path: Path) -> None:
1053+
captured: dict = {}
1054+
1055+
class _CapturingDockerManager:
1056+
def ensure_network(self, name: str) -> None:
1057+
pass
1058+
1059+
def networks_with_running_containers(self) -> list[str]:
1060+
return []
1061+
1062+
def pull_image(self, image: str) -> None:
1063+
pass
1064+
1065+
def ensure_proxy(self, **kwargs) -> None: # type: ignore[no-untyped-def]
1066+
pass
1067+
1068+
def run_agent(self, **kwargs) -> object: # type: ignore[no-untyped-def]
1069+
captured.update(kwargs)
1070+
container = type(
1071+
"_Container",
1072+
(),
1073+
{
1074+
"name": "vibepod-gemini-test",
1075+
"id": "abc123",
1076+
"status": "running",
1077+
"attrs": {"NetworkSettings": {"Networks": {}}},
1078+
"reload": lambda self: None,
1079+
"labels": {},
1080+
"logs": lambda self, **kw: b"",
1081+
},
1082+
)()
1083+
return container
1084+
1085+
monkeypatch.setenv("TERM", "xterm-256color")
1086+
monkeypatch.setenv("COLORTERM", "truecolor")
1087+
monkeypatch.setenv("TERM_PROGRAM", "vscode")
1088+
monkeypatch.setenv("TERM_PROGRAM_VERSION", "1.100.0")
1089+
monkeypatch.setenv("LANG", "en_US.UTF-8")
1090+
monkeypatch.setattr(run_cmd, "get_config", lambda: _make_config())
1091+
monkeypatch.setattr(run_cmd, "DockerManager", _CapturingDockerManager)
1092+
1093+
run_cmd.run(agent="gemini", workspace=tmp_path, detach=True)
1094+
1095+
env = captured["env"]
1096+
assert env["TERM"] == "xterm-256color"
1097+
assert env["COLORTERM"] == "truecolor"
1098+
assert env["TERM_PROGRAM"] == "vscode"
1099+
assert env["TERM_PROGRAM_VERSION"] == "1.100.0"
1100+
assert env["LANG"] == "en_US.UTF-8"
1101+
1102+
1103+
def test_run_sets_default_term_when_host_term_missing(monkeypatch, tmp_path: Path) -> None:
1104+
captured: dict = {}
1105+
1106+
class _CapturingDockerManager:
1107+
def ensure_network(self, name: str) -> None:
1108+
pass
1109+
1110+
def networks_with_running_containers(self) -> list[str]:
1111+
return []
1112+
1113+
def pull_image(self, image: str) -> None:
1114+
pass
1115+
1116+
def ensure_proxy(self, **kwargs) -> None: # type: ignore[no-untyped-def]
1117+
pass
1118+
1119+
def run_agent(self, **kwargs) -> object: # type: ignore[no-untyped-def]
1120+
captured.update(kwargs)
1121+
container = type(
1122+
"_Container",
1123+
(),
1124+
{
1125+
"name": "vibepod-gemini-test",
1126+
"id": "abc123",
1127+
"status": "running",
1128+
"attrs": {"NetworkSettings": {"Networks": {}}},
1129+
"reload": lambda self: None,
1130+
"labels": {},
1131+
"logs": lambda self, **kw: b"",
1132+
},
1133+
)()
1134+
return container
1135+
1136+
monkeypatch.delenv("TERM", raising=False)
1137+
monkeypatch.delenv("COLORTERM", raising=False)
1138+
monkeypatch.delenv("TERM_PROGRAM", raising=False)
1139+
monkeypatch.delenv("TERM_PROGRAM_VERSION", raising=False)
1140+
monkeypatch.delenv("LANG", raising=False)
1141+
monkeypatch.setattr(run_cmd, "get_config", lambda: _make_config())
1142+
monkeypatch.setattr(run_cmd, "DockerManager", _CapturingDockerManager)
1143+
1144+
run_cmd.run(agent="gemini", workspace=tmp_path, detach=True)
1145+
1146+
env = captured["env"]
1147+
assert env["TERM"] == "xterm-256color"

0 commit comments

Comments
 (0)