fix(codex): sync codex_patcher.py with TOML inline-table fix

Mirrors claude_code_patcher@661eced — fixes Windows ucodex_install.ps1
crash 'Error loading config.toml: missing assignment' caused by
toml_value(dict) emitting Python str(dict) instead of TOML syntax.

Existing broken installs: re-run installer or manually delete the bad
section from ~/.codex/config.toml then re-run ucodex_install.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
delta-cloud-208e
2026-04-25 16:53:52 +00:00
parent 31f9c3e1a8
commit 34868eea72

View File

@@ -221,25 +221,56 @@ def read_toml_raw(path):
return f.read()
def _toml_str(s):
"""Escape a Python string as a TOML basic-string literal.
Backslashes (Windows paths!) and double-quotes must be escaped per
TOML spec — otherwise the parser sees `C:\\Windows` as `C:Windows`
or fails with `Unescaped '\\' in a string`.
"""
escaped = s.replace("\\", "\\\\").replace('"', '\\"')
return f'"{escaped}"'
def toml_value(v):
"""Format a Python value as TOML."""
"""Format a Python value as TOML.
Dicts → inline table `{ key = "val", k2 = "v2" }` (NOT Python str(dict)
which uses single quotes + colons and breaks `tomllib.load`).
Strings → basic-string with backslash/quote escapes (Windows paths).
"""
if isinstance(v, bool):
return "true" if v else "false"
if isinstance(v, str):
return f'"{v}"'
return _toml_str(v)
if isinstance(v, (int, float)):
return str(v)
if isinstance(v, list):
items = ", ".join(toml_value(i) for i in v)
return f"[{items}]"
return str(v)
if isinstance(v, dict):
# TOML inline table: keys may need quoting (special chars / dots),
# values recurse through toml_value.
items = ", ".join(
f"{toml_key(k)} = {toml_value(val)}" for k, val in v.items()
)
return f"{{ {items} }}" if items else "{}"
return _toml_str(str(v))
def toml_key(k):
"""Format a TOML key, quoting if it contains dots or special chars."""
if "." in k or " " in k:
return f'"{k}"'
"""Format a TOML key, quoting if it contains anything other than the
bare-key charset (A-Z a-z 0-9 _ -). Examples that MUST be quoted:
"PROGRAMFILES(X86)" — parens
"key.with.dots" — would parse as nested
"key with spaces"
"""
import re as _re
if _re.fullmatch(r"[A-Za-z0-9_-]+", k):
return k
# Quote, escape backslashes and quotes
escaped = k.replace("\\", "\\\\").replace('"', '\\"')
return f'"{escaped}"'
def generate_config_toml(existing, config, home_dir=None):