mirror of
https://github.com/headroomlabs-ai/headroom.git
synced 2026-08-10 14:27:00 -04:00
fix(codex): respect CODEX_HOME for wrap config (#731)
> ⚠️ This PR was opened using Codex (gpt-5.5 on `xhigh`) Fixes #730. ## Summary - centralize Codex config path resolution in `headroom wrap codex` so it honors `CODEX_HOME` when set - make the Codex MCP registrar use `$CODEX_HOME/config.toml` instead of always writing to `~/.codex/config.toml` - route optional memory MCP and global Codex `AGENTS.md` injection through the same Codex home helper - make `headroom unwrap codex` print a warning, but still succeed, when `CODEX_HOME` is unset and the default Codex config has no Headroom markers - add regression coverage for provider injection, prepare-only wrapping, MCP registration under a custom Codex home, and the ambiguous unwrap warning - update `CHANGELOG.md` under `Unreleased > Bug Fixes` ## Real behavior proof Setup tested on: - Linux `7.0.10-2-cachyos` - Python 3.14.3 via `uv` - local fork branch `fix/codex-home` - custom Codex home created outside `~/.codex` Exact command run after the patch: ```bash tmp_home=$(mktemp -d) mkdir -p "$tmp_home/codex_custom" HOME="$tmp_home" USERPROFILE="$tmp_home" CODEX_HOME="$tmp_home/codex_custom" \ UV_SKIP_WHEEL_FILENAME_CHECK=1 PYO3_USE_ABI3_FORWARD_COMPATIBILITY=1 \ uv run --with fastapi --with uvicorn --with httpx --with websockets \ headroom wrap codex --no-context-tool --no-serena --prepare-only --port 8787 find "$tmp_home" -maxdepth 3 -type f -print | sort sed -n '1,140p' "$tmp_home/codex_custom/config.toml" test -e "$tmp_home/.codex/config.toml" && echo yes || echo no HOME="$tmp_home" USERPROFILE="$tmp_home" \ UV_SKIP_WHEEL_FILENAME_CHECK=1 PYO3_USE_ABI3_FORWARD_COMPATIBILITY=1 \ uv run --with fastapi --with uvicorn --with httpx --with websockets \ headroom unwrap codex --no-stop-proxy ``` After-fix evidence + observed result: Interactive check: - A full `CODEX_HOME="$HOME/.codex_p" headroom wrap codex --no-serena` launch was tested locally after the patch and worked as expected. - The prepare-only proof below shows the same config path behavior without requiring an interactive Codex session in CI/reviewer environments. ```text MCP retrieve tool: registered (restart OpenAI Codex CLI if it was already running) Codex config: injected Headroom provider (WS + HTTP) into /tmp/tmp.UPUvloGNYE/codex_custom/config.toml --- files --- /tmp/tmp.UPUvloGNYE/codex_custom/config.toml /tmp/tmp.UPUvloGNYE/codex_custom/config.toml.headroom-backup --- custom config --- # --- Headroom proxy (auto-injected by headroom wrap codex) --- model_provider = "headroom" openai_base_url = "http://127.0.0.1:8787/v1" # --- end Headroom --- # --- Headroom MCP server --- [mcp_servers.headroom] command = "headroom" args = ["mcp", "serve"] # --- end Headroom MCP server --- # --- Headroom proxy (auto-injected by headroom wrap codex) --- [model_providers.headroom] name = "OpenAI via Headroom proxy" base_url = "http://127.0.0.1:8787/v1" supports_websockets = true # --- end Headroom --- --- default config exists? --- no Warning: found no Headroom wrap markers in the default Codex config. If you wrapped Codex with CODEX_HOME, rerun unwrap with the same environment variable, e.g. CODEX_HOME=/path/to/codex-home headroom unwrap codex. Nothing to undo: .../.codex/config.toml has no Headroom wrap markers. ``` What I did not test: - Windows/macOS path behavior - full repository test suite, because this local Python 3.14 environment hits optional dependency/build constraints outside this patch ## Testing ```bash UV_SKIP_WHEEL_FILENAME_CHECK=1 PYO3_USE_ABI3_FORWARD_COMPATIBILITY=1 uv run --with pytest --with fastapi --with uvicorn --with httpx --with websockets pytest tests/test_mcp_registry/test_codex_registrar.py tests/test_cli/test_wrap_codex.py -q UV_SKIP_WHEEL_FILENAME_CHECK=1 PYO3_USE_ABI3_FORWARD_COMPATIBILITY=1 uv run --with ruff ruff check headroom/cli/wrap.py headroom/mcp_registry/codex.py tests/test_cli/test_wrap_codex.py tests/test_mcp_registry/test_codex_registrar.py UV_SKIP_WHEEL_FILENAME_CHECK=1 PYO3_USE_ABI3_FORWARD_COMPATIBILITY=1 uv run --with ruff ruff format --check headroom/cli/wrap.py headroom/mcp_registry/codex.py tests/test_cli/test_wrap_codex.py tests/test_mcp_registry/test_codex_registrar.py ``` Results: ```text 63 passed, 1 warning in 1.48s All checks passed! 4 files already formatted ``` Notes: - Python 3.14 required `PYO3_USE_ABI3_FORWARD_COMPATIBILITY=1` for the editable build. - `uv` required `UV_SKIP_WHEEL_FILENAME_CHECK=1` because the existing `uv.lock` has a GitPython wheel filename/version mismatch.
This commit is contained in:
parent
27befef694
commit
96abf38b09
5 changed files with 155 additions and 19 deletions
|
|
@ -55,6 +55,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
### Bug Fixes
|
||||
|
||||
* **codex:** respect `CODEX_HOME` when `headroom wrap codex` writes provider, MCP, memory, backup, and global `AGENTS.md` config, and warn when `unwrap codex` may be looking at the default Codex home because `CODEX_HOME` is unset.
|
||||
* **startup:** suppress proxy startup log noise — litellm banner, trafilatura parse errors, HuggingFace Hub unauthenticated warnings, tiktoken fallback warning, and httpx INFO lines from sentence_transformers HEAD checks. Affected files: `headroom/providers/litellm.py`, `headroom/transforms/html_extractor.py`, `headroom/memory/adapters/embedders.py`, `headroom/providers/anthropic.py`, `headroom/providers/registry.py`, `headroom/image/onnx_router.py`, `headroom/transforms/kompress_compressor.py`.
|
||||
|
||||
* **deps:** move `gunicorn` to `[proxy-prod]` extra with `sys_platform != 'win32'` guard; removed from `[proxy]` to avoid forcing a Unix-only package on dev, CI, and Windows users ([#537](https://github.com/chopratejas/headroom/pull/537))
|
||||
* **startup:** suppress proxy startup log noise — litellm banner, trafilatura parse errors, HuggingFace Hub unauthenticated warnings, tiktoken fallback warning, and httpx INFO lines from sentence_transformers HEAD checks. Affected files: `headroom/providers/litellm.py`, `headroom/transforms/html_extractor.py`, `headroom/memory/adapters/embedders.py`, `headroom/providers/anthropic.py`, `headroom/providers/registry.py`, `headroom/image/onnx_router.py`, `headroom/transforms/kompress_compressor.py`.
|
||||
|
|
|
|||
|
|
@ -746,16 +746,24 @@ _CODEX_TOP_LEVEL_MARKER = "# --- Headroom proxy (auto-injected by headroom wrap
|
|||
_CODEX_END_MARKER = "# --- end Headroom ---"
|
||||
_CODEX_MCP_MARKER = "# --- Headroom MCP server ---"
|
||||
_CODEX_MCP_END = "# --- end Headroom MCP server ---"
|
||||
# File name used for the pre-wrap snapshot of ~/.codex/config.toml. The
|
||||
# File name used for the pre-wrap snapshot of the Codex config file. The
|
||||
# snapshot lets `headroom unwrap codex` restore the exact prior state, even
|
||||
# if the user had their own `model_provider` / `[model_providers.*]` config
|
||||
# before running wrap.
|
||||
_CODEX_CONFIG_BACKUP_SUFFIX = ".headroom-backup"
|
||||
|
||||
|
||||
def _codex_home_dir() -> Path:
|
||||
"""Return Codex's config directory, respecting ``CODEX_HOME`` when set."""
|
||||
codex_home = os.environ.get("CODEX_HOME")
|
||||
if codex_home:
|
||||
return Path(codex_home).expanduser()
|
||||
return Path.home() / ".codex"
|
||||
|
||||
|
||||
def _codex_config_paths() -> tuple[Path, Path]:
|
||||
"""Return ``(config_file, backup_file)`` paths for the Codex TOML config."""
|
||||
config_dir = Path.home() / ".codex"
|
||||
config_dir = _codex_home_dir()
|
||||
config_file = config_dir / "config.toml"
|
||||
backup_file = config_dir / f"config.toml{_CODEX_CONFIG_BACKUP_SUFFIX}"
|
||||
return config_file, backup_file
|
||||
|
|
@ -895,7 +903,7 @@ def _inject_codex_provider_config(port: int) -> None:
|
|||
Safe to call multiple times — the injected block is fully replaced on
|
||||
each call, so re-running with a different ``port`` updates the config.
|
||||
Before the first injection, the pre-wrap file is snapshotted to
|
||||
``~/.codex/config.toml.headroom-backup`` so ``headroom unwrap codex``
|
||||
``config.toml.headroom-backup`` so ``headroom unwrap codex``
|
||||
can restore it byte-for-byte.
|
||||
"""
|
||||
config_file, backup_file = _codex_config_paths()
|
||||
|
|
@ -953,7 +961,7 @@ def _inject_codex_provider_config(port: int) -> None:
|
|||
|
||||
|
||||
def _restore_codex_provider_config() -> tuple[str, Path]:
|
||||
"""Undo ``_inject_codex_provider_config`` for ``~/.codex/config.toml``.
|
||||
"""Undo ``_inject_codex_provider_config`` for the active Codex config file.
|
||||
|
||||
Returns a tuple of ``(status, config_file)`` where status is one of:
|
||||
|
||||
|
|
@ -1175,8 +1183,8 @@ def _inject_memory_mcp_config(db_path: str, user_id: str) -> None:
|
|||
"""
|
||||
import sys
|
||||
|
||||
config_dir = Path.home() / ".codex"
|
||||
config_file = config_dir / "config.toml"
|
||||
config_file, _ = _codex_config_paths()
|
||||
config_dir = config_file.parent
|
||||
|
||||
# Use forward slashes in TOML paths (works on all platforms, avoids
|
||||
# backslash escaping issues on Windows)
|
||||
|
|
@ -2815,8 +2823,8 @@ def codex(
|
|||
Sets OPENAI_BASE_URL to route all OpenAI API calls through Headroom.
|
||||
Sets up the selected CLI context tool so Codex uses token-optimized
|
||||
commands (60-90% savings on shell output). Also
|
||||
registers the headroom MCP server in ~/.codex/config.toml so Codex
|
||||
can call ``headroom_retrieve`` on compression markers.
|
||||
registers the headroom MCP server in the active Codex config file
|
||||
so Codex can call ``headroom_retrieve`` on compression markers.
|
||||
|
||||
\b
|
||||
Examples:
|
||||
|
|
@ -2828,7 +2836,7 @@ def codex(
|
|||
headroom wrap codex --port 9999 # Custom proxy port
|
||||
headroom wrap codex --backend anyllm --anyllm-provider groq
|
||||
"""
|
||||
# Snapshot ~/.codex/config.toml BEFORE any wrap-time mutation so
|
||||
# Snapshot Codex config.toml BEFORE any wrap-time mutation so
|
||||
# `headroom unwrap codex` can restore the user's pre-wrap state
|
||||
# byte-for-byte. The snapshot is a no-op if the backup already exists
|
||||
# or if the file already has Headroom markers, so this is safe to
|
||||
|
|
@ -2850,11 +2858,11 @@ def codex(
|
|||
agents_md = Path.cwd() / "AGENTS.md"
|
||||
_inject_rtk_instructions(agents_md, verbose=verbose)
|
||||
|
||||
# Also inject into global ~/.codex/AGENTS.md
|
||||
global_agents = Path.home() / ".codex" / "AGENTS.md"
|
||||
# Also inject into global Codex AGENTS.md
|
||||
global_agents = _codex_home_dir() / "AGENTS.md"
|
||||
_inject_rtk_instructions(global_agents, verbose=verbose)
|
||||
|
||||
# Register headroom MCP server in ~/.codex/config.toml so Codex can
|
||||
# Register headroom MCP server in Codex config.toml so Codex can
|
||||
# call headroom_retrieve on compression markers from the proxy.
|
||||
if not no_mcp:
|
||||
from headroom.mcp_registry import CodexRegistrar
|
||||
|
|
@ -3950,7 +3958,7 @@ def unwrap_openclaw(
|
|||
@click.option("--port", "-p", default=8787, type=int, help="Proxy port (default: 8787)")
|
||||
@click.option("--no-stop-proxy", is_flag=True, help="Do not stop the local Headroom proxy")
|
||||
def unwrap_codex(port: int, no_stop_proxy: bool) -> None:
|
||||
"""Undo ``headroom wrap codex`` edits to ``~/.codex/config.toml``.
|
||||
"""Undo ``headroom wrap codex`` edits to the active Codex config file.
|
||||
|
||||
Behaviour:
|
||||
|
||||
|
|
@ -3962,7 +3970,10 @@ def unwrap_codex(port: int, no_stop_proxy: bool) -> None:
|
|||
* If the config only ever contained Headroom-written content, the file
|
||||
is removed entirely so Codex falls back to its defaults.
|
||||
* If neither a backup nor a Headroom block is present, this is a safe
|
||||
no-op (the user either never wrapped, or already unwrapped).
|
||||
no-op (the user either never wrapped that config, or already unwrapped
|
||||
it). When ``CODEX_HOME`` is unset, print a warning hint because Headroom
|
||||
may be looking at the default config while Codex was wrapped with a
|
||||
custom home.
|
||||
"""
|
||||
click.echo()
|
||||
click.echo(" ╔═══════════════════════════════════════════════╗")
|
||||
|
|
@ -3982,6 +3993,13 @@ def unwrap_codex(port: int, no_stop_proxy: bool) -> None:
|
|||
elif status == "removed":
|
||||
click.echo(f" Removed {config_file} (contained only Headroom-written config).")
|
||||
else:
|
||||
if not os.environ.get("CODEX_HOME"):
|
||||
click.echo(
|
||||
" Warning: found no Headroom wrap markers in the default Codex config. "
|
||||
"If you wrapped Codex with CODEX_HOME, rerun unwrap with the same "
|
||||
"environment variable, e.g. CODEX_HOME=/path/to/codex-home "
|
||||
"headroom unwrap codex."
|
||||
)
|
||||
click.echo(f" Nothing to undo: {config_file} has no Headroom wrap markers.")
|
||||
|
||||
click.echo()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
"""OpenAI Codex CLI MCP registrar.
|
||||
|
||||
Codex stores MCP server config in ``~/.codex/config.toml`` as
|
||||
Codex stores MCP server config in ``$CODEX_HOME/config.toml`` when
|
||||
``CODEX_HOME`` is set, otherwise ``~/.codex/config.toml``, as
|
||||
``[mcp_servers.<name>]`` tables (with optional ``[mcp_servers.<name>.env]``
|
||||
sub-tables). There is no general-purpose CLI for adding entries, so we
|
||||
edit the file in place — using marker-delimited blocks so we can
|
||||
|
|
@ -11,6 +12,7 @@ anything else the user has configured.
|
|||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
|
@ -47,9 +49,13 @@ class CodexRegistrar(MCPRegistrar):
|
|||
display_name = "OpenAI Codex CLI"
|
||||
|
||||
def __init__(self, *, home_dir: Path | None = None) -> None:
|
||||
home = home_dir if home_dir is not None else Path.home()
|
||||
self._codex_dir = home / ".codex"
|
||||
self._config_file = home / ".codex" / "config.toml"
|
||||
if home_dir is not None:
|
||||
self._codex_dir = home_dir / ".codex"
|
||||
elif os.environ.get("CODEX_HOME"):
|
||||
self._codex_dir = Path(os.environ["CODEX_HOME"]).expanduser()
|
||||
else:
|
||||
self._codex_dir = Path.home() / ".codex"
|
||||
self._config_file = self._codex_dir / "config.toml"
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# MCPRegistrar interface
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ def _set_test_home(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
|
|||
home = str(tmp_path)
|
||||
monkeypatch.setenv("HOME", home)
|
||||
monkeypatch.setenv("USERPROFILE", home)
|
||||
monkeypatch.delenv("CODEX_HOME", raising=False)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
|
@ -176,6 +177,23 @@ class TestInjectAndRestoreRoundTrip:
|
|||
assert not config_file.exists()
|
||||
assert not (tmp_path / ".codex" / "config.toml.headroom-backup").exists()
|
||||
|
||||
def test_wrap_unwrap_respects_codex_home(
|
||||
self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
||||
) -> None:
|
||||
_set_test_home(monkeypatch, tmp_path)
|
||||
codex_home = tmp_path / "custom-codex-home"
|
||||
monkeypatch.setenv("CODEX_HOME", str(codex_home))
|
||||
config_file = codex_home / "config.toml"
|
||||
|
||||
wrap_mod._inject_codex_provider_config(8787)
|
||||
assert config_file.exists()
|
||||
assert 'model_provider = "headroom"' in config_file.read_text()
|
||||
assert not (tmp_path / ".codex" / "config.toml").exists()
|
||||
|
||||
status, _ = wrap_mod._restore_codex_provider_config()
|
||||
assert status == "removed"
|
||||
assert not config_file.exists()
|
||||
|
||||
def test_wrap_unwrap_restores_prior_model_provider(
|
||||
self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
||||
) -> None:
|
||||
|
|
@ -411,6 +429,68 @@ def test_wrap_codex_prepare_only_creates_backup_and_config(
|
|||
assert backup.read_text() == original
|
||||
|
||||
|
||||
def test_wrap_codex_prepare_only_respects_codex_home(
|
||||
runner: CliRunner, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
||||
) -> None:
|
||||
_set_test_home(monkeypatch, tmp_path)
|
||||
codex_home = tmp_path / "custom-codex-home"
|
||||
codex_home.mkdir()
|
||||
monkeypatch.setenv("CODEX_HOME", str(codex_home))
|
||||
|
||||
with patch("headroom.cli.wrap._ensure_rtk_binary", return_value=None):
|
||||
result = runner.invoke(
|
||||
main,
|
||||
["wrap", "codex", "--prepare-only", "--no-serena", "--port", "8787"],
|
||||
)
|
||||
|
||||
assert result.exit_code == 0, result.output
|
||||
config_file = codex_home / "config.toml"
|
||||
assert config_file.exists()
|
||||
content = config_file.read_text()
|
||||
assert 'model_provider = "headroom"' in content
|
||||
assert "[mcp_servers.headroom]" in content
|
||||
assert not (tmp_path / ".codex" / "config.toml").exists()
|
||||
|
||||
|
||||
def test_unwrap_codex_without_codex_home_warns_on_ambiguous_noop(
|
||||
runner: CliRunner, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
||||
) -> None:
|
||||
_set_test_home(monkeypatch, tmp_path)
|
||||
codex_home = tmp_path / "custom-codex-home"
|
||||
codex_home.mkdir()
|
||||
monkeypatch.setenv("CODEX_HOME", str(codex_home))
|
||||
|
||||
with patch("headroom.cli.wrap._ensure_rtk_binary", return_value=None):
|
||||
wrap_result = runner.invoke(
|
||||
main,
|
||||
[
|
||||
"wrap",
|
||||
"codex",
|
||||
"--prepare-only",
|
||||
"--no-mcp",
|
||||
"--no-serena",
|
||||
"--port",
|
||||
"8787",
|
||||
],
|
||||
)
|
||||
|
||||
assert wrap_result.exit_code == 0, wrap_result.output
|
||||
config_file = codex_home / "config.toml"
|
||||
assert 'openai_base_url = "http://127.0.0.1:8787/v1"' in config_file.read_text()
|
||||
|
||||
monkeypatch.delenv("CODEX_HOME", raising=False)
|
||||
unwrap_result = runner.invoke(main, ["unwrap", "codex", "--no-stop-proxy"])
|
||||
|
||||
assert unwrap_result.exit_code == 0, unwrap_result.output
|
||||
assert "Warning: found no Headroom wrap markers in the default Codex config" in (
|
||||
unwrap_result.output
|
||||
)
|
||||
assert "If you wrapped Codex with CODEX_HOME" in unwrap_result.output
|
||||
assert "CODEX_HOME=/path/to/codex-home headroom unwrap codex" in unwrap_result.output
|
||||
assert "Nothing to undo" in unwrap_result.output
|
||||
assert 'openai_base_url = "http://127.0.0.1:8787/v1"' in config_file.read_text()
|
||||
|
||||
|
||||
def test_start_proxy_uses_separate_session_for_signal_isolation(
|
||||
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
||||
) -> None:
|
||||
|
|
@ -576,6 +656,7 @@ def test_unwrap_codex_no_stop_proxy_leaves_proxy_alone(
|
|||
runner: CliRunner, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
||||
) -> None:
|
||||
_set_test_home(monkeypatch, tmp_path)
|
||||
monkeypatch.setenv("CODEX_HOME", str(tmp_path / "explicit-codex-home"))
|
||||
|
||||
with patch("headroom.cli.wrap._stop_local_proxy_for_unwrap") as stop_proxy:
|
||||
result = runner.invoke(main, ["unwrap", "codex", "--no-stop-proxy"])
|
||||
|
|
@ -613,14 +694,16 @@ def test_stop_local_proxy_for_unwrap_refuses_unidentified_listener(
|
|||
kill_proxy.assert_not_called()
|
||||
|
||||
|
||||
def test_unwrap_codex_is_safe_noop_with_no_prior_wrap(
|
||||
def test_unwrap_codex_is_safe_noop_with_explicit_codex_home(
|
||||
runner: CliRunner, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
||||
) -> None:
|
||||
_set_test_home(monkeypatch, tmp_path)
|
||||
monkeypatch.setenv("CODEX_HOME", str(tmp_path / "explicit-codex-home"))
|
||||
|
||||
result = runner.invoke(main, ["unwrap", "codex"])
|
||||
assert result.exit_code == 0, result.output
|
||||
assert "Nothing to undo" in result.output
|
||||
assert "Warning:" not in result.output
|
||||
assert not (tmp_path / ".codex" / "config.toml").exists()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -49,6 +49,10 @@ def _config_path(tmp_path: Path) -> Path:
|
|||
return tmp_path / ".codex" / "config.toml"
|
||||
|
||||
|
||||
def _codex_home_config_path(codex_home: Path) -> Path:
|
||||
return codex_home / "config.toml"
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# detect()
|
||||
# ----------------------------------------------------------------------
|
||||
|
|
@ -63,6 +67,29 @@ def test_detect_false_when_codex_dir_missing(tmp_path: Path) -> None:
|
|||
assert _make_registrar(tmp_path).detect() is False
|
||||
|
||||
|
||||
def test_detect_true_when_codex_home_exists(
|
||||
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
||||
) -> None:
|
||||
codex_home = tmp_path / "custom-codex-home"
|
||||
codex_home.mkdir()
|
||||
monkeypatch.setenv("CODEX_HOME", str(codex_home))
|
||||
|
||||
assert CodexRegistrar().detect() is True
|
||||
|
||||
|
||||
def test_register_uses_codex_home_env(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
|
||||
codex_home = tmp_path / "custom-codex-home"
|
||||
monkeypatch.setenv("CODEX_HOME", str(codex_home))
|
||||
|
||||
result = CodexRegistrar().register_server(_spec())
|
||||
|
||||
assert result.status == RegisterStatus.REGISTERED
|
||||
assert _codex_home_config_path(codex_home).exists()
|
||||
assert not _config_path(tmp_path).exists()
|
||||
text = _codex_home_config_path(codex_home).read_text()
|
||||
assert "[mcp_servers.headroom]" in text
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# get_server()
|
||||
# ----------------------------------------------------------------------
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue