# shellcheck shell=bash # Sourced helpers for omarchy-cn-ai-* commands CN_AI_PROVIDERS="$OMARCHY_PATH/cn/registry/ai-providers.json" CN_AI_HARNESSES="$OMARCHY_PATH/cn/registry/ai-harnesses.json" CN_AI_COMPAT="$OMARCHY_PATH/cn/registry/ai-compatibility.json" CN_AI_PROFILE_DIR="$HOME/.config/omarchycn/ai/profiles" CN_AI_CURRENT="$HOME/.config/omarchycn/ai/current" cn_ai_provider_ids() { jq -r '.providers | keys[]' "$CN_AI_PROVIDERS"; } cn_ai_harness_ids() { jq -r '.harnesses | keys[]' "$CN_AI_HARNESSES"; } cn_ai_endpoint() { jq -re --arg p "$1" --arg proto "$2" '.providers[$p].endpoints[$proto]' "$CN_AI_PROVIDERS" } cn_ai_models() { jq -r --arg p "$1" '.providers[$p].models[].id' "$CN_AI_PROVIDERS" } cn_ai_model_by_alias() { jq -re --arg p "$1" --arg a "$2" \ '.providers[$p].models[] | select(.aliases // [] | index($a)) | .id' "$CN_AI_PROVIDERS" } cn_ai_fast_model() { cn_ai_model_by_alias "$1" fast 2>/dev/null || cn_ai_model_by_alias "$1" default-coding } cn_ai_harness_field() { jq -re --arg h "$1" --arg f "$2" '.harnesses[$h][$f]' "$CN_AI_HARNESSES" } # Providers with a fixed token (local servers) need no stored secret. # `// empty` keeps stdout clean when absent (bare -re would print "null"). cn_ai_static_token() { jq -re --arg p "$1" '.providers[$p].static_token // empty' "$CN_AI_PROVIDERS" } cn_ai_resolve_key() { cn_ai_static_token "$1" 2>/dev/null || omarchy-cn-ai-secret get "$1" } # Dynamic-model providers list models at runtime, not in the registry cn_ai_models_dynamic() { jq -e --arg p "$1" '.providers[$p].models_dynamic == true' "$CN_AI_PROVIDERS" > /dev/null } cn_ai_combo_level() { jq -r --arg h "$1" --arg p "$2" '.combos[$h][$p].level // "unsupported"' "$CN_AI_COMPAT" } # Combo-specific model allowlist; empty output means no restriction cn_ai_combo_models() { jq -r --arg h "$1" --arg p "$2" '.combos[$h][$p].models // [] | .[]' "$CN_AI_COMPAT" } cn_ai_model_protocol() { jq -r --arg p "$1" --arg m "$2" \ '.providers[$p].models[] | select(.id == $m) | .protocol // ""' "$CN_AI_PROVIDERS" } # Profile files are flat key="value" lines cn_ai_profile_field() { local file="$CN_AI_PROFILE_DIR/$1.toml" key="$2" grep -E "^$key = " "$file" | head -1 | cut -d\" -f2 } cn_ai_current_profile() { if [[ ! -f $CN_AI_CURRENT ]]; then echo "No default AI profile set (run: omarchycn ai profile use )" >&2 return 1 fi cat "$CN_AI_CURRENT" } # Print export statements for the profile's harness x provider combo. # Secrets are resolved at launch time only; nothing is written to disk. cn_ai_render_env() { local harness="$1" provider="$2" model="$3" key="$4" local base fast case "$harness" in claude-code) base=$(cn_ai_endpoint "$provider" anthropic) # Dynamic-model providers have no fast alias: every tier maps to the model fast=$(cn_ai_fast_model "$provider" 2>/dev/null || printf '%s' "$model") printf 'export ANTHROPIC_BASE_URL=%q\n' "$base" # Docs vary between AUTH_TOKEN (deepseek/zai) and API_KEY (kimi): set both printf 'export ANTHROPIC_AUTH_TOKEN=%q\n' "$key" printf 'export ANTHROPIC_API_KEY=%q\n' "$key" printf 'export ANTHROPIC_MODEL=%q\n' "$model" printf 'export ANTHROPIC_DEFAULT_SONNET_MODEL=%q\n' "$model" printf 'export ANTHROPIC_DEFAULT_OPUS_MODEL=%q\n' "$model" printf 'export ANTHROPIC_DEFAULT_HAIKU_MODEL=%q\n' "$fast" ;; opencode) if [[ $provider != "deepseek" ]]; then echo "opencode adapter renders only the deepseek combo (native provider)" >&2 return 1 fi printf 'export DEEPSEEK_API_KEY=%q\n' "$key" ;; *) echo "No env renderer for harness: $harness" >&2 return 1 ;; esac } # Write ~/.codex/config.toml + models.json per DeepSeek's official codex # integration contract (mirrors cdn.deepseek.com codex-deepseek-setup script) cn_ai_render_codex_config() { local provider="$1" model="$2" key="$3" local base cfg="$HOME/.codex/config.toml" if [[ $provider != "deepseek" ]]; then echo "codex adapter renders only the deepseek combo (official integration)" >&2 return 1 fi base="https://api.deepseek.com/" mkdir -p "${cfg%/*}" touch "$cfg" # Strip our old blocks first, then check for an unmanaged provider table rm -f "$cfg.omarchycn-tmp" "$cfg.omarchycn-new" ( umask 077 awk ' /^# OmarchyCN ai (model|provider) begin$/ { skip = 1; next } /^# OmarchyCN ai (model|provider) end$/ { skip = 0; next } skip { next } /^[[:space:]]*\[/ { in_section = 1 } !in_section && /^[[:space:]]*(model|model_provider|model_catalog_json|preferred_auth_method|forced_login_method|model_reasoning_effort)[[:space:]]*=/ { next } { print } ' "$cfg" > "$cfg.omarchycn-tmp" ) if grep -qE "^[[:space:]]*\[[[:space:]]*model_providers[[:space:]]*\.[[:space:]]*\"?$provider\"?[[:space:]]*\]" "$cfg.omarchycn-tmp"; then rm -f "$cfg.omarchycn-tmp" echo "codex: $cfg 已有非托管的 [model_providers.$provider],请手动清理后重试" >&2 return 1 fi if [[ -s $cfg ]]; then cp "$cfg" "$cfg.omarchycn-bak-$(date +%Y%m%d-%H%M%S)" fi cp "$OMARCHY_PATH/cn/registry/codex-deepseek-models.json" "$HOME/.codex/models.json" ( umask 077 { echo "# OmarchyCN ai model begin" echo "model = \"$model\"" echo "model_provider = \"$provider\"" echo "model_catalog_json = \"~/.codex/models.json\"" echo "preferred_auth_method = \"apikey\"" echo "forced_login_method = \"api\"" echo "model_reasoning_effort = \"high\"" echo "# OmarchyCN ai model end" cat "$cfg.omarchycn-tmp" echo "# OmarchyCN ai provider begin" echo "[model_providers.$provider]" echo "name = \"$provider\"" echo "base_url = \"$base\"" echo "wire_api = \"responses\"" echo "experimental_bearer_token = \"$key\"" echo "# OmarchyCN ai provider end" } > "$cfg.omarchycn-new" ) mv "$cfg.omarchycn-new" "$cfg" rm -f "$cfg.omarchycn-tmp" }