Spaces:
Building
Building
refactor: remove cloudflare-worker.js and rename workspace-sync.py to openclaw-sync.py
Browse files- CHANGELOG.md +2 -2
- Dockerfile +2 -1
- README.md +0 -8
- cloudflare-worker.js +0 -103
- workspace-sync.py β openclaw-sync.py +0 -0
- start.sh +4 -4
CHANGELOG.md
CHANGED
|
@@ -13,7 +13,7 @@ All notable changes to this project will be documented in this file.
|
|
| 13 |
|
| 14 |
- **HF backup flow simplified** β HuggingClaw now uses `huggingface_hub` directly for restore and sync, matching the safer dataset-based pattern used in Hugging8n
|
| 15 |
- **HF username no longer required in most cases** β backup namespace resolution now works from `HF_USERNAME`, `SPACE_AUTHOR_NAME`, or the authenticated HF token, so `HF_TOKEN` is usually enough on its own
|
| 16 |
-
- **Startup restore path modernized** β startup now restores workspace and hidden state through `
|
| 17 |
- **README refreshed for the new backup model** β documentation now describes token-only backup setup, the removed git sync assumptions, and the hardened dashboard helper behavior
|
| 18 |
- **Telegram networking simplified** β removed the channel-specific Telegram transport tweaks in favor of the generic Cloudflare outbound proxy path
|
| 19 |
- **DNS monkey-patch removed** β HuggingClaw now relies on the Cloudflare outbound proxy path instead of the old `dns-fix.js` preload
|
|
@@ -63,7 +63,7 @@ All notable changes to this project will be documented in this file.
|
|
| 63 |
### Added
|
| 64 |
|
| 65 |
- **Pre-built Docker image** β uses `ghcr.io/openclaw/openclaw:latest` multi-stage build for much faster builds (minutes instead of 30+)
|
| 66 |
-
- **Python huggingface_hub sync** β `
|
| 67 |
- **Password auth** β `OPENCLAW_PASSWORD` for simpler login (optional alternative to token)
|
| 68 |
- **Trusted proxies** β `TRUSTED_PROXIES` env var fixes "Proxy headers detected from untrusted address" errors on HF Spaces
|
| 69 |
- **Allowed origins** β `ALLOWED_ORIGINS` env var to lock down Control UI access
|
|
|
|
| 13 |
|
| 14 |
- **HF backup flow simplified** β HuggingClaw now uses `huggingface_hub` directly for restore and sync, matching the safer dataset-based pattern used in Hugging8n
|
| 15 |
- **HF username no longer required in most cases** β backup namespace resolution now works from `HF_USERNAME`, `SPACE_AUTHOR_NAME`, or the authenticated HF token, so `HF_TOKEN` is usually enough on its own
|
| 16 |
+
- **Startup restore path modernized** β startup now restores workspace and hidden state through `openclaw-sync.py restore` instead of configuring a token-bearing git remote
|
| 17 |
- **README refreshed for the new backup model** β documentation now describes token-only backup setup, the removed git sync assumptions, and the hardened dashboard helper behavior
|
| 18 |
- **Telegram networking simplified** β removed the channel-specific Telegram transport tweaks in favor of the generic Cloudflare outbound proxy path
|
| 19 |
- **DNS monkey-patch removed** β HuggingClaw now relies on the Cloudflare outbound proxy path instead of the old `dns-fix.js` preload
|
|
|
|
| 63 |
### Added
|
| 64 |
|
| 65 |
- **Pre-built Docker image** β uses `ghcr.io/openclaw/openclaw:latest` multi-stage build for much faster builds (minutes instead of 30+)
|
| 66 |
+
- **Python huggingface_hub sync** β `openclaw-sync.py` uses the `huggingface_hub` library for more reliable HF Dataset sync (handles auth, LFS, retries). Falls back to git-based sync automatically
|
| 67 |
- **Password auth** β `OPENCLAW_PASSWORD` for simpler login (optional alternative to token)
|
| 68 |
- **Trusted proxies** β `TRUSTED_PROXIES` env var fixes "Proxy headers detected from untrusted address" errors on HF Spaces
|
| 69 |
- **Allowed origins** β `ALLOWED_ORIGINS` env var to lock down Control UI access
|
Dockerfile
CHANGED
|
@@ -70,7 +70,8 @@ COPY --chown=1000:1000 iframe-fix.cjs /home/node/app/iframe-fix.cjs
|
|
| 70 |
COPY --chown=1000:1000 start.sh /home/node/app/start.sh
|
| 71 |
COPY --chown=1000:1000 wa-guardian.js /home/node/app/wa-guardian.js
|
| 72 |
COPY --chown=1000:1000 cloudflare-keepalive-setup.py /home/node/app/cloudflare-keepalive-setup.py
|
| 73 |
-
|
|
|
|
| 74 |
|
| 75 |
USER node
|
| 76 |
|
|
|
|
| 70 |
COPY --chown=1000:1000 start.sh /home/node/app/start.sh
|
| 71 |
COPY --chown=1000:1000 wa-guardian.js /home/node/app/wa-guardian.js
|
| 72 |
COPY --chown=1000:1000 cloudflare-keepalive-setup.py /home/node/app/cloudflare-keepalive-setup.py
|
| 73 |
+
COPY --chown=1000:1000 openclaw-sync.py /home/node/app/openclaw-sync.py
|
| 74 |
+
RUN chmod +x /home/node/app/start.sh /home/node/app/cloudflare-proxy-setup.py /home/node/app/cloudflare-keepalive-setup.py /home/node/app/openclaw-sync.py
|
| 75 |
|
| 76 |
USER node
|
| 77 |
|
README.md
CHANGED
|
@@ -137,14 +137,6 @@ This is the easiest way. HuggingClaw will handle the deployment for you.
|
|
| 137 |
- It generates a secure, private `CLOUDFLARE_PROXY_SECRET`.
|
| 138 |
- All restricted outbound traffic is automatically routed through this Worker.
|
| 139 |
|
| 140 |
-
### π οΈ Manual Setup
|
| 141 |
-
|
| 142 |
-
If you prefer to manage the Worker yourself:
|
| 143 |
-
|
| 144 |
-
1. Create a new Cloudflare Worker.
|
| 145 |
-
2. Paste the code from [cloudflare-worker.js](./cloudflare-worker.js) and deploy.
|
| 146 |
-
3. Add the Worker URL to your Space as `CLOUDFLARE_PROXY_URL`.
|
| 147 |
-
4. (Optional) Set a `CLOUDFLARE_PROXY_SECRET` in both the Worker (as a variable) and the Space (as a secret).
|
| 148 |
|
| 149 |
## π¬ WhatsApp Setup *(Optional)*
|
| 150 |
|
|
|
|
| 137 |
- It generates a secure, private `CLOUDFLARE_PROXY_SECRET`.
|
| 138 |
- All restricted outbound traffic is automatically routed through this Worker.
|
| 139 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 140 |
|
| 141 |
## π¬ WhatsApp Setup *(Optional)*
|
| 142 |
|
cloudflare-worker.js
DELETED
|
@@ -1,103 +0,0 @@
|
|
| 1 |
-
/**
|
| 2 |
-
* Cloudflare Worker: Universal Outbound Proxy
|
| 3 |
-
*
|
| 4 |
-
* Manual setup:
|
| 5 |
-
* 1. Create a Cloudflare Worker.
|
| 6 |
-
* 2. Paste this file and deploy it.
|
| 7 |
-
* 3. Use the worker URL as CLOUDFLARE_PROXY_URL.
|
| 8 |
-
*
|
| 9 |
-
* Optional worker vars:
|
| 10 |
-
* - PROXY_SHARED_SECRET
|
| 11 |
-
* - ALLOWED_TARGETS
|
| 12 |
-
* - ALLOW_PROXY_ALL
|
| 13 |
-
*/
|
| 14 |
-
|
| 15 |
-
function normalizeList(raw) {
|
| 16 |
-
return String(raw || "")
|
| 17 |
-
.split(",")
|
| 18 |
-
.map((value) => value.trim().toLowerCase())
|
| 19 |
-
.filter(Boolean);
|
| 20 |
-
}
|
| 21 |
-
|
| 22 |
-
export default {
|
| 23 |
-
async fetch(request, env) {
|
| 24 |
-
const url = new URL(request.url);
|
| 25 |
-
const queryTarget = url.searchParams.get("proxy_target");
|
| 26 |
-
const targetHost = request.headers.get("x-target-host") || queryTarget;
|
| 27 |
-
const proxySecret = (
|
| 28 |
-
env.PROXY_SHARED_SECRET ||
|
| 29 |
-
env.CLOUDFLARE_PROXY_SECRET ||
|
| 30 |
-
""
|
| 31 |
-
).trim();
|
| 32 |
-
|
| 33 |
-
if (proxySecret) {
|
| 34 |
-
const providedSecret = request.headers.get("x-proxy-key") || url.searchParams.get("proxy_key") || "";
|
| 35 |
-
if (providedSecret !== proxySecret) {
|
| 36 |
-
// Fallback: allow Telegram requests via path without secret if it looks like a bot API call.
|
| 37 |
-
// This is safe because it only proxies to api.telegram.org.
|
| 38 |
-
if (url.pathname.startsWith("/bot") && !targetHost) {
|
| 39 |
-
// Allowed
|
| 40 |
-
} else {
|
| 41 |
-
return new Response("Unauthorized: Invalid proxy key", { status: 401 });
|
| 42 |
-
}
|
| 43 |
-
}
|
| 44 |
-
}
|
| 45 |
-
|
| 46 |
-
const allowProxyAll =
|
| 47 |
-
String(env.ALLOW_PROXY_ALL || "true").toLowerCase() === "true";
|
| 48 |
-
const allowedTargets = normalizeList(
|
| 49 |
-
env.ALLOWED_TARGETS || "api.telegram.org,discord.com,discordapp.com,gateway.discord.gg,status.discord.com,web.whatsapp.com,graph.facebook.com,googleapis.com,google.com,googleusercontent.com,gstatic.com",
|
| 50 |
-
);
|
| 51 |
-
|
| 52 |
-
const isAllowedHost = (hostname) => {
|
| 53 |
-
const normalized = String(hostname || "")
|
| 54 |
-
.trim()
|
| 55 |
-
.toLowerCase();
|
| 56 |
-
if (!normalized) return false;
|
| 57 |
-
if (allowProxyAll) return true;
|
| 58 |
-
return allowedTargets.some(
|
| 59 |
-
(domain) => normalized === domain || normalized.endsWith(`.${domain}`),
|
| 60 |
-
);
|
| 61 |
-
};
|
| 62 |
-
|
| 63 |
-
let targetBase = "";
|
| 64 |
-
if (targetHost) {
|
| 65 |
-
if (!isAllowedHost(targetHost)) {
|
| 66 |
-
return new Response(`Forbidden: Host ${targetHost} is not allowed.`, { status: 403 });
|
| 67 |
-
}
|
| 68 |
-
targetBase = `https://${targetHost}`;
|
| 69 |
-
} else if (url.pathname.startsWith("/bot")) {
|
| 70 |
-
targetBase = "https://api.telegram.org";
|
| 71 |
-
} else {
|
| 72 |
-
return new Response("Invalid request: No target host provided.", { status: 400 });
|
| 73 |
-
}
|
| 74 |
-
|
| 75 |
-
const cleanSearch = new URLSearchParams(url.search);
|
| 76 |
-
cleanSearch.delete("proxy_target");
|
| 77 |
-
cleanSearch.delete("proxy_key");
|
| 78 |
-
const searchStr = cleanSearch.toString();
|
| 79 |
-
const targetUrl = targetBase + url.pathname + (searchStr ? `?${searchStr}` : "");
|
| 80 |
-
|
| 81 |
-
const headers = new Headers(request.headers);
|
| 82 |
-
headers.delete("cf-connecting-ip");
|
| 83 |
-
headers.delete("cf-ray");
|
| 84 |
-
headers.delete("cf-visitor");
|
| 85 |
-
headers.delete("host");
|
| 86 |
-
headers.delete("x-real-ip");
|
| 87 |
-
headers.delete("x-target-host");
|
| 88 |
-
headers.delete("x-proxy-key");
|
| 89 |
-
|
| 90 |
-
const proxiedRequest = new Request(targetUrl, {
|
| 91 |
-
method: request.method,
|
| 92 |
-
headers,
|
| 93 |
-
body: request.body,
|
| 94 |
-
redirect: "follow",
|
| 95 |
-
});
|
| 96 |
-
|
| 97 |
-
try {
|
| 98 |
-
return await fetch(proxiedRequest);
|
| 99 |
-
} catch (error) {
|
| 100 |
-
return new Response(`Proxy Error: ${error.message}`, { status: 502 });
|
| 101 |
-
}
|
| 102 |
-
},
|
| 103 |
-
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
workspace-sync.py β openclaw-sync.py
RENAMED
|
File without changes
|
start.sh
CHANGED
|
@@ -136,7 +136,7 @@ chmod 700 /home/node/.openclaw/credentials
|
|
| 136 |
BACKUP_DATASET="${BACKUP_DATASET_NAME:-huggingclaw-backup}"
|
| 137 |
if [ -n "${HF_TOKEN:-}" ]; then
|
| 138 |
echo "Restoring workspace from HF Dataset..."
|
| 139 |
-
python3 /home/node/app/
|
| 140 |
else
|
| 141 |
echo "HF_TOKEN not set β running without dataset persistence."
|
| 142 |
fi
|
|
@@ -467,9 +467,9 @@ fi
|
|
| 467 |
# ββ Trap SIGTERM for graceful shutdown ββ
|
| 468 |
graceful_shutdown() {
|
| 469 |
echo "Shutting down..."
|
| 470 |
-
if [ -f "/home/node/app/
|
| 471 |
echo "Saving state before exit..."
|
| 472 |
-
python3 /home/node/app/
|
| 473 |
echo "Warning: could not complete shutdown sync"
|
| 474 |
fi
|
| 475 |
kill $(jobs -p) 2>/dev/null
|
|
@@ -560,7 +560,7 @@ warmup_browser
|
|
| 560 |
|
| 561 |
# 12. Start Workspace Sync after startup settles
|
| 562 |
if [ -n "${HF_TOKEN:-}" ]; then
|
| 563 |
-
python3 -u /home/node/app/
|
| 564 |
fi
|
| 565 |
|
| 566 |
# Wait for gateway (allows trap to fire)
|
|
|
|
| 136 |
BACKUP_DATASET="${BACKUP_DATASET_NAME:-huggingclaw-backup}"
|
| 137 |
if [ -n "${HF_TOKEN:-}" ]; then
|
| 138 |
echo "Restoring workspace from HF Dataset..."
|
| 139 |
+
python3 /home/node/app/openclaw-sync.py restore || true
|
| 140 |
else
|
| 141 |
echo "HF_TOKEN not set β running without dataset persistence."
|
| 142 |
fi
|
|
|
|
| 467 |
# ββ Trap SIGTERM for graceful shutdown ββ
|
| 468 |
graceful_shutdown() {
|
| 469 |
echo "Shutting down..."
|
| 470 |
+
if [ -f "/home/node/app/openclaw-sync.py" ]; then
|
| 471 |
echo "Saving state before exit..."
|
| 472 |
+
python3 /home/node/app/openclaw-sync.py sync-once || \
|
| 473 |
echo "Warning: could not complete shutdown sync"
|
| 474 |
fi
|
| 475 |
kill $(jobs -p) 2>/dev/null
|
|
|
|
| 560 |
|
| 561 |
# 12. Start Workspace Sync after startup settles
|
| 562 |
if [ -n "${HF_TOKEN:-}" ]; then
|
| 563 |
+
python3 -u /home/node/app/openclaw-sync.py loop &
|
| 564 |
fi
|
| 565 |
|
| 566 |
# Wait for gateway (allows trap to fire)
|