sharween commited on
Commit
4b045d4
·
verified ·
1 Parent(s): 8fe0c57

Upload config-generator.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. config-generator.py +172 -0
config-generator.py ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ config-generator.py — 环境变量驱动的 openclaw.json 生成器
4
+
5
+ 三层配置合并:
6
+ 1. 基础模板 (安全默认值)
7
+ 2. 从备份恢复的 openclaw.json
8
+ 3. 环境变量覆盖 (最高优先级)
9
+
10
+ 端口: 18888 (HF Space 暴露端口)
11
+ """
12
+
13
+ import json, os, copy
14
+
15
+ STATE_DIR = os.environ.get("OPENCLAW_STATE_DIR", "/root/.openclaw")
16
+ PORT = int(os.environ.get("PORT", 18888))
17
+
18
+ def deep_merge(base: dict, overlay: dict) -> dict:
19
+ result = copy.deepcopy(base)
20
+ for k, v in overlay.items():
21
+ if k in result and isinstance(result[k], dict) and isinstance(v, dict):
22
+ result[k] = deep_merge(result[k], v)
23
+ else:
24
+ result[k] = copy.deepcopy(v)
25
+ return result
26
+
27
+ def _get_gw_password() -> str:
28
+ pw = os.environ.get("OPENCLAW_GATEWAY_PASSWORD", "") or os.environ.get("OPENCLAW_GATEWAY_TOKEN", "")
29
+ if not pw:
30
+ import secrets
31
+ pw = "openclaw-" + secrets.token_hex(8)
32
+ print(f"[config] Generated gateway password: {pw}")
33
+ return pw
34
+
35
+ def generate() -> dict:
36
+ # ── Layer 1: 基础模板 ──
37
+ config = {
38
+ "models": {"providers": {}},
39
+ "agents": {"defaults": {}, "list": []},
40
+ "skills": {"entries": {}, "allowBundled": []},
41
+ "plugins": {"entries": {}, "allow": []},
42
+ "gateway": {
43
+ "mode": "local",
44
+ "bind": "lan",
45
+ "port": PORT,
46
+ "trustedProxies": ["0.0.0.0/0"],
47
+ "auth": {
48
+ "mode": "token",
49
+ "token": _get_gw_password(),
50
+ },
51
+ "controlUi": {
52
+ "enabled": True,
53
+ "allowInsecureAuth": True,
54
+ "dangerouslyDisableDeviceAuth": True,
55
+ "dangerouslyAllowHostHeaderOriginFallback": True,
56
+ },
57
+ },
58
+ "browser": {
59
+ "enabled": False,
60
+ "headless": True,
61
+ },
62
+ }
63
+
64
+ # ── Layer 2: 合并恢复的配置 ──
65
+ restored_path = os.path.join(STATE_DIR, "openclaw.json")
66
+ if os.path.exists(restored_path):
67
+ try:
68
+ with open(restored_path) as f:
69
+ restored = json.load(f)
70
+ config = deep_merge(config, restored)
71
+ except Exception as e:
72
+ print(f"[config] Warning: could not load restored config: {e}")
73
+
74
+ # ── Layer 3: 环境变量覆盖 ──
75
+
76
+ # 3a. 模型提供商 (只在有 API base + key 时生成)
77
+ provider = os.environ.get("LLM_PROVIDER", "default")
78
+ base_url = os.environ.get("OPENAI_API_BASE", "").rstrip("/")
79
+ base_url = base_url.replace("/chat/completions", "")
80
+
81
+ if base_url and os.environ.get("OPENAI_API_KEY"):
82
+ config["models"]["providers"][provider] = {
83
+ "baseUrl": base_url,
84
+ "apiKey": os.environ.get("OPENAI_API_KEY", ""),
85
+ "api": "openai-completions",
86
+ "models": [
87
+ {
88
+ "id": os.environ.get("MODEL", "gpt-4o"),
89
+ "name": os.environ.get("MODEL", "gpt-4o"),
90
+ "contextWindow": 128000,
91
+ }
92
+ ],
93
+ }
94
+ # 额外模型 (JSON array)
95
+ extra = os.environ.get("EXTRA_MODELS", "")
96
+ if extra:
97
+ try:
98
+ config["models"]["providers"][provider]["models"].extend(json.loads(extra))
99
+ except Exception:
100
+ pass
101
+ else:
102
+ # 无 API 配置时占位,避免配置验证失败
103
+ config["models"]["providers"]["placeholder"] = {
104
+ "baseUrl": "http://localhost:8080/v1",
105
+ "apiKey": "not-configured-yet",
106
+ "api": "openai-completions",
107
+ "models": [{"id": "placeholder", "name": "placeholder", "contextWindow": 4096}],
108
+ }
109
+ print("[config] Warning: OPENAI_API_BASE or OPENAI_API_KEY not set, using placeholder")
110
+
111
+ # 3b. 兼容旧版 plugins.allow → plugins.bundledDiscovery
112
+ config["plugins"]["bundledDiscovery"] = "compat"
113
+
114
+ # 3b. Agent 默认配置
115
+ default_model = f"{provider}/{os.environ.get('MODEL', 'gpt-4o')}"
116
+ config["agents"]["defaults"] = {
117
+ "model": {"primary": default_model},
118
+ "workspace": os.path.join(STATE_DIR, "workspace"),
119
+ }
120
+
121
+ # 3c. 5 Agent (env var 可覆盖)
122
+ default_names = ["开发组-产品经理", "开发组-架构师", "开发组-开发", "开发组-测试", "开发组-项目经理"]
123
+ for i in range(1, 6):
124
+ name = os.environ.get(f"AGENT_{i}_NAME", default_names[i - 1])
125
+ model_override = os.environ.get(f"AGENT_{i}_MODEL", default_model)
126
+
127
+ agent_def = {
128
+ "id": f"agent-{i}",
129
+ "name": name,
130
+ "workspace": os.path.join(STATE_DIR, f"workspace-agent-{i}"),
131
+ "model": {"primary": model_override},
132
+ }
133
+
134
+ skills_str = os.environ.get(f"AGENT_{i}_SKILLS", "")
135
+ if skills_str:
136
+ agent_def["skills"] = [s.strip() for s in skills_str.split(",")]
137
+
138
+ config["agents"]["list"].append(agent_def)
139
+
140
+ # 3d. Skills
141
+ skills_enabled = os.environ.get("SKILLS_ENABLED", "")
142
+ if skills_enabled:
143
+ for s in skills_enabled.split(","):
144
+ s = s.strip()
145
+ if s:
146
+ config["skills"]["entries"][s] = {"enabled": True}
147
+
148
+ # 3e. Plugins
149
+ plugins_allow = os.environ.get("PLUGINS_ALLOW", "")
150
+ if plugins_allow:
151
+ config["plugins"]["allow"] = [p.strip() for p in plugins_allow.split(",")]
152
+
153
+ # 微信插件
154
+ if os.environ.get("WEIXIN_ENABLED", "true").lower() == "true":
155
+ config["plugins"]["entries"]["openclaw-weixin"] = {"enabled": True}
156
+ if "openclaw-weixin" not in config["plugins"]["allow"]:
157
+ config["plugins"]["allow"].append("openclaw-weixin")
158
+
159
+ # ccswitch 插件 (预留)
160
+ if os.environ.get("CCSWITCH_ENABLED", "false").lower() == "true":
161
+ config.setdefault("plugins", {}).setdefault("entries", {})
162
+ config["plugins"]["entries"]["ccswitch"] = {"enabled": True}
163
+
164
+ return config
165
+
166
+ if __name__ == "__main__":
167
+ config = generate()
168
+ os.makedirs(STATE_DIR, exist_ok=True)
169
+ out = os.path.join(STATE_DIR, "openclaw.json")
170
+ with open(out, "w") as f:
171
+ json.dump(config, f, indent=2, ensure_ascii=False)
172
+ print(f"[config] Generated → {out}")