294 lines
8.7 KiB
Bash
Executable File
294 lines
8.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
|
|
POLICY_CONFIG="$ROOT_DIR/config/copilot-policy-guard.config.json"
|
|
BUDGET_CONFIG="$ROOT_DIR/config/model-budget-guard.config.json"
|
|
AUTH_CONFIG="$ROOT_DIR/config/copilot-auth-watchdog.config.json"
|
|
MODEL_PROFILES_CONFIG="$ROOT_DIR/config/model-profiles.config.json"
|
|
MODEL_SCHEDULE_CONFIG="$ROOT_DIR/config/model-schedule.config.json"
|
|
|
|
if ! command -v openclaw >/dev/null 2>&1; then
|
|
echo "[configure-guardrails] openclaw CLI is required" >&2
|
|
exit 1
|
|
fi
|
|
if ! command -v jq >/dev/null 2>&1; then
|
|
echo "[configure-guardrails] jq is required" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ ! -f "$MODEL_PROFILES_CONFIG" ]]; then
|
|
cat > "$MODEL_PROFILES_CONFIG" <<'JSON'
|
|
{
|
|
"profiles": {
|
|
"paid": {
|
|
"description": "Work-hours higher-quality Copilot model",
|
|
"primary": "",
|
|
"fallbacks": []
|
|
},
|
|
"free": {
|
|
"description": "Off-hours low-cost/free Copilot model stack",
|
|
"primary": "",
|
|
"fallbacks": []
|
|
}
|
|
}
|
|
}
|
|
JSON
|
|
fi
|
|
|
|
if [[ ! -f "$MODEL_SCHEDULE_CONFIG" ]]; then
|
|
cat > "$MODEL_SCHEDULE_CONFIG" <<'JSON'
|
|
{
|
|
"enabled": true,
|
|
"dayProfile": "paid",
|
|
"nightProfile": "free",
|
|
"dayStartHour": 8,
|
|
"nightStartHour": 18,
|
|
"sessionKey": "agent:main:main",
|
|
"switchScript": "./scripts/model_profile_switch.sh",
|
|
"stateFile": "~/.openclaw/model-schedule-state.json"
|
|
}
|
|
JSON
|
|
fi
|
|
|
|
is_cheap_model() {
|
|
local m
|
|
m="$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]')"
|
|
[[ "$m" =~ (haiku|mini|flash|fast|nano|small|lite|economy) ]]
|
|
}
|
|
|
|
copilot_models=()
|
|
while IFS= read -r model; do
|
|
[[ -z "$model" ]] && continue
|
|
copilot_models+=("$model")
|
|
done < <(
|
|
openclaw models list 2>/dev/null \
|
|
| awk 'NR>1 {print $1}' \
|
|
| grep '^github-copilot/' \
|
|
| awk '!seen[$0]++'
|
|
)
|
|
|
|
if [[ ${#copilot_models[@]} -eq 0 ]]; then
|
|
cat >&2 <<MSG
|
|
[configure-guardrails] No github-copilot models found.
|
|
Do this first on the target machine:
|
|
1) copilot auth login
|
|
2) openclaw models refresh || true
|
|
3) openclaw models list
|
|
Then re-run:
|
|
bash ./scripts/configure_copilot_guardrails_defaults.sh
|
|
MSG
|
|
exit 1
|
|
fi
|
|
|
|
primary_model=""
|
|
for m in "${copilot_models[@]}"; do
|
|
if is_cheap_model "$m"; then
|
|
primary_model="$m"
|
|
break
|
|
fi
|
|
done
|
|
if [[ -z "$primary_model" ]]; then
|
|
primary_model="${copilot_models[0]}"
|
|
fi
|
|
|
|
# Build free/low-cost fallback candidates (excluding primary)
|
|
declare -a cheap_candidates=()
|
|
for m in "${copilot_models[@]}"; do
|
|
[[ "$m" == "$primary_model" ]] && continue
|
|
if is_cheap_model "$m"; then
|
|
cheap_candidates+=("$m")
|
|
fi
|
|
done
|
|
|
|
# If none matched cheap pattern, pick up to 2 non-primary models as fallback
|
|
if [[ ${#cheap_candidates[@]} -eq 0 ]]; then
|
|
for m in "${copilot_models[@]}"; do
|
|
[[ "$m" == "$primary_model" ]] && continue
|
|
cheap_candidates+=("$m")
|
|
[[ ${#cheap_candidates[@]} -ge 2 ]] && break
|
|
done
|
|
fi
|
|
|
|
# Keep at most 2 fallbacks
|
|
if [[ ${#cheap_candidates[@]} -gt 2 ]]; then
|
|
cheap_candidates=("${cheap_candidates[@]:0:2}")
|
|
fi
|
|
|
|
# High models: anything non-cheap and non-primary
|
|
declare -a high_models=()
|
|
for m in "${copilot_models[@]}"; do
|
|
[[ "$m" == "$primary_model" ]] && continue
|
|
if ! is_cheap_model "$m"; then
|
|
high_models+=("$m")
|
|
fi
|
|
done
|
|
|
|
# Ensure at least one high model if multiple exist
|
|
if [[ ${#high_models[@]} -eq 0 && ${#copilot_models[@]} -gt 1 ]]; then
|
|
for m in "${copilot_models[@]}"; do
|
|
[[ "$m" == "$primary_model" ]] && continue
|
|
high_models+=("$m")
|
|
break
|
|
done
|
|
fi
|
|
|
|
paid_model=""
|
|
if [[ ${#high_models[@]} -gt 0 ]]; then
|
|
paid_model="${high_models[0]}"
|
|
fi
|
|
if [[ -z "$paid_model" ]]; then
|
|
for m in "${copilot_models[@]}"; do
|
|
[[ "$m" == "$primary_model" ]] && continue
|
|
paid_model="$m"
|
|
break
|
|
done
|
|
fi
|
|
if [[ -z "$paid_model" ]]; then
|
|
paid_model="$primary_model"
|
|
fi
|
|
|
|
# free profile = lowest-cost primary + lowest-cost fallbacks
|
|
declare -a free_fallbacks=("${cheap_candidates[@]}")
|
|
|
|
# paid profile = strongest available model + cheap fallback chain
|
|
declare -a paid_fallbacks=()
|
|
if [[ "$primary_model" != "$paid_model" ]]; then
|
|
paid_fallbacks+=("$primary_model")
|
|
fi
|
|
for m in "${cheap_candidates[@]}"; do
|
|
[[ "$m" == "$paid_model" ]] && continue
|
|
skip="false"
|
|
for cur in "${paid_fallbacks[@]}"; do
|
|
if [[ "$cur" == "$m" ]]; then
|
|
skip="true"
|
|
break
|
|
fi
|
|
done
|
|
[[ "$skip" == "true" ]] && continue
|
|
paid_fallbacks+=("$m")
|
|
done
|
|
|
|
active_profile="paid"
|
|
current_hour="$(date +%H)"
|
|
current_hour=$((10#$current_hour))
|
|
if (( current_hour >= 18 || current_hour < 8 )); then
|
|
active_profile="free"
|
|
fi
|
|
|
|
active_primary="$paid_model"
|
|
active_fallbacks=("${paid_fallbacks[@]}")
|
|
if [[ "$active_profile" == "free" ]]; then
|
|
active_primary="$primary_model"
|
|
active_fallbacks=("${free_fallbacks[@]}")
|
|
fi
|
|
|
|
fallbacks_json="$(printf '%s\n' "${active_fallbacks[@]}" | jq -R . | jq -s .)"
|
|
high_json="$(printf '%s\n' "${high_models[@]}" | jq -R . | jq -s .)"
|
|
free_fallbacks_json="$(printf '%s\n' "${free_fallbacks[@]}" | jq -R . | jq -s .)"
|
|
paid_fallbacks_json="$(printf '%s\n' "${paid_fallbacks[@]}" | jq -R . | jq -s .)"
|
|
|
|
# Update configs
|
|
jq \
|
|
--arg primary "$active_primary" \
|
|
--argjson fallbacks "$fallbacks_json" \
|
|
'.enabled=true
|
|
| .autoFix=true
|
|
| .desiredPrimaryModel=$primary
|
|
| .enforceFallbackAllowlist=true
|
|
| .allowedFallbacks=$fallbacks
|
|
| .providerPolicy["github-copilot"]=true
|
|
| .providerPolicy["openai"]=false
|
|
| .providerPolicy["anthropic"]=false
|
|
| .providerPolicy["openrouter"]=false' \
|
|
"$POLICY_CONFIG" > "$POLICY_CONFIG.tmp" && mv "$POLICY_CONFIG.tmp" "$POLICY_CONFIG"
|
|
|
|
jq \
|
|
--arg low "$primary_model" \
|
|
--argjson highs "$high_json" \
|
|
'.enabled=true
|
|
| .lowModel=$low
|
|
| .highModels=$highs
|
|
| .warnAfterMinutes=2
|
|
| .revertAfterMinutes=45
|
|
| .minWarnIntervalMinutes=20' \
|
|
"$BUDGET_CONFIG" > "$BUDGET_CONFIG.tmp" && mv "$BUDGET_CONFIG.tmp" "$BUDGET_CONFIG"
|
|
|
|
jq '.enabled=true | .minAlertIntervalMinutes=30 | .checkOpenClawModelStatus=true' \
|
|
"$AUTH_CONFIG" > "$AUTH_CONFIG.tmp" && mv "$AUTH_CONFIG.tmp" "$AUTH_CONFIG"
|
|
|
|
jq \
|
|
--arg paid "$paid_model" \
|
|
--arg free "$primary_model" \
|
|
--argjson paid_fallbacks "$paid_fallbacks_json" \
|
|
--argjson free_fallbacks "$free_fallbacks_json" \
|
|
'.profiles.paid.description="Work-hours higher-quality Copilot model"
|
|
| .profiles.paid.primary=$paid
|
|
| .profiles.paid.fallbacks=$paid_fallbacks
|
|
| .profiles.paid.providerPolicy["github-copilot"]=true
|
|
| .profiles.paid.providerPolicy["openai"]=false
|
|
| .profiles.paid.providerPolicy["anthropic"]=false
|
|
| .profiles.paid.providerPolicy["openrouter"]=false
|
|
| .profiles.free.description="Off-hours low-cost/free Copilot model stack"
|
|
| .profiles.free.primary=$free
|
|
| .profiles.free.fallbacks=$free_fallbacks
|
|
| .profiles.free.providerPolicy["github-copilot"]=true
|
|
| .profiles.free.providerPolicy["openai"]=false
|
|
| .profiles.free.providerPolicy["anthropic"]=false
|
|
| .profiles.free.providerPolicy["openrouter"]=false' \
|
|
"$MODEL_PROFILES_CONFIG" > "$MODEL_PROFILES_CONFIG.tmp" && mv "$MODEL_PROFILES_CONFIG.tmp" "$MODEL_PROFILES_CONFIG"
|
|
|
|
jq \
|
|
'.enabled=true
|
|
| .dayProfile="paid"
|
|
| .nightProfile="free"
|
|
| .dayStartHour=(.dayStartHour // 8)
|
|
| .nightStartHour=(.nightStartHour // 18)
|
|
| .sessionKey=(.sessionKey // "agent:main:main")
|
|
| .switchScript=(.switchScript // "./scripts/model_profile_switch.sh")
|
|
| .stateFile=(.stateFile // "~/.openclaw/model-schedule-state.json")' \
|
|
"$MODEL_SCHEDULE_CONFIG" > "$MODEL_SCHEDULE_CONFIG.tmp" && mv "$MODEL_SCHEDULE_CONFIG.tmp" "$MODEL_SCHEDULE_CONFIG"
|
|
|
|
# Apply live policy/routing on target machine
|
|
openclaw models set "$active_primary" >/dev/null
|
|
openclaw models fallbacks clear >/dev/null
|
|
for fb in "${active_fallbacks[@]}"; do
|
|
openclaw models fallbacks add "$fb" >/dev/null
|
|
|
|
done
|
|
openclaw config set --json providers.github-copilot.enabled true >/dev/null
|
|
openclaw config set --json providers.openai.enabled false >/dev/null
|
|
openclaw config set --json providers.anthropic.enabled false >/dev/null
|
|
openclaw config set --json providers.openrouter.enabled false >/dev/null
|
|
|
|
echo "Configured Copilot guardrails defaults:"
|
|
echo " active_profile_now: $active_profile"
|
|
echo " active_primary_model: $active_primary"
|
|
if [[ ${#active_fallbacks[@]} -gt 0 ]]; then
|
|
echo " active_fallbacks: ${active_fallbacks[*]}"
|
|
else
|
|
echo " active_fallbacks: (none)"
|
|
fi
|
|
echo " paid_profile_primary: $paid_model"
|
|
if [[ ${#paid_fallbacks[@]} -gt 0 ]]; then
|
|
echo " paid_profile_fallbacks: ${paid_fallbacks[*]}"
|
|
else
|
|
echo " paid_profile_fallbacks: (none)"
|
|
fi
|
|
echo " free_profile_primary: $primary_model"
|
|
if [[ ${#free_fallbacks[@]} -gt 0 ]]; then
|
|
echo " free_profile_fallbacks: ${free_fallbacks[*]}"
|
|
else
|
|
echo " free_profile_fallbacks: (none)"
|
|
fi
|
|
|
|
echo ""
|
|
echo "Applied live routing/provider policy and updated config files:"
|
|
echo " $POLICY_CONFIG"
|
|
echo " $BUDGET_CONFIG"
|
|
echo " $AUTH_CONFIG"
|
|
echo " $MODEL_PROFILES_CONFIG"
|
|
echo " $MODEL_SCHEDULE_CONFIG"
|