mirror of
https://github.com/HKUDS/OpenSpace.git
synced 2026-08-28 05:15:00 +00:00
196 lines
6.8 KiB
Bash
Executable file
196 lines
6.8 KiB
Bash
Executable file
#!/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" "$@"
|