Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions install.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
$ErrorActionPreference = "Stop"

$REPO_DIR = Split-Path -Parent $MyInvocation.MyCommand.Path
$OC_HOME = Join-Path $env:USERPROFILE ".openclaw"
$OC_HOME = if ($env:OPENCLAW_HOME) { $env:OPENCLAW_HOME } else { Join-Path $env:USERPROFILE ".openclaw" }
$OC_CFG = Join-Path $OC_HOME "openclaw.json"

function Write-Banner {
Expand Down Expand Up @@ -118,7 +118,10 @@ function Register-Agents {
$pyScript = @"
import json, pathlib, sys, os
cfg_path = pathlib.Path(os.environ['USERPROFILE']) / '.openclaw' / 'openclaw.json'
oc_home = pathlib.Path(
os.environ.get('OPENCLAW_HOME', str(pathlib.Path(os.environ['USERPROFILE']) / '.openclaw'))
).expanduser()
cfg_path = oc_home / 'openclaw.json'
cfg = json.loads(cfg_path.read_text(encoding='utf-8'))
AGENTS = [
Expand All @@ -142,7 +145,7 @@ existing_ids = {a['id'] for a in agents_list}
added = 0
for ag in AGENTS:
ag_id = ag['id']
ws = str(pathlib.Path(os.environ['USERPROFILE']) / f'.openclaw/workspace-{ag_id}')
ws = str(oc_home / f'workspace-{ag_id}')
if ag_id not in existing_ids:
entry = {'id': ag_id, 'workspace': ws, **{k:v for k,v in ag.items() if k!='id'}}
agents_list.append(entry)
Expand Down
9 changes: 5 additions & 4 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
set -e

REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
OC_HOME="$HOME/.openclaw"
OC_HOME="${OPENCLAW_HOME:-$HOME/.openclaw}"
OC_CFG="$OC_HOME/openclaw.json"

RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; NC='\033[0m'
Expand Down Expand Up @@ -129,9 +129,10 @@ register_agents() {
log "已备份配置: $OC_CFG.bak.*"

python3 << 'PYEOF'
import json, pathlib, sys
import json, os as _os, pathlib, sys

cfg_path = pathlib.Path.home() / '.openclaw' / 'openclaw.json'
oc_home = pathlib.Path(_os.environ.get('OPENCLAW_HOME', str(pathlib.Path.home() / '.openclaw'))).expanduser()
cfg_path = oc_home / 'openclaw.json'
cfg = json.loads(cfg_path.read_text())

AGENTS = [
Expand All @@ -155,7 +156,7 @@ existing_ids = {a['id'] for a in agents_list}
added = 0
for ag in AGENTS:
ag_id = ag['id']
ws = str(pathlib.Path.home() / f'.openclaw/workspace-{ag_id}')
ws = str(oc_home / f'workspace-{ag_id}')
if ag_id not in existing_ids:
entry = {'id': ag_id, 'workspace': ws, **{k:v for k,v in ag.items() if k!='id'}}
agents_list.append(entry)
Expand Down
4 changes: 3 additions & 1 deletion scripts/apply_model_changes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
"""应用 data/pending_model_changes.json → openclaw.json,并重启 Gateway"""
import json, pathlib, subprocess, datetime, shutil, logging, glob
from file_lock import atomic_json_write, atomic_json_read
from utils import get_openclaw_home

log = logging.getLogger('model_change')
logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(name)s] %(message)s', datefmt='%H:%M:%S')

BASE = pathlib.Path(__file__).parent.parent
DATA = BASE / 'data'
OPENCLAW_CFG = pathlib.Path.home() / '.openclaw' / 'openclaw.json'
OPENCLAW_HOME = get_openclaw_home()
OPENCLAW_CFG = OPENCLAW_HOME / 'openclaw.json'
PENDING = DATA / 'pending_model_changes.json'
CHANGE_LOG = DATA / 'model_change_log.json'
MAX_BACKUPS = 10
Expand Down
10 changes: 5 additions & 5 deletions scripts/skill_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))
from utils import now_iso, safe_name, read_json
from utils import get_openclaw_home, now_iso, safe_name, read_json

OCLAW_HOME = Path.home() / '.openclaw'
OCLAW_HOME = get_openclaw_home()


def _download_file(url: str, timeout: int = 30, retries: int = 3) -> str:
Expand Down Expand Up @@ -230,8 +230,8 @@ def remove_remote(agent_id: str, name: str) -> bool:

def _get_hub_url(skill_name):
"""获取 skill 的 Hub URL,支持环境变量覆盖"""
base = (Path.home() / '.openclaw' / 'skills-hub-url').read_text().strip() \
if (Path.home() / '.openclaw' / 'skills-hub-url').exists() else None
hub_url_file = OCLAW_HOME / 'skills-hub-url'
base = hub_url_file.read_text().strip() if hub_url_file.exists() else None
base = base or os.environ.get(_HUB_BASE_ENV) or OFFICIAL_SKILLS_HUB_BASE
return f'{base.rstrip("/")}/{skill_name}/SKILL.md'

Expand Down Expand Up @@ -306,7 +306,7 @@ def import_official_hub(agent_ids: list) -> bool:
print(f' 1. 检查网络: curl -I {OFFICIAL_SKILLS_HUB_BASE}/code_review/SKILL.md')
print(f' 2. 设置代理: export https_proxy=http://your-proxy:port')
print(f' 3. 使用镜像: export {_HUB_BASE_ENV}=https://ghproxy.com/{OFFICIAL_SKILLS_HUB_BASE}')
print(f' 4. 自定义源: echo "https://your-mirror/skills" > ~/.openclaw/skills-hub-url')
print(f' 4. 自定义源: echo "https://your-mirror/skills" > {OCLAW_HOME / "skills-hub-url"}')
print(f' 5. 单独重试: python3 scripts/skill_manager.py add-remote --agent <agent> --name <skill> --source <url>')
return success == total

Expand Down
24 changes: 13 additions & 11 deletions scripts/sync_agent_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@
"""
import json, os, pathlib, datetime, logging
from file_lock import atomic_json_write
from utils import get_openclaw_home

log = logging.getLogger('sync_agent_config')
logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(name)s] %(message)s', datefmt='%H:%M:%S')

# Auto-detect project root (parent of scripts/)
BASE = pathlib.Path(__file__).parent.parent
DATA = BASE / 'data'
OPENCLAW_CFG = pathlib.Path.home() / '.openclaw' / 'openclaw.json'
OPENCLAW_HOME = get_openclaw_home()
OPENCLAW_CFG = OPENCLAW_HOME / 'openclaw.json'

ID_LABEL = {
'taizi': {'label': '太子', 'role': '太子', 'duty': '飞书消息分拣与回奏', 'emoji': '🤴'},
Expand Down Expand Up @@ -137,7 +139,7 @@ def main():
if ag_id not in ID_LABEL:
continue
meta = ID_LABEL[ag_id]
workspace = ag.get('workspace', str(pathlib.Path.home() / f'.openclaw/workspace-{ag_id}'))
workspace = ag.get('workspace', str(OPENCLAW_HOME / f'workspace-{ag_id}'))
if 'allowAgents' in ag:
allow_agents = ag.get('allowAgents', []) or []
else:
Expand All @@ -155,13 +157,13 @@ def main():

# 补充不在 openclaw.json agents list 中的 agent(兼容旧版 main)
EXTRA_AGENTS = {
'taizi': {'model': default_model, 'workspace': str(pathlib.Path.home() / '.openclaw/workspace-taizi'),
'taizi': {'model': default_model, 'workspace': str(OPENCLAW_HOME / 'workspace-taizi'),
'allowAgents': ['zhongshu']},
'main': {'model': default_model, 'workspace': str(pathlib.Path.home() / '.openclaw/workspace-main'),
'main': {'model': default_model, 'workspace': str(OPENCLAW_HOME / 'workspace-main'),
'allowAgents': ['zhongshu','menxia','shangshu','hubu','libu','bingbu','xingbu','gongbu','libu_hr']},
'zaochao': {'model': default_model, 'workspace': str(pathlib.Path.home() / '.openclaw/workspace-zaochao'),
'zaochao': {'model': default_model, 'workspace': str(OPENCLAW_HOME / 'workspace-zaochao'),
'allowAgents': []},
'libu_hr': {'model': default_model, 'workspace': str(pathlib.Path.home() / '.openclaw/workspace-libu_hr'),
'libu_hr': {'model': default_model, 'workspace': str(OPENCLAW_HOME / 'workspace-libu_hr'),
'allowAgents': ['shangshu']},
}
for ag_id, extra in EXTRA_AGENTS.items():
Expand Down Expand Up @@ -265,7 +267,7 @@ def sync_scripts_to_workspaces():
return
synced = 0
for proj_name, runtime_id in _SOUL_DEPLOY_MAP.items():
ws_scripts = pathlib.Path.home() / f'.openclaw/workspace-{runtime_id}' / 'scripts'
ws_scripts = OPENCLAW_HOME / f'workspace-{runtime_id}' / 'scripts'
ws_scripts.mkdir(parents=True, exist_ok=True)
for src_file in scripts_src.iterdir():
if src_file.suffix not in ('.py', '.sh') or src_file.stem.startswith('__'):
Expand All @@ -277,7 +279,7 @@ def sync_scripts_to_workspaces():
except Exception:
continue
# also sync to workspace-main for legacy compatibility
ws_main_scripts = pathlib.Path.home() / '.openclaw/workspace-main/scripts'
ws_main_scripts = OPENCLAW_HOME / 'workspace-main' / 'scripts'
ws_main_scripts.mkdir(parents=True, exist_ok=True)
for src_file in scripts_src.iterdir():
if src_file.suffix not in ('.py', '.sh') or src_file.stem.startswith('__'):
Expand All @@ -300,7 +302,7 @@ def deploy_soul_files():
src = agents_dir / proj_name / 'SOUL.md'
if not src.exists():
continue
ws_dst = pathlib.Path.home() / f'.openclaw/workspace-{runtime_id}' / 'soul.md'
ws_dst = OPENCLAW_HOME / f'workspace-{runtime_id}' / 'soul.md'
ws_dst.parent.mkdir(parents=True, exist_ok=True)
# 只在内容不同时更新(避免不必要的写入)
src_text = src.read_text(encoding='utf-8', errors='ignore')
Expand All @@ -313,7 +315,7 @@ def deploy_soul_files():
deployed += 1
# 太子兼容:同步一份到 legacy main agent 目录
if runtime_id == 'taizi':
ag_dst = pathlib.Path.home() / '.openclaw/agents/main/SOUL.md'
ag_dst = OPENCLAW_HOME / 'agents' / 'main' / 'SOUL.md'
ag_dst.parent.mkdir(parents=True, exist_ok=True)
try:
ag_text = ag_dst.read_text(encoding='utf-8', errors='ignore')
Expand All @@ -322,7 +324,7 @@ def deploy_soul_files():
if src_text != ag_text:
ag_dst.write_text(src_text, encoding='utf-8')
# 确保 sessions 目录存在
sess_dir = pathlib.Path.home() / f'.openclaw/agents/{runtime_id}/sessions'
sess_dir = OPENCLAW_HOME / 'agents' / runtime_id / 'sessions'
sess_dir.mkdir(parents=True, exist_ok=True)
if deployed:
log.info(f'{deployed} SOUL.md files deployed')
Expand Down
3 changes: 2 additions & 1 deletion scripts/sync_from_openclaw_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import traceback
import logging
from file_lock import atomic_json_write, atomic_json_read
from utils import get_openclaw_home

log = logging.getLogger('sync_runtime')
logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(name)s] %(message)s', datefmt='%H:%M:%S')
Expand All @@ -14,7 +15,7 @@
DATA = BASE / 'data'
DATA.mkdir(exist_ok=True)
SYNC_STATUS = DATA / 'sync_status.json'
SESSIONS_ROOT = pathlib.Path.home() / '.openclaw' / 'agents'
SESSIONS_ROOT = get_openclaw_home() / 'agents'


def write_status(**kwargs):
Expand Down
6 changes: 4 additions & 2 deletions scripts/sync_officials_stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@
"""同步各官员统计数据 → data/officials_stats.json"""
import json, pathlib, datetime, logging
from file_lock import atomic_json_write
from utils import get_openclaw_home

log = logging.getLogger('officials')
logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(name)s] %(message)s', datefmt='%H:%M:%S')

BASE = pathlib.Path(__file__).resolve().parent.parent
DATA = BASE / 'data'
AGENTS_ROOT = pathlib.Path.home() / '.openclaw' / 'agents'
OPENCLAW_CFG = pathlib.Path.home() / '.openclaw' / 'openclaw.json'
OPENCLAW_HOME = get_openclaw_home()
AGENTS_ROOT = OPENCLAW_HOME / 'agents'
OPENCLAW_CFG = OPENCLAW_HOME / 'openclaw.json'

# Anthropic 定价(每1M token,美元)
MODEL_PRICING = {
Expand Down
9 changes: 9 additions & 0 deletions scripts/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
三省六部 · 公共工具函数
避免 read_json / now_iso 等基础函数在多个脚本中重复定义
"""
import os
import json, pathlib, datetime


Expand All @@ -14,6 +15,14 @@ def read_json(path, default=None):
return default if default is not None else {}


def get_openclaw_home() -> pathlib.Path:
"""Return OpenClaw home directory, respecting OPENCLAW_HOME env var."""
env = os.environ.get('OPENCLAW_HOME')
if env:
return pathlib.Path(env).expanduser()
return pathlib.Path.home() / '.openclaw'


def now_iso():
"""返回 UTC ISO 8601 时间字符串(末尾 Z)"""
return datetime.datetime.now(datetime.timezone.utc).isoformat().replace('+00:00', 'Z')
Expand Down