hugg-claw / setup-hf-config.mjs
Tonic's picture
Update setup-hf-config.mjs
0c183d5 verified
#!/usr/bin/env node
/**
* One-time setup for OpenClaw on Hugging Face Spaces.
* Runs at container startup; writes or merges openclaw.json from env (Secrets/Variables):
* - agents.defaults.model.primary from OPENCLAW_HF_DEFAULT_MODEL (default: DeepSeek-R1).
* - gateway.auth: OPENCLAW_GATEWAY_TOKEN (token) or OPENCLAW_GATEWAY_PASSWORD (password); token wins if both set.
* - gateway.controlUi.dangerouslyDisableDeviceAuth when auth is set (no device pairing in Spaces).
* - gateway.trustedProxies from OPENCLAW_GATEWAY_TRUSTED_PROXIES, or default HF proxy IPs so the UI works without extra config.
* - gateway.controlUi.allowedOrigins from OPENCLAW_CONTROL_UI_ALLOWED_ORIGINS (comma-separated origins).
* HF_TOKEN is read by the gateway at runtime; this script only writes the above into config.
*/
import fs from "node:fs";
import path from "node:path";
const home = process.env.OPENCLAW_HOME || process.env.HOME || "/home/user";
const stateDir = path.join(home, ".openclaw");
const configPath = path.join(stateDir, "openclaw.json");
// Token: env var, or read from file if OPENCLAW_GATEWAY_TOKEN_FILE is set (for platforms that mount secrets as files)
function readGatewayToken() {
const fromEnv = process.env.OPENCLAW_GATEWAY_TOKEN?.trim();
if (fromEnv) return fromEnv;
const filePath = process.env.OPENCLAW_GATEWAY_TOKEN_FILE?.trim();
if (filePath && fs.existsSync(filePath)) {
try {
return fs.readFileSync(filePath, "utf-8").trim();
} catch {
return "";
}
}
return "";
}
const defaultModel =
process.env.OPENCLAW_HF_DEFAULT_MODEL?.trim() || "huggingface/deepseek-ai/DeepSeek-R1";
const gatewayToken = readGatewayToken();
const gatewayPassword = process.env.OPENCLAW_GATEWAY_PASSWORD?.trim();
// Trusted proxies: from env, or default HF Space proxy IPs so the Control UI works without setting OPENCLAW_GATEWAY_TRUSTED_PROXIES
const DEFAULT_HF_TRUSTED_PROXY_IPS = [
"10.16.4.123",
"10.16.34.155",
"10.20.1.9",
"10.20.1.222",
"10.20.26.157",
"10.20.31.87",
];
const trustedProxiesRaw = process.env.OPENCLAW_GATEWAY_TRUSTED_PROXIES?.trim();
const trustedProxies =
trustedProxiesRaw && trustedProxiesRaw.length > 0
? trustedProxiesRaw.split(",").map((s) => s.trim()).filter(Boolean)
: DEFAULT_HF_TRUSTED_PROXY_IPS;
// Comma-separated origins allowed for Control UI/WebSocket (e.g. https://your-space.hf.space)
const allowedOriginsRaw = process.env.OPENCLAW_CONTROL_UI_ALLOWED_ORIGINS?.trim();
const allowedOrigins = allowedOriginsRaw
? allowedOriginsRaw.split(",").map((s) => s.trim()).filter(Boolean)
: [];
let config = {};
if (fs.existsSync(configPath)) {
try {
config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
} catch {
// keep config empty
}
}
if (!config.agents) config.agents = {};
if (!config.agents.defaults) config.agents.defaults = {};
if (!config.agents.defaults.model) config.agents.defaults.model = {};
config.agents.defaults.model.primary = defaultModel;
// Auth: token wins if both set; otherwise password
const useTokenAuth = Boolean(gatewayToken);
const usePasswordAuth = Boolean(gatewayPassword) && !useTokenAuth;
if (useTokenAuth || usePasswordAuth) {
if (!config.gateway) config.gateway = {};
if (!config.gateway.auth) config.gateway.auth = {};
if (useTokenAuth) {
config.gateway.auth.mode = "token";
config.gateway.auth.token = gatewayToken;
} else {
config.gateway.auth.mode = "password";
config.gateway.auth.password = gatewayPassword;
}
}
// So Control UI can connect with token/password only (no device pairing); Spaces have no CLI for approve.
if (useTokenAuth || usePasswordAuth) {
if (!config.gateway) config.gateway = {};
if (!config.gateway.controlUi) config.gateway.controlUi = {};
config.gateway.controlUi.dangerouslyDisableDeviceAuth = true;
}
// Always set trustedProxies (we have a default for HF so the Control UI works behind the HF proxy)
if (!config.gateway) config.gateway = {};
config.gateway.trustedProxies = trustedProxies;
if (allowedOrigins.length > 0) {
if (!config.gateway) config.gateway = {};
if (!config.gateway.controlUi) config.gateway.controlUi = {};
config.gateway.controlUi.allowedOrigins = allowedOrigins;
}
fs.mkdirSync(stateDir, { recursive: true });
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
// Startup log: confirm env was applied (check token_present=1 and auth=token so the UI can connect)
const authKind = useTokenAuth ? "token" : usePasswordAuth ? "password" : "none";
const parts = [
`token_present=${useTokenAuth ? "1" : "0"}`,
`password_present=${usePasswordAuth ? "1" : "0"}`,
`auth=${authKind}`,
`trustedProxies=${trustedProxies.length}`,
`allowedOrigins=${allowedOrigins.length}`,
];
console.log(`[openclaw-hf-setup] ${parts.join(" ")} -> ${configPath}`);
if (authKind === "none") {
console.warn(
"[openclaw-hf-setup] No auth set. Add OPENCLAW_GATEWAY_TOKEN or OPENCLAW_GATEWAY_PASSWORD in Space Secrets, then restart.",
);
}