From 34868eea72c90571169284030bc4732154339676 Mon Sep 17 00:00:00 2001 From: delta-cloud-208e Date: Sat, 25 Apr 2026 16:53:52 +0000 Subject: [PATCH] fix(codex): sync codex_patcher.py with TOML inline-table fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- codex/codex_patcher.py | 45 +++++++++++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/codex/codex_patcher.py b/codex/codex_patcher.py index dd93071..80f68dd 100755 --- a/codex/codex_patcher.py +++ b/codex/codex_patcher.py @@ -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}"' - return 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):