#!/usr/bin/env bash set -euo pipefail SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd -- "$SCRIPT_DIR/.." && pwd)" ENV_FILE="$REPO_ROOT/openspace/.env" PRIMARY_CODEX_HOME="${PRIMARY_CODEX_HOME:-$HOME/.codex}" PROFILE_HOME="${OPENSPACE_CODEX_HOME:-$HOME/.codex-openspace-desktop}" PROJECT_NAME="$(basename "$REPO_ROOT")" PROJECT_SKILL_DIR="$PROFILE_HOME/projects/$PROJECT_NAME/skills" REPO_PYTHON="$REPO_ROOT/.venv/bin/python" if [[ ! -f "$PRIMARY_CODEX_HOME/config.toml" ]]; then echo "Missing $PRIMARY_CODEX_HOME/config.toml" >&2 exit 1 fi if [[ ! -f "$PRIMARY_CODEX_HOME/auth.json" ]]; then echo "Missing $PRIMARY_CODEX_HOME/auth.json" >&2 exit 1 fi read_env_value() { local key="$1" if [[ ! -f "$ENV_FILE" ]]; then return 0 fi python3 - <<'PY' "$ENV_FILE" "$key" from pathlib import Path import sys env_path = Path(sys.argv[1]) target = sys.argv[2] for line in env_path.read_text(encoding="utf-8").splitlines(): stripped = line.strip() if not stripped or stripped.startswith("#") or "=" not in stripped: continue key, value = stripped.split("=", 1) if key.strip() != target: continue value = value.strip().strip('"').strip("'") print(value, end="") break PY } OPENSPACE_MODEL="${OPENSPACE_MODEL:-$(read_env_value OPENSPACE_MODEL)}" OPENSPACE_MODEL="${OPENSPACE_MODEL:-gpt-5.4}" OPENSPACE_LLM_API_KEY="${OPENSPACE_LLM_API_KEY:-$(read_env_value OPENSPACE_LLM_API_KEY)}" OPENSPACE_LLM_API_BASE="${OPENSPACE_LLM_API_BASE:-$(read_env_value OPENSPACE_LLM_API_BASE)}" OPENSPACE_LLM_API_BASE="${OPENSPACE_LLM_API_BASE:-https://codexapi.space/v1}" OPENSPACE_LLM_OPENAI_STREAM_COMPAT="${OPENSPACE_LLM_OPENAI_STREAM_COMPAT:-$(read_env_value OPENSPACE_LLM_OPENAI_STREAM_COMPAT)}" OPENSPACE_LLM_OPENAI_STREAM_COMPAT="${OPENSPACE_LLM_OPENAI_STREAM_COMPAT:-true}" OPENSPACE_SKILL_EMBEDDING_BACKEND="${OPENSPACE_SKILL_EMBEDDING_BACKEND:-$(read_env_value OPENSPACE_SKILL_EMBEDDING_BACKEND)}" OPENSPACE_SKILL_EMBEDDING_BACKEND="${OPENSPACE_SKILL_EMBEDDING_BACKEND:-local}" OPENSPACE_SKILL_EMBEDDING_MODEL="${OPENSPACE_SKILL_EMBEDDING_MODEL:-$(read_env_value OPENSPACE_SKILL_EMBEDDING_MODEL)}" OPENSPACE_SKILL_EMBEDDING_MODEL="${OPENSPACE_SKILL_EMBEDDING_MODEL:-BAAI/bge-small-en-v1.5}" OPENSPACE_SKILL_EMBEDDING_API_KEY="${OPENSPACE_SKILL_EMBEDDING_API_KEY:-$(read_env_value OPENSPACE_SKILL_EMBEDDING_API_KEY)}" OPENSPACE_SKILL_EMBEDDING_API_BASE="${OPENSPACE_SKILL_EMBEDDING_API_BASE:-$(read_env_value OPENSPACE_SKILL_EMBEDDING_API_BASE)}" EMBEDDING_API_KEY="${EMBEDDING_API_KEY:-$(read_env_value EMBEDDING_API_KEY)}" EMBEDDING_BASE_URL="${EMBEDDING_BASE_URL:-$(read_env_value EMBEDDING_BASE_URL)}" EMBEDDING_MODEL="${EMBEDDING_MODEL:-$(read_env_value EMBEDDING_MODEL)}" sync_profile_dir() { local name="$1" local src="$PRIMARY_CODEX_HOME/$name" local dst="$PROFILE_HOME/$name" if [[ ! -d "$src" ]]; then return fi mkdir -p "$dst" rsync -a --delete "$src/" "$dst/" } bootstrap_profile_home() { mkdir -p "$PROFILE_HOME" mkdir -p "$PROJECT_SKILL_DIR" mkdir -p "$PROFILE_HOME/skills" sync_profile_dir "plugins" sync_profile_dir "skills" sync_profile_dir "pua" cp "$PRIMARY_CODEX_HOME/auth.json" "$PROFILE_HOME/auth.json" python3 - <<'PY' "$PRIMARY_CODEX_HOME/config.toml" "$PROFILE_HOME/config.toml" "$REPO_ROOT" "$REPO_PYTHON" "$PROJECT_SKILL_DIR" "$PROFILE_HOME/skills" "$OPENSPACE_MODEL" "$OPENSPACE_LLM_API_KEY" "$OPENSPACE_LLM_API_BASE" "$OPENSPACE_LLM_OPENAI_STREAM_COMPAT" "$OPENSPACE_SKILL_EMBEDDING_BACKEND" "$OPENSPACE_SKILL_EMBEDDING_MODEL" "$OPENSPACE_SKILL_EMBEDDING_API_KEY" "$OPENSPACE_SKILL_EMBEDDING_API_BASE" "$EMBEDDING_API_KEY" "$EMBEDDING_BASE_URL" "$EMBEDDING_MODEL" from pathlib import Path import os import sys src = Path(sys.argv[1]) dst = Path(sys.argv[2]) repo_root = sys.argv[3] python_cmd = sys.argv[4] project_skill_dir = sys.argv[5] profile_skill_dir = sys.argv[6] model = sys.argv[7] api_key = sys.argv[8] api_base = sys.argv[9] stream_compat = sys.argv[10] skill_embedding_backend = sys.argv[11] skill_embedding_model = sys.argv[12] skill_embedding_api_key = sys.argv[13] skill_embedding_api_base = sys.argv[14] embedding_api_key = sys.argv[15] embedding_base_url = sys.argv[16] embedding_model = sys.argv[17] def strip_tables(text: str, table_names: set[str]) -> str: kept = [] skipping = False for line in text.splitlines(): stripped = line.strip() header = stripped.split("#", 1)[0].rstrip() if header.startswith("[") and header.endswith("]"): skipping = header in table_names or header.startswith("[mcp_servers.openspace_evolution.") if skipping: continue if skipping: continue kept.append(line) return "\n".join(kept).rstrip() + "\n" base = strip_tables( src.read_text(encoding="utf-8"), { "[mcp_servers.openspace_evolution]", "[mcp_servers.openspace_evolution.env]", }, ) project_marker = f'[projects."{repo_root}"]' if project_marker not in base: base += f'\n{project_marker}\ntrust_level = "trusted"\n' if api_key and Path(python_cmd).is_file() and os.access(python_cmd, os.X_OK): base += f''' [mcp_servers.openspace_evolution] command = "{python_cmd}" args = ["-m", "openspace.evolution_mcp_server", "--transport", "stdio"] [mcp_servers.openspace_evolution.env] OPENSPACE_WORKSPACE = "{repo_root}" OPENSPACE_HOST_SKILL_DIRS = "{project_skill_dir},{profile_skill_dir}" OPENSPACE_MODEL = "{model}" OPENSPACE_LLM_API_KEY = "{api_key}" OPENSPACE_LLM_API_BASE = "{api_base}" OPENSPACE_LLM_OPENAI_STREAM_COMPAT = "{stream_compat}" OPENSPACE_SKILL_EMBEDDING_BACKEND = "{skill_embedding_backend}" OPENSPACE_SKILL_EMBEDDING_MODEL = "{skill_embedding_model}" OPENSPACE_SKILL_EMBEDDING_API_KEY = "{skill_embedding_api_key}" OPENSPACE_SKILL_EMBEDDING_API_BASE = "{skill_embedding_api_base}" EMBEDDING_API_KEY = "{embedding_api_key}" EMBEDDING_BASE_URL = "{embedding_base_url}" EMBEDDING_MODEL = "{embedding_model}" OPENSPACE_ENABLE_RECORDING = "false" OPENSPACE_BACKEND_SCOPE = "shell,system" ''' dst.write_text(base, encoding="utf-8") PY } bootstrap_profile_home clear_openspace_env() { local var for var in ${!OPENSPACE_@}; do unset "$var" done unset EMBEDDING_API_KEY unset EMBEDDING_BASE_URL unset EMBEDDING_MODEL } if [[ -z "$OPENSPACE_LLM_API_KEY" ]]; then echo "Warning: OpenSpace evolution sidecar is disabled because no provider key was found in $ENV_FILE or the current environment." >&2 elif [[ ! -x "$REPO_PYTHON" ]]; then echo "Warning: OpenSpace evolution sidecar is disabled because $REPO_PYTHON is missing." >&2 fi clear_openspace_env if [[ "${1:-}" == "app" ]]; then shift if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then exec env CODEX_HOME="$PROFILE_HOME" codex app "$@" fi exec env CODEX_HOME="$PROFILE_HOME" codex app "$@" "$REPO_ROOT" fi exec env CODEX_HOME="$PROFILE_HOME" codex -C "$REPO_ROOT" "$@"