Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
This commit is contained in:
parent
363036e188
commit
9dc95e0f29
10
README.md
10
README.md
@ -19,9 +19,19 @@ cd /Volumes/Data/openclaw-setups/openclaw-setup-max
|
|||||||
cat README.md
|
cat README.md
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Existing install upgrade (in-place):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /Volumes/Data/openclaw-setups/openclaw-setup-max
|
||||||
|
bash ./scripts/update_openclaw.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
This update workflow also reapplies schedule/budget guardrails and self-heals gateway LaunchAgent issues for external-drive state dirs.
|
||||||
|
|
||||||
Primary docs:
|
Primary docs:
|
||||||
- `openclaw-setup-max/README.md`
|
- `openclaw-setup-max/README.md`
|
||||||
- `openclaw-setup-max/PRD.md`
|
- `openclaw-setup-max/PRD.md`
|
||||||
|
- `openclaw-setup-max/docs/operations/UPGRADING.md`
|
||||||
|
|
||||||
### Copilot setup
|
### Copilot setup
|
||||||
|
|
||||||
|
|||||||
@ -36,7 +36,7 @@ Operators need an easy way to:
|
|||||||
|
|
||||||
- As an operator, I can run one command to switch to free local models immediately.
|
- As an operator, I can run one command to switch to free local models immediately.
|
||||||
- As an operator, I can switch back to paid model for deep tasks.
|
- As an operator, I can switch back to paid model for deep tasks.
|
||||||
- As an operator, I can set free mode from 9pm-7am automatically.
|
- As an operator, I can set free mode from 10pm-7am automatically.
|
||||||
- As an operator, I get warned if expensive model remains active too long.
|
- As an operator, I get warned if expensive model remains active too long.
|
||||||
- As a maintainer, I can copy this folder to another Mac and follow docs to bootstrap.
|
- As a maintainer, I can copy this folder to another Mac and follow docs to bootstrap.
|
||||||
|
|
||||||
@ -104,6 +104,21 @@ Implementation:
|
|||||||
Implementation:
|
Implementation:
|
||||||
- `README.md`
|
- `README.md`
|
||||||
|
|
||||||
|
### FR-7 Existing-Install Upgrade Workflow
|
||||||
|
- Must provide one command for in-place upgrades on existing machines.
|
||||||
|
- Must back up OpenClaw config/session files before migration/update.
|
||||||
|
- Must run config migration (`openclaw doctor --fix`) before attempting update.
|
||||||
|
- Must update CLI using built-in updater with timeout + npm fallback.
|
||||||
|
- Must emit timestamped update logs for operator traceability.
|
||||||
|
- Must stop gateway before update and bring gateway back after update.
|
||||||
|
- Must ensure gateway LaunchAgent is loaded/running after update.
|
||||||
|
- If `~/.openclaw` is symlinked to `/Volumes/...`, must ensure gateway LaunchAgent logs use `/tmp/openclaw-gateway.launchd*.log` to avoid launchd `EX_CONFIG`.
|
||||||
|
- Must reinstall and kickstart budget/schedule guard LaunchAgents after update.
|
||||||
|
|
||||||
|
Implementation:
|
||||||
|
- `scripts/update_openclaw.sh`
|
||||||
|
- `setup/setup_openclaw_ollama.sh`
|
||||||
|
|
||||||
## 7) Non-Functional Requirements
|
## 7) Non-Functional Requirements
|
||||||
|
|
||||||
- NFR-1 Reliability: scripts should be safe to run repeatedly.
|
- NFR-1 Reliability: scripts should be safe to run repeatedly.
|
||||||
@ -154,11 +169,14 @@ Outputs:
|
|||||||
## 10) Acceptance Criteria
|
## 10) Acceptance Criteria
|
||||||
|
|
||||||
- AC-1: Running `bash ./scripts/model_profile_switch.sh free` changes default to `openrouter/qwen/qwen3-coder:free`.
|
- AC-1: Running `bash ./scripts/model_profile_switch.sh free` changes default to `openrouter/qwen/qwen3-coder:free`.
|
||||||
- AC-2: Running `bash ./scripts/model_profile_switch.sh paid` restores default to `openrouter/qwen/qwen3-coder:free`.
|
- AC-2: Running `bash ./scripts/model_profile_switch.sh paid` restores default to `openrouter/moonshotai/kimi-k2.5`.
|
||||||
- AC-3: With schedule enabled (`21`/`7`), profile changes correctly by local time window.
|
- AC-3: With schedule enabled (`22`/`7`), profile changes correctly by local time window.
|
||||||
- AC-4: If operator manually drifts model during scheduled window, schedule guard re-aligns on next run.
|
- AC-4: If operator manually drifts model during scheduled window, schedule guard re-aligns on next run.
|
||||||
- AC-5: Budget guard warns and reverts when high model remains active beyond thresholds.
|
- AC-5: Budget guard warns and reverts when high model remains active beyond thresholds.
|
||||||
- AC-6: New operator can follow README from zero knowledge and complete setup without editing scripts.
|
- AC-6: New operator can follow README from zero knowledge and complete setup without editing scripts.
|
||||||
|
- AC-7: Running `bash ./scripts/update_openclaw.sh` upgrades an existing install and prints a healthy gateway/model status at completion.
|
||||||
|
- AC-8: After update, schedule + budget LaunchAgents are loaded and can run immediately when kickstarted.
|
||||||
|
- AC-9: Upgrade output lines include timestamps to support restart/audit troubleshooting.
|
||||||
|
|
||||||
## 11) Risks and Mitigations
|
## 11) Risks and Mitigations
|
||||||
|
|
||||||
@ -170,6 +188,8 @@ Outputs:
|
|||||||
- Mitigation: prerequisite checks in README.
|
- Mitigation: prerequisite checks in README.
|
||||||
- Risk: user confusion between ChatGPT subscription and API billing.
|
- Risk: user confusion between ChatGPT subscription and API billing.
|
||||||
- Mitigation: clear FAQ note in README.
|
- Mitigation: clear FAQ note in README.
|
||||||
|
- Risk: gateway LaunchAgent exits with `EX_CONFIG` when logs point into external-volume symlink paths.
|
||||||
|
- Mitigation: force gateway LaunchAgent stdout/stderr to `/tmp/openclaw-gateway.launchd*.log` when `~/.openclaw` resolves to `/Volumes/...`.
|
||||||
|
|
||||||
## 12) Rollout Plan
|
## 12) Rollout Plan
|
||||||
|
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
# OpenClaw Setup Max (Paid + Free Model Switching)
|
# OpenClaw Setup Max (Paid + Free Model Switching)
|
||||||
|
|
||||||
This workspace runs OpenClaw with:
|
This workspace runs OpenClaw with:
|
||||||
- A paid high-quality model profile (`openrouter/qwen/qwen3-coder:free`)
|
- A paid high-quality model profile (`openrouter/moonshotai/kimi-k2.5`)
|
||||||
- A free OpenRouter profile
|
- A free OpenRouter profile
|
||||||
- One-command live switching
|
- One-command live switching
|
||||||
- Optional automatic day/night switching (example: free from 9pm-7am)
|
- Optional automatic day/night switching (example: free from 10pm-7am)
|
||||||
- Optional budget guard that warns and auto-reverts from expensive models
|
- Optional budget guard that warns and auto-reverts from expensive models
|
||||||
|
|
||||||
This guide is written for first-time users.
|
This guide covers both first-time setup and in-place upgrades.
|
||||||
|
|
||||||
## 0) Workspace Layout
|
## 0) Workspace Layout
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ Everything else is grouped by function:
|
|||||||
## 1) What You Get
|
## 1) What You Get
|
||||||
|
|
||||||
- `paid` profile:
|
- `paid` profile:
|
||||||
- Primary: `openrouter/qwen/qwen3-coder:free`
|
- Primary: `openrouter/moonshotai/kimi-k2.5`
|
||||||
- Fallbacks: `openrouter/qwen/qwen3-coder-next`, `openrouter/qwen/qwen3-14b`
|
- Fallbacks: `openrouter/qwen/qwen3-coder-next`, `openrouter/qwen/qwen3-14b`
|
||||||
- `free` profile:
|
- `free` profile:
|
||||||
- Primary: `openrouter/qwen/qwen3-coder:free`
|
- Primary: `openrouter/qwen/qwen3-coder:free`
|
||||||
@ -49,6 +49,9 @@ Profiles are defined in `config/model-profiles.config.json`.
|
|||||||
- `scripts/install_model_schedule_guard_launchd.sh`: installs schedule LaunchAgent
|
- `scripts/install_model_schedule_guard_launchd.sh`: installs schedule LaunchAgent
|
||||||
- `scripts/install_model_budget_guard_launchd.sh`: installs budget LaunchAgent
|
- `scripts/install_model_budget_guard_launchd.sh`: installs budget LaunchAgent
|
||||||
- `scripts/install_local_model_guardrails.sh`: installs both LaunchAgents
|
- `scripts/install_local_model_guardrails.sh`: installs both LaunchAgents
|
||||||
|
- `scripts/update_openclaw.sh`: in-place OpenClaw upgrade + migration workflow
|
||||||
|
- `docs/operations/UPGRADING.md`: upgrade runbook for existing installs
|
||||||
|
- `docs/operations/troubleshooting.md`: known issue recovery commands
|
||||||
|
|
||||||
## 3) Prerequisites
|
## 3) Prerequisites
|
||||||
|
|
||||||
@ -70,6 +73,7 @@ Script behavior:
|
|||||||
- `openclaw`
|
- `openclaw`
|
||||||
- `jq`
|
- `jq`
|
||||||
- `python3`
|
- `python3`
|
||||||
|
- If `~/.openclaw` is symlinked to an external volume (`/Volumes/...`), setup patches gateway LaunchAgent logs to `/tmp/openclaw-gateway.launchd*.log` to avoid launchd `EX_CONFIG` failures.
|
||||||
- It will prompt before:
|
- It will prompt before:
|
||||||
- Installing Ollama (if missing)
|
- Installing Ollama (if missing)
|
||||||
- Pulling local Ollama models (large downloads)
|
- Pulling local Ollama models (large downloads)
|
||||||
@ -84,6 +88,45 @@ AUTO_YES=true bash ./setup/setup_openclaw_ollama.sh
|
|||||||
PULL_LOCAL_MODELS=false bash ./setup/setup_openclaw_ollama.sh
|
PULL_LOCAL_MODELS=false bash ./setup/setup_openclaw_ollama.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 3A) Update Existing Install (In-place)
|
||||||
|
|
||||||
|
Use this for upgrades on an already-working machine:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /Volumes/Data/openclaw-setups/openclaw-setup-max
|
||||||
|
bash ./scripts/update_openclaw.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
What it handles:
|
||||||
|
- Backup of `~/.openclaw/openclaw.json` + main `sessions.json`
|
||||||
|
- Config migration (`openclaw doctor --fix --non-interactive`)
|
||||||
|
- CLI update with timeout + npm fallback
|
||||||
|
- Timestamped step logging in terminal output
|
||||||
|
- Explicit gateway stop/start
|
||||||
|
- Gateway LaunchAgent self-heal (install if missing)
|
||||||
|
- External-drive launchd log-path patch (`/tmp/openclaw-gateway.launchd*.log`)
|
||||||
|
- Reinstall + immediate kickstart of budget/schedule guards
|
||||||
|
- Final model/profile/gateway verification output
|
||||||
|
|
||||||
|
Useful options:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Switch update channel
|
||||||
|
bash ./scripts/update_openclaw.sh --channel beta
|
||||||
|
|
||||||
|
# Pin one update run to a version/tag
|
||||||
|
bash ./scripts/update_openclaw.sh --tag 2026.2.24
|
||||||
|
|
||||||
|
# Increase built-in updater timeout (seconds)
|
||||||
|
UPDATE_MAX_SECONDS=900 bash ./scripts/update_openclaw.sh
|
||||||
|
|
||||||
|
# Save a timestamped run log file
|
||||||
|
bash ./scripts/update_openclaw.sh 2>&1 | tee "/tmp/openclaw-update-$(date +%Y%m%d-%H%M%S).log"
|
||||||
|
```
|
||||||
|
|
||||||
|
Detailed runbook:
|
||||||
|
- `docs/operations/UPGRADING.md`
|
||||||
|
|
||||||
## 4) Quick Start (Most Common)
|
## 4) Quick Start (Most Common)
|
||||||
|
|
||||||
Use env-driven sync + hygiene workflow (recommended after any model changes):
|
Use env-driven sync + hygiene workflow (recommended after any model changes):
|
||||||
@ -145,7 +188,7 @@ If you want config-only change (no live session message), use:
|
|||||||
bash ./scripts/model_profile_switch.sh free --no-live
|
bash ./scripts/model_profile_switch.sh free --no-live
|
||||||
```
|
```
|
||||||
|
|
||||||
## 6) Enable Schedule (Example: Free 9pm-7am)
|
## 6) Enable Schedule (Example: Free 10pm-7am)
|
||||||
|
|
||||||
1. Edit config:
|
1. Edit config:
|
||||||
|
|
||||||
@ -162,7 +205,7 @@ open config/model-schedule.config.json
|
|||||||
"dayProfile": "paid",
|
"dayProfile": "paid",
|
||||||
"nightProfile": "free",
|
"nightProfile": "free",
|
||||||
"dayStartHour": 7,
|
"dayStartHour": 7,
|
||||||
"nightStartHour": 21
|
"nightStartHour": 22
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -224,7 +267,7 @@ bash ./scripts/install_local_model_guardrails.sh
|
|||||||
|
|
||||||
Budget behavior from `config/model-budget-guard.config.json`:
|
Budget behavior from `config/model-budget-guard.config.json`:
|
||||||
- Warn after 2 minutes on high-cost model
|
- Warn after 2 minutes on high-cost model
|
||||||
- Auto-revert to `ollama/qwen3:14b` after 45 minutes
|
- Auto-revert to `openrouter/qwen/qwen3-coder:free` after 45 minutes
|
||||||
- Prevent repeated spam with minimum warning interval
|
- Prevent repeated spam with minimum warning interval
|
||||||
|
|
||||||
## 9) View Current LaunchAgents
|
## 9) View Current LaunchAgents
|
||||||
@ -239,6 +282,7 @@ Logs:
|
|||||||
```bash
|
```bash
|
||||||
tail -f /tmp/openclaw-model-schedule-guard.log /tmp/openclaw-model-schedule-guard.err.log
|
tail -f /tmp/openclaw-model-schedule-guard.log /tmp/openclaw-model-schedule-guard.err.log
|
||||||
tail -f /tmp/openclaw-model-budget-guard.log /tmp/openclaw-model-budget-guard.err.log
|
tail -f /tmp/openclaw-model-budget-guard.log /tmp/openclaw-model-budget-guard.err.log
|
||||||
|
tail -f /tmp/openclaw-gateway.launchd.log /tmp/openclaw-gateway.launchd.err.log
|
||||||
```
|
```
|
||||||
|
|
||||||
## 10) Manual Switching Tips
|
## 10) Manual Switching Tips
|
||||||
|
|||||||
@ -49,6 +49,13 @@ openclaw gateway stop
|
|||||||
openclaw gateway restart
|
openclaw gateway restart
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Repo-specific in-place upgrade (existing install):**
|
||||||
|
```bash
|
||||||
|
cd /Volumes/Data/openclaw-setups/openclaw-setup-max
|
||||||
|
bash ./scripts/update_openclaw.sh
|
||||||
|
```
|
||||||
|
This script stops gateway, upgrades OpenClaw, reapplies schedule/budget guardrails, and restarts gateway.
|
||||||
|
|
||||||
### Directory Structure
|
### Directory Structure
|
||||||
|
|
||||||
**Workspace Location:**
|
**Workspace Location:**
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
This setup adds two model profiles:
|
This setup adds two model profiles:
|
||||||
|
|
||||||
- `paid` -> `openrouter/qwen/qwen3-coder:free` with OpenRouter fallbacks
|
- `paid` -> `openrouter/moonshotai/kimi-k2.5` with OpenRouter fallbacks
|
||||||
- `free` -> `openrouter/qwen/qwen3-coder:free` with OpenRouter fallback
|
- `free` -> `openrouter/qwen/qwen3-coder:free` with OpenRouter fallback
|
||||||
|
|
||||||
## Recommended workflow (prevents legacy drift)
|
## Recommended workflow (prevents legacy drift)
|
||||||
@ -40,7 +40,7 @@ Notes:
|
|||||||
bash ./scripts/model_profile_switch.sh status
|
bash ./scripts/model_profile_switch.sh status
|
||||||
```
|
```
|
||||||
|
|
||||||
## Schedule auto-switching (example: free from 9pm-7am)
|
## Schedule auto-switching (example: free from 10pm-7am)
|
||||||
|
|
||||||
1. Edit config:
|
1. Edit config:
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ Ensure:
|
|||||||
"dayProfile": "paid",
|
"dayProfile": "paid",
|
||||||
"nightProfile": "free",
|
"nightProfile": "free",
|
||||||
"dayStartHour": 7,
|
"dayStartHour": 7,
|
||||||
"nightStartHour": 21
|
"nightStartHour": 22
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -100,3 +100,14 @@ bash ./scripts/install_local_model_guardrails.sh
|
|||||||
launchctl bootout gui/$(id -u)/ai.openclaw.local.model-schedule-guard 2>/dev/null || true
|
launchctl bootout gui/$(id -u)/ai.openclaw.local.model-schedule-guard 2>/dev/null || true
|
||||||
launchctl bootout gui/$(id -u)/ai.openclaw.local.model-budget-guard 2>/dev/null || true
|
launchctl bootout gui/$(id -u)/ai.openclaw.local.model-budget-guard 2>/dev/null || true
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## After OpenClaw upgrades
|
||||||
|
|
||||||
|
Use the update workflow so guards/profile state are reapplied automatically:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /Volumes/Data/openclaw-setups/openclaw-setup-max
|
||||||
|
bash ./scripts/update_openclaw.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
The update script reinstalls both guard LaunchAgents and kickstarts them immediately, so budget/profile logic is re-evaluated in the same run.
|
||||||
|
|||||||
66
openclaw-setup-max/docs/operations/UPGRADING.md
Normal file
66
openclaw-setup-max/docs/operations/UPGRADING.md
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
# OpenClaw Upgrade Runbook (Existing Install)
|
||||||
|
|
||||||
|
Use this when OpenClaw is already installed and you want to upgrade in place.
|
||||||
|
|
||||||
|
## Recommended command
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /Volumes/Data/openclaw-setups/openclaw-setup-max
|
||||||
|
bash ./scripts/update_openclaw.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## What `update_openclaw.sh` does
|
||||||
|
|
||||||
|
- Backs up:
|
||||||
|
- `~/.openclaw/openclaw.json`
|
||||||
|
- `~/.openclaw/agents/main/sessions/sessions.json`
|
||||||
|
- Stops local model guard LaunchAgents and gateway before update
|
||||||
|
- Runs `openclaw doctor --fix --non-interactive`
|
||||||
|
- Runs `openclaw update` with timeout protection
|
||||||
|
- Falls back to `npm install -g openclaw@...` if updater hangs/fails
|
||||||
|
- Prints timestamped step logs for restart/update traceability
|
||||||
|
- Starts gateway and installs service if missing
|
||||||
|
- If `~/.openclaw` is symlinked to `/Volumes/...`, patches gateway LaunchAgent logs to:
|
||||||
|
- `/tmp/openclaw-gateway.launchd.log`
|
||||||
|
- `/tmp/openclaw-gateway.launchd.err.log`
|
||||||
|
- Reinstalls schedule + budget guard LaunchAgents
|
||||||
|
- Kickstarts both guards immediately so profile/budget logic is applied now
|
||||||
|
- Prints gateway + model/guard status summary
|
||||||
|
|
||||||
|
## Useful options
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# switch update channel
|
||||||
|
bash ./scripts/update_openclaw.sh --channel beta
|
||||||
|
|
||||||
|
# one-off version/tag
|
||||||
|
bash ./scripts/update_openclaw.sh --tag 2026.2.24
|
||||||
|
|
||||||
|
# increase built-in update timeout (seconds)
|
||||||
|
UPDATE_MAX_SECONDS=900 bash ./scripts/update_openclaw.sh
|
||||||
|
|
||||||
|
# capture a timestamped run log file
|
||||||
|
bash ./scripts/update_openclaw.sh 2>&1 | tee "/tmp/openclaw-update-$(date +%Y%m%d-%H%M%S).log"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Verify after upgrade
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openclaw --version
|
||||||
|
openclaw gateway status
|
||||||
|
openclaw gateway health --json
|
||||||
|
bash ./scripts/model_profile_switch.sh status
|
||||||
|
launchctl print gui/$(id -u)/ai.openclaw.local.model-schedule-guard | rg 'last exit code|state'
|
||||||
|
launchctl print gui/$(id -u)/ai.openclaw.local.model-budget-guard | rg 'last exit code|state'
|
||||||
|
ls -lT /tmp/openclaw-gateway.launchd.log /tmp/openclaw-gateway.launchd.err.log
|
||||||
|
```
|
||||||
|
|
||||||
|
## If gateway is loaded but not running
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /Volumes/Data/openclaw-setups/openclaw-setup-max
|
||||||
|
bash ./scripts/update_openclaw.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
If still failing, see:
|
||||||
|
- `docs/operations/troubleshooting.md`
|
||||||
@ -106,30 +106,30 @@ openclaw gateway run --force
|
|||||||
- Launchd reports exit code `78: EX_CONFIG`.
|
- Launchd reports exit code `78: EX_CONFIG`.
|
||||||
|
|
||||||
### Fix that worked
|
### Fix that worked
|
||||||
- Ensure LaunchAgent command includes `gateway run`.
|
- Preferred: run the upgrade workflow, which self-heals LaunchAgent + guards.
|
||||||
- Keep logs in `/tmp` (avoids potential external-volume path issues).
|
- `update_openclaw.sh` prints timestamped logs so restart timing is visible during the run.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /Volumes/Data/openclaw-setups/openclaw-setup-max
|
||||||
|
bash ./scripts/update_openclaw.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
- Manual fallback:
|
||||||
|
1. Keep LaunchAgent logs in `/tmp` (avoids external-volume symlink path issues).
|
||||||
|
2. Reinstall and restart the gateway service.
|
||||||
|
|
||||||
File:
|
File:
|
||||||
- `~/Library/LaunchAgents/ai.openclaw.gateway.plist`
|
- `~/Library/LaunchAgents/ai.openclaw.gateway.plist`
|
||||||
|
|
||||||
Expected ProgramArguments section:
|
Set log paths:
|
||||||
```xml
|
```bash
|
||||||
<array>
|
/usr/libexec/PlistBuddy -c "Set :StandardOutPath /tmp/openclaw-gateway.launchd.log" ~/Library/LaunchAgents/ai.openclaw.gateway.plist
|
||||||
<string>/opt/homebrew/bin/node</string>
|
/usr/libexec/PlistBuddy -c "Set :StandardErrorPath /tmp/openclaw-gateway.launchd.err.log" ~/Library/LaunchAgents/ai.openclaw.gateway.plist
|
||||||
<string>/opt/homebrew/lib/node_modules/openclaw/dist/index.js</string>
|
|
||||||
<string>gateway</string>
|
|
||||||
<string>run</string>
|
|
||||||
<string>--port</string>
|
|
||||||
<string>18789</string>
|
|
||||||
</array>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Expected log paths:
|
|
||||||
- `/tmp/openclaw-gateway.launchd.log`
|
|
||||||
- `/tmp/openclaw-gateway.launchd.err.log`
|
|
||||||
|
|
||||||
Reload service:
|
Reload service:
|
||||||
```bash
|
```bash
|
||||||
|
openclaw gateway install --force
|
||||||
launchctl bootout gui/$(id -u)/ai.openclaw.gateway 2>/dev/null || true
|
launchctl bootout gui/$(id -u)/ai.openclaw.gateway 2>/dev/null || true
|
||||||
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/ai.openclaw.gateway.plist
|
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/ai.openclaw.gateway.plist
|
||||||
launchctl kickstart -k gui/$(id -u)/ai.openclaw.gateway
|
launchctl kickstart -k gui/$(id -u)/ai.openclaw.gateway
|
||||||
@ -137,8 +137,10 @@ launchctl kickstart -k gui/$(id -u)/ai.openclaw.gateway
|
|||||||
|
|
||||||
Verify:
|
Verify:
|
||||||
```bash
|
```bash
|
||||||
openclaw status --deep
|
launchctl print gui/$(id -u)/ai.openclaw.gateway | rg 'state =|last exit code|runs ='
|
||||||
|
openclaw gateway status
|
||||||
openclaw gateway health --json
|
openclaw gateway health --json
|
||||||
|
ls -lT /tmp/openclaw-gateway.launchd.log /tmp/openclaw-gateway.launchd.err.log
|
||||||
```
|
```
|
||||||
|
|
||||||
## 6) docs/context/BOOT.md hook setup
|
## 6) docs/context/BOOT.md hook setup
|
||||||
|
|||||||
365
openclaw-setup-max/scripts/update_openclaw.sh
Executable file
365
openclaw-setup-max/scripts/update_openclaw.sh
Executable file
@ -0,0 +1,365 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<'USAGE'
|
||||||
|
Usage:
|
||||||
|
bash ./scripts/update_openclaw.sh [options]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--channel <stable|beta|dev> Update channel (default: stable)
|
||||||
|
--tag <dist-tag|version> One-off npm tag/version (example: beta, 2026.2.24)
|
||||||
|
--skip-guard-stop Do not stop local model guard LaunchAgents before upgrade
|
||||||
|
--skip-guard-reinstall Do not reinstall local model guard LaunchAgents after upgrade
|
||||||
|
-h, --help Show this help
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
bash ./scripts/update_openclaw.sh
|
||||||
|
bash ./scripts/update_openclaw.sh --channel beta
|
||||||
|
bash ./scripts/update_openclaw.sh --tag 2026.2.24
|
||||||
|
USAGE
|
||||||
|
}
|
||||||
|
|
||||||
|
now_ts() { date '+%Y-%m-%d %H:%M:%S%z'; }
|
||||||
|
log() { printf '[%s] [update-openclaw] %s\n' "$(now_ts)" "$*"; }
|
||||||
|
warn() { printf '[%s] [update-openclaw] WARN: %s\n' "$(now_ts)" "$*" >&2; }
|
||||||
|
err() { printf '[%s] [update-openclaw] ERROR: %s\n' "$(now_ts)" "$*" >&2; }
|
||||||
|
|
||||||
|
require_cmd() {
|
||||||
|
if ! command -v "$1" >/dev/null 2>&1; then
|
||||||
|
err "Required command missing: $1"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
run_with_timeout() {
|
||||||
|
local timeout_s="$1"
|
||||||
|
shift
|
||||||
|
|
||||||
|
local start_ts elapsed pid
|
||||||
|
start_ts="$(date +%s)"
|
||||||
|
"$@" &
|
||||||
|
pid=$!
|
||||||
|
|
||||||
|
while kill -0 "$pid" 2>/dev/null; do
|
||||||
|
elapsed="$(( $(date +%s) - start_ts ))"
|
||||||
|
if (( elapsed >= timeout_s )); then
|
||||||
|
warn "Command timed out after ${timeout_s}s: $*"
|
||||||
|
kill -INT "$pid" 2>/dev/null || true
|
||||||
|
sleep 2
|
||||||
|
kill -TERM "$pid" 2>/dev/null || true
|
||||||
|
sleep 1
|
||||||
|
kill -KILL "$pid" 2>/dev/null || true
|
||||||
|
wait "$pid" 2>/dev/null || true
|
||||||
|
return 124
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
wait "$pid"
|
||||||
|
}
|
||||||
|
|
||||||
|
extract_version() {
|
||||||
|
local raw
|
||||||
|
raw="$("$1" --version 2>&1 || true)"
|
||||||
|
printf '%s\n' "$raw" | grep -Eo '[0-9]{4}\.[0-9]+\.[0-9]+' | head -n1
|
||||||
|
}
|
||||||
|
|
||||||
|
print_guard_status() {
|
||||||
|
local label="$1"
|
||||||
|
launchctl print "gui/$(id -u)/$label" 2>/dev/null | \
|
||||||
|
grep -E 'state =|last exit code|runs =|path =|program =' || true
|
||||||
|
}
|
||||||
|
|
||||||
|
is_launchagent_loaded() {
|
||||||
|
local label="$1"
|
||||||
|
launchctl print "gui/$(id -u)/$label" >/dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
is_gateway_launchagent_running() {
|
||||||
|
local label="$1"
|
||||||
|
launchctl print "gui/$(id -u)/$label" 2>/dev/null | grep -q 'state = running'
|
||||||
|
}
|
||||||
|
|
||||||
|
fix_gateway_launchagent_log_paths_for_external_state() {
|
||||||
|
local label="$1"
|
||||||
|
local plist="$HOME/Library/LaunchAgents/$label.plist"
|
||||||
|
local state_dir_link_target=""
|
||||||
|
|
||||||
|
if [[ ! -f "$plist" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# launchd may fail with EX_CONFIG when stdout/stderr paths point into
|
||||||
|
# ~/.openclaw on an external-volume symlink. Force local /tmp logs.
|
||||||
|
if [[ -L "$HOME/.openclaw" ]]; then
|
||||||
|
state_dir_link_target="$(readlink "$HOME/.openclaw" || true)"
|
||||||
|
fi
|
||||||
|
if [[ "$state_dir_link_target" != /Volumes/* ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v /usr/libexec/PlistBuddy >/dev/null 2>&1; then
|
||||||
|
warn "PlistBuddy not found; skipping LaunchAgent log-path patch."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local desired_out="/tmp/openclaw-gateway.launchd.log"
|
||||||
|
local desired_err="/tmp/openclaw-gateway.launchd.err.log"
|
||||||
|
local current_out=""
|
||||||
|
local current_err=""
|
||||||
|
|
||||||
|
current_out="$(/usr/libexec/PlistBuddy -c 'Print :StandardOutPath' "$plist" 2>/dev/null || true)"
|
||||||
|
current_err="$(/usr/libexec/PlistBuddy -c 'Print :StandardErrorPath' "$plist" 2>/dev/null || true)"
|
||||||
|
|
||||||
|
if [[ "$current_out" == "$desired_out" && "$current_err" == "$desired_err" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Patching gateway LaunchAgent logs to local /tmp paths (external state dir detected)"
|
||||||
|
/usr/libexec/PlistBuddy -c "Set :StandardOutPath $desired_out" "$plist"
|
||||||
|
/usr/libexec/PlistBuddy -c "Set :StandardErrorPath $desired_err" "$plist"
|
||||||
|
|
||||||
|
launchctl bootout "gui/$(id -u)/$label" 2>/dev/null || true
|
||||||
|
launchctl bootstrap "gui/$(id -u)" "$plist"
|
||||||
|
launchctl kickstart -k "gui/$(id -u)/$label"
|
||||||
|
}
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||||
|
|
||||||
|
CHANNEL="stable"
|
||||||
|
TAG=""
|
||||||
|
STOP_GUARDS="true"
|
||||||
|
REINSTALL_GUARDS="true"
|
||||||
|
UPDATE_MAX_SECONDS="${UPDATE_MAX_SECONDS:-600}"
|
||||||
|
|
||||||
|
while (( "$#" )); do
|
||||||
|
case "$1" in
|
||||||
|
--channel)
|
||||||
|
shift
|
||||||
|
if [[ $# -eq 0 ]]; then
|
||||||
|
err "--channel requires a value"
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
CHANNEL="$1"
|
||||||
|
;;
|
||||||
|
--tag)
|
||||||
|
shift
|
||||||
|
if [[ $# -eq 0 ]]; then
|
||||||
|
err "--tag requires a value"
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
TAG="$1"
|
||||||
|
;;
|
||||||
|
--skip-guard-stop)
|
||||||
|
STOP_GUARDS="false"
|
||||||
|
;;
|
||||||
|
--skip-guard-reinstall)
|
||||||
|
REINSTALL_GUARDS="false"
|
||||||
|
;;
|
||||||
|
-h|--help)
|
||||||
|
usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
err "Unknown argument: $1"
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
case "$CHANNEL" in
|
||||||
|
stable|beta|dev) ;;
|
||||||
|
*)
|
||||||
|
err "Invalid --channel '$CHANNEL'. Expected stable, beta, or dev."
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if ! [[ "$UPDATE_MAX_SECONDS" =~ ^[0-9]+$ ]] || [[ "$UPDATE_MAX_SECONDS" -lt 1 ]]; then
|
||||||
|
err "UPDATE_MAX_SECONDS must be a positive integer."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
require_cmd openclaw
|
||||||
|
require_cmd launchctl
|
||||||
|
require_cmd cp
|
||||||
|
require_cmd mkdir
|
||||||
|
require_cmd date
|
||||||
|
|
||||||
|
if ! command -v npm >/dev/null 2>&1; then
|
||||||
|
warn "npm not found; fallback npm upgrade path will be unavailable."
|
||||||
|
fi
|
||||||
|
|
||||||
|
ts="$(date +%Y%m%d-%H%M%S)"
|
||||||
|
backup_dir="$HOME/.openclaw/backups/update-$ts"
|
||||||
|
mkdir -p "$backup_dir"
|
||||||
|
|
||||||
|
config_file="$HOME/.openclaw/openclaw.json"
|
||||||
|
sessions_file="$HOME/.openclaw/agents/main/sessions/sessions.json"
|
||||||
|
|
||||||
|
log "Backing up OpenClaw files to: $backup_dir"
|
||||||
|
if [[ -f "$config_file" ]]; then
|
||||||
|
cp "$config_file" "$backup_dir/openclaw.json"
|
||||||
|
else
|
||||||
|
warn "Config not found: $config_file"
|
||||||
|
fi
|
||||||
|
if [[ -f "$sessions_file" ]]; then
|
||||||
|
cp "$sessions_file" "$backup_dir/sessions.json"
|
||||||
|
else
|
||||||
|
warn "Sessions file not found: $sessions_file"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$STOP_GUARDS" == "true" ]]; then
|
||||||
|
log "Stopping local model guard LaunchAgents to avoid config races during update"
|
||||||
|
launchctl bootout "gui/$(id -u)/ai.openclaw.local.model-budget-guard" 2>/dev/null || true
|
||||||
|
launchctl bootout "gui/$(id -u)/ai.openclaw.local.model-schedule-guard" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Stopping OpenClaw gateway before update"
|
||||||
|
if ! openclaw gateway stop; then
|
||||||
|
warn "Gateway stop returned non-zero (it may already be stopped). Continuing."
|
||||||
|
fi
|
||||||
|
|
||||||
|
current_version="$(extract_version openclaw || true)"
|
||||||
|
if [[ -n "$current_version" ]]; then
|
||||||
|
log "Current OpenClaw version: $current_version"
|
||||||
|
else
|
||||||
|
warn "Could not parse current OpenClaw version from 'openclaw --version'."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if command -v npm >/dev/null 2>&1; then
|
||||||
|
latest_npm_version="$(npm view openclaw version 2>/dev/null || true)"
|
||||||
|
if [[ -n "$latest_npm_version" ]]; then
|
||||||
|
log "Latest npm OpenClaw version: $latest_npm_version"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Running doctor fix for schema/config migration"
|
||||||
|
if ! openclaw doctor --fix --non-interactive; then
|
||||||
|
warn "Doctor fix returned non-zero. Continuing with upgrade."
|
||||||
|
fi
|
||||||
|
|
||||||
|
update_cmd=(openclaw update --yes --no-restart --channel "$CHANNEL")
|
||||||
|
if [[ -n "$TAG" ]]; then
|
||||||
|
update_cmd+=(--tag "$TAG")
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Running OpenClaw updater: ${update_cmd[*]}"
|
||||||
|
if ! run_with_timeout "$UPDATE_MAX_SECONDS" "${update_cmd[@]}"; then
|
||||||
|
warn "Built-in updater failed."
|
||||||
|
if ! command -v npm >/dev/null 2>&1; then
|
||||||
|
err "npm not available for fallback update."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
pkg="openclaw@latest"
|
||||||
|
if [[ -n "$TAG" ]]; then
|
||||||
|
pkg="openclaw@$TAG"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Falling back to npm global install: npm install -g $pkg"
|
||||||
|
npm install -g "$pkg"
|
||||||
|
fi
|
||||||
|
|
||||||
|
new_version="$(extract_version openclaw || true)"
|
||||||
|
if [[ -n "$new_version" ]]; then
|
||||||
|
log "Updated OpenClaw version: $new_version"
|
||||||
|
else
|
||||||
|
warn "Could not parse updated version from 'openclaw --version'."
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Starting OpenClaw gateway after update"
|
||||||
|
if ! openclaw gateway start; then
|
||||||
|
err "Gateway failed to start after update."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Some OpenClaw versions return success from "gateway start" even when the
|
||||||
|
# LaunchAgent is not installed/loaded; enforce a service-managed gateway.
|
||||||
|
gateway_label="ai.openclaw.gateway"
|
||||||
|
if ! launchctl print "gui/$(id -u)/$gateway_label" >/dev/null 2>&1; then
|
||||||
|
warn "Gateway LaunchAgent is not loaded after start; installing and retrying."
|
||||||
|
if ! openclaw gateway install --force; then
|
||||||
|
err "Failed to install gateway service."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! openclaw gateway start; then
|
||||||
|
err "Gateway failed to start after installing service."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! launchctl print "gui/$(id -u)/$gateway_label" >/dev/null 2>&1; then
|
||||||
|
err "Gateway LaunchAgent is still not loaded after install/start."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
fix_gateway_launchagent_log_paths_for_external_state "$gateway_label"
|
||||||
|
|
||||||
|
if ! is_gateway_launchagent_running "$gateway_label"; then
|
||||||
|
warn "Gateway LaunchAgent is loaded but not running; attempting kickstart."
|
||||||
|
launchctl kickstart -k "gui/$(id -u)/$gateway_label" || true
|
||||||
|
sleep 2
|
||||||
|
fi
|
||||||
|
if ! is_gateway_launchagent_running "$gateway_label"; then
|
||||||
|
err "Gateway LaunchAgent is still not running."
|
||||||
|
launchctl print "gui/$(id -u)/$gateway_label" | grep -E 'state =|last exit code|runs =|path =|program =' || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Running post-upgrade health checks"
|
||||||
|
openclaw doctor --non-interactive || true
|
||||||
|
openclaw gateway status || true
|
||||||
|
openclaw models status || true
|
||||||
|
|
||||||
|
if [[ "$REINSTALL_GUARDS" == "true" ]]; then
|
||||||
|
installer="$SCRIPT_DIR/install_local_model_guardrails.sh"
|
||||||
|
if [[ -x "$installer" ]]; then
|
||||||
|
log "Reinstalling local model guard LaunchAgents"
|
||||||
|
bash "$installer"
|
||||||
|
|
||||||
|
# Run both periodic guards immediately after reinstall so profile/budget
|
||||||
|
# state is refreshed in this update run (not only on the next interval).
|
||||||
|
launchctl kickstart -k "gui/$(id -u)/ai.openclaw.local.model-schedule-guard" 2>/dev/null || true
|
||||||
|
launchctl kickstart -k "gui/$(id -u)/ai.openclaw.local.model-budget-guard" 2>/dev/null || true
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
if ! is_launchagent_loaded "ai.openclaw.local.model-schedule-guard"; then
|
||||||
|
err "Schedule guard LaunchAgent is not loaded after reinstall."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! is_launchagent_loaded "ai.openclaw.local.model-budget-guard"; then
|
||||||
|
err "Budget guard LaunchAgent is not loaded after reinstall."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "Guard installer missing or not executable: $installer"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Local guard status summary"
|
||||||
|
print_guard_status "ai.openclaw.local.model-budget-guard"
|
||||||
|
print_guard_status "ai.openclaw.local.model-schedule-guard"
|
||||||
|
|
||||||
|
if [[ -x "$SCRIPT_DIR/model_profile_switch.sh" ]]; then
|
||||||
|
log "Profile status"
|
||||||
|
bash "$SCRIPT_DIR/model_profile_switch.sh" status || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
cat <<MSG
|
||||||
|
|
||||||
|
Upgrade complete.
|
||||||
|
- Backup directory: $backup_dir
|
||||||
|
- Channel: $CHANNEL
|
||||||
|
- Tag override: ${TAG:-none}
|
||||||
|
- Current version: ${new_version:-unknown}
|
||||||
|
|
||||||
|
If needed, inspect logs:
|
||||||
|
tail -n 80 /tmp/openclaw-model-budget-guard.log /tmp/openclaw-model-budget-guard.err.log
|
||||||
|
tail -n 80 /tmp/openclaw-model-schedule-guard.log /tmp/openclaw-model-schedule-guard.err.log
|
||||||
|
MSG
|
||||||
@ -115,11 +115,59 @@ node_major_version() {
|
|||||||
node -v 2>/dev/null | sed 's/^v//' | cut -d. -f1
|
node -v 2>/dev/null | sed 's/^v//' | cut -d. -f1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fix_gateway_launchagent_logs_for_external_state() {
|
||||||
|
local plist="$HOME/Library/LaunchAgents/ai.openclaw.gateway.plist"
|
||||||
|
local desired_out="/tmp/openclaw-gateway.launchd.log"
|
||||||
|
local desired_err="/tmp/openclaw-gateway.launchd.err.log"
|
||||||
|
|
||||||
|
if [[ ! -L "$HOME/.openclaw" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
local state_dir_link_target
|
||||||
|
state_dir_link_target="$(readlink "$HOME/.openclaw" || true)"
|
||||||
|
if [[ "$state_dir_link_target" != /Volumes/* ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -f "$plist" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if ! command -v /usr/libexec/PlistBuddy >/dev/null 2>&1; then
|
||||||
|
log_warn "PlistBuddy not found; skipping gateway LaunchAgent log-path patch."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local current_out current_err
|
||||||
|
current_out="$(/usr/libexec/PlistBuddy -c 'Print :StandardOutPath' "$plist" 2>/dev/null || true)"
|
||||||
|
current_err="$(/usr/libexec/PlistBuddy -c 'Print :StandardErrorPath' "$plist" 2>/dev/null || true)"
|
||||||
|
|
||||||
|
if [[ "$current_out" == "$desired_out" && "$current_err" == "$desired_err" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_warn "Patching gateway LaunchAgent logs to /tmp (external OpenClaw state dir detected)."
|
||||||
|
/usr/libexec/PlistBuddy -c "Set :StandardOutPath $desired_out" "$plist"
|
||||||
|
/usr/libexec/PlistBuddy -c "Set :StandardErrorPath $desired_err" "$plist"
|
||||||
|
|
||||||
|
launchctl bootout "gui/$(id -u)/ai.openclaw.gateway" 2>/dev/null || true
|
||||||
|
launchctl bootstrap "gui/$(id -u)" "$plist" 2>/dev/null || true
|
||||||
|
launchctl kickstart -k "gui/$(id -u)/ai.openclaw.gateway" 2>/dev/null || true
|
||||||
|
}
|
||||||
|
|
||||||
link_data_dir() {
|
link_data_dir() {
|
||||||
local source="$1"
|
local source="$1"
|
||||||
local target="$2"
|
local target="$2"
|
||||||
local label="$3"
|
local label="$3"
|
||||||
|
|
||||||
|
# Safety: target must be a real directory on the external volume, not a symlink.
|
||||||
|
if [ -L "$target" ]; then
|
||||||
|
local target_link
|
||||||
|
target_link="$(readlink "$target" || true)"
|
||||||
|
log_err "ERROR: ${label} target is a symlink: ${target} -> ${target_link}"
|
||||||
|
log_err "Expected a real directory on external storage. Remove/fix target symlink and re-run."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
mkdir -p "$target"
|
mkdir -p "$target"
|
||||||
|
|
||||||
if [ -L "$source" ]; then
|
if [ -L "$source" ]; then
|
||||||
@ -150,6 +198,12 @@ link_data_dir() {
|
|||||||
|
|
||||||
ln -s "$target" "$source"
|
ln -s "$target" "$source"
|
||||||
log_info "${label}: symlink created ${source} -> ${target}"
|
log_info "${label}: symlink created ${source} -> ${target}"
|
||||||
|
|
||||||
|
# Verify direction is exactly source -> target.
|
||||||
|
if [ ! -L "$source" ] || [ "$(readlink "$source")" != "$target" ]; then
|
||||||
|
log_err "ERROR: ${label} symlink direction check failed for ${source} -> ${target}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
ensure_ollama_running() {
|
ensure_ollama_running() {
|
||||||
@ -201,6 +255,7 @@ fi
|
|||||||
mkdir -p "$OLLAMA_DATA_TARGET" "$OPENCLAW_DATA_TARGET"
|
mkdir -p "$OLLAMA_DATA_TARGET" "$OPENCLAW_DATA_TARGET"
|
||||||
link_data_dir "$HOME/.ollama" "$OLLAMA_DATA_TARGET" "Ollama"
|
link_data_dir "$HOME/.ollama" "$OLLAMA_DATA_TARGET" "Ollama"
|
||||||
link_data_dir "$HOME/.openclaw" "$OPENCLAW_DATA_TARGET" "OpenClaw"
|
link_data_dir "$HOME/.openclaw" "$OPENCLAW_DATA_TARGET" "OpenClaw"
|
||||||
|
fix_gateway_launchagent_logs_for_external_state
|
||||||
|
|
||||||
# Step 2: Install / verify Ollama
|
# Step 2: Install / verify Ollama
|
||||||
if ! command -v ollama >/dev/null 2>&1; then
|
if ! command -v ollama >/dev/null 2>&1; then
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user