Remove openclaw-setup-copilot files
This commit is contained in:
parent
e0ef96e753
commit
fb924a7fa6
@ -1,125 +0,0 @@
|
||||
# AGENTS.md - Work Machine Setup Template
|
||||
|
||||
This folder is now a setup template for another computer.
|
||||
|
||||
Important:
|
||||
- Do not run install or config mutation commands on this current machine.
|
||||
- Use these docs as the source of truth when setting up your work computer.
|
||||
|
||||
## Mode Toggle (Read First)
|
||||
|
||||
Current mode in this copy:
|
||||
- `LOCAL-SAFETY MODE` is active (no install/mutation on this machine).
|
||||
|
||||
When you copy this folder to the target work machine:
|
||||
1. Remove or rewrite the `LOCAL-SAFETY MODE` block below.
|
||||
2. Uncomment the `TARGET-INSTALL MODE` block.
|
||||
3. Follow `docs/operations/WORK_SETUP_CHECKLIST.md`.
|
||||
|
||||
### LOCAL-SAFETY MODE (ACTIVE NOW)
|
||||
|
||||
- Do not run install commands here.
|
||||
- Do not run `scripts/install_copilot_guardrails.sh` here.
|
||||
- Do not run `scripts/finalize_copilot_setup.sh` here.
|
||||
- Do not run provider/model mutation commands here.
|
||||
- Use this copy only for editing docs/scripts before transfer.
|
||||
|
||||
<!--
|
||||
### TARGET-INSTALL MODE
|
||||
|
||||
- This folder is now running on the intended target work machine.
|
||||
- You are allowed to run install and mutation commands in this workspace.
|
||||
|
||||
Required setup order:
|
||||
1. `bash ./setup/setup_openclaw_copilot.sh`
|
||||
2. `bash ./scripts/finalize_copilot_setup.sh`
|
||||
3. `openclaw status --deep`
|
||||
|
||||
Required verification:
|
||||
- `openclaw models status` shows `github-copilot/*` routing.
|
||||
- LaunchAgents loaded:
|
||||
- `ai.openclaw.model-budget-guard`
|
||||
- `ai.openclaw.copilot-policy-guard`
|
||||
- `ai.openclaw.copilot-auth-watchdog`
|
||||
- `ai.openclaw.copilot-model-schedule-guard`
|
||||
- Schedule behavior:
|
||||
- `08:00-18:00` local -> `paid` profile
|
||||
- `18:00-08:00` local -> `free` profile
|
||||
-->
|
||||
|
||||
## Goal
|
||||
|
||||
Create a reliable OpenClaw setup that uses GitHub Copilot CLI Enterprise as the main provider, with a Senior iOS Engineer persona focused on architecture and refactoring.
|
||||
|
||||
## Setup Flow (target computer only)
|
||||
|
||||
1. Run `setup/setup_openclaw_copilot.sh` for Copilot-first baseline.
|
||||
2. Run `scripts/finalize_copilot_setup.sh` to complete auth + profiles + guardrails in one command.
|
||||
3. Verify gateway/model health.
|
||||
|
||||
## Copilot CLI Install and Auth
|
||||
|
||||
```bash
|
||||
# preferred for latest models
|
||||
brew install copilot-cli@prerelease
|
||||
# or
|
||||
npm install -g @github/copilot-cli
|
||||
|
||||
copilot auth login
|
||||
copilot auth status
|
||||
```
|
||||
|
||||
## OpenClaw Copilot Model Routing
|
||||
|
||||
### If your OpenClaw supports `config patch`
|
||||
|
||||
Use your allowlist patch workflow.
|
||||
|
||||
### If your OpenClaw does NOT support `config patch`
|
||||
|
||||
Use direct commands:
|
||||
|
||||
```bash
|
||||
openclaw models set github-copilot/claude-sonnet-4.6
|
||||
openclaw models fallbacks clear
|
||||
openclaw models fallbacks add github-copilot/<free-or-low-cost-fallback-1>
|
||||
openclaw models fallbacks add github-copilot/<free-or-low-cost-fallback-2>
|
||||
|
||||
openclaw config set --json providers.github-copilot.enabled true
|
||||
openclaw config set --json providers.openai.enabled false
|
||||
openclaw config set --json providers.anthropic.enabled false
|
||||
openclaw config set --json providers.openrouter.enabled false
|
||||
```
|
||||
|
||||
## Session Startup Routine
|
||||
|
||||
1. Read `docs/context/SOUL.md`
|
||||
2. Read `docs/context/USER.md`
|
||||
3. Read `memory/YYYY-MM-DD.md` (today and yesterday)
|
||||
4. In direct owner chat, also read `docs/context/MEMORY.md`
|
||||
5. Run health check:
|
||||
|
||||
```bash
|
||||
openclaw status --deep
|
||||
openclaw models status
|
||||
```
|
||||
|
||||
Model guard should be active on target machine:
|
||||
|
||||
```bash
|
||||
launchctl print gui/$(id -u)/ai.openclaw.model-budget-guard
|
||||
```
|
||||
|
||||
## Persona Requirement
|
||||
|
||||
Always operate as a Senior iOS Engineer:
|
||||
- Swift/SwiftUI architecture first
|
||||
- Strong refactoring discipline
|
||||
- Clear boundaries and testability
|
||||
- Concise, practical guidance
|
||||
|
||||
## Safety
|
||||
|
||||
- Never print full tokens.
|
||||
- Never post externally without explicit instruction.
|
||||
- Prefer non-destructive actions.
|
||||
@ -1,41 +0,0 @@
|
||||
# PRD: OpenClaw Copilot Workstation Setup
|
||||
|
||||
## Purpose
|
||||
Provide a repeatable, low-risk setup for a Copilot-only OpenClaw workstation with cost controls and schedule-based model routing.
|
||||
|
||||
## Goals
|
||||
- Enforce `github-copilot/*` as the only provider/model family.
|
||||
- Use paid profile during work hours (`08:00-18:00` local).
|
||||
- Use free/low-cost profile off-hours (`18:00-08:00` local).
|
||||
- Auto-heal routing/policy drift via launchd guardrails.
|
||||
- Keep installation easy for AI-assisted and human-assisted setup.
|
||||
|
||||
## Scope
|
||||
- Config-driven profile routing (`config/model-profiles.config.json`).
|
||||
- Schedule guard (`scripts/model_schedule_guard.sh` + launchd).
|
||||
- Budget guard, policy guard, auth watchdog.
|
||||
- Staged launchd runtime under:
|
||||
- `~/Library/Application Support/openclaw-copilot-guard`
|
||||
|
||||
## Non-Goals
|
||||
- Supporting non-Copilot providers in normal operation.
|
||||
- Cross-platform install workflows (target is macOS).
|
||||
- Billing automation outside model/provider selection.
|
||||
|
||||
## Required Outcomes
|
||||
- `scripts/install_copilot_guardrails.sh` installs all 4 guards:
|
||||
- `ai.openclaw.model-budget-guard`
|
||||
- `ai.openclaw.copilot-policy-guard`
|
||||
- `ai.openclaw.copilot-auth-watchdog`
|
||||
- `ai.openclaw.copilot-model-schedule-guard`
|
||||
- Off-hours traffic defaults to free/low-cost profile.
|
||||
- Work-hours defaults return to paid profile.
|
||||
- Docs clearly describe setup, verification, troubleshooting.
|
||||
|
||||
## Success Criteria
|
||||
- Fresh machine setup succeeds using:
|
||||
- `setup/setup_openclaw_copilot.sh`
|
||||
- `copilot auth login`
|
||||
- `bash ./scripts/install_copilot_guardrails.sh`
|
||||
- `openclaw models status` reflects expected profile by local time window.
|
||||
- Launchd checks/logs show healthy runs and no repeated hard failures.
|
||||
@ -1,340 +0,0 @@
|
||||
# OpenClaw Work Setup Template
|
||||
|
||||
Template workspace for setting up OpenClaw on a work computer with GitHub Copilot CLI Enterprise as the primary model provider, plus an iOS-focused assistant persona.
|
||||
|
||||
## Purpose
|
||||
|
||||
Use this folder as a repeatable setup baseline on another machine.
|
||||
|
||||
- Primary provider: GitHub Copilot Enterprise
|
||||
- Persona: Senior iOS Engineer (architecture + refactoring focused)
|
||||
|
||||
## Important
|
||||
|
||||
This repository is intended as a guide/template.
|
||||
Do not run mutation commands on an already-stable machine unless intended.
|
||||
|
||||
## Workspace Layout
|
||||
|
||||
Keep only operator docs at root:
|
||||
- `AGENTS.md`
|
||||
- `README.md`
|
||||
- `PRD.md`
|
||||
|
||||
Everything else is organized by purpose:
|
||||
- `setup/`: one-time bootstrap installers
|
||||
- `scripts/`: guardrails, switching, and launchd installers
|
||||
- `config/`: editable policy/profile/schedule/auth JSON
|
||||
- `docs/context/`: runtime persona + user/context files
|
||||
- `docs/operations/`: handoff/checklist/troubleshooting docs
|
||||
- `memory/`: session memory files
|
||||
|
||||
## Workspace Files
|
||||
|
||||
- `AGENTS.md`: operating rules and setup sequence
|
||||
- `docs/context/BOOT.md`: startup checks
|
||||
- `docs/context/SOUL.md`: persona behavior
|
||||
- `docs/context/USER.md`: user context
|
||||
- `docs/context/TOOLS.md`: command reference
|
||||
- `docs/operations/troubleshooting.md`: failure recovery runbook
|
||||
- `setup/setup_openclaw_copilot.sh`: primary Copilot Enterprise setup
|
||||
- `scripts/finalize_copilot_setup.sh`: one-command auth + model/profile + guardrail finalize
|
||||
- `config/copilot-policy-guard.config.json`: Copilot routing/provider policy guard config
|
||||
- `config/copilot-auth-watchdog.config.json`: Copilot auth watchdog config
|
||||
- `config/model-profiles.config.json`: paid/free profile routing definitions
|
||||
- `config/model-schedule.config.json`: work-hours/off-hours schedule config
|
||||
- `scripts/install_copilot_guardrails.sh`: installs all launchd guardrails
|
||||
|
||||
## Target Machine Prerequisites
|
||||
|
||||
- macOS with Homebrew
|
||||
- Node.js + npm
|
||||
- OpenClaw installed
|
||||
- GitHub Enterprise account with Copilot CLI entitlement
|
||||
|
||||
## Telegram Note (If Used)
|
||||
|
||||
- You do **not** need a new Telegram user account or phone number.
|
||||
- One personal Telegram account is enough.
|
||||
- Create a bot with `@BotFather`; bots are separate from user accounts.
|
||||
- You chat with the bot from your existing Telegram account.
|
||||
|
||||
## Setup (Target Computer)
|
||||
|
||||
AI handoff:
|
||||
- Use `docs/operations/AI_SETUP_HANDOFF.md` when delegating setup to another AI assistant.
|
||||
- It includes a strict prompt, command order, and verification checks.
|
||||
|
||||
Quick path (copy/paste):
|
||||
|
||||
```bash
|
||||
bash ./setup/setup_openclaw_copilot.sh
|
||||
bash ./scripts/finalize_copilot_setup.sh
|
||||
openclaw status --deep
|
||||
```
|
||||
|
||||
Optional custom install locations:
|
||||
|
||||
```bash
|
||||
# Move OpenClaw runtime data (~/.openclaw) to another path
|
||||
OPENCLAW_DATA_TARGET=/Volumes/Data/openclaw-copilot bash ./setup/setup_openclaw_copilot.sh
|
||||
|
||||
# Install global npm CLIs into a custom prefix/bin
|
||||
NPM_GLOBAL_PREFIX="$HOME/.npm-global" bash ./setup/setup_openclaw_copilot.sh
|
||||
|
||||
# Use both
|
||||
OPENCLAW_DATA_TARGET=/Volumes/Data/openclaw-copilot \
|
||||
NPM_GLOBAL_PREFIX="$HOME/.npm-global" \
|
||||
bash ./setup/setup_openclaw_copilot.sh
|
||||
```
|
||||
|
||||
Order and dependency (important):
|
||||
|
||||
1. `setup/setup_openclaw_copilot.sh` installs tooling only (`openclaw`, `copilot`, Node).
|
||||
2. `scripts/finalize_copilot_setup.sh` handles login, model discovery, profile selection, guardrails, hooks, and gateway restart.
|
||||
3. If browser auth cannot complete inside finalize flow, run `copilot auth login`, then rerun finalize.
|
||||
|
||||
Why:
|
||||
- OpenClaw installation does not require Copilot login.
|
||||
- `github-copilot/*` model selection and policy checks do require Copilot auth.
|
||||
- Finalize script prevents users from forgetting model/profile/guardrail wiring after login.
|
||||
|
||||
|
||||
1. Primary Copilot setup:
|
||||
|
||||
```bash
|
||||
bash ./setup/setup_openclaw_copilot.sh
|
||||
```
|
||||
|
||||
If using a custom location:
|
||||
|
||||
```bash
|
||||
OPENCLAW_DATA_TARGET=/Volumes/Data/openclaw-copilot bash ./setup/setup_openclaw_copilot.sh
|
||||
```
|
||||
|
||||
2. Finalize in one command (recommended):
|
||||
|
||||
```bash
|
||||
bash ./scripts/finalize_copilot_setup.sh
|
||||
```
|
||||
|
||||
What finalize does:
|
||||
- Opens `copilot auth login` if needed
|
||||
- Refreshes model catalog
|
||||
- Prompts you to choose the paid profile model from non-free candidates
|
||||
- Auto-picks the free profile model/fallbacks from low-cost candidates
|
||||
- Installs all guardrails
|
||||
- Enables recommended hooks
|
||||
- Restarts gateway
|
||||
|
||||
3. Verify:
|
||||
|
||||
```bash
|
||||
copilot auth status
|
||||
openclaw models status
|
||||
openclaw status --deep
|
||||
```
|
||||
|
||||
If login is missing/expired, finalize will prompt for login and retry setup.
|
||||
|
||||
For unattended/non-interactive automation:
|
||||
|
||||
```bash
|
||||
PROMPT_FOR_PAID_MODEL=false bash ./scripts/finalize_copilot_setup.sh
|
||||
```
|
||||
|
||||
### How To Choose Copilot Models (and Why It Matters)
|
||||
|
||||
Choosing the right primary and fallback models is important because it controls:
|
||||
|
||||
- Response speed in chat/Telegram
|
||||
- Quality of coding/refactoring output
|
||||
- Enterprise quota burn and cost exposure
|
||||
- Reliability when one model is rate-limited or unavailable
|
||||
|
||||
Fallback policy for this template:
|
||||
- Fallbacks must be free-tier or lowest-cost models only.
|
||||
- Do not use premium fallbacks (for example Opus-class) as defaults.
|
||||
- In strict Copilot-only mode, "free" means no extra external provider billing and lowest-burn models in your enterprise seat.
|
||||
|
||||
Recommended strategy:
|
||||
|
||||
1. Set a fast, general coding model as `primary` for daily work.
|
||||
2. Add 1-2 low-cost fallbacks only.
|
||||
3. Keep names exactly as shown in `openclaw models list` to avoid unknown-model failures.
|
||||
|
||||
Selection guide:
|
||||
|
||||
- Fast default: choose the quickest Sonnet/Codex variant your seat exposes
|
||||
- Prefer fast/cheap variants over premium models for fallback
|
||||
- Avoid premium fallbacks as defaults to prevent silent quota drain
|
||||
|
||||
4. Set Copilot model routing:
|
||||
|
||||
```bash
|
||||
openclaw models set github-copilot/claude-sonnet-4.6
|
||||
openclaw models fallbacks clear
|
||||
openclaw models fallbacks add github-copilot/<free-or-low-cost-fallback-1>
|
||||
openclaw models fallbacks add github-copilot/<free-or-low-cost-fallback-2>
|
||||
```
|
||||
|
||||
After setting this, always verify:
|
||||
|
||||
```bash
|
||||
openclaw models status
|
||||
```
|
||||
|
||||
You should see a Copilot model as default plus your fallback chain.
|
||||
|
||||
5. Optional strict provider lock (if policy requires strict allowlist):
|
||||
|
||||
```bash
|
||||
openclaw config set --json providers.github-copilot.enabled true
|
||||
openclaw config set --json providers.openai.enabled false
|
||||
openclaw config set --json providers.anthropic.enabled false
|
||||
openclaw config set --json providers.openrouter.enabled false
|
||||
```
|
||||
|
||||
6. Restart and verify:
|
||||
|
||||
```bash
|
||||
openclaw gateway restart
|
||||
openclaw status --deep
|
||||
openclaw models status
|
||||
```
|
||||
|
||||
7. Configure + install Copilot guardrails (recommended):
|
||||
|
||||
```bash
|
||||
bash ./scripts/install_copilot_guardrails.sh
|
||||
```
|
||||
|
||||
Guard config files:
|
||||
- `config/model-budget-guard.config.json`
|
||||
- `config/copilot-policy-guard.config.json`
|
||||
- `config/copilot-auth-watchdog.config.json`
|
||||
- `config/model-profiles.config.json`
|
||||
- `config/model-schedule.config.json`
|
||||
|
||||
Runtime staging note (important):
|
||||
- Installer stages active guard scripts/config into:
|
||||
- `~/Library/Application Support/openclaw-copilot-guard`
|
||||
- Launchd runs guards from that staged folder, not directly from repository path.
|
||||
- After editing guard scripts/config in this repo, re-run:
|
||||
- `bash ./scripts/install_copilot_guardrails.sh`
|
||||
to sync staged runtime files.
|
||||
|
||||
What it does:
|
||||
- Model budget guard: warns on high-cost model usage and auto-reverts to low-cost model
|
||||
- Copilot policy guard: enforces Copilot-only provider/model policy and fixes drift
|
||||
- Copilot auth watchdog: alerts when Copilot auth expires or becomes unhealthy
|
||||
- Model schedule guard: uses paid profile during work hours and free profile off-hours
|
||||
|
||||
Why this is important:
|
||||
- Prevents someone from staying on high-tier models all day
|
||||
- Reduces accidental enterprise quota burn
|
||||
- Keeps day-to-day latency faster with a low-cost default
|
||||
|
||||
Default schedule behavior:
|
||||
- Work hours (`08:00` to `18:00` local time): `paid` profile
|
||||
- Off-hours (`18:00` to `08:00` local time): `free` profile
|
||||
|
||||
Schedule wiring (important):
|
||||
- `config/model-schedule.config.json` controls time windows and which script applies profiles.
|
||||
- `switchScript` should be `./scripts/model_profile_switch.sh`.
|
||||
- `config/model-profiles.config.json` defines what `paid` and `free` actually mean (primary + fallbacks).
|
||||
|
||||
Expected schedule config shape:
|
||||
|
||||
```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"
|
||||
}
|
||||
```
|
||||
|
||||
Important behavior difference vs Max:
|
||||
- Copilot `config/model-profiles.config.json` starts with empty model IDs in this template copy.
|
||||
- `bash ./scripts/finalize_copilot_setup.sh` (or `bash ./scripts/install_copilot_guardrails.sh`) runs `configure_copilot_guardrails_defaults.sh`, detects available `github-copilot/*` models, and writes concrete `paid`/`free` models into `config/model-profiles.config.json`.
|
||||
- Until that step runs on the target machine (after `copilot auth login`), schedule/profile switching has no concrete model IDs to apply.
|
||||
|
||||
Quick verify commands:
|
||||
|
||||
```bash
|
||||
cat config/model-schedule.config.json
|
||||
cat config/model-profiles.config.json
|
||||
```
|
||||
|
||||
After installing guardrails, verify staged runtime files used by launchd:
|
||||
|
||||
```bash
|
||||
cat ~/Library/Application\ Support/openclaw-copilot-guard/model-schedule.config.json
|
||||
cat ~/Library/Application\ Support/openclaw-copilot-guard/model-profiles.config.json
|
||||
```
|
||||
|
||||
Manual profile switch:
|
||||
|
||||
```bash
|
||||
bash ./scripts/model_profile_switch.sh paid
|
||||
bash ./scripts/model_profile_switch.sh free
|
||||
bash ./scripts/model_profile_switch.sh status
|
||||
```
|
||||
|
||||
If you need a quick switch without sending a live `/model` message:
|
||||
|
||||
```bash
|
||||
bash ./scripts/model_profile_switch.sh free --no-live
|
||||
```
|
||||
|
||||
8. Enable recommended hooks:
|
||||
|
||||
```bash
|
||||
openclaw hooks enable boot-md
|
||||
openclaw hooks enable command-logger
|
||||
openclaw hooks enable session-memory
|
||||
```
|
||||
|
||||
9. Verify hooks:
|
||||
|
||||
```bash
|
||||
openclaw hooks list
|
||||
openclaw hooks list --eligible
|
||||
```
|
||||
|
||||
## Daily Checks
|
||||
|
||||
```bash
|
||||
openclaw status --deep
|
||||
openclaw models status
|
||||
copilot auth status
|
||||
```
|
||||
|
||||
If responses feel slow or weak, re-check model routing first before debugging gateway/network.
|
||||
|
||||
Model guard health:
|
||||
|
||||
```bash
|
||||
launchctl print gui/$(id -u)/ai.openclaw.model-budget-guard
|
||||
launchctl print gui/$(id -u)/ai.openclaw.copilot-policy-guard
|
||||
launchctl print gui/$(id -u)/ai.openclaw.copilot-auth-watchdog
|
||||
launchctl print gui/$(id -u)/ai.openclaw.copilot-model-schedule-guard
|
||||
tail -n 30 /tmp/openclaw-model-budget-guard.log /tmp/openclaw-model-budget-guard.err.log
|
||||
tail -n 30 /tmp/openclaw-copilot-policy-guard.log /tmp/openclaw-copilot-policy-guard.err.log
|
||||
tail -n 30 /tmp/openclaw-copilot-auth-watchdog.log /tmp/openclaw-copilot-auth-watchdog.err.log
|
||||
tail -n 30 /tmp/openclaw-copilot-model-schedule-guard.log /tmp/openclaw-copilot-model-schedule-guard.err.log
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
Start with `docs/operations/troubleshooting.md`.
|
||||
|
||||
## Security Notes
|
||||
|
||||
- Never print full API keys/tokens in logs or chat.
|
||||
- Rotate any secret that may have been exposed.
|
||||
@ -1,7 +0,0 @@
|
||||
{
|
||||
"enabled": false,
|
||||
"sessionKey": "agent:main:main",
|
||||
"minAlertIntervalMinutes": 30,
|
||||
"stateFile": "~/.openclaw/copilot-auth-watchdog-state.json",
|
||||
"checkOpenClawModelStatus": true
|
||||
}
|
||||
@ -1,18 +0,0 @@
|
||||
{
|
||||
"enabled": false,
|
||||
"autoFix": true,
|
||||
"sessionKey": "agent:main:main",
|
||||
"minAlertIntervalMinutes": 20,
|
||||
"stateFile": "~/.openclaw/copilot-policy-guard-state.json",
|
||||
"requiredPrimaryPrefix": "github-copilot/",
|
||||
"requiredFallbackPrefix": "github-copilot/",
|
||||
"desiredPrimaryModel": "",
|
||||
"enforceFallbackAllowlist": true,
|
||||
"allowedFallbacks": [],
|
||||
"providerPolicy": {
|
||||
"github-copilot": true,
|
||||
"openai": false,
|
||||
"anthropic": false,
|
||||
"openrouter": false
|
||||
}
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
{
|
||||
"enabled": false,
|
||||
"sessionKey": "agent:main:main",
|
||||
"lowModel": "",
|
||||
"highModels": [],
|
||||
"warnAfterMinutes": 2,
|
||||
"revertAfterMinutes": 45,
|
||||
"minWarnIntervalMinutes": 20,
|
||||
"stateFile": "~/.openclaw/model-budget-guard-state.json"
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
{
|
||||
"profiles": {
|
||||
"paid": {
|
||||
"description": "Work-hours higher-quality Copilot model",
|
||||
"primary": "",
|
||||
"fallbacks": [],
|
||||
"providerPolicy": {
|
||||
"github-copilot": true,
|
||||
"openai": false,
|
||||
"anthropic": false,
|
||||
"openrouter": false
|
||||
}
|
||||
},
|
||||
"free": {
|
||||
"description": "Off-hours low-cost/free Copilot model stack",
|
||||
"primary": "",
|
||||
"fallbacks": [],
|
||||
"providerPolicy": {
|
||||
"github-copilot": true,
|
||||
"openai": false,
|
||||
"anthropic": false,
|
||||
"openrouter": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
@ -1,72 +0,0 @@
|
||||
# docs/context/BOOT.md - Target Computer Startup
|
||||
|
||||
This boot file is for the work machine, not this machine.
|
||||
|
||||
## 1) Verify services
|
||||
|
||||
```bash
|
||||
openclaw status --deep
|
||||
```
|
||||
|
||||
## 2) Verify Copilot auth
|
||||
|
||||
```bash
|
||||
copilot auth status
|
||||
```
|
||||
|
||||
If not authenticated:
|
||||
|
||||
```bash
|
||||
copilot auth login
|
||||
```
|
||||
|
||||
## 3) Verify model routing
|
||||
|
||||
```bash
|
||||
openclaw models list
|
||||
openclaw models status
|
||||
```
|
||||
|
||||
Expected state:
|
||||
- Active/default model is a `github-copilot/*` model.
|
||||
- Fallbacks are `github-copilot/*` models only.
|
||||
|
||||
## 4) Gateway and channels
|
||||
|
||||
```bash
|
||||
openclaw gateway restart
|
||||
openclaw status --deep
|
||||
```
|
||||
|
||||
Confirm channel health shows `OK`.
|
||||
|
||||
## 5) Model budget guardrail
|
||||
|
||||
```bash
|
||||
launchctl print gui/$(id -u)/ai.openclaw.model-budget-guard
|
||||
```
|
||||
|
||||
If missing, install on target machine:
|
||||
|
||||
```bash
|
||||
bash ./scripts/install_copilot_guardrails.sh
|
||||
```
|
||||
|
||||
## 6) Copilot policy/auth guardrails
|
||||
|
||||
```bash
|
||||
launchctl print gui/$(id -u)/ai.openclaw.copilot-policy-guard
|
||||
launchctl print gui/$(id -u)/ai.openclaw.copilot-auth-watchdog
|
||||
launchctl print gui/$(id -u)/ai.openclaw.copilot-model-schedule-guard
|
||||
```
|
||||
|
||||
Schedule expectation:
|
||||
- `08:00-18:00` local: `paid` profile
|
||||
- `18:00-08:00` local: `free` profile
|
||||
|
||||
## 7) Hook status
|
||||
|
||||
```bash
|
||||
openclaw hooks list
|
||||
openclaw hooks list --eligible
|
||||
```
|
||||
@ -1,10 +0,0 @@
|
||||
# docs/context/HEARTBEAT.md
|
||||
|
||||
Run these checks only on the target work machine:
|
||||
|
||||
- `openclaw status --deep`
|
||||
- `openclaw models status`
|
||||
- `copilot auth status`
|
||||
|
||||
If all healthy and no action needed, reply:
|
||||
`HEARTBEAT_OK`
|
||||
@ -1,9 +0,0 @@
|
||||
# docs/context/IDENTITY.md - Runtime Identity
|
||||
|
||||
- Name: ClawCraft iOS
|
||||
- Role: Senior iOS Engineer assistant
|
||||
- Vibe: concise, pragmatic, architecture-first
|
||||
- Focus: Swift, SwiftUI, refactoring, testing, clean architecture
|
||||
- Signature: [ios-arch]
|
||||
|
||||
Primary mission is engineering execution quality, not generic assistant chatter.
|
||||
@ -1,22 +0,0 @@
|
||||
# docs/context/MEMORY.md - Long-Term Context
|
||||
|
||||
## User Context
|
||||
|
||||
- Owner: Matt Bruce
|
||||
- Prefers practical command-level guidance
|
||||
- Wants local-first efficiency and provider flexibility
|
||||
|
||||
## Technical Direction
|
||||
|
||||
- Primary target: OpenClaw + GitHub Copilot CLI Enterprise on work machine
|
||||
- Keep response quality tuned for iOS architecture and refactoring work
|
||||
|
||||
## Known Lessons
|
||||
|
||||
- Tool-capability mismatches can break Telegram/chat flows.
|
||||
- Session resets (`/new`) reduce latency and token burn.
|
||||
- Stable gateway launch settings and log paths matter on macOS.
|
||||
|
||||
## Scope Guardrail
|
||||
|
||||
This repository is now a setup guide template. Avoid changing runtime config on this current machine unless explicitly requested.
|
||||
@ -1,22 +0,0 @@
|
||||
# docs/context/SOUL.md - Operating Profile
|
||||
|
||||
You are a Senior iOS Engineer assistant.
|
||||
|
||||
## Standards
|
||||
|
||||
- Be concrete and technically correct.
|
||||
- Refactor toward clarity, maintainability, and testability.
|
||||
- Prefer modern Swift and SwiftUI patterns.
|
||||
- Keep explanations short unless deeper detail is requested.
|
||||
|
||||
## Decision Style
|
||||
|
||||
- Recommend practical paths.
|
||||
- State tradeoffs clearly.
|
||||
- Verify assumptions with evidence before advising.
|
||||
|
||||
## Boundaries
|
||||
|
||||
- Do not expose secrets.
|
||||
- Ask before external actions.
|
||||
- Avoid destructive operations without explicit approval.
|
||||
@ -1,121 +0,0 @@
|
||||
# docs/context/TOOLS.md - Command Reference (Target Work Machine)
|
||||
|
||||
## GitHub Copilot CLI (Enterprise)
|
||||
|
||||
Primary setup script:
|
||||
|
||||
```bash
|
||||
bash ./setup/setup_openclaw_copilot.sh
|
||||
```
|
||||
|
||||
Custom locations (optional):
|
||||
|
||||
```bash
|
||||
OPENCLAW_DATA_TARGET=/Volumes/Data/openclaw-copilot bash ./setup/setup_openclaw_copilot.sh
|
||||
NPM_GLOBAL_PREFIX="$HOME/.npm-global" bash ./setup/setup_openclaw_copilot.sh
|
||||
```
|
||||
|
||||
Install:
|
||||
|
||||
```bash
|
||||
brew install copilot-cli@prerelease
|
||||
# or
|
||||
npm install -g @github/copilot-cli
|
||||
```
|
||||
|
||||
Authenticate:
|
||||
|
||||
```bash
|
||||
copilot auth login
|
||||
copilot auth status
|
||||
```
|
||||
|
||||
## OpenClaw Model Discovery and Routing
|
||||
|
||||
```bash
|
||||
openclaw models refresh
|
||||
openclaw models list
|
||||
openclaw models status
|
||||
```
|
||||
|
||||
If `models refresh` is unavailable on your OpenClaw version, use `openclaw models list` and continue.
|
||||
|
||||
## Lock to Copilot-only providers
|
||||
|
||||
Preferred (if supported):
|
||||
- `openclaw config patch '{...}'`
|
||||
|
||||
Fallback command style:
|
||||
|
||||
```bash
|
||||
openclaw config set --json providers.github-copilot.enabled true
|
||||
openclaw config set --json providers.openai.enabled false
|
||||
openclaw config set --json providers.anthropic.enabled false
|
||||
openclaw config set --json providers.openrouter.enabled false
|
||||
```
|
||||
|
||||
## Health and Logs
|
||||
|
||||
```bash
|
||||
openclaw status --deep
|
||||
openclaw logs --follow
|
||||
openclaw gateway restart
|
||||
```
|
||||
|
||||
## Model Budget Guardrail (Target Machine)
|
||||
|
||||
Files:
|
||||
- `config/model-budget-guard.config.json`
|
||||
- `config/copilot-policy-guard.config.json`
|
||||
- `config/copilot-auth-watchdog.config.json`
|
||||
- `scripts/model_budget_guard.sh`
|
||||
- `scripts/copilot_policy_guard.sh`
|
||||
- `scripts/copilot_auth_watchdog.sh`
|
||||
- `scripts/install_model_budget_guard_launchd.sh`
|
||||
- `scripts/install_copilot_policy_guard_launchd.sh`
|
||||
- `scripts/install_copilot_auth_watchdog_launchd.sh`
|
||||
- `scripts/configure_copilot_guardrails_defaults.sh`
|
||||
- `scripts/install_copilot_guardrails.sh`
|
||||
|
||||
Configure + install:
|
||||
|
||||
```bash
|
||||
bash ./scripts/install_copilot_guardrails.sh
|
||||
```
|
||||
|
||||
This command also runs:
|
||||
|
||||
```bash
|
||||
bash ./scripts/configure_copilot_guardrails_defaults.sh
|
||||
```
|
||||
|
||||
Verify:
|
||||
|
||||
```bash
|
||||
launchctl print gui/$(id -u)/ai.openclaw.model-budget-guard
|
||||
launchctl print gui/$(id -u)/ai.openclaw.copilot-policy-guard
|
||||
launchctl print gui/$(id -u)/ai.openclaw.copilot-auth-watchdog
|
||||
tail -n 30 /tmp/openclaw-model-budget-guard.log /tmp/openclaw-model-budget-guard.err.log
|
||||
tail -n 30 /tmp/openclaw-copilot-policy-guard.log /tmp/openclaw-copilot-policy-guard.err.log
|
||||
tail -n 30 /tmp/openclaw-copilot-auth-watchdog.log /tmp/openclaw-copilot-auth-watchdog.err.log
|
||||
```
|
||||
|
||||
Behavior:
|
||||
- warns when expensive model stays active
|
||||
- auto-switches back to configured low model
|
||||
- auto-fixes Copilot-only provider/model policy drift
|
||||
- alerts on Copilot auth problems
|
||||
|
||||
## Hooks (Recommended)
|
||||
|
||||
```bash
|
||||
openclaw hooks enable boot-md
|
||||
openclaw hooks enable command-logger
|
||||
openclaw hooks enable session-memory
|
||||
openclaw hooks list
|
||||
```
|
||||
|
||||
## Security
|
||||
|
||||
- Never paste full API keys or bot tokens into chat/logs.
|
||||
- Rotate secrets if exposed.
|
||||
@ -1,38 +0,0 @@
|
||||
# docs/context/USER.md - Human Context
|
||||
|
||||
- Name: `<user-name>`
|
||||
- Preferred name: `<preferred-name>`
|
||||
- Current objective: Copilot-only OpenClaw setup on work machine
|
||||
|
||||
## Priorities
|
||||
|
||||
- OpenClaw reliability
|
||||
- Copilot Enterprise auth and model routing
|
||||
- Senior iOS engineering persona quality
|
||||
- Clear fallback strategy for provider/model issues
|
||||
|
||||
## Working Style
|
||||
|
||||
- Wants direct fixes and exact commands
|
||||
- Comfortable in terminal workflows
|
||||
- Values architecture/refactor quality over shortcuts
|
||||
|
||||
## Secrets Inputs (Placeholders Only)
|
||||
|
||||
Use this section for setup prompts. Do not commit real secrets.
|
||||
|
||||
- `COPILOT_AUTH`: browser login required (`copilot auth login`) — no static token stored here
|
||||
- `TELEGRAM_BOT_TOKEN`: `<set-at-runtime-if-using-telegram>`
|
||||
- `OPENCLAW_GATEWAY_TOKEN`: `<generated-by-openclaw-rotate-if-exposed>`
|
||||
|
||||
## Setup Prompt Checklist
|
||||
|
||||
When assisting a new user, prompt for:
|
||||
|
||||
1. Whether Telegram is needed (`yes/no`)
|
||||
2. If yes, ask for `TELEGRAM_BOT_TOKEN` at runtime
|
||||
3. Confirm Copilot browser login is completed
|
||||
|
||||
Telegram clarification:
|
||||
- Existing personal Telegram account is sufficient.
|
||||
- No new phone number/user account is needed to run a bot.
|
||||
@ -1,62 +0,0 @@
|
||||
# docs/operations/AI_SETUP_HANDOFF.md
|
||||
|
||||
Give this file to an AI agent on the target machine.
|
||||
|
||||
## Copy/Paste Prompt For AI
|
||||
|
||||
```text
|
||||
You are setting up this folder on this machine as a strict Copilot-only OpenClaw install.
|
||||
|
||||
Follow docs/operations/WORK_SETUP_CHECKLIST.md exactly.
|
||||
Also follow the constraints below:
|
||||
|
||||
Constraints:
|
||||
- Do NOT use Ollama.
|
||||
- Do NOT enable non-Copilot providers.
|
||||
- Use only github-copilot/* models.
|
||||
- Fallbacks must be free/low-cost models only.
|
||||
- Run commands in order and stop after each step to report status.
|
||||
- If a step fails, provide exact fix and retry.
|
||||
|
||||
Run in this order:
|
||||
1) bash ./setup/setup_openclaw_copilot.sh
|
||||
2) bash ./scripts/finalize_copilot_setup.sh
|
||||
- if prompted, pause and wait for user to complete browser login
|
||||
- if prompted for paid model, show model list to user and ask user to choose
|
||||
3) openclaw status --deep
|
||||
|
||||
After setup, verify and report:
|
||||
- copilot auth status is healthy
|
||||
- openclaw models status shows github-copilot/* primary
|
||||
- fallbacks are low-cost only
|
||||
- providers.github-copilot.enabled = true
|
||||
- providers.openai.enabled = false
|
||||
- providers.anthropic.enabled = false
|
||||
- providers.openrouter.enabled = false
|
||||
- launchd guards running:
|
||||
- ai.openclaw.model-budget-guard
|
||||
- ai.openclaw.copilot-policy-guard
|
||||
- ai.openclaw.copilot-auth-watchdog
|
||||
- ai.openclaw.copilot-model-schedule-guard
|
||||
- schedule policy:
|
||||
- paid profile during 08:00-18:00 local
|
||||
- free profile during 18:00-08:00 local
|
||||
- hooks enabled:
|
||||
- boot-md
|
||||
- command-logger
|
||||
- session-memory
|
||||
|
||||
If any check fails, fix it and rerun verification.
|
||||
```
|
||||
|
||||
## Manual User Actions Expected
|
||||
|
||||
- Complete enterprise login in browser when `finalize_copilot_setup.sh` opens `copilot auth login`.
|
||||
- Approve enterprise/SSO/MFA prompts as required.
|
||||
|
||||
## One-Line Start (for human)
|
||||
|
||||
```bash
|
||||
# open this file and copy the prompt to your AI agent
|
||||
cat docs/operations/AI_SETUP_HANDOFF.md
|
||||
```
|
||||
@ -1,244 +0,0 @@
|
||||
# docs/operations/WORK_SETUP_CHECKLIST.md
|
||||
|
||||
Use this checklist on the **target work computer only**.
|
||||
Do not run these mutation steps on your current stable machine.
|
||||
|
||||
## 0) Open terminal in this folder
|
||||
|
||||
```bash
|
||||
cd /path/to/openclaw-setup
|
||||
```
|
||||
|
||||
If delegating to another AI assistant, point it to:
|
||||
- `docs/operations/AI_SETUP_HANDOFF.md`
|
||||
|
||||
## 1) Preflight
|
||||
|
||||
```bash
|
||||
uname -a
|
||||
sw_vers
|
||||
which brew || echo "brew missing"
|
||||
which node || echo "node missing"
|
||||
which npm || echo "npm missing"
|
||||
```
|
||||
|
||||
## 2) Primary Copilot setup
|
||||
|
||||
```bash
|
||||
bash ./setup/setup_openclaw_copilot.sh
|
||||
```
|
||||
|
||||
Optional custom locations:
|
||||
|
||||
```bash
|
||||
OPENCLAW_DATA_TARGET=/Volumes/Data/openclaw-copilot bash ./setup/setup_openclaw_copilot.sh
|
||||
NPM_GLOBAL_PREFIX="$HOME/.npm-global" bash ./setup/setup_openclaw_copilot.sh
|
||||
```
|
||||
|
||||
Verify:
|
||||
|
||||
```bash
|
||||
which openclaw
|
||||
openclaw --version
|
||||
which copilot
|
||||
copilot --version
|
||||
```
|
||||
|
||||
## 3) Finalize setup in one command (recommended)
|
||||
|
||||
```bash
|
||||
bash ./scripts/finalize_copilot_setup.sh
|
||||
```
|
||||
|
||||
Expected:
|
||||
- Authenticated with enterprise-linked account
|
||||
- Copilot models discovered
|
||||
- If prompted, paid profile model selected from non-free candidates
|
||||
- Guardrails installed + running
|
||||
- Hooks enabled (`boot-md`, `command-logger`, `session-memory`)
|
||||
- Gateway restarted
|
||||
|
||||
If this step succeeds, you can skip to step 10.
|
||||
|
||||
## 4) Manual fallback path (advanced, only if step 3 fails)
|
||||
|
||||
If finalize fails due auth/browser flow, run:
|
||||
|
||||
```bash
|
||||
copilot auth login
|
||||
copilot auth status
|
||||
```
|
||||
|
||||
Then continue below.
|
||||
## 5) Start/verify OpenClaw gateway
|
||||
|
||||
```bash
|
||||
openclaw gateway restart
|
||||
openclaw status --deep
|
||||
```
|
||||
|
||||
Expected: gateway reachable.
|
||||
|
||||
## 6) Discover available models
|
||||
|
||||
```bash
|
||||
openclaw models refresh || true
|
||||
openclaw models list
|
||||
openclaw models status
|
||||
```
|
||||
|
||||
Note: If `models refresh` is unsupported in your version, ignore and continue.
|
||||
|
||||
How to choose from the list:
|
||||
|
||||
- Pick a fast daily model as primary (usually Sonnet-Fast or Codex-Fast style naming).
|
||||
- Pick only free-tier or lowest-cost fallbacks.
|
||||
- Avoid premium fallbacks (Opus-class) in default routing.
|
||||
- Keep model IDs exact from `openclaw models list`.
|
||||
|
||||
Why this matters:
|
||||
|
||||
- Better latency for normal work
|
||||
- Better quality when complexity spikes
|
||||
- Lower quota burn by not overusing heavyweight models
|
||||
- Better uptime through fallback failover
|
||||
|
||||
## 7) Set Copilot primary + fallbacks
|
||||
|
||||
Replace models below with names from your `openclaw models list` output if needed.
|
||||
|
||||
```bash
|
||||
openclaw models set github-copilot/claude-sonnet-4.6
|
||||
openclaw models fallbacks clear
|
||||
openclaw models fallbacks add github-copilot/<free-or-low-cost-fallback-1>
|
||||
openclaw models fallbacks add github-copilot/<free-or-low-cost-fallback-2>
|
||||
```
|
||||
|
||||
Verify:
|
||||
|
||||
```bash
|
||||
openclaw models status
|
||||
```
|
||||
|
||||
Expected:
|
||||
|
||||
- Default model is your fast daily Copilot model
|
||||
- Fallback chain includes only free-tier or low-cost models
|
||||
- Only `github-copilot/*` appears if strict enterprise policy is required
|
||||
|
||||
## 8) Optional strict provider lock (Copilot-only)
|
||||
|
||||
Run if your enterprise policy requires only Copilot provider traffic:
|
||||
|
||||
```bash
|
||||
openclaw config set --json providers.github-copilot.enabled true
|
||||
openclaw config set --json providers.openai.enabled false
|
||||
openclaw config set --json providers.anthropic.enabled false
|
||||
openclaw config set --json providers.openrouter.enabled false
|
||||
openclaw gateway restart
|
||||
```
|
||||
|
||||
Verify:
|
||||
|
||||
```bash
|
||||
openclaw models list
|
||||
openclaw models status
|
||||
```
|
||||
|
||||
## 9) Configure + install Copilot guardrails (recommended)
|
||||
|
||||
If step 3 already succeeded, this is already done.
|
||||
|
||||
This one command auto-detects your available Copilot models, asks for paid model selection (interactive), picks low-cost free defaults, applies policy, and installs launchd guards:
|
||||
|
||||
```bash
|
||||
bash ./scripts/install_copilot_guardrails.sh
|
||||
```
|
||||
|
||||
For unattended/non-interactive automation:
|
||||
|
||||
```bash
|
||||
PROMPT_FOR_PAID_MODEL=false bash ./scripts/install_copilot_guardrails.sh
|
||||
```
|
||||
|
||||
3. Verify:
|
||||
|
||||
```bash
|
||||
launchctl print gui/$(id -u)/ai.openclaw.model-budget-guard
|
||||
launchctl print gui/$(id -u)/ai.openclaw.copilot-policy-guard
|
||||
launchctl print gui/$(id -u)/ai.openclaw.copilot-auth-watchdog
|
||||
launchctl print gui/$(id -u)/ai.openclaw.copilot-model-schedule-guard
|
||||
tail -n 30 /tmp/openclaw-model-budget-guard.log /tmp/openclaw-model-budget-guard.err.log
|
||||
tail -n 30 /tmp/openclaw-copilot-policy-guard.log /tmp/openclaw-copilot-policy-guard.err.log
|
||||
tail -n 30 /tmp/openclaw-copilot-auth-watchdog.log /tmp/openclaw-copilot-auth-watchdog.err.log
|
||||
tail -n 30 /tmp/openclaw-copilot-model-schedule-guard.log /tmp/openclaw-copilot-model-schedule-guard.err.log
|
||||
```
|
||||
|
||||
Why this matters:
|
||||
|
||||
- User gets prompted when high model remains active
|
||||
- Session is auto-switched back to lower model after timeout
|
||||
- Copilot-only policy is auto-corrected if drift occurs
|
||||
- Expired Copilot auth is surfaced quickly
|
||||
- Work-hours/off-hours profile schedule is auto-enforced
|
||||
- Protects enterprise quota and keeps routine latency low
|
||||
|
||||
## 10) Enable recommended hooks
|
||||
|
||||
If step 3 already succeeded, hooks were already enabled.
|
||||
|
||||
```bash
|
||||
openclaw hooks enable boot-md
|
||||
openclaw hooks enable command-logger
|
||||
openclaw hooks enable session-memory
|
||||
openclaw hooks list
|
||||
```
|
||||
|
||||
## 11) Persona/startup docs sanity
|
||||
|
||||
Confirm these files exist in workspace root:
|
||||
|
||||
```bash
|
||||
ls -la AGENTS.md docs/context/BOOT.md docs/context/SOUL.md docs/context/IDENTITY.md docs/context/USER.md docs/context/TOOLS.md docs/operations/troubleshooting.md
|
||||
```
|
||||
|
||||
## 12) Telegram/channel check (if used)
|
||||
|
||||
Telegram account rule:
|
||||
- No new personal Telegram account is required.
|
||||
- Use your existing Telegram account and create a bot via `@BotFather`.
|
||||
- Bot token is the only setup secret needed for Telegram.
|
||||
|
||||
```bash
|
||||
openclaw status --deep
|
||||
```
|
||||
|
||||
Expected: channel `OK` and gateway reachable.
|
||||
|
||||
## 13) First chat checks
|
||||
|
||||
In your chat surface:
|
||||
|
||||
1. `/new`
|
||||
2. Ask: `reply with your active model and one sentence`
|
||||
|
||||
Expected: response is fast and uses a `github-copilot/*` model.
|
||||
|
||||
## 14) Daily operations
|
||||
|
||||
```bash
|
||||
openclaw status --deep
|
||||
openclaw models status
|
||||
copilot auth status
|
||||
```
|
||||
|
||||
## 15) Fast failure recovery
|
||||
|
||||
```bash
|
||||
openclaw gateway restart
|
||||
openclaw logs --follow
|
||||
openclaw models status
|
||||
copilot auth status
|
||||
```
|
||||
|
||||
If still blocked, use `docs/operations/troubleshooting.md`.
|
||||
@ -1,210 +0,0 @@
|
||||
# Troubleshooting - OpenClaw + Copilot Enterprise (Target Machine)
|
||||
|
||||
This guide is for the work computer setup.
|
||||
|
||||
## Quick triage
|
||||
|
||||
```bash
|
||||
openclaw status --deep
|
||||
openclaw models status
|
||||
copilot auth status
|
||||
```
|
||||
|
||||
## 1) Copilot auth failure
|
||||
|
||||
Symptoms:
|
||||
- `copilot auth status` not authenticated
|
||||
- prompts fail with auth errors
|
||||
|
||||
Fix:
|
||||
|
||||
```bash
|
||||
copilot auth login
|
||||
copilot auth status
|
||||
```
|
||||
|
||||
Make sure browser login uses the enterprise-linked GitHub account.
|
||||
|
||||
## 2) No Copilot model entitlement
|
||||
|
||||
Symptoms:
|
||||
- auth is valid but model usage denied
|
||||
|
||||
Cause:
|
||||
- org/enterprise policy does not permit Copilot CLI
|
||||
|
||||
Fix:
|
||||
- ask enterprise admin to enable Copilot CLI entitlement for your seat
|
||||
|
||||
## 3) `Unknown model` in OpenClaw
|
||||
|
||||
Fix:
|
||||
|
||||
```bash
|
||||
openclaw models refresh
|
||||
openclaw models list
|
||||
```
|
||||
|
||||
If `models refresh` is missing in your version, skip it and use `openclaw models list`.
|
||||
|
||||
## 4) OpenClaw `config patch` not available
|
||||
|
||||
Symptoms:
|
||||
- guide references `openclaw config patch`, command not found
|
||||
|
||||
Fix:
|
||||
- use `openclaw config set --json <path> <value>` commands instead
|
||||
- use `openclaw models set` and `openclaw models fallbacks` commands for routing
|
||||
|
||||
## 5) Still seeing non-Copilot providers
|
||||
|
||||
Fix provider toggles:
|
||||
|
||||
```bash
|
||||
openclaw config set --json providers.github-copilot.enabled true
|
||||
openclaw config set --json providers.openai.enabled false
|
||||
openclaw config set --json providers.anthropic.enabled false
|
||||
openclaw config set --json providers.openrouter.enabled false
|
||||
openclaw gateway restart
|
||||
```
|
||||
|
||||
## 6) Telegram/channel slow responses
|
||||
|
||||
Fixes:
|
||||
- start a fresh session with `/new`
|
||||
- keep default model on a fast Copilot model
|
||||
- verify gateway health and provider auth
|
||||
|
||||
```bash
|
||||
openclaw status --deep
|
||||
openclaw models status
|
||||
```
|
||||
|
||||
Telegram account confusion:
|
||||
- You do not need a second Telegram user account.
|
||||
- Keep your current personal Telegram account.
|
||||
- Create/connect only the bot token from `@BotFather`.
|
||||
|
||||
## 7) Cost and quota management
|
||||
|
||||
- Use fast/cheap Copilot model as default
|
||||
- Use `/new` for unrelated tasks
|
||||
- Use compaction if available in your OpenClaw build
|
||||
|
||||
## 8) Wrong model mix (common quality/latency issue)
|
||||
|
||||
Symptoms:
|
||||
- Replies are slow even when gateway is healthy
|
||||
- Refactor suggestions are weak/inconsistent
|
||||
- Quota burns too fast for routine tasks
|
||||
|
||||
Cause:
|
||||
- Heavy reasoning model set as default for all prompts
|
||||
- No fallback diversity (all fast or all heavy)
|
||||
|
||||
Fix:
|
||||
1. Set a fast default model for day-to-day requests.
|
||||
2. Keep fallbacks free-tier or low-cost only.
|
||||
3. Remove premium fallback models from default routing.
|
||||
|
||||
Then verify:
|
||||
|
||||
```bash
|
||||
openclaw models list
|
||||
openclaw models status
|
||||
```
|
||||
|
||||
## 9) High-model guardrail not prompting or not switching back
|
||||
|
||||
Checks:
|
||||
|
||||
```bash
|
||||
launchctl print gui/$(id -u)/ai.openclaw.model-budget-guard
|
||||
tail -n 100 /tmp/openclaw-model-budget-guard.log /tmp/openclaw-model-budget-guard.err.log
|
||||
cat config/model-budget-guard.config.json
|
||||
cat ~/Library/Application\ Support/openclaw-copilot-guard/model-budget-guard.config.json
|
||||
```
|
||||
|
||||
Common fixes:
|
||||
- Wrong model IDs in `highModels`
|
||||
- Use exact IDs from `openclaw models list`.
|
||||
- `lowModel` not available
|
||||
- Set to a model your seat can access.
|
||||
- Guard not installed/loaded
|
||||
- Re-run `bash ./scripts/install_copilot_guardrails.sh`.
|
||||
- Repo config changed but staged runtime is stale
|
||||
- Re-run `bash ./scripts/install_copilot_guardrails.sh` to re-stage current files.
|
||||
- Session key mismatch
|
||||
- Keep `sessionKey` as `agent:main:main` unless you intentionally use another session.
|
||||
|
||||
## 10) Copilot policy guard keeps fixing config unexpectedly
|
||||
|
||||
Cause:
|
||||
- `config/copilot-policy-guard.config.json` has `autoFix=true` and policy values that differ from your manual changes.
|
||||
|
||||
Fix:
|
||||
1. Edit `config/copilot-policy-guard.config.json`.
|
||||
2. Set your intended `providerPolicy`, `desiredPrimaryModel`, and fallback rules.
|
||||
3. If you want alert-only mode, set `autoFix` to `false`.
|
||||
|
||||
Verify logs:
|
||||
|
||||
```bash
|
||||
tail -n 100 /tmp/openclaw-copilot-policy-guard.log /tmp/openclaw-copilot-policy-guard.err.log
|
||||
cat ~/Library/Application\ Support/openclaw-copilot-guard/copilot-policy-guard.config.json
|
||||
```
|
||||
|
||||
## 11) Manual model switch keeps changing back
|
||||
|
||||
Cause:
|
||||
- Schedule guard is enabled and re-applies the expected profile by time window.
|
||||
- `08:00-18:00` -> paid profile
|
||||
- `18:00-08:00` -> free profile
|
||||
|
||||
Checks:
|
||||
|
||||
```bash
|
||||
launchctl print gui/$(id -u)/ai.openclaw.copilot-model-schedule-guard
|
||||
tail -n 100 /tmp/openclaw-copilot-model-schedule-guard.log /tmp/openclaw-copilot-model-schedule-guard.err.log
|
||||
cat config/model-schedule.config.json
|
||||
cat config/model-profiles.config.json
|
||||
cat ~/Library/Application\ Support/openclaw-copilot-guard/model-schedule.config.json
|
||||
cat ~/Library/Application\ Support/openclaw-copilot-guard/model-profiles.config.json
|
||||
```
|
||||
|
||||
Fixes:
|
||||
- If behavior is correct, do nothing (schedule is enforcing policy).
|
||||
- To temporarily hold a manual switch, disable schedule in `config/model-schedule.config.json` and re-run:
|
||||
- `bash ./scripts/install_copilot_guardrails.sh`
|
||||
- For a quick manual switch without live push:
|
||||
- `bash ./scripts/model_profile_switch.sh free --no-live`
|
||||
- If `profiles.paid.primary` / `profiles.free.primary` are empty, run:
|
||||
- `bash ./scripts/finalize_copilot_setup.sh`
|
||||
This populates `config/model-profiles.config.json` and restages launchd runtime configs.
|
||||
|
||||
## 12) Copilot auth watchdog alerting repeatedly
|
||||
|
||||
Checks:
|
||||
|
||||
```bash
|
||||
copilot auth status
|
||||
openclaw models status --check
|
||||
cat config/copilot-auth-watchdog.config.json
|
||||
cat ~/Library/Application\ Support/openclaw-copilot-guard/copilot-auth-watchdog.config.json
|
||||
```
|
||||
|
||||
Fixes:
|
||||
- Re-authenticate with `copilot auth login`.
|
||||
- Increase `minAlertIntervalMinutes` in `config/copilot-auth-watchdog.config.json`.
|
||||
- If needed, temporarily disable watchdog by setting `enabled` to `false`.
|
||||
|
||||
## 13) Recommended hooks not active
|
||||
|
||||
Enable and verify:
|
||||
|
||||
```bash
|
||||
openclaw hooks enable boot-md
|
||||
openclaw hooks enable command-logger
|
||||
openclaw hooks enable session-memory
|
||||
openclaw hooks list
|
||||
```
|
||||
@ -1,371 +0,0 @@
|
||||
#!/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) ]]
|
||||
}
|
||||
|
||||
prompt_for_paid_model() {
|
||||
local default_model="$1"
|
||||
shift
|
||||
local -a candidates=("$@")
|
||||
|
||||
if [[ ${#candidates[@]} -eq 0 ]]; then
|
||||
echo "$default_model"
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "" >&2
|
||||
echo "[configure-guardrails] Choose paid profile primary model (non-free candidates):" >&2
|
||||
for i in "${!candidates[@]}"; do
|
||||
local n=$((i + 1))
|
||||
local marker=""
|
||||
if [[ "${candidates[$i]}" == "$default_model" ]]; then
|
||||
marker=" (default)"
|
||||
fi
|
||||
printf " %d) %s%s\n" "$n" "${candidates[$i]}" "$marker" >&2
|
||||
done
|
||||
printf "Select number [default=%s]: " "$default_model" >&2
|
||||
|
||||
local choice=""
|
||||
if ! IFS= read -r choice; then
|
||||
echo "" >&2
|
||||
echo "$default_model"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ -z "$choice" ]]; then
|
||||
echo "$default_model"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ "$choice" =~ ^[0-9]+$ ]]; then
|
||||
local idx=$((choice - 1))
|
||||
if (( idx >= 0 && idx < ${#candidates[@]} )); then
|
||||
echo "${candidates[$idx]}"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "[configure-guardrails] Invalid selection '$choice'; using default: $default_model" >&2
|
||||
echo "$default_model"
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
prompt_mode="${PROMPT_FOR_PAID_MODEL:-auto}"
|
||||
interactive_picker="false"
|
||||
case "$prompt_mode" in
|
||||
auto)
|
||||
if [[ -t 0 && -t 1 ]]; then
|
||||
interactive_picker="true"
|
||||
fi
|
||||
;;
|
||||
true|yes|1)
|
||||
interactive_picker="true"
|
||||
;;
|
||||
false|no|0)
|
||||
interactive_picker="false"
|
||||
;;
|
||||
*)
|
||||
echo "[configure-guardrails] Invalid PROMPT_FOR_PAID_MODEL='$prompt_mode' (expected auto/true/false). Using auto." >&2
|
||||
if [[ -t 0 && -t 1 ]]; then
|
||||
interactive_picker="true"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ "$interactive_picker" == "true" && ( ! -t 0 || ! -t 1 ) ]]; then
|
||||
echo "[configure-guardrails] PROMPT_FOR_PAID_MODEL enabled but no interactive TTY; falling back to auto selection." >&2
|
||||
interactive_picker="false"
|
||||
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
|
||||
|
||||
if [[ "$interactive_picker" == "true" && ${#high_models[@]} -gt 0 ]]; then
|
||||
paid_model="$(prompt_for_paid_model "$paid_model" "${high_models[@]}")"
|
||||
echo "[configure-guardrails] selected paid model: $paid_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"
|
||||
@ -1,134 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
CONFIG_PATH="${AUTH_WATCHDOG_CONFIG:-$ROOT_DIR/config/copilot-auth-watchdog.config.json}"
|
||||
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
echo "[copilot-auth-watchdog] jq is required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
expand_tilde() {
|
||||
local p="$1"
|
||||
if [[ "$p" == "~" ]]; then
|
||||
echo "$HOME"
|
||||
elif [[ "$p" == "~/"* ]]; then
|
||||
echo "$HOME/${p#~/}"
|
||||
else
|
||||
echo "$p"
|
||||
fi
|
||||
}
|
||||
|
||||
now_ms() {
|
||||
python3 - <<'PY'
|
||||
import time
|
||||
print(int(time.time()*1000))
|
||||
PY
|
||||
}
|
||||
|
||||
send_notice() {
|
||||
local text="$1"
|
||||
local last_channel="$2"
|
||||
local last_to="$3"
|
||||
|
||||
if [[ -z "$last_channel" || -z "$last_to" ]]; then
|
||||
echo "[copilot-auth-watchdog] notice skipped (missing channel/target): $text" >&2
|
||||
return 0
|
||||
fi
|
||||
|
||||
local target="$last_to"
|
||||
if [[ "$target" == *:* ]]; then
|
||||
target="${target#*:}"
|
||||
fi
|
||||
|
||||
openclaw message send \
|
||||
--channel "$last_channel" \
|
||||
--target "$target" \
|
||||
--message "$text" \
|
||||
>/dev/null 2>&1 || true
|
||||
}
|
||||
|
||||
if [[ ! -f "$CONFIG_PATH" ]]; then
|
||||
echo "[copilot-auth-watchdog] missing config: $CONFIG_PATH" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
enabled="$(jq -r '.enabled // true' "$CONFIG_PATH")"
|
||||
if [[ "$enabled" != "true" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
session_key="$(jq -r '.sessionKey // "agent:main:main"' "$CONFIG_PATH")"
|
||||
alert_interval_min="$(jq -r '.minAlertIntervalMinutes // 30' "$CONFIG_PATH")"
|
||||
state_file_raw="$(jq -r '.stateFile // "~/.openclaw/copilot-auth-watchdog-state.json"' "$CONFIG_PATH")"
|
||||
state_file="$(expand_tilde "$state_file_raw")"
|
||||
check_model_status="$(jq -r '.checkOpenClawModelStatus // true' "$CONFIG_PATH")"
|
||||
|
||||
state_dir="${OPENCLAW_STATE_DIR:-$HOME/.openclaw}"
|
||||
sessions_file="$state_dir/agents/main/sessions/sessions.json"
|
||||
last_channel=""
|
||||
last_to=""
|
||||
if [[ -r "$sessions_file" ]]; then
|
||||
session_json="$(jq -c --arg key "$session_key" '.[$key] // {}' "$sessions_file" 2>/dev/null || true)"
|
||||
if [[ -n "$session_json" && "$session_json" != "{}" ]]; then
|
||||
last_channel="$(jq -r '.lastChannel // empty' <<<"$session_json")"
|
||||
last_to="$(jq -r '.lastTo // empty' <<<"$session_json")"
|
||||
fi
|
||||
fi
|
||||
|
||||
declare -a issues=()
|
||||
|
||||
if ! command -v copilot >/dev/null 2>&1; then
|
||||
issues+=("copilot CLI not installed; run setup_openclaw_copilot.sh")
|
||||
else
|
||||
set +e
|
||||
copilot_out="$(copilot auth status 2>&1)"
|
||||
copilot_rc=$?
|
||||
set -e
|
||||
|
||||
if [[ $copilot_rc -ne 0 ]]; then
|
||||
issues+=("copilot auth status failed; run: copilot auth login")
|
||||
elif echo "$copilot_out" | grep -Eiq 'not authenticated|not logged|login required|expired|unauthorized|invalid'; then
|
||||
issues+=("copilot auth not healthy; run: copilot auth login")
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$check_model_status" == "true" ]]; then
|
||||
if ! command -v openclaw >/dev/null 2>&1; then
|
||||
issues+=("openclaw CLI not installed")
|
||||
else
|
||||
if ! openclaw models status --check >/dev/null 2>&1; then
|
||||
issues+=("openclaw model auth check failed; run: openclaw models status --json and copilot auth login")
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ ${#issues[@]} -eq 0 ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
mkdir -p "$(dirname "$state_file")"
|
||||
if [[ ! -f "$state_file" ]]; then
|
||||
printf '{}\n' > "$state_file"
|
||||
fi
|
||||
|
||||
state_json="$(cat "$state_file")"
|
||||
last_alert_at="$(jq -r --arg key "$session_key" '.[$key].lastAlertAt // 0' <<<"$state_json")"
|
||||
if [[ ! "$last_alert_at" =~ ^[0-9]+$ ]]; then
|
||||
last_alert_at=0
|
||||
fi
|
||||
|
||||
now="$(now_ms)"
|
||||
interval_ms=$((alert_interval_min * 60 * 1000))
|
||||
if (( now - last_alert_at < interval_ms )); then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
summary="Copilot auth watchdog alert: ${issues[*]}"
|
||||
echo "[copilot-auth-watchdog] $summary"
|
||||
send_notice "$summary" "$last_channel" "$last_to"
|
||||
|
||||
state_json="$(jq --arg key "$session_key" --argjson now "$now" '.[$key].lastAlertAt=$now' <<<"$state_json")"
|
||||
printf '%s\n' "$state_json" > "$state_file"
|
||||
@ -1,235 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
CONFIG_PATH="${POLICY_GUARD_CONFIG:-$ROOT_DIR/config/copilot-policy-guard.config.json}"
|
||||
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
echo "[copilot-policy-guard] jq is required" >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! command -v openclaw >/dev/null 2>&1; then
|
||||
echo "[copilot-policy-guard] openclaw CLI is required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
expand_tilde() {
|
||||
local p="$1"
|
||||
if [[ "$p" == "~" ]]; then
|
||||
echo "$HOME"
|
||||
elif [[ "$p" == "~/"* ]]; then
|
||||
echo "$HOME/${p#~/}"
|
||||
else
|
||||
echo "$p"
|
||||
fi
|
||||
}
|
||||
|
||||
now_ms() {
|
||||
python3 - <<'PY'
|
||||
import time
|
||||
print(int(time.time()*1000))
|
||||
PY
|
||||
}
|
||||
|
||||
send_notice() {
|
||||
local text="$1"
|
||||
local last_channel="$2"
|
||||
local last_to="$3"
|
||||
|
||||
if [[ -z "$last_channel" || -z "$last_to" ]]; then
|
||||
echo "[copilot-policy-guard] notice skipped (missing channel/target): $text" >&2
|
||||
return 0
|
||||
fi
|
||||
|
||||
local target="$last_to"
|
||||
if [[ "$target" == *:* ]]; then
|
||||
target="${target#*:}"
|
||||
fi
|
||||
|
||||
openclaw message send \
|
||||
--channel "$last_channel" \
|
||||
--target "$target" \
|
||||
--message "$text" \
|
||||
>/dev/null 2>&1 || true
|
||||
}
|
||||
|
||||
if [[ ! -f "$CONFIG_PATH" ]]; then
|
||||
echo "[copilot-policy-guard] missing config: $CONFIG_PATH" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
enabled="$(jq -r '.enabled // true' "$CONFIG_PATH")"
|
||||
if [[ "$enabled" != "true" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
auto_fix="$(jq -r '.autoFix // true' "$CONFIG_PATH")"
|
||||
session_key="$(jq -r '.sessionKey // "agent:main:main"' "$CONFIG_PATH")"
|
||||
alert_interval_min="$(jq -r '.minAlertIntervalMinutes // 20' "$CONFIG_PATH")"
|
||||
state_file_raw="$(jq -r '.stateFile // "~/.openclaw/copilot-policy-guard-state.json"' "$CONFIG_PATH")"
|
||||
state_file="$(expand_tilde "$state_file_raw")"
|
||||
required_primary_prefix="$(jq -r '.requiredPrimaryPrefix // "github-copilot/"' "$CONFIG_PATH")"
|
||||
required_fallback_prefix="$(jq -r '.requiredFallbackPrefix // "github-copilot/"' "$CONFIG_PATH")"
|
||||
desired_primary_model="$(jq -r '.desiredPrimaryModel // empty' "$CONFIG_PATH")"
|
||||
enforce_allowlist="$(jq -r '.enforceFallbackAllowlist // false' "$CONFIG_PATH")"
|
||||
|
||||
state_dir="${OPENCLAW_STATE_DIR:-$HOME/.openclaw}"
|
||||
sessions_file="$state_dir/agents/main/sessions/sessions.json"
|
||||
last_channel=""
|
||||
last_to=""
|
||||
if [[ -r "$sessions_file" ]]; then
|
||||
session_json="$(jq -c --arg key "$session_key" '.[$key] // {}' "$sessions_file" 2>/dev/null || true)"
|
||||
if [[ -n "$session_json" && "$session_json" != "{}" ]]; then
|
||||
last_channel="$(jq -r '.lastChannel // empty' <<<"$session_json")"
|
||||
last_to="$(jq -r '.lastTo // empty' <<<"$session_json")"
|
||||
fi
|
||||
fi
|
||||
|
||||
declare -a issues=()
|
||||
declare -a fixes=()
|
||||
|
||||
models_json="$(openclaw models status --json 2>/dev/null || true)"
|
||||
if [[ -z "$models_json" ]]; then
|
||||
issues+=("openclaw models status --json failed")
|
||||
else
|
||||
default_model="$(jq -r '.defaultModel // empty' <<<"$models_json")"
|
||||
|
||||
if [[ -n "$default_model" && "$default_model" != ${required_primary_prefix}* ]]; then
|
||||
issues+=("primary model is not Copilot: $default_model")
|
||||
if [[ "$auto_fix" == "true" && -n "$desired_primary_model" ]]; then
|
||||
if openclaw models set "$desired_primary_model" >/dev/null 2>&1; then
|
||||
fixes+=("set primary model to $desired_primary_model")
|
||||
else
|
||||
issues+=("failed to set primary model to $desired_primary_model")
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
fallbacks=()
|
||||
while IFS= read -r fb; do
|
||||
[[ -z "$fb" ]] && continue
|
||||
fallbacks+=("$fb")
|
||||
done < <(jq -r '.fallbacks[]? // empty' <<<"$models_json")
|
||||
bad_prefix=0
|
||||
bad_allowlist=0
|
||||
for fb in "${fallbacks[@]}"; do
|
||||
if [[ "$fb" != ${required_fallback_prefix}* ]]; then
|
||||
bad_prefix=1
|
||||
issues+=("fallback is not Copilot: $fb")
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ "$enforce_allowlist" == "true" ]]; then
|
||||
allowed_fallbacks=()
|
||||
while IFS= read -r af; do
|
||||
[[ -z "$af" ]] && continue
|
||||
allowed_fallbacks+=("$af")
|
||||
done < <(jq -r '.allowedFallbacks[]? // empty' "$CONFIG_PATH")
|
||||
if [[ ${#allowed_fallbacks[@]} -gt 0 ]]; then
|
||||
for fb in "${fallbacks[@]}"; do
|
||||
found=0
|
||||
for af in "${allowed_fallbacks[@]}"; do
|
||||
if [[ "$fb" == "$af" ]]; then
|
||||
found=1
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [[ $found -eq 0 ]]; then
|
||||
bad_allowlist=1
|
||||
issues+=("fallback not in allowlist: $fb")
|
||||
fi
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$auto_fix" == "true" && ( $bad_prefix -eq 1 || $bad_allowlist -eq 1 ) ]]; then
|
||||
desired_fallbacks=()
|
||||
|
||||
if [[ "$enforce_allowlist" == "true" ]]; then
|
||||
while IFS= read -r af; do
|
||||
[[ -n "$af" ]] && desired_fallbacks+=("$af")
|
||||
done < <(jq -r '.allowedFallbacks[]? // empty' "$CONFIG_PATH")
|
||||
fi
|
||||
|
||||
if [[ ${#desired_fallbacks[@]} -eq 0 ]]; then
|
||||
for fb in "${fallbacks[@]}"; do
|
||||
if [[ "$fb" == ${required_fallback_prefix}* ]]; then
|
||||
desired_fallbacks+=("$fb")
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
if openclaw models fallbacks clear >/dev/null 2>&1; then
|
||||
fixes+=("cleared fallback list")
|
||||
for fb in "${desired_fallbacks[@]}"; do
|
||||
if openclaw models fallbacks add "$fb" >/dev/null 2>&1; then
|
||||
fixes+=("added fallback $fb")
|
||||
else
|
||||
issues+=("failed to add fallback $fb")
|
||||
fi
|
||||
done
|
||||
else
|
||||
issues+=("failed to clear fallback list")
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
while IFS='=' read -r provider desired; do
|
||||
[[ -z "$provider" ]] && continue
|
||||
desired_bool="false"
|
||||
if [[ "$desired" == "true" ]]; then
|
||||
desired_bool="true"
|
||||
fi
|
||||
|
||||
current="$(openclaw config get "providers.${provider}.enabled" 2>/dev/null || true)"
|
||||
current_trim="$(printf '%s' "$current" | tr -d '[:space:]')"
|
||||
|
||||
if [[ "$current_trim" != "$desired_bool" ]]; then
|
||||
issues+=("provider policy drift: providers.${provider}.enabled expected ${desired_bool}, got ${current_trim:-unset}")
|
||||
if [[ "$auto_fix" == "true" ]]; then
|
||||
if openclaw config set --json "providers.${provider}.enabled" "$desired_bool" >/dev/null 2>&1; then
|
||||
fixes+=("set providers.${provider}.enabled=${desired_bool}")
|
||||
else
|
||||
issues+=("failed to set providers.${provider}.enabled=${desired_bool}")
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done < <(jq -r '.providerPolicy // {} | to_entries[] | "\(.key)=\(.value)"' "$CONFIG_PATH")
|
||||
|
||||
if [[ ${#issues[@]} -eq 0 && ${#fixes[@]} -eq 0 ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
mkdir -p "$(dirname "$state_file")"
|
||||
if [[ ! -f "$state_file" ]]; then
|
||||
printf '{}\n' > "$state_file"
|
||||
fi
|
||||
|
||||
state_json="$(cat "$state_file")"
|
||||
last_alert_at="$(jq -r --arg key "$session_key" '.[$key].lastAlertAt // 0' <<<"$state_json")"
|
||||
if [[ ! "$last_alert_at" =~ ^[0-9]+$ ]]; then
|
||||
last_alert_at=0
|
||||
fi
|
||||
|
||||
now="$(now_ms)"
|
||||
interval_ms=$((alert_interval_min * 60 * 1000))
|
||||
should_alert="false"
|
||||
if (( now - last_alert_at >= interval_ms )); then
|
||||
should_alert="true"
|
||||
fi
|
||||
|
||||
summary=""
|
||||
if [[ ${#fixes[@]} -gt 0 ]]; then
|
||||
summary="Copilot policy guard auto-fixed: ${fixes[*]}"
|
||||
else
|
||||
summary="Copilot policy drift detected: ${issues[*]}"
|
||||
fi
|
||||
|
||||
echo "[copilot-policy-guard] $summary"
|
||||
|
||||
if [[ "$should_alert" == "true" ]]; then
|
||||
send_notice "$summary" "$last_channel" "$last_to"
|
||||
state_json="$(jq --arg key "$session_key" --argjson now "$now" '.[$key].lastAlertAt=$now' <<<"$state_json")"
|
||||
printf '%s\n' "$state_json" > "$state_file"
|
||||
fi
|
||||
@ -1,100 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
log_info() { echo "[finalize] $*"; }
|
||||
log_warn() { echo "[finalize] WARN: $*" >&2; }
|
||||
log_err() { echo "[finalize] ERROR: $*" >&2; }
|
||||
|
||||
require_cmd() {
|
||||
local cmd="$1"
|
||||
if ! command -v "$cmd" >/dev/null 2>&1; then
|
||||
log_err "Missing required command: $cmd"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
copilot_authed() {
|
||||
copilot auth status >/dev/null 2>&1
|
||||
}
|
||||
|
||||
refresh_models() {
|
||||
openclaw models refresh >/dev/null 2>&1 || true
|
||||
}
|
||||
|
||||
copilot_model_count() {
|
||||
openclaw models list 2>/dev/null \
|
||||
| awk 'NR>1 && /^github-copilot\// {count++} END {print count+0}'
|
||||
}
|
||||
|
||||
require_cmd openclaw
|
||||
require_cmd copilot
|
||||
require_cmd jq
|
||||
|
||||
if ! copilot_authed; then
|
||||
log_warn "Copilot auth is not active."
|
||||
if [[ -t 0 ]]; then
|
||||
log_info "Launching interactive login: copilot auth login"
|
||||
copilot auth login
|
||||
else
|
||||
log_err "Non-interactive shell cannot complete browser login."
|
||||
log_err "Run: copilot auth login"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! copilot_authed; then
|
||||
log_err "Copilot auth still not active after login."
|
||||
log_err "Run 'copilot auth status' and resolve auth issues, then retry."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "Refreshing model catalog..."
|
||||
refresh_models
|
||||
|
||||
count="$(copilot_model_count)"
|
||||
if [[ "$count" -eq 0 ]]; then
|
||||
log_warn "No github-copilot models detected yet. Restarting gateway and retrying once."
|
||||
openclaw gateway restart >/dev/null 2>&1 || true
|
||||
sleep 1
|
||||
refresh_models
|
||||
count="$(copilot_model_count)"
|
||||
fi
|
||||
|
||||
if [[ "$count" -eq 0 ]]; then
|
||||
log_err "No github-copilot models available to configure."
|
||||
log_err "Check entitlement/auth, then rerun this script."
|
||||
openclaw models list || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "Detected $count Copilot model(s). Installing guardrails + model profiles..."
|
||||
bash "$SCRIPT_DIR/install_copilot_guardrails.sh"
|
||||
|
||||
log_info "Enabling recommended hooks..."
|
||||
openclaw hooks enable boot-md >/dev/null 2>&1 || true
|
||||
openclaw hooks enable command-logger >/dev/null 2>&1 || true
|
||||
openclaw hooks enable session-memory >/dev/null 2>&1 || true
|
||||
|
||||
log_info "Restarting gateway..."
|
||||
openclaw gateway restart >/dev/null 2>&1 || true
|
||||
|
||||
log_info "Final status checks:"
|
||||
copilot auth status || true
|
||||
openclaw models status || true
|
||||
|
||||
for label in \
|
||||
ai.openclaw.model-budget-guard \
|
||||
ai.openclaw.copilot-policy-guard \
|
||||
ai.openclaw.copilot-auth-watchdog \
|
||||
ai.openclaw.copilot-model-schedule-guard; do
|
||||
if launchctl print "gui/$(id -u)/$label" >/dev/null 2>&1; then
|
||||
echo "[finalize] launchd OK: $label"
|
||||
else
|
||||
echo "[finalize] launchd WARN: $label not loaded"
|
||||
fi
|
||||
done
|
||||
|
||||
log_info "Done. Copilot setup is fully configured."
|
||||
@ -1,78 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
echo "[install-copilot-auth-watchdog] jq is required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
LABEL="ai.openclaw.copilot-auth-watchdog"
|
||||
PLIST_PATH="$HOME/Library/LaunchAgents/$LABEL.plist"
|
||||
STAGE_DIR="${MODEL_GUARD_STAGE_DIR:-$HOME/Library/Application Support/openclaw-copilot-guard}"
|
||||
STAGE_SCRIPTS_DIR="$STAGE_DIR/scripts"
|
||||
STAGE_CONFIG="$STAGE_DIR/copilot-auth-watchdog.config.json"
|
||||
AUTH_WATCHDOG_CONFIG="${AUTH_WATCHDOG_CONFIG:-$STAGE_CONFIG}"
|
||||
INTERVAL_SECONDS="${INTERVAL_SECONDS:-300}"
|
||||
|
||||
mkdir -p "$STAGE_SCRIPTS_DIR"
|
||||
cp "$ROOT_DIR/scripts/copilot_auth_watchdog.sh" "$STAGE_SCRIPTS_DIR/copilot_auth_watchdog.sh"
|
||||
jq \
|
||||
--arg state "$STAGE_DIR/copilot-auth-watchdog-state.json" \
|
||||
'.stateFile=$state' \
|
||||
"$ROOT_DIR/config/copilot-auth-watchdog.config.json" > "$STAGE_CONFIG"
|
||||
chmod +x "$STAGE_SCRIPTS_DIR/copilot_auth_watchdog.sh"
|
||||
|
||||
cat > "$PLIST_PATH" <<PLIST
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>$LABEL</string>
|
||||
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/bin/bash</string>
|
||||
<string>$STAGE_SCRIPTS_DIR/copilot_auth_watchdog.sh</string>
|
||||
</array>
|
||||
|
||||
<key>EnvironmentVariables</key>
|
||||
<dict>
|
||||
<key>PATH</key>
|
||||
<string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
|
||||
<key>AUTH_WATCHDOG_CONFIG</key>
|
||||
<string>$AUTH_WATCHDOG_CONFIG</string>
|
||||
</dict>
|
||||
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
|
||||
<key>StartInterval</key>
|
||||
<integer>$INTERVAL_SECONDS</integer>
|
||||
|
||||
<key>StandardOutPath</key>
|
||||
<string>/tmp/openclaw-copilot-auth-watchdog.log</string>
|
||||
|
||||
<key>StandardErrorPath</key>
|
||||
<string>/tmp/openclaw-copilot-auth-watchdog.err.log</string>
|
||||
</dict>
|
||||
</plist>
|
||||
PLIST
|
||||
|
||||
launchctl bootout "gui/$(id -u)/$LABEL" 2>/dev/null || true
|
||||
launchctl bootstrap "gui/$(id -u)" "$PLIST_PATH"
|
||||
launchctl kickstart -k "gui/$(id -u)/$LABEL"
|
||||
|
||||
cat <<MSG
|
||||
Installed LaunchAgent:
|
||||
$PLIST_PATH
|
||||
Staged runtime files:
|
||||
$STAGE_DIR
|
||||
|
||||
Check status:
|
||||
launchctl print gui/$(id -u)/$LABEL
|
||||
tail -f /tmp/openclaw-copilot-auth-watchdog.log /tmp/openclaw-copilot-auth-watchdog.err.log
|
||||
MSG
|
||||
@ -1,15 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
# Detect available Copilot models, choose low-cost defaults, and apply policy.
|
||||
bash "$SCRIPT_DIR/configure_copilot_guardrails_defaults.sh"
|
||||
|
||||
# Install launchd jobs.
|
||||
bash "$SCRIPT_DIR/install_model_budget_guard_launchd.sh"
|
||||
bash "$SCRIPT_DIR/install_copilot_policy_guard_launchd.sh"
|
||||
bash "$SCRIPT_DIR/install_copilot_auth_watchdog_launchd.sh"
|
||||
bash "$SCRIPT_DIR/install_model_schedule_guard_launchd.sh"
|
||||
|
||||
echo "All Copilot guardrails configured and installed (budget + policy + auth + schedule)."
|
||||
@ -1,78 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
echo "[install-copilot-policy-guard] jq is required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
LABEL="ai.openclaw.copilot-policy-guard"
|
||||
PLIST_PATH="$HOME/Library/LaunchAgents/$LABEL.plist"
|
||||
STAGE_DIR="${MODEL_GUARD_STAGE_DIR:-$HOME/Library/Application Support/openclaw-copilot-guard}"
|
||||
STAGE_SCRIPTS_DIR="$STAGE_DIR/scripts"
|
||||
STAGE_CONFIG="$STAGE_DIR/copilot-policy-guard.config.json"
|
||||
POLICY_GUARD_CONFIG="${POLICY_GUARD_CONFIG:-$STAGE_CONFIG}"
|
||||
INTERVAL_SECONDS="${INTERVAL_SECONDS:-180}"
|
||||
|
||||
mkdir -p "$STAGE_SCRIPTS_DIR"
|
||||
cp "$ROOT_DIR/scripts/copilot_policy_guard.sh" "$STAGE_SCRIPTS_DIR/copilot_policy_guard.sh"
|
||||
jq \
|
||||
--arg state "$STAGE_DIR/copilot-policy-guard-state.json" \
|
||||
'.stateFile=$state' \
|
||||
"$ROOT_DIR/config/copilot-policy-guard.config.json" > "$STAGE_CONFIG"
|
||||
chmod +x "$STAGE_SCRIPTS_DIR/copilot_policy_guard.sh"
|
||||
|
||||
cat > "$PLIST_PATH" <<PLIST
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>$LABEL</string>
|
||||
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/bin/bash</string>
|
||||
<string>$STAGE_SCRIPTS_DIR/copilot_policy_guard.sh</string>
|
||||
</array>
|
||||
|
||||
<key>EnvironmentVariables</key>
|
||||
<dict>
|
||||
<key>PATH</key>
|
||||
<string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
|
||||
<key>POLICY_GUARD_CONFIG</key>
|
||||
<string>$POLICY_GUARD_CONFIG</string>
|
||||
</dict>
|
||||
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
|
||||
<key>StartInterval</key>
|
||||
<integer>$INTERVAL_SECONDS</integer>
|
||||
|
||||
<key>StandardOutPath</key>
|
||||
<string>/tmp/openclaw-copilot-policy-guard.log</string>
|
||||
|
||||
<key>StandardErrorPath</key>
|
||||
<string>/tmp/openclaw-copilot-policy-guard.err.log</string>
|
||||
</dict>
|
||||
</plist>
|
||||
PLIST
|
||||
|
||||
launchctl bootout "gui/$(id -u)/$LABEL" 2>/dev/null || true
|
||||
launchctl bootstrap "gui/$(id -u)" "$PLIST_PATH"
|
||||
launchctl kickstart -k "gui/$(id -u)/$LABEL"
|
||||
|
||||
cat <<MSG
|
||||
Installed LaunchAgent:
|
||||
$PLIST_PATH
|
||||
Staged runtime files:
|
||||
$STAGE_DIR
|
||||
|
||||
Check status:
|
||||
launchctl print gui/$(id -u)/$LABEL
|
||||
tail -f /tmp/openclaw-copilot-policy-guard.log /tmp/openclaw-copilot-policy-guard.err.log
|
||||
MSG
|
||||
@ -1,78 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
echo "[install-model-budget-guard] jq is required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
LABEL="ai.openclaw.model-budget-guard"
|
||||
PLIST_PATH="$HOME/Library/LaunchAgents/$LABEL.plist"
|
||||
STAGE_DIR="${MODEL_GUARD_STAGE_DIR:-$HOME/Library/Application Support/openclaw-copilot-guard}"
|
||||
STAGE_SCRIPTS_DIR="$STAGE_DIR/scripts"
|
||||
STAGE_CONFIG="$STAGE_DIR/model-budget-guard.config.json"
|
||||
MODEL_GUARD_CONFIG="${MODEL_GUARD_CONFIG:-$STAGE_CONFIG}"
|
||||
INTERVAL_SECONDS="${INTERVAL_SECONDS:-120}"
|
||||
|
||||
mkdir -p "$STAGE_SCRIPTS_DIR"
|
||||
cp "$ROOT_DIR/scripts/model_budget_guard.sh" "$STAGE_SCRIPTS_DIR/model_budget_guard.sh"
|
||||
jq \
|
||||
--arg state "$STAGE_DIR/model-budget-guard-state.json" \
|
||||
'.stateFile=$state' \
|
||||
"$ROOT_DIR/config/model-budget-guard.config.json" > "$STAGE_CONFIG"
|
||||
chmod +x "$STAGE_SCRIPTS_DIR/model_budget_guard.sh"
|
||||
|
||||
cat > "$PLIST_PATH" <<PLIST
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>$LABEL</string>
|
||||
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/bin/bash</string>
|
||||
<string>$STAGE_SCRIPTS_DIR/model_budget_guard.sh</string>
|
||||
</array>
|
||||
|
||||
<key>EnvironmentVariables</key>
|
||||
<dict>
|
||||
<key>PATH</key>
|
||||
<string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
|
||||
<key>MODEL_GUARD_CONFIG</key>
|
||||
<string>$MODEL_GUARD_CONFIG</string>
|
||||
</dict>
|
||||
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
|
||||
<key>StartInterval</key>
|
||||
<integer>$INTERVAL_SECONDS</integer>
|
||||
|
||||
<key>StandardOutPath</key>
|
||||
<string>/tmp/openclaw-model-budget-guard.log</string>
|
||||
|
||||
<key>StandardErrorPath</key>
|
||||
<string>/tmp/openclaw-model-budget-guard.err.log</string>
|
||||
</dict>
|
||||
</plist>
|
||||
PLIST
|
||||
|
||||
launchctl bootout "gui/$(id -u)/$LABEL" 2>/dev/null || true
|
||||
launchctl bootstrap "gui/$(id -u)" "$PLIST_PATH"
|
||||
launchctl kickstart -k "gui/$(id -u)/$LABEL"
|
||||
|
||||
cat <<MSG
|
||||
Installed LaunchAgent:
|
||||
$PLIST_PATH
|
||||
Staged runtime files:
|
||||
$STAGE_DIR
|
||||
|
||||
Check status:
|
||||
launchctl print gui/$(id -u)/$LABEL
|
||||
tail -f /tmp/openclaw-model-budget-guard.log /tmp/openclaw-model-budget-guard.err.log
|
||||
MSG
|
||||
@ -1,86 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
echo "[install-model-schedule-guard] jq is required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
LABEL="ai.openclaw.copilot-model-schedule-guard"
|
||||
PLIST_PATH="$HOME/Library/LaunchAgents/$LABEL.plist"
|
||||
STAGE_DIR="${MODEL_GUARD_STAGE_DIR:-$HOME/Library/Application Support/openclaw-copilot-guard}"
|
||||
STAGE_SCRIPTS_DIR="$STAGE_DIR/scripts"
|
||||
STAGE_SCHEDULE_CONFIG="$STAGE_DIR/model-schedule.config.json"
|
||||
STAGE_PROFILES_CONFIG="$STAGE_DIR/model-profiles.config.json"
|
||||
STAGE_POLICY_CONFIG="$STAGE_DIR/copilot-policy-guard.config.json"
|
||||
MODEL_SCHEDULE_CONFIG="${MODEL_SCHEDULE_CONFIG:-$STAGE_SCHEDULE_CONFIG}"
|
||||
INTERVAL_SECONDS="${INTERVAL_SECONDS:-300}"
|
||||
|
||||
mkdir -p "$STAGE_SCRIPTS_DIR"
|
||||
cp "$ROOT_DIR/scripts/model_schedule_guard.sh" "$STAGE_SCRIPTS_DIR/model_schedule_guard.sh"
|
||||
cp "$ROOT_DIR/scripts/model_profile_switch.sh" "$STAGE_SCRIPTS_DIR/model_profile_switch.sh"
|
||||
cp "$ROOT_DIR/config/model-profiles.config.json" "$STAGE_PROFILES_CONFIG"
|
||||
jq \
|
||||
--arg state "$STAGE_DIR/model-schedule-state.json" \
|
||||
'.stateFile=$state' \
|
||||
"$ROOT_DIR/config/model-schedule.config.json" > "$STAGE_SCHEDULE_CONFIG"
|
||||
chmod +x "$STAGE_SCRIPTS_DIR/model_schedule_guard.sh" "$STAGE_SCRIPTS_DIR/model_profile_switch.sh"
|
||||
|
||||
cat > "$PLIST_PATH" <<PLIST
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>$LABEL</string>
|
||||
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/bin/bash</string>
|
||||
<string>$STAGE_SCRIPTS_DIR/model_schedule_guard.sh</string>
|
||||
</array>
|
||||
|
||||
<key>EnvironmentVariables</key>
|
||||
<dict>
|
||||
<key>PATH</key>
|
||||
<string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
|
||||
<key>MODEL_SCHEDULE_CONFIG</key>
|
||||
<string>$MODEL_SCHEDULE_CONFIG</string>
|
||||
<key>MODEL_PROFILES_CONFIG</key>
|
||||
<string>$STAGE_PROFILES_CONFIG</string>
|
||||
<key>POLICY_GUARD_CONFIG</key>
|
||||
<string>$STAGE_POLICY_CONFIG</string>
|
||||
</dict>
|
||||
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
|
||||
<key>StartInterval</key>
|
||||
<integer>$INTERVAL_SECONDS</integer>
|
||||
|
||||
<key>StandardOutPath</key>
|
||||
<string>/tmp/openclaw-copilot-model-schedule-guard.log</string>
|
||||
|
||||
<key>StandardErrorPath</key>
|
||||
<string>/tmp/openclaw-copilot-model-schedule-guard.err.log</string>
|
||||
</dict>
|
||||
</plist>
|
||||
PLIST
|
||||
|
||||
launchctl bootout "gui/$(id -u)/$LABEL" 2>/dev/null || true
|
||||
launchctl bootstrap "gui/$(id -u)" "$PLIST_PATH"
|
||||
launchctl kickstart -k "gui/$(id -u)/$LABEL"
|
||||
|
||||
cat <<MSG
|
||||
Installed LaunchAgent:
|
||||
$PLIST_PATH
|
||||
Staged runtime files:
|
||||
$STAGE_DIR
|
||||
|
||||
Check status:
|
||||
launchctl print gui/$(id -u)/$LABEL
|
||||
tail -f /tmp/openclaw-copilot-model-schedule-guard.log /tmp/openclaw-copilot-model-schedule-guard.err.log
|
||||
MSG
|
||||
@ -1,202 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
CONFIG_PATH="${MODEL_GUARD_CONFIG:-$ROOT_DIR/config/model-budget-guard.config.json}"
|
||||
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
echo "[model-guard] jq is required" >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! command -v openclaw >/dev/null 2>&1; then
|
||||
echo "[model-guard] openclaw CLI is required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
expand_tilde() {
|
||||
local p="$1"
|
||||
if [[ "$p" == "~" ]]; then
|
||||
echo "$HOME"
|
||||
elif [[ "$p" == "~/"* ]]; then
|
||||
echo "$HOME/${p#~/}"
|
||||
else
|
||||
echo "$p"
|
||||
fi
|
||||
}
|
||||
|
||||
safe_now_ms() {
|
||||
python3 - <<'PY'
|
||||
import time
|
||||
print(int(time.time()*1000))
|
||||
PY
|
||||
}
|
||||
|
||||
send_notice() {
|
||||
local text="$1"
|
||||
local last_channel="$2"
|
||||
local last_to="$3"
|
||||
|
||||
if [[ -z "$last_channel" || -z "$last_to" ]]; then
|
||||
echo "[model-guard] notice skipped (missing channel/target): $text" >&2
|
||||
return 0
|
||||
fi
|
||||
|
||||
local target="$last_to"
|
||||
if [[ "$target" == *:* ]]; then
|
||||
target="${target#*:}"
|
||||
fi
|
||||
|
||||
openclaw message send \
|
||||
--channel "$last_channel" \
|
||||
--target "$target" \
|
||||
--message "$text" \
|
||||
>/dev/null 2>&1 || true
|
||||
}
|
||||
|
||||
if [[ ! -f "$CONFIG_PATH" ]]; then
|
||||
echo "[model-guard] missing config: $CONFIG_PATH" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
enabled="$(jq -r '.enabled // true' "$CONFIG_PATH")"
|
||||
if [[ "$enabled" != "true" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
session_key="$(jq -r '.sessionKey // "agent:main:main"' "$CONFIG_PATH")"
|
||||
low_model="$(jq -r '.lowModel // empty' "$CONFIG_PATH")"
|
||||
warn_after_min="$(jq -r '.warnAfterMinutes // 2' "$CONFIG_PATH")"
|
||||
revert_after_min="$(jq -r '.revertAfterMinutes // 45' "$CONFIG_PATH")"
|
||||
min_warn_interval_min="$(jq -r '.minWarnIntervalMinutes // 20' "$CONFIG_PATH")"
|
||||
state_file_raw="$(jq -r '.stateFile // "~/.openclaw/model-budget-guard-state.json"' "$CONFIG_PATH")"
|
||||
state_file="$(expand_tilde "$state_file_raw")"
|
||||
|
||||
if [[ -z "$low_model" ]]; then
|
||||
echo "[model-guard] lowModel must be set" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
state_dir="${OPENCLAW_STATE_DIR:-$HOME/.openclaw}"
|
||||
sessions_file="$state_dir/agents/main/sessions/sessions.json"
|
||||
|
||||
if [[ ! -r "$sessions_file" ]]; then
|
||||
echo "[model-guard] sessions file not found: $sessions_file" >&2
|
||||
exit 0
|
||||
fi
|
||||
|
||||
session_json="$(jq -c --arg key "$session_key" '.[$key] // {}' "$sessions_file" 2>/dev/null || true)"
|
||||
if [[ -z "$session_json" || "$session_json" == "{}" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
session_id="$(jq -r '.sessionId // empty' <<<"$session_json")"
|
||||
updated_at="$(jq -r '.updatedAt // 0' <<<"$session_json")"
|
||||
model_raw="$(jq -r '.model // empty' <<<"$session_json")"
|
||||
provider_raw="$(jq -r '.provider // empty' <<<"$session_json")"
|
||||
last_channel="$(jq -r '.lastChannel // empty' <<<"$session_json")"
|
||||
last_to="$(jq -r '.lastTo // empty' <<<"$session_json")"
|
||||
|
||||
if [[ -z "$model_raw" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
model_full="$model_raw"
|
||||
if [[ "$model_raw" != */* && -n "$provider_raw" && "$provider_raw" != "null" ]]; then
|
||||
model_full="$provider_raw/$model_raw"
|
||||
fi
|
||||
|
||||
high_match="$(jq -r --arg m "$model_full" '.highModels // [] | index($m) != null' "$CONFIG_PATH")"
|
||||
if [[ "$high_match" != "true" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ ! "$updated_at" =~ ^[0-9]+$ ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
now_ms="$(safe_now_ms)"
|
||||
age_ms=$((now_ms - updated_at))
|
||||
if (( age_ms < 0 )); then age_ms=0; fi
|
||||
warn_after_ms=$((warn_after_min * 60 * 1000))
|
||||
revert_after_ms=$((revert_after_min * 60 * 1000))
|
||||
min_warn_interval_ms=$((min_warn_interval_min * 60 * 1000))
|
||||
|
||||
mkdir -p "$(dirname "$state_file")"
|
||||
if [[ ! -f "$state_file" ]]; then
|
||||
cat > "$state_file" <<'JSON'
|
||||
{}
|
||||
JSON
|
||||
fi
|
||||
|
||||
state_json="$(cat "$state_file")"
|
||||
last_warned_model="$(jq -r --arg key "$session_key" '.[$key].lastWarnedModel // empty' <<<"$state_json")"
|
||||
last_warned_session="$(jq -r --arg key "$session_key" '.[$key].lastWarnedSessionId // empty' <<<"$state_json")"
|
||||
last_warn_at="$(jq -r --arg key "$session_key" '.[$key].lastWarnAt // 0' <<<"$state_json")"
|
||||
last_revert_at="$(jq -r --arg key "$session_key" '.[$key].lastRevertAt // 0' <<<"$state_json")"
|
||||
|
||||
should_warn="false"
|
||||
if (( age_ms >= warn_after_ms )); then
|
||||
if [[ "$last_warned_model" != "$model_full" || "$last_warned_session" != "$session_id" ]]; then
|
||||
should_warn="true"
|
||||
elif [[ "$last_warn_at" =~ ^[0-9]+$ ]] && (( now_ms - last_warn_at >= min_warn_interval_ms )); then
|
||||
should_warn="true"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$should_warn" == "true" ]]; then
|
||||
send_notice "Heads up: high-cost model active ($model_full). Switch back after heavy work: /model $low_model" "$last_channel" "$last_to"
|
||||
state_json="$(jq \
|
||||
--arg key "$session_key" \
|
||||
--arg model "$model_full" \
|
||||
--arg sid "$session_id" \
|
||||
--argjson now "$now_ms" \
|
||||
'.[$key].lastWarnedModel=$model | .[$key].lastWarnedSessionId=$sid | .[$key].lastWarnAt=$now' \
|
||||
<<<"$state_json")"
|
||||
fi
|
||||
|
||||
if (( age_ms >= revert_after_ms )); then
|
||||
can_revert="true"
|
||||
if [[ "$last_revert_at" =~ ^[0-9]+$ ]] && (( now_ms - last_revert_at < min_warn_interval_ms )); then
|
||||
can_revert="false"
|
||||
fi
|
||||
|
||||
if [[ "$can_revert" == "true" && -n "$session_id" ]]; then
|
||||
python3 - "$session_id" "$low_model" <<'PY'
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
session_id = sys.argv[1]
|
||||
low_model = sys.argv[2]
|
||||
cmd = [
|
||||
"openclaw",
|
||||
"agent",
|
||||
"--session-id", session_id,
|
||||
"--channel", "last",
|
||||
"--message", f"/model {low_model}",
|
||||
]
|
||||
try:
|
||||
subprocess.run(
|
||||
cmd,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
check=False,
|
||||
timeout=20,
|
||||
)
|
||||
except subprocess.TimeoutExpired:
|
||||
pass
|
||||
PY
|
||||
|
||||
send_notice "Auto-switched back to $low_model to avoid high-model quota burn. Use high model only for deep tasks." "$last_channel" "$last_to"
|
||||
|
||||
state_json="$(jq \
|
||||
--arg key "$session_key" \
|
||||
--arg model "$low_model" \
|
||||
--arg sid "$session_id" \
|
||||
--argjson now "$now_ms" \
|
||||
'.[$key].lastRevertAt=$now | .[$key].lastWarnedModel=$model | .[$key].lastWarnedSessionId=$sid | .[$key].lastWarnAt=$now' \
|
||||
<<<"$state_json")"
|
||||
fi
|
||||
fi
|
||||
|
||||
printf '%s\n' "$state_json" > "$state_file"
|
||||
@ -1,186 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
CONFIG_PATH="${MODEL_PROFILES_CONFIG:-$ROOT_DIR/config/model-profiles.config.json}"
|
||||
POLICY_CONFIG_PATH="${POLICY_GUARD_CONFIG:-$ROOT_DIR/config/copilot-policy-guard.config.json}"
|
||||
SESSION_KEY="${MODEL_SWITCH_SESSION_KEY:-agent:main:main}"
|
||||
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
echo "[model-switch] jq is required" >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! command -v openclaw >/dev/null 2>&1; then
|
||||
echo "[model-switch] openclaw CLI is required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
usage() {
|
||||
cat <<'USAGE'
|
||||
Usage:
|
||||
bash ./scripts/model_profile_switch.sh status
|
||||
bash ./scripts/model_profile_switch.sh <profile> [--no-live] [--no-status]
|
||||
|
||||
Examples:
|
||||
bash ./scripts/model_profile_switch.sh free
|
||||
bash ./scripts/model_profile_switch.sh paid --no-live
|
||||
USAGE
|
||||
}
|
||||
|
||||
get_session_id() {
|
||||
local session_key="$1"
|
||||
local state_dir="${OPENCLAW_STATE_DIR:-$HOME/.openclaw}"
|
||||
local sessions_file="$state_dir/agents/main/sessions/sessions.json"
|
||||
if [[ ! -f "$sessions_file" ]]; then
|
||||
return 0
|
||||
fi
|
||||
jq -r --arg key "$session_key" '.[$key].sessionId // empty' "$sessions_file" 2>/dev/null || true
|
||||
}
|
||||
|
||||
sync_policy_config() {
|
||||
local primary="$1"
|
||||
shift
|
||||
local -a fb_models=("$@")
|
||||
|
||||
if [[ ! -f "$POLICY_CONFIG_PATH" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
local fallbacks_json
|
||||
if [[ ${#fb_models[@]} -eq 0 ]]; then
|
||||
fallbacks_json="[]"
|
||||
else
|
||||
fallbacks_json="$(printf '%s\n' "${fb_models[@]}" | jq -R . | jq -s .)"
|
||||
fi
|
||||
|
||||
if jq \
|
||||
--arg primary "$primary" \
|
||||
--argjson fallbacks "$fallbacks_json" \
|
||||
'.desiredPrimaryModel=$primary | .allowedFallbacks=$fallbacks | .enforceFallbackAllowlist=true' \
|
||||
"$POLICY_CONFIG_PATH" > "$POLICY_CONFIG_PATH.tmp"; then
|
||||
mv "$POLICY_CONFIG_PATH.tmp" "$POLICY_CONFIG_PATH" || true
|
||||
else
|
||||
rm -f "$POLICY_CONFIG_PATH.tmp"
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ ! -f "$CONFIG_PATH" ]]; then
|
||||
echo "[model-switch] missing config: $CONFIG_PATH" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
profile="${1:-}"
|
||||
if [[ -z "$profile" ]]; then
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
shift || true
|
||||
|
||||
apply_live="true"
|
||||
show_status="true"
|
||||
while (( "$#" )); do
|
||||
case "$1" in
|
||||
--no-live) apply_live="false" ;;
|
||||
--no-status) show_status="false" ;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "[model-switch] unknown argument: $1" >&2
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if [[ "$profile" == "status" ]]; then
|
||||
echo "Available profiles:"
|
||||
jq -r '.profiles | to_entries[] | "- \(.key): \(.value.primary // "(unset)") (\(.value.description // "no description"))"' "$CONFIG_PATH"
|
||||
echo ""
|
||||
openclaw models status
|
||||
exit 0
|
||||
fi
|
||||
|
||||
profile_exists="$(jq -r --arg p "$profile" '.profiles[$p] != null' "$CONFIG_PATH")"
|
||||
if [[ "$profile_exists" != "true" ]]; then
|
||||
echo "[model-switch] unknown profile '$profile' in $CONFIG_PATH" >&2
|
||||
jq -r '.profiles | keys[]' "$CONFIG_PATH" | sed 's/^/ - /'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
primary="$(jq -r --arg p "$profile" '.profiles[$p].primary // empty' "$CONFIG_PATH")"
|
||||
if [[ -z "$primary" ]]; then
|
||||
echo "[model-switch] profile '$profile' has no primary model configured" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
fallbacks=()
|
||||
while IFS= read -r fb; do
|
||||
[[ -z "$fb" ]] && continue
|
||||
fallbacks+=("$fb")
|
||||
done < <(jq -r --arg p "$profile" '.profiles[$p].fallbacks[]? // empty' "$CONFIG_PATH")
|
||||
|
||||
echo "[model-switch] Applying profile: $profile"
|
||||
echo "[model-switch] primary: $primary"
|
||||
if [[ ${#fallbacks[@]} -gt 0 ]]; then
|
||||
echo "[model-switch] fallbacks: ${fallbacks[*]}"
|
||||
else
|
||||
echo "[model-switch] fallbacks: (none)"
|
||||
fi
|
||||
|
||||
openclaw models set "$primary" >/dev/null
|
||||
openclaw models fallbacks clear >/dev/null
|
||||
for fb in "${fallbacks[@]}"; do
|
||||
openclaw models fallbacks add "$fb" >/dev/null
|
||||
done
|
||||
|
||||
provider_policy_len="$(jq -r --arg p "$profile" '.profiles[$p].providerPolicy // {} | length' "$CONFIG_PATH")"
|
||||
if [[ "$provider_policy_len" != "0" ]]; then
|
||||
while IFS=$'\t' read -r provider enabled; do
|
||||
[[ -z "$provider" || -z "$enabled" ]] && continue
|
||||
openclaw config set --json "providers.$provider.enabled" "$enabled" >/dev/null || true
|
||||
done < <(jq -r --arg p "$profile" '.profiles[$p].providerPolicy // {} | to_entries[] | "\(.key)\t\(.value)"' "$CONFIG_PATH")
|
||||
fi
|
||||
|
||||
sync_policy_config "$primary" "${fallbacks[@]}"
|
||||
|
||||
if [[ "$apply_live" == "true" ]]; then
|
||||
session_id="$(get_session_id "$SESSION_KEY")"
|
||||
if [[ -n "$session_id" ]]; then
|
||||
python3 - "$session_id" "$primary" <<'PY'
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
session_id = sys.argv[1]
|
||||
primary = sys.argv[2]
|
||||
cmd = [
|
||||
"openclaw",
|
||||
"agent",
|
||||
"--session-id", session_id,
|
||||
"--channel", "last",
|
||||
"--message", f"/model {primary}",
|
||||
]
|
||||
try:
|
||||
subprocess.run(
|
||||
cmd,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
check=False,
|
||||
timeout=20,
|
||||
)
|
||||
except subprocess.TimeoutExpired:
|
||||
pass
|
||||
PY
|
||||
echo "[model-switch] live session updated: $session_id"
|
||||
else
|
||||
echo "[model-switch] no active session found for key $SESSION_KEY"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$show_status" == "true" ]]; then
|
||||
echo ""
|
||||
openclaw models status
|
||||
fi
|
||||
@ -1,118 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
CONFIG_PATH="${MODEL_SCHEDULE_CONFIG:-$ROOT_DIR/config/model-schedule.config.json}"
|
||||
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
echo "[model-schedule] jq is required" >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! command -v openclaw >/dev/null 2>&1; then
|
||||
echo "[model-schedule] openclaw CLI is required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
expand_tilde() {
|
||||
local p="$1"
|
||||
if [[ "$p" == "~" ]]; then
|
||||
echo "$HOME"
|
||||
elif [[ "$p" == "~/"* ]]; then
|
||||
echo "$HOME/${p#~/}"
|
||||
else
|
||||
echo "$p"
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ ! -f "$CONFIG_PATH" ]]; then
|
||||
echo "[model-schedule] missing config: $CONFIG_PATH" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
enabled="$(jq -r '.enabled // false' "$CONFIG_PATH")"
|
||||
if [[ "$enabled" != "true" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
day_profile="$(jq -r '.dayProfile // "paid"' "$CONFIG_PATH")"
|
||||
night_profile="$(jq -r '.nightProfile // "free"' "$CONFIG_PATH")"
|
||||
day_start_hour="$(jq -r '.dayStartHour // 8' "$CONFIG_PATH")"
|
||||
night_start_hour="$(jq -r '.nightStartHour // 18' "$CONFIG_PATH")"
|
||||
session_key="$(jq -r '.sessionKey // "agent:main:main"' "$CONFIG_PATH")"
|
||||
switch_script_raw="$(jq -r '.switchScript // "./scripts/model_profile_switch.sh"' "$CONFIG_PATH")"
|
||||
state_file_raw="$(jq -r '.stateFile // "~/.openclaw/model-schedule-state.json"' "$CONFIG_PATH")"
|
||||
state_file="$(expand_tilde "$state_file_raw")"
|
||||
profiles_config="${MODEL_PROFILES_CONFIG:-$ROOT_DIR/config/model-profiles.config.json}"
|
||||
|
||||
if [[ "$switch_script_raw" = /* ]]; then
|
||||
switch_script="$switch_script_raw"
|
||||
else
|
||||
switch_script="$ROOT_DIR/${switch_script_raw#./}"
|
||||
fi
|
||||
|
||||
if [[ ! -x "$switch_script" ]]; then
|
||||
echo "[model-schedule] switch script is not executable: $switch_script" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! [[ "$day_start_hour" =~ ^[0-9]+$ && "$night_start_hour" =~ ^[0-9]+$ ]]; then
|
||||
echo "[model-schedule] dayStartHour/nightStartHour must be integers (0-23)" >&2
|
||||
exit 1
|
||||
fi
|
||||
if (( day_start_hour < 0 || day_start_hour > 23 || night_start_hour < 0 || night_start_hour > 23 )); then
|
||||
echo "[model-schedule] dayStartHour/nightStartHour must be in 0..23" >&2
|
||||
exit 1
|
||||
fi
|
||||
if (( day_start_hour == night_start_hour )); then
|
||||
echo "[model-schedule] dayStartHour and nightStartHour cannot be equal" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
current_hour="$(date +%H)"
|
||||
current_hour=$((10#$current_hour))
|
||||
|
||||
is_night="false"
|
||||
if (( night_start_hour > day_start_hour )); then
|
||||
if (( current_hour >= night_start_hour || current_hour < day_start_hour )); then
|
||||
is_night="true"
|
||||
fi
|
||||
else
|
||||
if (( current_hour >= night_start_hour && current_hour < day_start_hour )); then
|
||||
is_night="true"
|
||||
fi
|
||||
fi
|
||||
|
||||
desired_profile="$day_profile"
|
||||
if [[ "$is_night" == "true" ]]; then
|
||||
desired_profile="$night_profile"
|
||||
fi
|
||||
|
||||
mkdir -p "$(dirname "$state_file")"
|
||||
if [[ ! -f "$state_file" ]]; then
|
||||
printf '{}\n' > "$state_file"
|
||||
fi
|
||||
|
||||
last_profile="$(jq -r '.lastAppliedProfile // empty' "$state_file")"
|
||||
desired_primary=""
|
||||
if [[ -f "$profiles_config" ]]; then
|
||||
desired_primary="$(jq -r --arg p "$desired_profile" '.profiles[$p].primary // empty' "$profiles_config")"
|
||||
fi
|
||||
current_primary="$(openclaw models status --json 2>/dev/null | jq -r '.defaultModel // empty')"
|
||||
|
||||
if [[ "$last_profile" == "$desired_profile" ]]; then
|
||||
if [[ -n "$desired_primary" && "$current_primary" == "$desired_primary" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "[model-schedule] hour=$current_hour desired_profile=$desired_profile last_profile=${last_profile:-none} current_primary=${current_primary:-unknown}"
|
||||
MODEL_SWITCH_SESSION_KEY="$session_key" bash "$switch_script" "$desired_profile" --no-live --no-status
|
||||
|
||||
tmp_file="$state_file.tmp"
|
||||
jq \
|
||||
--arg profile "$desired_profile" \
|
||||
--arg when "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" \
|
||||
--arg session "$session_key" \
|
||||
'.lastAppliedProfile=$profile | .lastAppliedAt=$when | .sessionKey=$session' \
|
||||
"$state_file" > "$tmp_file" && mv "$tmp_file" "$state_file"
|
||||
@ -1,201 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Optional overrides:
|
||||
# OPENCLAW_DATA_TARGET=/Volumes/Data/openclaw-copilot bash ./setup/setup_openclaw_copilot.sh
|
||||
# NPM_GLOBAL_PREFIX="$HOME/.npm-global" bash ./setup/setup_openclaw_copilot.sh
|
||||
# OPENCLAW_DATA_TARGET=/Volumes/Data/openclaw-copilot NPM_GLOBAL_PREFIX="$HOME/.npm-global" bash ./setup/setup_openclaw_copilot.sh
|
||||
OPENCLAW_DATA_TARGET="${OPENCLAW_DATA_TARGET:-}"
|
||||
NPM_GLOBAL_PREFIX="${NPM_GLOBAL_PREFIX:-}"
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_info() { echo -e "${GREEN}$*${NC}"; }
|
||||
log_warn() { echo -e "${YELLOW}$*${NC}"; }
|
||||
log_err() { echo -e "${RED}$*${NC}"; }
|
||||
|
||||
require_cmd() {
|
||||
local cmd="$1"
|
||||
if ! command -v "$cmd" >/dev/null 2>&1; then
|
||||
log_err "ERROR: Missing required command: $cmd"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
node_major_version() {
|
||||
node -v 2>/dev/null | sed 's/^v//' | cut -d. -f1
|
||||
}
|
||||
|
||||
expand_tilde() {
|
||||
local p="$1"
|
||||
if [[ "$p" == "~" ]]; then
|
||||
echo "$HOME"
|
||||
elif [[ "$p" == "~/"* ]]; then
|
||||
echo "$HOME/${p#~/}"
|
||||
else
|
||||
echo "$p"
|
||||
fi
|
||||
}
|
||||
|
||||
link_data_dir() {
|
||||
local source="$1"
|
||||
local target="$2"
|
||||
local label="$3"
|
||||
|
||||
mkdir -p "$target"
|
||||
|
||||
if [ -L "$source" ]; then
|
||||
local current_link
|
||||
current_link="$(readlink "$source")"
|
||||
if [ "$current_link" != "$target" ]; then
|
||||
rm "$source"
|
||||
ln -s "$target" "$source"
|
||||
log_info "${label}: updated symlink ${source} -> ${target}"
|
||||
else
|
||||
log_info "${label}: symlink already correct (${source} -> ${target})"
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ -d "$source" ]; then
|
||||
log_warn "${label}: migrating existing ${source} data to ${target}..."
|
||||
if command -v rsync >/dev/null 2>&1; then
|
||||
rsync -a "$source"/ "$target"/
|
||||
else
|
||||
cp -a "$source"/. "$target"/
|
||||
fi
|
||||
rm -rf "$source"
|
||||
elif [ -e "$source" ]; then
|
||||
log_err "ERROR: ${source} exists but is not a directory/symlink. Resolve manually."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ln -s "$target" "$source"
|
||||
log_info "${label}: symlink created ${source} -> ${target}"
|
||||
}
|
||||
|
||||
install_npm_global() {
|
||||
local pkg="$1"
|
||||
require_cmd npm
|
||||
if [[ -n "$NPM_GLOBAL_PREFIX" ]]; then
|
||||
mkdir -p "$NPM_GLOBAL_PREFIX"
|
||||
npm install -g --prefix "$NPM_GLOBAL_PREFIX" "$pkg"
|
||||
else
|
||||
npm install -g "$pkg"
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ -n "$OPENCLAW_DATA_TARGET" ]]; then
|
||||
OPENCLAW_DATA_TARGET="$(expand_tilde "$OPENCLAW_DATA_TARGET")"
|
||||
fi
|
||||
if [[ -n "$NPM_GLOBAL_PREFIX" ]]; then
|
||||
NPM_GLOBAL_PREFIX="$(expand_tilde "$NPM_GLOBAL_PREFIX")"
|
||||
fi
|
||||
|
||||
if [[ -n "$NPM_GLOBAL_PREFIX" ]]; then
|
||||
export PATH="$NPM_GLOBAL_PREFIX/bin:$PATH"
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}=== OpenClaw + GitHub Copilot CLI Setup ===${NC}"
|
||||
echo "Current time: $(date)"
|
||||
if [[ -n "$OPENCLAW_DATA_TARGET" ]]; then
|
||||
echo "OpenClaw data target: $OPENCLAW_DATA_TARGET"
|
||||
fi
|
||||
if [[ -n "$NPM_GLOBAL_PREFIX" ]]; then
|
||||
echo "npm global prefix: $NPM_GLOBAL_PREFIX"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
require_cmd curl
|
||||
|
||||
# Step 1: Install / verify Node.js >= 22
|
||||
need_node_install=false
|
||||
if ! command -v node >/dev/null 2>&1; then
|
||||
need_node_install=true
|
||||
else
|
||||
node_major="$(node_major_version || true)"
|
||||
if ! echo "$node_major" | grep -Eq '^[0-9]+$' || [ "$node_major" -lt 22 ]; then
|
||||
need_node_install=true
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$need_node_install" = true ]; then
|
||||
log_warn "Node.js >= 22 not found. Installing via Homebrew..."
|
||||
if ! command -v brew >/dev/null 2>&1; then
|
||||
log_err "Homebrew not found. Install it first:"
|
||||
echo '/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"'
|
||||
exit 1
|
||||
fi
|
||||
brew install node
|
||||
fi
|
||||
log_info "Node.js ready ($(node -v))."
|
||||
|
||||
# Step 2: Install / verify OpenClaw
|
||||
if ! command -v openclaw >/dev/null 2>&1; then
|
||||
log_warn "OpenClaw not found. Installing via npm..."
|
||||
require_cmd npm
|
||||
install_npm_global openclaw
|
||||
else
|
||||
log_info "OpenClaw already installed ($(openclaw --version))."
|
||||
fi
|
||||
|
||||
# Step 3: Install / verify GitHub Copilot CLI
|
||||
if ! command -v copilot >/dev/null 2>&1; then
|
||||
if command -v brew >/dev/null 2>&1; then
|
||||
log_warn "Copilot CLI not found. Installing prerelease via Homebrew..."
|
||||
brew install copilot-cli@prerelease || {
|
||||
log_warn "Homebrew install failed, falling back to npm package..."
|
||||
install_npm_global @github/copilot-cli
|
||||
}
|
||||
else
|
||||
log_warn "Homebrew not found. Installing Copilot CLI via npm..."
|
||||
install_npm_global @github/copilot-cli
|
||||
fi
|
||||
else
|
||||
log_info "Copilot CLI already installed ($(copilot --version 2>/dev/null || echo present))."
|
||||
fi
|
||||
|
||||
if [[ -n "$OPENCLAW_DATA_TARGET" ]]; then
|
||||
link_data_dir "$HOME/.openclaw" "$OPENCLAW_DATA_TARGET" "OpenClaw"
|
||||
else
|
||||
log_info "OpenClaw data location unchanged (~/.openclaw). Set OPENCLAW_DATA_TARGET to move it."
|
||||
fi
|
||||
|
||||
FINALIZE_SCRIPT="$SCRIPT_DIR/../scripts/finalize_copilot_setup.sh"
|
||||
if [[ -x "$FINALIZE_SCRIPT" ]]; then
|
||||
if copilot auth status >/dev/null 2>&1; then
|
||||
log_info "Copilot auth already active. Running finalize flow now..."
|
||||
if ! bash "$FINALIZE_SCRIPT"; then
|
||||
log_warn "Finalize flow failed. Re-run manually after checking auth/models:"
|
||||
echo " bash ./scripts/finalize_copilot_setup.sh"
|
||||
fi
|
||||
else
|
||||
log_warn "Final setup step still needed after login:"
|
||||
echo " bash ./scripts/finalize_copilot_setup.sh"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
log_info "Setup complete (Copilot-first)."
|
||||
echo ""
|
||||
echo "Next steps (target machine):"
|
||||
echo "1. Run one-command finalize (recommended):"
|
||||
echo " bash ./scripts/finalize_copilot_setup.sh"
|
||||
echo "2. If finalize cannot open browser login, authenticate first:"
|
||||
echo " copilot auth login"
|
||||
echo " copilot auth status"
|
||||
echo " bash ./scripts/finalize_copilot_setup.sh"
|
||||
echo "3. Verify:"
|
||||
echo " openclaw status --deep"
|
||||
echo " openclaw models status"
|
||||
if [[ -n "$NPM_GLOBAL_PREFIX" ]]; then
|
||||
echo ""
|
||||
echo "If this is a new shell, ensure PATH includes your npm prefix bin:"
|
||||
echo " export PATH=\"$NPM_GLOBAL_PREFIX/bin:\$PATH\""
|
||||
fi
|
||||
Loading…
Reference in New Issue
Block a user