Files
unlimitedcoding/codex/updater/config_validator.py
delta-cloud-208e deb1c2cfd2 fix(codex): drop Python 3.11 requirement, auto-install python3
- tomllib fallback: try tomllib (3.11+) -> tomli -> minimal parser
- Works with Python 3.8+ (Ubuntu 20.04, Debian 11, etc.)
- Auto-install python3 if not found (like Gemini/Qwen scripts)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 10:58:16 +00:00

295 lines
8.8 KiB
Python
Executable File

"""Config validator for Codex Patcher — validates 6 config targets.
Unlike Claude/Gemini patchers (regex-based), Codex validation is state-based:
checks config.toml values and environment variables.
"""
import os
try:
import tomllib
except ModuleNotFoundError:
try:
import tomli as tomllib
except ModuleNotFoundError:
import re as _re
class _T:
@staticmethod
def load(f):
raw = f.read()
return _T._parse(raw.decode("utf-8") if isinstance(raw, bytes) else raw)
@staticmethod
def _parse(text):
result, cur = {}, None
for line in text.split("\n"):
line = line.strip()
if not line or line.startswith("#"):
continue
m = _re.match(r'^\[([^\]]+)\]$', line)
if m:
keys = [k.strip() for k in m.group(1).split(".")]
cur = result
for k in keys:
cur = cur.setdefault(k, {})
continue
m = _re.match(r'^([^=]+?)\s*=\s*(.+)$', line)
if m:
k, v = m.group(1).strip(), m.group(2).strip()
if v.startswith('"') and v.endswith('"'): v = v[1:-1]
elif v == "true": v = True
elif v == "false": v = False
elif _re.match(r'^-?\d+$', v): v = int(v)
(cur if cur is not None else result)[k] = v
return result
tomllib = _T()
from dataclasses import dataclass
from typing import Callable, Optional
@dataclass
class ConfigTarget:
name: str
description: str
check_key: str # "config_toml" | "env" | "auth"
PATCH_TARGETS = [
ConfigTarget(
name="api_endpoint",
description="Custom proxy via model_providers",
check_key="config_toml",
),
ConfigTarget(
name="authentication",
description="API key auth configured",
check_key="auth",
),
ConfigTarget(
name="analytics_disabled",
description="Analytics/telemetry disabled",
check_key="config_toml",
),
ConfigTarget(
name="approval_bypass",
description="Approval policy set to never",
check_key="config_toml",
),
ConfigTarget(
name="sandbox_bypass",
description="Sandbox set to danger-full-access",
check_key="config_toml",
),
ConfigTarget(
name="env_vars",
description="System environment variables configured",
check_key="env",
),
]
def _read_toml(path):
"""Read TOML file, return dict or empty dict."""
if not os.path.isfile(path):
return None
with open(path, "rb") as f:
return tomllib.load(f)
def _check_api_endpoint(toml_data, config):
"""Check Target 1: model_providers.custom with correct base_url."""
if toml_data is None:
return "RED", "config.toml not found"
mp = toml_data.get("model_providers", {})
custom = mp.get("custom", {}) if isinstance(mp, dict) else {}
base_url = config["base_url"].rstrip("/")
if not base_url.endswith("/v1"):
base_url += "/v1"
if not custom:
return "RED", "model_providers.custom section missing"
if custom.get("base_url") != base_url:
return "YELLOW", f"base_url mismatch: {custom.get('base_url')} != {base_url}"
if toml_data.get("model_provider") != "custom":
return "YELLOW", "model_provider != 'custom'"
return "GREEN", f"base_url={base_url}"
def _check_auth(codex_dir, config):
"""Check Target 2: API key authentication."""
# Check env var
env_key = os.environ.get("OPENAI_API_KEY", "")
if env_key == config["api_key"]:
return "GREEN", "OPENAI_API_KEY set correctly"
# Check /etc/environment
etc_env = "/etc/environment"
if os.path.isfile(etc_env):
with open(etc_env) as f:
content = f.read()
if config["api_key"] in content:
return "GREEN", "API key in /etc/environment"
if env_key:
return "YELLOW", "OPENAI_API_KEY set but different value"
return "RED", "OPENAI_API_KEY not set"
def _check_analytics(toml_data):
"""Check Target 3: analytics disabled."""
if toml_data is None:
return "RED", "config.toml not found"
analytics = toml_data.get("analytics", {})
if not isinstance(analytics, dict):
return "RED", "[analytics] section missing"
if analytics.get("enabled") is False:
return "GREEN", "analytics.enabled = false"
if "enabled" not in analytics:
return "YELLOW", "[analytics] exists but 'enabled' key missing"
return "YELLOW", f"analytics.enabled = {analytics.get('enabled')}"
def _check_approval(toml_data, config):
"""Check Target 4: approval_policy."""
if toml_data is None:
return "RED", "config.toml not found"
target_policy = config.get("approval_policy", "never")
current = toml_data.get("approval_policy")
if current == target_policy:
return "GREEN", f'approval_policy = "{target_policy}"'
if current is not None:
return "YELLOW", f'approval_policy = "{current}" (expected "{target_policy}")'
return "RED", "approval_policy not set"
def _check_sandbox(toml_data, config):
"""Check Target 5: sandbox_mode."""
if toml_data is None:
return "RED", "config.toml not found"
target_mode = config.get("sandbox_mode", "danger-full-access")
current = toml_data.get("sandbox_mode")
if current == target_mode:
return "GREEN", f'sandbox_mode = "{target_mode}"'
if current is not None:
return "YELLOW", f'sandbox_mode = "{current}" (expected "{target_mode}")'
return "RED", "sandbox_mode not set"
def _check_env_vars(config):
"""Check Target 6: system environment variables."""
base_url = config["base_url"].rstrip("/")
if not base_url.endswith("/v1"):
base_url += "/v1"
etc_env = "/etc/environment"
if not os.path.isfile(etc_env):
return "RED", "/etc/environment not found"
with open(etc_env) as f:
content = f.read()
has_base = "OPENAI_BASE_URL" in content
has_key = "OPENAI_API_KEY" in content
if has_base and has_key:
return "GREEN", "OPENAI_BASE_URL + OPENAI_API_KEY set"
missing = []
if not has_base:
missing.append("OPENAI_BASE_URL")
if not has_key:
missing.append("OPENAI_API_KEY")
return "YELLOW" if (has_base or has_key) else "RED", f"Missing: {', '.join(missing)}"
def validate_all(codex_dir, config):
"""Validate all 6 targets. Returns list of (target, status, message) tuples."""
config_path = os.path.join(codex_dir, "config.toml")
toml_data = _read_toml(config_path)
results = []
# Target 1: API endpoint
status, msg = _check_api_endpoint(toml_data, config)
results.append((PATCH_TARGETS[0], status, msg))
# Target 2: Auth
status, msg = _check_auth(codex_dir, config)
results.append((PATCH_TARGETS[1], status, msg))
# Target 3: Analytics
status, msg = _check_analytics(toml_data)
results.append((PATCH_TARGETS[2], status, msg))
# Target 4: Approval
status, msg = _check_approval(toml_data, config)
results.append((PATCH_TARGETS[3], status, msg))
# Target 5: Sandbox
status, msg = _check_sandbox(toml_data, config)
results.append((PATCH_TARGETS[4], status, msg))
# Target 6: Env vars
status, msg = _check_env_vars(config)
results.append((PATCH_TARGETS[5], status, msg))
return results
# ANSI colors
GREEN_C = "\033[92m"
YELLOW_C = "\033[93m"
RED_C = "\033[91m"
BOLD_C = "\033[1m"
RESET_C = "\033[0m"
STATUS_COLORS = {
"GREEN": GREEN_C,
"YELLOW": YELLOW_C,
"RED": RED_C,
}
def print_validation_report(results):
"""Print formatted validation report."""
print(f"\n {BOLD_C}Codex Patcher — Validation Report{RESET_C}")
print(" " + "" * 50)
counts = {"GREEN": 0, "YELLOW": 0, "RED": 0}
for target, status, msg in results:
color = STATUS_COLORS.get(status, "")
print(f" {color}[{status:6s}]{RESET_C} {target.name}: {target.description}")
if status != "GREEN":
print(f"{msg}")
counts[status] = counts.get(status, 0) + 1
print(" " + "" * 50)
total = len(results)
print(f" {GREEN_C}{counts['GREEN']}{RESET_C}/{total} GREEN "
f"{YELLOW_C}{counts['YELLOW']}{RESET_C} YELLOW "
f"{RED_C}{counts['RED']}{RESET_C} RED")
if counts["GREEN"] == total:
print(f"\n {GREEN_C}All targets configured correctly!{RESET_C}")
elif counts["RED"] > 0:
print(f"\n {RED_C}Critical targets missing. Run: python3 codex_patcher.py --apply{RESET_C}")
return counts