Update setup-hf-config.mjs

#2
by ubix - opened
Files changed (1) hide show
  1. setup-hf-config.mjs +126 -21
setup-hf-config.mjs CHANGED
@@ -2,11 +2,30 @@
2
  /**
3
  * One-time setup for OpenClaw on Hugging Face Spaces.
4
  * Runs at container startup; writes or merges openclaw.json from env (Secrets/Variables):
5
- * - agents.defaults.model.primary from OPENCLAW_HF_DEFAULT_MODEL (default: DeepSeek-R1).
6
- * - gateway.auth: OPENCLAW_GATEWAY_TOKEN (token) or OPENCLAW_GATEWAY_PASSWORD (password); token wins if both set.
7
- * - gateway.controlUi.dangerouslyDisableDeviceAuth when auth is set (no device pairing in Spaces).
8
- * - gateway.trustedProxies from OPENCLAW_GATEWAY_TRUSTED_PROXIES, or default HF proxy IPs so the UI works without extra config.
9
- * - gateway.controlUi.allowedOrigins from OPENCLAW_CONTROL_UI_ALLOWED_ORIGINS (comma-separated origins).
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  * HF_TOKEN is read by the gateway at runtime; this script only writes the above into config.
11
  */
12
  import fs from "node:fs";
@@ -16,7 +35,7 @@ const home = process.env.OPENCLAW_HOME || process.env.HOME || "/home/user";
16
  const stateDir = path.join(home, ".openclaw");
17
  const configPath = path.join(stateDir, "openclaw.json");
18
 
19
- // Token: env var, or read from file if OPENCLAW_GATEWAY_TOKEN_FILE is set (for platforms that mount secrets as files)
20
  function readGatewayToken() {
21
  const fromEnv = process.env.OPENCLAW_GATEWAY_TOKEN?.trim();
22
  if (fromEnv) return fromEnv;
@@ -31,12 +50,34 @@ function readGatewayToken() {
31
  return "";
32
  }
33
 
34
- const defaultModel =
35
- process.env.OPENCLAW_HF_DEFAULT_MODEL?.trim() || "huggingface/deepseek-ai/DeepSeek-R1";
36
- const gatewayToken = readGatewayToken();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  const gatewayPassword = process.env.OPENCLAW_GATEWAY_PASSWORD?.trim();
 
38
 
39
- // Trusted proxies: from env, or default HF Space proxy IPs so the Control UI works without setting OPENCLAW_GATEWAY_TRUSTED_PROXIES
 
 
 
40
  const DEFAULT_HF_TRUSTED_PROXY_IPS = [
41
  "10.16.4.123",
42
  "10.16.34.155",
@@ -50,72 +91,136 @@ const trustedProxies =
50
  trustedProxiesRaw && trustedProxiesRaw.length > 0
51
  ? trustedProxiesRaw.split(",").map((s) => s.trim()).filter(Boolean)
52
  : DEFAULT_HF_TRUSTED_PROXY_IPS;
53
- // Comma-separated origins allowed for Control UI/WebSocket (e.g. https://your-space.hf.space)
 
54
  const allowedOriginsRaw = process.env.OPENCLAW_CONTROL_UI_ALLOWED_ORIGINS?.trim();
55
  const allowedOrigins = allowedOriginsRaw
56
  ? allowedOriginsRaw.split(",").map((s) => s.trim()).filter(Boolean)
57
  : [];
58
 
 
59
  let config = {};
60
  if (fs.existsSync(configPath)) {
61
  try {
62
  config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
63
  } catch {
64
- // keep config empty
65
  }
66
  }
67
 
 
 
68
  if (!config.agents) config.agents = {};
69
  if (!config.agents.defaults) config.agents.defaults = {};
70
  if (!config.agents.defaults.model) config.agents.defaults.model = {};
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  config.agents.defaults.model.primary = defaultModel;
72
 
73
- // Auth: token wins if both set; otherwise password
74
- const useTokenAuth = Boolean(gatewayToken);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  const usePasswordAuth = Boolean(gatewayPassword) && !useTokenAuth;
 
76
  if (useTokenAuth || usePasswordAuth) {
77
  if (!config.gateway) config.gateway = {};
78
  if (!config.gateway.auth) config.gateway.auth = {};
79
  if (useTokenAuth) {
80
- config.gateway.auth.mode = "token";
81
  config.gateway.auth.token = gatewayToken;
82
  } else {
83
- config.gateway.auth.mode = "password";
84
  config.gateway.auth.password = gatewayPassword;
85
  }
86
  }
87
- // So Control UI can connect with token/password only (no device pairing); Spaces have no CLI for approve.
 
 
88
  if (useTokenAuth || usePasswordAuth) {
89
  if (!config.gateway) config.gateway = {};
90
  if (!config.gateway.controlUi) config.gateway.controlUi = {};
91
  config.gateway.controlUi.dangerouslyDisableDeviceAuth = true;
92
  }
93
 
94
- // Always set trustedProxies (we have a default for HF so the Control UI works behind the HF proxy)
95
  if (!config.gateway) config.gateway = {};
96
  config.gateway.trustedProxies = trustedProxies;
97
 
 
98
  if (allowedOrigins.length > 0) {
99
- if (!config.gateway) config.gateway = {};
100
  if (!config.gateway.controlUi) config.gateway.controlUi = {};
101
  config.gateway.controlUi.allowedOrigins = allowedOrigins;
102
  }
103
 
 
104
  fs.mkdirSync(stateDir, { recursive: true });
105
  fs.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
106
 
107
- // Startup log: confirm env was applied (check token_present=1 and auth=token so the UI can connect)
108
  const authKind = useTokenAuth ? "token" : usePasswordAuth ? "password" : "none";
109
  const parts = [
110
  `token_present=${useTokenAuth ? "1" : "0"}`,
111
  `password_present=${usePasswordAuth ? "1" : "0"}`,
112
  `auth=${authKind}`,
 
 
 
 
113
  `trustedProxies=${trustedProxies.length}`,
114
  `allowedOrigins=${allowedOrigins.length}`,
115
  ];
116
  console.log(`[openclaw-hf-setup] ${parts.join(" ")} -> ${configPath}`);
 
117
  if (authKind === "none") {
118
  console.warn(
119
- "[openclaw-hf-setup] No auth set. Add OPENCLAW_GATEWAY_TOKEN or OPENCLAW_GATEWAY_PASSWORD in Space Secrets, then restart.",
 
120
  );
121
  }
 
2
  /**
3
  * One-time setup for OpenClaw on Hugging Face Spaces.
4
  * Runs at container startup; writes or merges openclaw.json from env (Secrets/Variables):
5
+ *
6
+ * Custom Ollama provider (hf-ollama-qwen3-vl) β€” PRIMARY default model:
7
+ * - providers["hf-ollama-qwen3-vl"] registered with baseUrl + apiKey
8
+ * - agents.defaults.model.primary = "hf-ollama-qwen3-vl/voytas26/openclaw-qwen3vl-8b-opt"
9
+ * - Configure via:
10
+ * OPENCLAW_OLLAMA_BASE_URL (default: https://ubix-Clawd.hf.space/v1)
11
+ * OPENCLAW_OLLAMA_API_KEY (default: "ollama")
12
+ * - Falls back to OPENCLAW_HF_DEFAULT_MODEL (or HF DeepSeek-R1) when OPENCLAW_OLLAMA_BASE_URL
13
+ * is explicitly set to empty string to disable.
14
+ *
15
+ * OpenRouter / Perplexity (search-augmented model):
16
+ * - providers.openrouter.apiKey from OPENROUTER_API_KEY
17
+ * - agents.defaults.model.search set to openrouter/perplexity/sonar
18
+ * (only written when OPENROUTER_API_KEY is present)
19
+ *
20
+ * Gateway auth:
21
+ * - gateway.auth: OPENCLAW_GATEWAY_TOKEN (token) or OPENCLAW_GATEWAY_PASSWORD (password)
22
+ * token wins if both are set
23
+ * - gateway.controlUi.dangerouslyDisableDeviceAuth when auth is set (no device pairing in Spaces)
24
+ *
25
+ * Networking:
26
+ * - gateway.trustedProxies from OPENCLAW_GATEWAY_TRUSTED_PROXIES, or default HF proxy IPs
27
+ * - gateway.controlUi.allowedOrigins from OPENCLAW_CONTROL_UI_ALLOWED_ORIGINS (comma-separated)
28
+ *
29
  * HF_TOKEN is read by the gateway at runtime; this script only writes the above into config.
30
  */
31
  import fs from "node:fs";
 
35
  const stateDir = path.join(home, ".openclaw");
36
  const configPath = path.join(stateDir, "openclaw.json");
37
 
38
+ // ── Gateway token (supports file-mounted secrets for platforms that mount secrets as files) ──
39
  function readGatewayToken() {
40
  const fromEnv = process.env.OPENCLAW_GATEWAY_TOKEN?.trim();
41
  if (fromEnv) return fromEnv;
 
50
  return "";
51
  }
52
 
53
+ // ── Env reads ──────────────────────────────────────────────────────────────────────────────────
54
+
55
+ // Custom Ollama/OpenAI-compat provider hosted on HF Space
56
+ // Set OPENCLAW_OLLAMA_BASE_URL="" to disable and fall back to HF Inference
57
+ const ollamaBaseUrl = process.env.OPENCLAW_OLLAMA_BASE_URL !== undefined
58
+ ? process.env.OPENCLAW_OLLAMA_BASE_URL.trim()
59
+ : "https://ubix-Clawd.hf.space/v1";
60
+ const ollamaApiKey = process.env.OPENCLAW_OLLAMA_API_KEY?.trim() || "ollama";
61
+ const ollamaEnabled = ollamaBaseUrl.length > 0;
62
+
63
+ // Fallback HF Inference model (used only when Ollama provider is disabled)
64
+ const hfFallbackModel =
65
+ process.env.OPENCLAW_HF_DEFAULT_MODEL?.trim() ||
66
+ "huggingface/deepseek-ai/DeepSeek-R1";
67
+
68
+ // Resolved primary model
69
+ const defaultModel = ollamaEnabled
70
+ ? "hf-ollama-qwen3-vl/voytas26/openclaw-qwen3vl-8b-opt"
71
+ : hfFallbackModel;
72
+
73
+ const gatewayToken = readGatewayToken();
74
  const gatewayPassword = process.env.OPENCLAW_GATEWAY_PASSWORD?.trim();
75
+ const openrouterKey = process.env.OPENROUTER_API_KEY?.trim();
76
 
77
+ // ── Trusted proxies ────────────────────────────────────────────────────────────────────────────
78
+ // Default HF Space proxy IPs so the Control UI works without extra config.
79
+ // Override with OPENCLAW_GATEWAY_TRUSTED_PROXIES (comma-separated) if you see
80
+ // "Proxy headers detected from untrusted address" in the logs.
81
  const DEFAULT_HF_TRUSTED_PROXY_IPS = [
82
  "10.16.4.123",
83
  "10.16.34.155",
 
91
  trustedProxiesRaw && trustedProxiesRaw.length > 0
92
  ? trustedProxiesRaw.split(",").map((s) => s.trim()).filter(Boolean)
93
  : DEFAULT_HF_TRUSTED_PROXY_IPS;
94
+
95
+ // ── Allowed origins ──���─────────────────────────────────────────────────────────────────────────
96
  const allowedOriginsRaw = process.env.OPENCLAW_CONTROL_UI_ALLOWED_ORIGINS?.trim();
97
  const allowedOrigins = allowedOriginsRaw
98
  ? allowedOriginsRaw.split(",").map((s) => s.trim()).filter(Boolean)
99
  : [];
100
 
101
+ // ── Load existing config (merge, don't overwrite) ─────────────────────────────────────────────
102
  let config = {};
103
  if (fs.existsSync(configPath)) {
104
  try {
105
  config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
106
  } catch {
107
+ // keep config empty on parse error
108
  }
109
  }
110
 
111
+ // ── 1. Register providers & set default model ─────────────────────────────────────────────────
112
+ if (!config.providers) config.providers = {};
113
  if (!config.agents) config.agents = {};
114
  if (!config.agents.defaults) config.agents.defaults = {};
115
  if (!config.agents.defaults.model) config.agents.defaults.model = {};
116
+
117
+ // 1a. Custom Ollama/OpenAI-compat provider (hf-ollama-qwen3-vl)
118
+ if (ollamaEnabled) {
119
+ config.providers["hf-ollama-qwen3-vl"] = {
120
+ baseUrl: ollamaBaseUrl,
121
+ apiKey: ollamaApiKey,
122
+ api: "openai-completions",
123
+ models: [
124
+ {
125
+ id: "voytas26/openclaw-qwen3vl-8b-opt",
126
+ name: "qwen3vl (HF Space - CPU)",
127
+ reasoning: true,
128
+ input: ["text"],
129
+ contextWindow: 128000,
130
+ maxTokens: 32000,
131
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
132
+ },
133
+ ],
134
+ };
135
+ console.log(
136
+ `[openclaw-hf-setup] hf-ollama-qwen3-vl provider registered -> ${ollamaBaseUrl}`
137
+ );
138
+ } else {
139
+ console.log(
140
+ "[openclaw-hf-setup] OPENCLAW_OLLAMA_BASE_URL is empty β€” Ollama provider disabled, " +
141
+ `falling back to: ${hfFallbackModel}`
142
+ );
143
+ }
144
+
145
+ // 1b. Set primary default model
146
  config.agents.defaults.model.primary = defaultModel;
147
 
148
+ // ── 2. OpenRouter provider + Perplexity Sonar as search model ─────────────────────────────────
149
+ if (openrouterKey) {
150
+ config.providers.openrouter = {
151
+ apiKey: openrouterKey,
152
+ };
153
+
154
+ // Use Perplexity Sonar (web-search-augmented) for search/research tasks.
155
+ config.agents.defaults.model.search = "openrouter/perplexity/sonar";
156
+
157
+ console.log(
158
+ "[openclaw-hf-setup] OpenRouter configured -> search model: openrouter/perplexity/sonar"
159
+ );
160
+ } else {
161
+ console.warn(
162
+ "[openclaw-hf-setup] OPENROUTER_API_KEY not set. " +
163
+ "Perplexity Sonar search model will not be available. " +
164
+ "Add OPENROUTER_API_KEY in Space Secrets to enable web search."
165
+ );
166
+ }
167
+
168
+ // ── 3. Gateway auth ────────────────────────────────────────────────────────────────────────────
169
+ const useTokenAuth = Boolean(gatewayToken);
170
  const usePasswordAuth = Boolean(gatewayPassword) && !useTokenAuth;
171
+
172
  if (useTokenAuth || usePasswordAuth) {
173
  if (!config.gateway) config.gateway = {};
174
  if (!config.gateway.auth) config.gateway.auth = {};
175
  if (useTokenAuth) {
176
+ config.gateway.auth.mode = "token";
177
  config.gateway.auth.token = gatewayToken;
178
  } else {
179
+ config.gateway.auth.mode = "password";
180
  config.gateway.auth.password = gatewayPassword;
181
  }
182
  }
183
+
184
+ // Disable device pairing β€” Spaces have no CLI to approve pairing requests.
185
+ // Control UI will accept token/password only.
186
  if (useTokenAuth || usePasswordAuth) {
187
  if (!config.gateway) config.gateway = {};
188
  if (!config.gateway.controlUi) config.gateway.controlUi = {};
189
  config.gateway.controlUi.dangerouslyDisableDeviceAuth = true;
190
  }
191
 
192
+ // ── 4. Trusted proxies ─────────────────────────────────────────────────────────────────────────
193
  if (!config.gateway) config.gateway = {};
194
  config.gateway.trustedProxies = trustedProxies;
195
 
196
+ // ── 5. Allowed origins ─────────────────────────────────────────────────────────────────────────
197
  if (allowedOrigins.length > 0) {
 
198
  if (!config.gateway.controlUi) config.gateway.controlUi = {};
199
  config.gateway.controlUi.allowedOrigins = allowedOrigins;
200
  }
201
 
202
+ // ── Write config ───────────────────────────────────────────────────────────────────────────────
203
  fs.mkdirSync(stateDir, { recursive: true });
204
  fs.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
205
 
206
+ // ── Startup summary ────────────────────────────────────────────────────────────────────────────
207
  const authKind = useTokenAuth ? "token" : usePasswordAuth ? "password" : "none";
208
  const parts = [
209
  `token_present=${useTokenAuth ? "1" : "0"}`,
210
  `password_present=${usePasswordAuth ? "1" : "0"}`,
211
  `auth=${authKind}`,
212
+ `ollama_provider=${ollamaEnabled ? "1" : "0"}`,
213
+ `default_model=${defaultModel}`,
214
+ `openrouter_configured=${openrouterKey ? "1" : "0"}`,
215
+ `search_model=${openrouterKey ? "openrouter/perplexity/sonar" : "none"}`,
216
  `trustedProxies=${trustedProxies.length}`,
217
  `allowedOrigins=${allowedOrigins.length}`,
218
  ];
219
  console.log(`[openclaw-hf-setup] ${parts.join(" ")} -> ${configPath}`);
220
+
221
  if (authKind === "none") {
222
  console.warn(
223
+ "[openclaw-hf-setup] No auth set. " +
224
+ "Add OPENCLAW_GATEWAY_TOKEN or OPENCLAW_GATEWAY_PASSWORD in Space Secrets, then restart."
225
  );
226
  }