From 8d7fd92dd39a6959bc30c3bc99785f8fbd758e5b Mon Sep 17 00:00:00 2001 From: Petr Nyc Date: Thu, 21 May 2026 16:57:06 +0200 Subject: [PATCH] codex improvements --- bin/codex-devops-auth.sh | 165 ++++++++++++++++++++++++++++++++++++++- bin/retag-email | 4 +- 2 files changed, 166 insertions(+), 3 deletions(-) diff --git a/bin/codex-devops-auth.sh b/bin/codex-devops-auth.sh index df7aeda..7ba4e86 100755 --- a/bin/codex-devops-auth.sh +++ b/bin/codex-devops-auth.sh @@ -8,7 +8,12 @@ TOKEN_HOST="${TOKEN_HOST:-operator-access-token.svc.ad1.r2}" SSH_CONFIG_FILE="${SSH_CONFIG_FILE:-$HOME/.ssh/config.oci}" OCI_BIN="${OCI_BIN:-/opt/homebrew/bin/oci}" OCI_SESSION_REGION="${OCI_SESSION_REGION:-us-chicago-1}" -OCI_PROFILE_NAME="${OCI_PROFILE_NAME:-DEFAULT}" +OCI_PROFILE_NAME="${OCI_PROFILE_NAME:-MCP_GW_DEFAULT}" +OCI_CONFIG_FILE="${OCI_CONFIG_FILE:-${HOME}/.oci/config}" +OCI_PROFILE_SYNC_ENABLED="${OCI_PROFILE_SYNC_ENABLED:-1}" +OCI_PROFILE_SYNC_TARGETS="${OCI_PROFILE_SYNC_TARGETS:-DEFAULT}" +OCI_PROFILE_SYNC_KEYS="${OCI_PROFILE_SYNC_KEYS:-tenancy,region,security_token_file,key_file,fingerprint,pass_phrase,user}" +OCI_PROFILE_SYNC_PYTHON="${OCI_PROFILE_SYNC_PYTHON:-python3}" OCI_SESSION_VALIDATE_TIMEOUT_SECONDS="${OCI_SESSION_VALIDATE_TIMEOUT_SECONDS:-2}" RESET_AGENT="${RESET_AGENT:-0}" CODEX_DEVOPS_AUTH_ENV_OUT="${CODEX_DEVOPS_AUTH_ENV_OUT:-}" @@ -115,6 +120,162 @@ ensure_oci_session() { "${OCI_BIN}" session authenticate --region "${OCI_SESSION_REGION}" --profile-name "${OCI_PROFILE_NAME}" } +sync_oci_profiles() { + if [[ "${OCI_PROFILE_SYNC_ENABLED}" != "1" ]]; then + log "OCI profile sync disabled." + return 0 + fi + + if [[ -z "${OCI_PROFILE_SYNC_TARGETS}" ]]; then + log "OCI profile sync: no target profiles configured." + return 0 + fi + + if [[ ! -r "${OCI_CONFIG_FILE}" || ! -w "${OCI_CONFIG_FILE}" ]]; then + log "Warning: OCI config is not readable and writable; skipping profile sync: ${OCI_CONFIG_FILE}" + return 0 + fi + + if ! command -v "${OCI_PROFILE_SYNC_PYTHON}" >/dev/null 2>&1; then + log "Warning: ${OCI_PROFILE_SYNC_PYTHON} not found; skipping OCI profile sync." + return 0 + fi + + OCI_CONFIG_FILE_FOR_SYNC="${OCI_CONFIG_FILE}" \ + OCI_PROFILE_SYNC_SOURCE="${OCI_PROFILE_NAME}" \ + OCI_PROFILE_SYNC_TARGETS_FOR_SYNC="${OCI_PROFILE_SYNC_TARGETS}" \ + OCI_PROFILE_SYNC_KEYS_FOR_SYNC="${OCI_PROFILE_SYNC_KEYS}" \ + "${OCI_PROFILE_SYNC_PYTHON}" - <<'PY' +import os +from pathlib import Path +import re +import tempfile + +config_path = Path(os.environ["OCI_CONFIG_FILE_FOR_SYNC"]).expanduser() +source = os.environ["OCI_PROFILE_SYNC_SOURCE"] +targets = [ + target.strip() + for target in os.environ["OCI_PROFILE_SYNC_TARGETS_FOR_SYNC"].split(",") + if target.strip() and target.strip() != source +] +keys = [ + key.strip() + for key in os.environ["OCI_PROFILE_SYNC_KEYS_FOR_SYNC"].split(",") + if key.strip() +] + +if not targets or not keys: + raise SystemExit(0) + +section_re = re.compile(r"^(\s*)\[([^\]]+)\](\s*(?:[#;].*)?)$") +key_re = re.compile(r"^(\s*)([A-Za-z_][A-Za-z0-9_]*)(\s*=\s*)(.*?)(\s*(?:[#;].*)?)$") + +lines = config_path.read_text(encoding="utf-8").splitlines(keepends=True) +line_sections = [] +sections = {} +current = None + +for index, line in enumerate(lines): + match = section_re.match(line.rstrip("\n")) + if match: + current = match.group(2).strip() + sections.setdefault(current, {"start": index, "end": None}) + line_sections.append(current) + +for index, section in enumerate(line_sections): + if section is not None: + sections[section]["end"] = index + 1 + +if source not in sections: + raise SystemExit(f"OCI profile sync source profile not found: {source}") + +source_values = {} +source_start = int(sections[source]["start"]) +source_end = int(sections[source]["end"] or len(lines)) +for line in lines[source_start + 1 : source_end]: + match = key_re.match(line.rstrip("\n")) + if match and match.group(2).strip() in keys: + source_values[match.group(2).strip()] = match.group(4).rstrip() + +if not source_values: + raise SystemExit(f"OCI profile sync source profile has no syncable keys: {source}") + +changed = False +for target in targets: + if target not in sections: + insert_at = len(lines) + if lines and not lines[-1].endswith("\n"): + lines[-1] += "\n" + if lines and lines[-1].strip(): + lines.append("\n") + insert_at += 1 + lines.append(f"[{target}]\n") + sections[target] = {"start": insert_at, "end": insert_at + 1} + for known in sections: + if known != target and int(sections[known]["start"]) >= insert_at: + sections[known]["start"] = int(sections[known]["start"]) + 2 + if sections[known]["end"] is not None: + sections[known]["end"] = int(sections[known]["end"]) + 2 + changed = True + + target_start = int(sections[target]["start"]) + target_end = int(sections[target]["end"] or len(lines)) + present: set[str] = set() + + index = target_start + 1 + while index < target_end: + raw = lines[index] + newline = "\n" if raw.endswith("\n") else "" + match = key_re.match(raw.rstrip("\n")) + if match: + key = match.group(2).strip() + if key in source_values: + present.add(key) + replacement = f"{match.group(1)}{key}{match.group(3)}{source_values[key]}{match.group(5)}{newline}" + if replacement != raw: + lines[index] = replacement + changed = True + index += 1 + + missing = [key for key in keys if key in source_values and key not in present] + if missing: + insert_at = target_end + additions = [f"{key} = {source_values[key]}\n" for key in missing] + lines[insert_at:insert_at] = additions + delta = len(additions) + sections[target]["end"] = target_end + delta + for known in sections: + if known != target and int(sections[known]["start"]) >= insert_at: + sections[known]["start"] = int(sections[known]["start"]) + delta + if sections[known]["end"] is not None: + sections[known]["end"] = int(sections[known]["end"]) + delta + changed = True + +if changed: + content = "".join(lines) + mode = config_path.stat().st_mode & 0o777 + with tempfile.NamedTemporaryFile( + "w", + encoding="utf-8", + dir=str(config_path.parent), + prefix=f".{config_path.name}.", + delete=False, + ) as tmp: + tmp.write(content) + tmp_name = tmp.name + os.chmod(tmp_name, mode) + os.replace(tmp_name, config_path) +PY + local sync_rc=$? + + if [[ ${sync_rc} -ne 0 ]]; then + log "OCI profile sync failed with exit code ${sync_rc}." + exit "${sync_rc}" + fi + + log "OCI profile sync complete: ${OCI_PROFILE_NAME} -> ${OCI_PROFILE_SYNC_TARGETS}" +} + ensure_ssh_agent() { log "Starting dedicated ssh-agent for Codex." unset SSH_AUTH_SOCK SSH_AGENT_PID @@ -394,6 +555,8 @@ trap cleanup EXIT INT TERM ensure_oci_session +sync_oci_profiles + prepare_agent refresh_operator_token diff --git a/bin/retag-email b/bin/retag-email index ba2321e..d3c505c 100755 --- a/bin/retag-email +++ b/bin/retag-email @@ -407,8 +407,8 @@ notmuch tag +solaris \ # seatch term to include in each search # commented out for debugging -# ST='tag:new' -ST="date:1/1/2026.." +ST='tag:new' +# ST="date:1/1/2026.." notmuch tag +osd \ "$ST" AND "(