From d367547fab684c8b69d9b3296a84aedb6502ad1e Mon Sep 17 00:00:00 2001 From: Petr Nyc Date: Wed, 20 May 2026 11:08:09 +0200 Subject: [PATCH] codex startup unification --- .codex/config.toml | 136 ++++++++++---------- bin/codex-devops-auth.sh | 263 ++++++++++++++++++++++++++++++++++++++- bin/codex-wrapper.sh | 174 +++++++++++++++++++++++++- 3 files changed, 501 insertions(+), 72 deletions(-) diff --git a/.codex/config.toml b/.codex/config.toml index 0bfa346..f885da1 100644 --- a/.codex/config.toml +++ b/.codex/config.toml @@ -225,85 +225,86 @@ approval_mode = "approve" [mcp_servers.oci-kb.tools.search] approval_mode = "approve" -[mcp_servers.devops_mcp] -command = "/Users/jetpac/bin/devops-mcp-wrapper.sh" -env_vars = ["OP_TOKEN", "OPERATOR_ACCESS_TOKEN"] -startup_timeout_sec = 180.0 +# Disabled by KB-11: prefer mcp_gateway devops__ tools for refreshable DevOps access. +# [mcp_servers.devops_mcp] +# command = "/Users/jetpac/bin/devops-mcp-wrapper.sh" +# env_vars = ["OP_TOKEN", "OPERATOR_ACCESS_TOKEN"] +# startup_timeout_sec = 180.0 -[mcp_servers.devops_mcp.tools.get_realms] -approval_mode = "approve" +# [mcp_servers.devops_mcp.tools.get_realms] +# approval_mode = "approve" -[mcp_servers.devops_mcp.tools.get_regions] -approval_mode = "approve" +# [mcp_servers.devops_mcp.tools.get_regions] +# approval_mode = "approve" -[mcp_servers.devops_mcp.tools.get_release_details] -approval_mode = "approve" +# [mcp_servers.devops_mcp.tools.get_release_details] +# approval_mode = "approve" -[mcp_servers.devops_mcp.tools.get_runbook_projects] -approval_mode = "approve" +# [mcp_servers.devops_mcp.tools.get_runbook_projects] +# approval_mode = "approve" -[mcp_servers.devops_mcp.tools.get_shepherd_execution_target_errors] -approval_mode = "approve" +# [mcp_servers.devops_mcp.tools.get_shepherd_execution_target_errors] +# approval_mode = "approve" -[mcp_servers.devops_mcp.tools.get_shepherd_flocks] -approval_mode = "approve" +# [mcp_servers.devops_mcp.tools.get_shepherd_flocks] +# approval_mode = "approve" -[mcp_servers.devops_mcp.tools.get_shepherd_phase_execution_targets] -approval_mode = "approve" +# [mcp_servers.devops_mcp.tools.get_shepherd_phase_execution_targets] +# approval_mode = "approve" -[mcp_servers.devops_mcp.tools.get_shepherd_projects] -approval_mode = "approve" +# [mcp_servers.devops_mcp.tools.get_shepherd_projects] +# approval_mode = "approve" -[mcp_servers.devops_mcp.tools.get_shepherd_release_target_logs] -approval_mode = "approve" +# [mcp_servers.devops_mcp.tools.get_shepherd_release_target_logs] +# approval_mode = "approve" -[mcp_servers.devops_mcp.tools.get_shepherd_releases] -approval_mode = "approve" +# [mcp_servers.devops_mcp.tools.get_shepherd_releases] +# approval_mode = "approve" -[mcp_servers.devops_mcp.tools.get_tenancy_by_name] -approval_mode = "approve" +# [mcp_servers.devops_mcp.tools.get_tenancy_by_name] +# approval_mode = "approve" -[mcp_servers.devops_mcp.tools.list_shepherd_region_details] -approval_mode = "approve" +# [mcp_servers.devops_mcp.tools.list_shepherd_region_details] +# approval_mode = "approve" -[mcp_servers.devops_mcp.tools.search_phonebook] -approval_mode = "approve" +# [mcp_servers.devops_mcp.tools.search_phonebook] +# approval_mode = "approve" -[mcp_servers.devops_mcp.tools.search_runbooks] -approval_mode = "approve" +# [mcp_servers.devops_mcp.tools.search_runbooks] +# approval_mode = "approve" -[mcp_servers.devops_mcp.tools.get_security_tasks_for_team] -approval_mode = "approve" +# [mcp_servers.devops_mcp.tools.get_security_tasks_for_team] +# approval_mode = "approve" -[mcp_servers.devops_mcp.tools.get_security_findings_for_team] -approval_mode = "approve" +# [mcp_servers.devops_mcp.tools.get_security_findings_for_team] +# approval_mode = "approve" -[mcp_servers.devops_mcp.tools.get_shepherd_release_all_target_logs] -approval_mode = "approve" +# [mcp_servers.devops_mcp.tools.get_shepherd_release_all_target_logs] +# approval_mode = "approve" -[mcp_servers.devops_mcp.tools.get_odo_application] -approval_mode = "approve" +# [mcp_servers.devops_mcp.tools.get_odo_application] +# approval_mode = "approve" -[mcp_servers.devops_mcp.tools.get_active_odo_deployments] -approval_mode = "approve" +# [mcp_servers.devops_mcp.tools.get_active_odo_deployments] +# approval_mode = "approve" -[mcp_servers.devops_mcp.tools.get_odo_deployments_by_time] -approval_mode = "approve" +# [mcp_servers.devops_mcp.tools.get_odo_deployments_by_time] +# approval_mode = "approve" -[mcp_servers.devops_mcp.tools.get_app_release_versions_by_tenants] -approval_mode = "approve" +# [mcp_servers.devops_mcp.tools.get_app_release_versions_by_tenants] +# approval_mode = "approve" -[mcp_servers.devops_mcp.tools.get_shepherd_execution_target_state] -approval_mode = "approve" +# [mcp_servers.devops_mcp.tools.get_shepherd_execution_target_state] +# approval_mode = "approve" -[mcp_servers.devops_mcp.tools.get_tenancy_by_id] -approval_mode = "approve" +# [mcp_servers.devops_mcp.tools.get_tenancy_by_id] +# approval_mode = "approve" -[mcp_servers.devops_mcp.tools.list_subscription_mappings] -approval_mode = "approve" +# [mcp_servers.devops_mcp.tools.list_subscription_mappings] +# approval_mode = "approve" -[mcp_servers.devops_mcp.tools.list_subscriptions] -approval_mode = "approve" +# [mcp_servers.devops_mcp.tools.list_subscriptions] +# approval_mode = "approve" [mcp_servers.mcp_shepherd] command = "uvx" @@ -593,17 +594,18 @@ approval_mode = "approve" [mcp_servers.oracle-bitbucket.tools.browse_repository] approval_mode = "approve" -[mcp_servers.mcp-atlassian] -command = "uvx" -args = ["--python=3.11", "mcp-atlassian"] -startup_timeout_sec = 90.0 +# Disabled by KB-11: prefer mcp_gateway jira__ and jirasd__ tools for Jira access. +# [mcp_servers.mcp-atlassian] +# command = "uvx" +# args = ["--python=3.11", "mcp-atlassian"] +# startup_timeout_sec = 90.0 -[mcp_servers.mcp-atlassian.env] -JIRA_PERSONAL_TOKEN = "MDM2MDg0OTkxNDE1OmRcmNouX2Uw1M3/riCygQz9ogos" -JIRA_SSL_VERIFY = "false" -JIRA_URL = "https://jira.oci.oraclecorp.com/" -JIRA_USERNAME = "petr.nyc@oracle.com" -READ_ONLY_MODE = "false" +# [mcp_servers.mcp-atlassian.env] +# JIRA_PERSONAL_TOKEN = "MDM2MDg0OTkxNDE1OmRcmNouX2Uw1M3/riCygQz9ogos" +# JIRA_SSL_VERIFY = "false" +# JIRA_URL = "https://jira.oci.oraclecorp.com/" +# JIRA_USERNAME = "petr.nyc@oracle.com" +# READ_ONLY_MODE = "false" # [mcp_servers.ident-scm] # command = "node" @@ -943,6 +945,12 @@ trust_level = "trusted" [projects."/Users/jetpac/src/symphony"] trust_level = "trusted" +[projects."/Users/jetpac/src/symphony/kanboard-workspaces/KB-10/repo"] +trust_level = "trusted" + +[projects."/private/tmp/kanban-jira"] +trust_level = "trusted" + [marketplaces.openai-bundled] last_updated = "2026-05-05T21:54:34Z" source_type = "local" diff --git a/bin/codex-devops-auth.sh b/bin/codex-devops-auth.sh index 18c5de4..df7aeda 100755 --- a/bin/codex-devops-auth.sh +++ b/bin/codex-devops-auth.sh @@ -3,6 +3,7 @@ set -euo pipefail PKCS11_LIB="${PKCS11_LIB:-/usr/local/lib/opensc-pkcs11.so}" +YUBIKEY_PIN_FILE="${YUBIKEY_PIN_FILE:-/tmp/pass}" 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}" @@ -10,15 +11,17 @@ OCI_SESSION_REGION="${OCI_SESSION_REGION:-us-chicago-1}" OCI_PROFILE_NAME="${OCI_PROFILE_NAME:-DEFAULT}" 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:-}" DEDICATED_AGENT_PID="" DEDICATED_AGENT_SOCK="" +PRESERVE_DEDICATED_AGENT="0" log() { print -u2 -- "$@" } cleanup() { - if [[ -n "${DEDICATED_AGENT_PID}" && -n "${DEDICATED_AGENT_SOCK}" ]]; then + if [[ "${PRESERVE_DEDICATED_AGENT}" != "1" && -n "${DEDICATED_AGENT_PID}" && -n "${DEDICATED_AGENT_SOCK}" ]]; then SSH_AGENT_PID="${DEDICATED_AGENT_PID}" SSH_AUTH_SOCK="${DEDICATED_AGENT_SOCK}" ssh-agent -k >/dev/null 2>&1 || true fi } @@ -120,9 +123,205 @@ ensure_ssh_agent() { DEDICATED_AGENT_SOCK="${SSH_AUTH_SOCK:-}" } +resolve_token_ssh_config_value() { + local key="$1" + + ssh -G -F "${SSH_CONFIG_FILE}" "${TOKEN_HOST}" 2>/dev/null | awk -v key="${key}" ' + $1 == key { + $1 = "" + sub(/^[[:space:]]+/, "") + print + exit + } + ' +} + +resolve_token_proxy_command() { + resolve_token_ssh_config_value proxycommand +} + +resolve_token_proxy_jump() { + resolve_token_ssh_config_value proxyjump +} + +resolve_token_ssh_user() { + resolve_token_ssh_config_value user +} + +quiet_proxy_command() { + local proxy_command="$1" + + proxy_command="${proxy_command// -vvv/}" + proxy_command="${proxy_command// -vv/}" + proxy_command="${proxy_command// -v/}" + print -r -- "${proxy_command}" +} + +proxy_ssh_prefix() { + local ssh_bin="$1" + local prefix="${ssh_bin} -F ${(q)SSH_CONFIG_FILE} -o LogLevel=ERROR" + + if [[ -n "${TOKEN_SSH_USER:-}" ]]; then + prefix+=" -l ${(q)TOKEN_SSH_USER}" + fi + + print -r -- "${prefix}" +} + +proxy_command_with_ssh_config() { + local proxy_command="$1" + local bastion_host + + if [[ -z "${proxy_command}" || "${proxy_command}" == "none" ]]; then + return 1 + fi + + if [[ "${proxy_command}" == bash\ -c\ * && "${proxy_command}" == *" -W %h:%p "* ]]; then + bastion_host="${proxy_command#* -W %h:%p }" + bastion_host="${bastion_host%% *}" + bastion_host="${bastion_host%%\'*}" + bastion_host="${bastion_host%%\"*}" + + if [[ -n "${bastion_host}" ]]; then + print -r -- "$(proxy_ssh_prefix ssh) -W %h:%p ${bastion_host}" + return 0 + fi + fi + + case "${proxy_command}" in + /usr/bin/ssh\ *) + print -r -- "$(proxy_ssh_prefix /usr/bin/ssh) ${proxy_command#/usr/bin/ssh }" + ;; + ssh\ *) + print -r -- "$(proxy_ssh_prefix ssh) ${proxy_command#ssh }" + ;; + *) + print -r -- "${proxy_command}" + ;; + esac +} + +proxy_jump_as_proxy_command() { + local proxy_jump="$1" + + if [[ -z "${proxy_jump}" || "${proxy_jump}" == "none" ]]; then + return 1 + fi + + print -r -- "$(proxy_ssh_prefix ssh) -W %h:%p ${proxy_jump}" +} + +build_token_ssh_args() { + TOKEN_SSH_ARGS=(-F "${SSH_CONFIG_FILE}" -o LogLevel=ERROR) + TOKEN_SSH_USER="$(resolve_token_ssh_user || true)" + + local proxy_command proxy_jump configured_proxy_command + proxy_command="$(resolve_token_proxy_command || true)" + configured_proxy_command="$(proxy_command_with_ssh_config "${proxy_command}" || true)" + + if [[ -z "${configured_proxy_command}" ]]; then + proxy_jump="$(resolve_token_proxy_jump || true)" + configured_proxy_command="$(proxy_jump_as_proxy_command "${proxy_jump}" || true)" + fi + + if [[ -n "${configured_proxy_command}" ]]; then + TOKEN_SSH_ARGS+=(-o "ProxyCommand=${configured_proxy_command}") + fi +} + +load_pkcs11_provider_with_expect() { + local expect_script="$1" + shift + + if ! command -v expect >/dev/null 2>&1; then + return 127 + fi + + if ! PKCS11_LIB_FOR_EXPECT="${PKCS11_LIB}" expect -c "${expect_script}"; then + return 1 + fi + + return 0 +} + +load_pkcs11_provider_from_pin_file() { + YUBIKEY_PIN_FILE_FOR_EXPECT="${YUBIKEY_PIN_FILE}" load_pkcs11_provider_with_expect ' + set timeout 30 + set pkcs11_lib $env(PKCS11_LIB_FOR_EXPECT) + set pin_file $env(YUBIKEY_PIN_FILE_FOR_EXPECT) + set fh [open $pin_file r] + gets $fh pin + close $fh + + spawn ssh-add -s $pkcs11_lib + expect { + -re {([Pp]assphrase|PIN|pin).*:} { + send -- "$pin\r" + exp_continue + } + eof { + catch wait result + exit [lindex $result 3] + } + timeout { + exit 124 + } + } + ' >/dev/null +} + +load_pkcs11_provider_with_prompt() { + load_pkcs11_provider_with_expect ' + set timeout 120 + set pkcs11_lib $env(PKCS11_LIB_FOR_EXPECT) + + send_user "YubiKey PIN: " + stty -echo + expect_user -re "(.*)\n" + stty echo + send_user "\n" + set pin $expect_out(1,string) + + log_user 0 + spawn ssh-add -s $pkcs11_lib + expect { + -re {([Pp]assphrase|PIN|pin).*:} { + send -- "$pin\r" + exp_continue + } + eof { + catch wait result + exit [lindex $result 3] + } + timeout { + exit 124 + } + } + ' +} + add_pkcs11_provider() { log "Loading PKCS#11 provider: ${PKCS11_LIB}" - ssh-add -s "${PKCS11_LIB}" >/dev/null + + if [[ -r "${YUBIKEY_PIN_FILE}" ]]; then + if ! load_pkcs11_provider_from_pin_file; then + log "Failed to load PKCS#11 provider using YubiKey PIN file ${YUBIKEY_PIN_FILE}." + return 1 + fi + elif [[ -t 0 ]]; then + if ! load_pkcs11_provider_with_prompt; then + log "Failed to load PKCS#11 provider using prompted YubiKey PIN." + return 1 + fi + else + log "YubiKey PIN file not readable at ${YUBIKEY_PIN_FILE}, and stdin is not a terminal." + return 1 + fi + + if ! ssh-add -l >/dev/null 2>&1; then + log "PKCS#11 provider loaded, but no SSH identities are visible to the dedicated agent." + return 1 + fi } prepare_agent() { @@ -130,6 +329,55 @@ prepare_agent() { add_pkcs11_provider } +refresh_operator_token() { + local operator_token + + log "Refreshing OPERATOR_ACCESS_TOKEN from ${TOKEN_HOST} using ${SSH_CONFIG_FILE}" + TOKEN_SSH_ARGS=() + build_token_ssh_args + + if ! operator_token="$(ssh "${TOKEN_SSH_ARGS[@]}" "${TOKEN_HOST}" "generate --mode jwt")"; then + log "Failed to refresh OPERATOR_ACCESS_TOKEN from ${TOKEN_HOST}." + return 1 + fi + + if [[ -z "${operator_token}" ]]; then + log "Token host ${TOKEN_HOST} returned an empty OPERATOR_ACCESS_TOKEN." + return 1 + fi + + export OPERATOR_ACCESS_TOKEN="${operator_token}" + export OP_TOKEN="${OPERATOR_ACCESS_TOKEN}" + + log "Using fresh OP_TOKEN for Codex and DevOps MCP." +} + +write_shell_export() { + local name="$1" + local value="$2" + + printf 'export %s=%q\n' "${name}" "${value}" +} + +write_auth_env() { + local env_out="$1" + local env_dir tmp + + env_dir="$(dirname -- "${env_out}")" + mkdir -p "${env_dir}" + tmp="$(mktemp "${env_out}.XXXXXX")" + + { + write_shell_export SSH_AUTH_SOCK "${DEDICATED_AGENT_SOCK}" + write_shell_export SSH_AGENT_PID "${DEDICATED_AGENT_PID}" + write_shell_export OPERATOR_ACCESS_TOKEN "${OPERATOR_ACCESS_TOKEN}" + write_shell_export OP_TOKEN "${OP_TOKEN}" + } > "${tmp}" + + chmod 600 "${tmp}" + mv -f "${tmp}" "${env_out}" +} + if [[ ! -f "${SSH_CONFIG_FILE}" ]]; then print -u2 "SSH config file not found: ${SSH_CONFIG_FILE}" exit 1 @@ -148,10 +396,13 @@ ensure_oci_session prepare_agent -log "Refreshing OPERATOR_ACCESS_TOKEN from ${TOKEN_HOST} using ${SSH_CONFIG_FILE}" -export OPERATOR_ACCESS_TOKEN="$(ssh -F "${SSH_CONFIG_FILE}" "${TOKEN_HOST}" "generate --mode jwt")" -export OP_TOKEN="${OPERATOR_ACCESS_TOKEN}" +refresh_operator_token -log "Using fresh OP_TOKEN for Codex and DevOps MCP." +if [[ -n "${CODEX_DEVOPS_AUTH_ENV_OUT}" ]]; then + write_auth_env "${CODEX_DEVOPS_AUTH_ENV_OUT}" + PRESERVE_DEDICATED_AGENT="1" + log "Wrote Codex auth environment to ${CODEX_DEVOPS_AUTH_ENV_OUT}." + exit 0 +fi /opt/homebrew/bin/codex "$@" diff --git a/bin/codex-wrapper.sh b/bin/codex-wrapper.sh index 8507eeb..4afff17 100755 --- a/bin/codex-wrapper.sh +++ b/bin/codex-wrapper.sh @@ -1,5 +1,175 @@ #!/usr/bin/env zsh -export BITBUCKET_TOKEN=NzQ0MDE3NjEzNDE1Oh6PpMt8Rl+a569vzoPOfCRJ+Kwt -"$HOME/bin/codex-devops-auth.sh" -a on-request -s danger-full-access "$@" +set -euo pipefail +CODEX_MCP_ENV_FILE="${CODEX_MCP_ENV_FILE:-${HOME}/.codex/mcp.env}" +MCPGW_SELECTED_SERVERS_FILE="${MCPGW_SELECTED_SERVERS_FILE:-${HOME}/.ora-gateway/selected-servers.json}" +MCPGW_OP_TOKEN_FILE="${MCPGW_OP_TOKEN_FILE:-${HOME}/.ora-gateway/op-token}" +CODEX_DEVOPS_AUTH_SCRIPT="${CODEX_DEVOPS_AUTH_SCRIPT:-${HOME}/bin/codex-devops-auth.sh}" +CODEX_BIN="${CODEX_BIN:-/opt/homebrew/bin/codex}" +CODEX_WRAPPER_AUTH_ENV_FILE="" +CODEX_WRAPPER_DEDICATED_AGENT_PID="" +CODEX_WRAPPER_DEDICATED_AGENT_SOCK="" + +log() { + print -u2 -- "$@" +} + +cleanup() { + if [[ -n "${CODEX_WRAPPER_DEDICATED_AGENT_PID}" && -n "${CODEX_WRAPPER_DEDICATED_AGENT_SOCK}" ]]; then + SSH_AGENT_PID="${CODEX_WRAPPER_DEDICATED_AGENT_PID}" SSH_AUTH_SOCK="${CODEX_WRAPPER_DEDICATED_AGENT_SOCK}" ssh-agent -k >/dev/null 2>&1 || true + fi + + if [[ -n "${CODEX_WRAPPER_AUTH_ENV_FILE}" ]]; then + rm -f "${CODEX_WRAPPER_AUTH_ENV_FILE}" >/dev/null 2>&1 || true + fi +} + +sanitize_mcpgw_output() { + local line clean redacted + + while IFS= read -r line || [[ -n "${line}" ]]; do + clean="$(printf '%s\n' "${line}" | perl -pe 's/\e\]8;;.*?\a//g; s/\e\[[0-?]*[ -\/]*[@-~]//g')" + redacted="$(printf '%s\n' "${clean}" | sed -E \ + -e 's#https?://[^[:space:]]+#[redacted URL]#g' \ + -e 's#([Aa][Cc][Cc][Ee][Ss][Ss]_[Tt][Oo][Kk][Ee][Nn]=)[^[:space:]]+#\1[redacted]#g' \ + -e 's#([Ii][Dd]_[Tt][Oo][Kk][Ee][Nn]=)[^[:space:]]+#\1[redacted]#g' \ + -e 's#([Rr][Ee][Ff][Rr][Ee][Ss][Hh]_[Tt][Oo][Kk][Ee][Nn]=)[^[:space:]]+#\1[redacted]#g' \ + -e 's#([Ss][Ee][Cc][Uu][Rr][Ii][Tt][Yy]_[Tt][Oo][Kk][Ee][Nn]=)[^[:space:]]+#\1[redacted]#g' \ + -e 's#([Cc][Ll][Ii][Ee][Nn][Tt]_[Ss][Ee][Cc][Rr][Ee][Tt]=)[^[:space:]]+#\1[redacted]#g' \ + -e 's#([Pp][Uu][Bb][Ll][Ii][Cc]_[Kk][Ee][Yy]=)[^[:space:]]+#\1[redacted]#g' \ + -e 's#([Aa][Uu][Tt][Hh][Oo][Rr][Ii][Zz][Aa][Tt][Ii][Oo][Nn]:[[:space:]]*).*#\1[redacted]#g' \ + -e 's#([Cc][Oo][Oo][Kk][Ii][Ee]:[[:space:]]*).*#\1[redacted]#g' \ + -e 's#([Ss][Ee][Tt]-[Cc][Oo][Oo][Kk][Ii][Ee]:[[:space:]]*).*#\1[redacted]#g' \ + -e 's#([^[:space:]]*/)?[.]oci/config#[redacted OCI config path]#g' \ + -e 's#(Config written to: ).*#\1[redacted config path]#')" + + log "${redacted}" + done +} + +is_truthy() { + case "${1:-}" in + 1|true|TRUE|yes|YES|on|ON) + return 0 + ;; + *) + return 1 + ;; + esac +} + +confluence_selected() { + local selected_servers_file="${MCPGW_SELECTED_SERVERS_FILE}" + + if [[ -r "${selected_servers_file}" ]] && LC_ALL=C grep -Eiq '"(Confluence|CentralConfluence|central_confluence|central-confluence)"' "${selected_servers_file}"; then + return 0 + fi + + case ",${CODEX_MCP_SERVERS:-}," in + *,Confluence,*|*,confluence,*|*,CentralConfluence,*|*,central_confluence,*|*,central-confluence,*) + return 0 + ;; + esac + + return 1 +} + +should_refresh_confluence_cookies() { + if is_truthy "${CODEX_MCP_REFRESH_COOKIES:-}" || \ + is_truthy "${CODEX_MCP_REFRESH_CONFLUENCE_COOKIES:-}" || \ + is_truthy "${MCPGW_REFRESH_COOKIES:-}" || \ + is_truthy "${CODEX_MCP_CONFLUENCE_COOKIES_STALE:-}" || \ + is_truthy "${MCPGW_CONFLUENCE_COOKIES_STALE:-}"; then + return 0 + fi + + confluence_selected +} + +run_mcpgw_required() { + local mcpgw_bin="$1" + shift + + log "MCP Gateway auth preflight: mcpgw $*" + "${mcpgw_bin}" "$@" 2>&1 | sanitize_mcpgw_output + local rc="${pipestatus[1]}" + + if [[ ${rc} -ne 0 ]]; then + log "MCP Gateway auth preflight failed: mcpgw $* exited with ${rc}." + exit "${rc}" + fi +} + +prepare_codex_auth() { + if [[ ! -x "${CODEX_DEVOPS_AUTH_SCRIPT}" ]]; then + log "Codex DevOps auth helper not found or not executable: ${CODEX_DEVOPS_AUTH_SCRIPT}" + exit 1 + fi + + CODEX_WRAPPER_AUTH_ENV_FILE="$(mktemp "${TMPDIR:-/tmp}/codex-devops-auth.XXXXXX")" + CODEX_DEVOPS_AUTH_ENV_OUT="${CODEX_WRAPPER_AUTH_ENV_FILE}" "${CODEX_DEVOPS_AUTH_SCRIPT}" + source "${CODEX_WRAPPER_AUTH_ENV_FILE}" + + CODEX_WRAPPER_DEDICATED_AGENT_PID="${SSH_AGENT_PID:-}" + CODEX_WRAPPER_DEDICATED_AGENT_SOCK="${SSH_AUTH_SOCK:-}" +} + +write_gateway_op_token() { + local token_file="${MCPGW_OP_TOKEN_FILE}" + local token_dir tmp + + if [[ -z "${OP_TOKEN:-}" ]]; then + log "Cannot write MCP Gateway OP token: OP_TOKEN is empty." + exit 1 + fi + + token_dir="$(dirname -- "${token_file}")" + mkdir -p "${token_dir}" + tmp="$(mktemp "${token_file}.XXXXXX")" + printf '%s\n' "${OP_TOKEN}" > "${tmp}" + chmod 600 "${tmp}" + mv -f "${tmp}" "${token_file}" + log "MCP Gateway auth preflight: wrote fresh operator token to ${token_file}." +} + +refresh_gateway_auth() { + local mcpgw_bin + mcpgw_bin="$(command -v mcpgw 2>/dev/null || true)" + + if [[ -n "${mcpgw_bin}" ]]; then + run_mcpgw_required "${mcpgw_bin}" refresh + else + log "Warning: mcpgw not found on PATH; skipping MCP Gateway auth refresh." + fi + + prepare_codex_auth + write_gateway_op_token + + if [[ -z "${mcpgw_bin}" ]]; then + return 0 + fi + + if should_refresh_confluence_cookies; then + run_mcpgw_required "${mcpgw_bin}" refresh-cookies + else + log "MCP Gateway auth preflight: skipping mcpgw refresh-cookies; Confluence auth was not requested." + fi + + run_mcpgw_required "${mcpgw_bin}" status +} + +trap cleanup EXIT INT TERM + +if [[ -r "${CODEX_MCP_ENV_FILE}" ]]; then + source "${CODEX_MCP_ENV_FILE}" +fi + +refresh_gateway_auth + +if is_truthy "${CODEX_WRAPPER_DRY_RUN:-}"; then + log "CODEX_WRAPPER_DRY_RUN is set; skipping Codex launch." + exit 0 +fi + +"${CODEX_BIN}" -a on-request -s danger-full-access "$@"