8900 commited on
Commit
2f40c71
·
verified ·
1 Parent(s): 7b8ac83

Update hf-sync-manager.mjs

Browse files
Files changed (1) hide show
  1. hf-sync-manager.mjs +85 -292
hf-sync-manager.mjs CHANGED
@@ -1,332 +1,125 @@
1
- import fs from "node:fs";
2
  import path from "node:path";
 
 
3
  import { execSync } from "node:child_process";
4
 
5
- var OPENCLAW_DIR = path.join(process.env.OPENCLAW_HOME || process.env.HOME || "/home/user", ".openclaw");
6
- var COMMAND_FILE = path.join(OPENCLAW_DIR, ".sync-command.json");
7
- var STATE_FILE = path.join(OPENCLAW_DIR, ".sync-state.json");
8
- var LOOP_INTERVAL = 8 * 1000;
 
 
 
 
 
 
 
 
9
 
10
  var REPO_DIR = "/tmp/hf-sync-repo";
 
11
 
12
  var ALWAYS_EXCLUDE = [".sync-state.json", ".git", "openclaw.json.bak"];
13
  var LOG_PATTERNS = [".log", ".log.", ".tmp", ".bak"];
14
 
 
 
 
 
 
 
 
 
 
15
  var tasks = {
16
- settings_upload: { active: false, timer: null, lastMsg: "idle" },
17
- settings_pull: { active: false, timer: null, lastMsg: "idle" },
18
- memory_upload: { active: false, timer: null, lastMsg: "idle" },
19
- memory_pull: { active: false, timer: null, lastMsg: "idle" }
20
  };
21
 
22
  function setTaskMsg(key, msg) {
23
- tasks[key].lastMsg = msg;
24
- console.log("[sync:" + key + "] " + msg);
25
  }
26
 
27
  function loadState() {
28
- try {
29
- if (fs.existsSync(STATE_FILE)) {
30
- return JSON.parse(fs.readFileSync(STATE_FILE, "utf-8"));
31
- }
32
- } catch (e) {}
33
- return {
34
- settings: { lastUpload: null, lastPull: null },
35
- memory: { lastUpload: null, lastPull: null }
36
- };
37
  }
38
 
39
  function saveState(state) {
40
- try {
41
- fs.mkdirSync(OPENCLAW_DIR, { recursive: true });
42
- fs.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2), "utf-8");
43
- } catch (e) {}
44
  }
45
 
46
  function nowTs() {
47
- return new Date().toISOString().replace("T", " ").substring(0, 19);
48
  }
49
 
50
  function bar(done, total) {
51
- var width = 10;
52
- var filled = total > 0 ? Math.round((done / total) * width) : 0;
53
- var pct = total > 0 ? Math.round((done / total) * 100) : 0;
54
- var b = "";
55
- for (var i = 0; i < width; i++) b += i < filled ? "=" : "-";
56
- return "[" + b + "] " + pct + "% (" + done + "/" + total + ")";
57
  }
58
 
59
- // Git helpers
60
  function repoUrl() {
61
- return "https://user:" + (process.env.HF_TOKEN || "") + "@huggingface.co/datasets/" + (process.env.HF_DATASET_ID || "");
 
62
  }
63
 
64
  function git(args, cwd) {
65
- return execSync("git " + args, {
66
- cwd: cwd || REPO_DIR,
67
- stdio: "pipe",
68
- timeout: 60000,
69
- env: Object.assign({}, process.env, { GIT_TERMINAL_PROMPT: "0" })
70
- }).toString().trim();
71
  }
72
 
73
  function ensureRepo() {
74
- if (fs.existsSync(path.join(REPO_DIR, ".git"))) return;
75
- execSync("rm -rf " + REPO_DIR, { stdio: "pipe" });
76
- try {
77
- git("clone --depth 1 " + repoUrl() + " " + REPO_DIR, "/tmp");
78
- } catch (e) {
79
- fs.mkdirSync(REPO_DIR, { recursive: true });
80
- git("init", REPO_DIR);
81
- git("remote add origin " + repoUrl(), REPO_DIR);
82
- }
83
- try {
84
- git("config user.email openclaw@hf.space");
85
- git("config user.name OpenClaw-Sync");
86
- } catch (e) {}
87
  }
88
-
89
- function pullLatest() {
90
- try {
91
- git("pull --quiet --no-rebase origin main");
92
- } catch (e) {
93
- try { git("pull --quiet --no-rebase origin master"); } catch (e2) {}
94
- }
95
  }
96
 
97
- function commitAndPush(msg) {
98
- git("add -A");
99
- var changed = git("diff --cached --name-only");
100
- if (!changed) return 0;
101
- var count = changed.split("\n").filter(Boolean).length;
102
- git('commit -m "' + msg + '"');
103
- try {
104
- git("push --quiet origin HEAD:main");
105
- } catch (e) {
106
- git("push --quiet origin HEAD:master");
107
- }
108
- return count;
109
- }
110
-
111
- // File operations
112
- function isExcluded(name) {
113
- for (var i = 0; i < ALWAYS_EXCLUDE.length; i++) {
114
- if (name === ALWAYS_EXCLUDE[i]) return true;
115
- }
116
- for (var i = 0; i < LOG_PATTERNS.length; i++) {
117
- if (name.indexOf(LOG_PATTERNS[i]) >= 0) return true;
118
- }
119
- return false;
120
- }
121
-
122
- function settingsToRepo() {
123
- var src = path.join(OPENCLAW_DIR, "openclaw.json");
124
- var dest = path.join(REPO_DIR, "settings");
125
- fs.mkdirSync(dest, { recursive: true });
126
- if (fs.existsSync(src)) {
127
- fs.copyFileSync(src, path.join(dest, "openclaw.json"));
128
- return 1;
129
- }
130
- return 0;
131
- }
132
-
133
- function settingsFromRepo() {
134
- var src = path.join(REPO_DIR, "settings", "openclaw.json");
135
- var dest = path.join(OPENCLAW_DIR, "openclaw.json");
136
- if (fs.existsSync(src)) {
137
- fs.mkdirSync(OPENCLAW_DIR, { recursive: true });
138
- fs.copyFileSync(src, dest);
139
- return 1;
140
- }
141
- return 0;
142
- }
143
-
144
- function memoryToRepo() {
145
- var dest = path.join(REPO_DIR, "memory");
146
- execSync("rm -rf " + dest, { stdio: "pipe" });
147
- fs.mkdirSync(dest, { recursive: true });
148
- if (!fs.existsSync(OPENCLAW_DIR)) return 0;
149
-
150
- var items = fs.readdirSync(OPENCLAW_DIR).filter(function(n) {
151
- return n !== "openclaw.json" && !isExcluded(n);
152
- });
153
- var copied = 0;
154
- items.forEach(function(name) {
155
- var s = path.join(OPENCLAW_DIR, name);
156
- var d = path.join(dest, name);
157
- try {
158
- if (fs.statSync(s).isDirectory()) {
159
- execSync("cp -r " + s + " " + d, { stdio: "pipe" });
160
- } else {
161
- fs.copyFileSync(s, d);
162
- }
163
- copied++;
164
- } catch (e) {}
165
- });
166
- return copied;
167
- }
168
-
169
- function memoryFromRepo() {
170
- var src = path.join(REPO_DIR, "memory");
171
- if (!fs.existsSync(src)) return 0;
172
- var items = fs.readdirSync(src);
173
- var copied = 0;
174
- items.forEach(function(name) {
175
- var s = path.join(src, name);
176
- var d = path.join(OPENCLAW_DIR, name);
177
- try {
178
- fs.mkdirSync(OPENCLAW_DIR, { recursive: true });
179
- if (fs.statSync(s).isDirectory()) {
180
- execSync("cp -r " + s + " " + d, { stdio: "pipe" });
181
- } else {
182
- fs.copyFileSync(s, d);
183
- }
184
- copied++;
185
- } catch (e) {}
186
- });
187
- return copied;
188
- }
189
-
190
- // Task loops
191
- async function runUploadCycle(track, chatId) {
192
- var key = track + "_upload";
193
- var label = track === "settings" ? "Settings" : "Memory";
194
-
195
- setTaskMsg(key, "Upload running...");
196
- try {
197
- ensureRepo();
198
- pullLatest();
199
- var files = track === "settings" ? settingsToRepo() : memoryToRepo();
200
- var changed = commitAndPush("upload-" + track + " " + nowTs());
201
-
202
- var state = loadState();
203
- state[track].lastUpload = nowTs();
204
- saveState(state);
205
-
206
- var result = changed > 0
207
- ? bar(changed, changed) + " " + changed + " file(s) uploaded"
208
- : "No changes detected";
209
-
210
- setTaskMsg(key, result);
211
- console.log("[sync] " + label + " Upload completed: " + result);
212
- } catch (e) {
213
- setTaskMsg(key, "Error: " + e.message);
214
- console.error("[sync] Upload error: " + e.message);
215
- }
216
- }
217
-
218
- async function runPullCycle(track, chatId) {
219
- var key = track + "_pull";
220
- var label = track === "settings" ? "Settings" : "Memory";
221
-
222
- setTaskMsg(key, "Pull running...");
223
- try {
224
- ensureRepo();
225
- pullLatest();
226
- var restored = track === "settings" ? settingsFromRepo() : memoryFromRepo();
227
-
228
- var state = loadState();
229
- state[track].lastPull = nowTs();
230
- saveState(state);
231
-
232
- var result = restored > 0
233
- ? bar(restored, restored) + " " + restored + " item(s) restored"
234
- : "No data found in repository";
235
-
236
- setTaskMsg(key, result);
237
- console.log("[sync] " + label + " Pull completed: " + result);
238
- } catch (e) {
239
- setTaskMsg(key, "Error: " + e.message);
240
- console.error("[sync] Pull error: " + e.message);
241
- }
242
- }
243
-
244
- function startTask(key, cycleFn) {
245
- if (tasks[key].active) return;
246
- tasks[key].active = true;
247
-
248
- function schedule() {
249
- if (!tasks[key].active) return;
250
- cycleFn().finally(function() {
251
- if (tasks[key].active) {
252
- tasks[key].timer = setTimeout(schedule, LOOP_INTERVAL);
253
- }
254
- });
255
- }
256
- schedule();
257
- }
258
-
259
- function stopTask(key) {
260
- tasks[key].active = false;
261
- if (tasks[key].timer) {
262
- clearTimeout(tasks[key].timer);
263
- tasks[key].timer = null;
264
- }
265
- setTaskMsg(key, "paused");
266
- }
267
-
268
- // Command handler (保留你原来的15个命令逻辑)
269
- async function handleCmd(cmd, chatId) {
270
- switch (cmd) {
271
- case "/sync_settings":
272
- startTask("settings_upload", function() { return runUploadCycle("settings", chatId); });
273
- console.log("[sync] Settings upload started");
274
- break;
275
- case "/sync_memory":
276
- startTask("memory_upload", function() { return runUploadCycle("memory", chatId); });
277
- console.log("[sync] Memory upload started");
278
- break;
279
- case "/sync_all":
280
- startTask("settings_upload", function() { return runUploadCycle("settings", chatId); });
281
- startTask("memory_upload", function() { return runUploadCycle("memory", chatId); });
282
- console.log("[sync] All uploads started");
283
- break;
284
- case "/pull_settings":
285
- startTask("settings_pull", function() { return runPullCycle("settings", chatId); });
286
- console.log("[sync] Settings pull started");
287
- break;
288
- case "/pull_memory":
289
- startTask("memory_pull", function() { return runPullCycle("memory", chatId); });
290
- console.log("[sync] Memory pull started");
291
- break;
292
- case "/pause_all":
293
- stopTask("settings_upload"); stopTask("memory_upload");
294
- stopTask("settings_pull"); stopTask("memory_pull");
295
- console.log("[sync] All tasks paused");
296
- break;
297
- case "/progress": {
298
- var st = loadState();
299
- var lines = ["*Current Task Progress*", ""];
300
- Object.keys(tasks).forEach(function(k) {
301
- lines.push("*" + k + "*: " + (tasks[k].active ? "RUNNING" : "paused") + " - " + tasks[k].lastMsg);
302
- });
303
- console.log(lines.join("\n"));
304
- break;
305
- }
306
- default:
307
- console.log("[sync] Unknown command: " + cmd);
308
- }
309
  }
310
-
311
- // Command file listener
312
- async function checkCommandFile() {
313
- if (!fs.existsSync(COMMAND_FILE)) return;
314
- try {
315
- var data = JSON.parse(fs.readFileSync(COMMAND_FILE, "utf-8"));
316
- fs.unlinkSync(COMMAND_FILE);
317
- console.log("[sync-manager] Received command: " + data.cmd);
318
- await handleCmd(data.cmd, data.chatId);
319
- } catch (e) {
320
- console.error("[sync-manager] Command file error: " + e.message);
321
- try { fs.unlinkSync(COMMAND_FILE); } catch {}
322
- }
323
  }
324
 
325
- async function main() {
326
- console.log("[sync-manager] Starting... (Webhook Safe - Strict Manual Control)");
327
- fs.mkdirSync(OPENCLAW_DIR, { recursive: true });
328
- setInterval(checkCommandFile, LOOP_INTERVAL);
329
- console.log("[sync-manager] Ready. Waiting for commands from OpenClaw via command file.");
330
- }
331
-
332
- main().catch(function(e) { console.error(e); });
 
 
 
1
+ import fs from "node:fs";
2
  import path from "node:path";
3
+ import http from "node:http";
4
+ import https from "node:https";
5
  import { execSync } from "node:child_process";
6
 
7
+ // ============================================================
8
+ // Config
9
+ // ============================================================
10
+
11
+ var OPENCLAW_DIR = path.join(
12
+ process.env.OPENCLAW_HOME || process.env.HOME || "/home/user",
13
+ ".openclaw"
14
+ );
15
+ var BOT_TOKEN = (process.env.TELEGRAM_BOT_TOKEN || "").trim();
16
+ var HF_TOKEN = (process.env.HF_TOKEN || "").trim();
17
+ var DATASET_ID = (process.env.HF_DATASET_ID || "").trim();
18
+ var ADMIN_CHAT_ID = (process.env.SYNC_ADMIN_CHAT_ID || "").trim();
19
 
20
  var REPO_DIR = "/tmp/hf-sync-repo";
21
+ var STATE_FILE = path.join(OPENCLAW_DIR, ".sync-state.json");
22
 
23
  var ALWAYS_EXCLUDE = [".sync-state.json", ".git", "openclaw.json.bak"];
24
  var LOG_PATTERNS = [".log", ".log.", ".tmp", ".bak"];
25
 
26
+ var SYNC_COMMANDS = [
27
+ "/sync_all", "/sync_settings", "/sync_memory",
28
+ "/pull_all", "/pull_settings", "/pull_memory",
29
+ "/pause_all", "/pause_settings", "/pause_memory",
30
+ "/pause_pull_settings", "/pause_pull_memory",
31
+ "/resume_all", "/resume_settings", "/resume_memory",
32
+ "/progress"
33
+ ];
34
+
35
  var tasks = {
36
+ settings_upload: { active: false, timer: null, lastMsg: "idle" },
37
+ settings_pull: { active: false, timer: null, lastMsg: "idle" },
38
+ memory_upload: { active: false, timer: null, lastMsg: "idle" },
39
+ memory_pull: { active: false, timer: null, lastMsg: "idle" }
40
  };
41
 
42
  function setTaskMsg(key, msg) {
43
+ tasks[key].lastMsg = msg;
44
+ console.log("[sync:" + key + "] " + msg);
45
  }
46
 
47
  function loadState() {
48
+ try {
49
+ if (fs.existsSync(STATE_FILE)) {
50
+ return JSON.parse(fs.readFileSync(STATE_FILE, "utf-8"));
51
+ }
52
+ } catch (e) {}
53
+ return {
54
+ settings: { lastUpload: null, lastPull: null },
55
+ memory: { lastUpload: null, lastPull: null }
56
+ };
57
  }
58
 
59
  function saveState(state) {
60
+ try {
61
+ fs.mkdirSync(OPENCLAW_DIR, { recursive: true });
62
+ fs.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2), "utf-8");
63
+ } catch (e) {}
64
  }
65
 
66
  function nowTs() {
67
+ return new Date().toISOString().replace("T", " ").substring(0, 19);
68
  }
69
 
70
  function bar(done, total) {
71
+ var width = 10;
72
+ var filled = total > 0 ? Math.round((done / total) * width) : 0;
73
+ var pct = total > 0 ? Math.round((done / total) * 100) : 0;
74
+ var b = "";
75
+ for (var i = 0; i < width; i++) b += i < filled ? "=" : "-";
76
+ return "[" + b + "] " + pct + "% (" + done + "/" + total + ")";
77
  }
78
 
 
79
  function repoUrl() {
80
+ return "https://user:" + HF_TOKEN +
81
+ "@huggingface.co/datasets/" + DATASET_ID;
82
  }
83
 
84
  function git(args, cwd) {
85
+ return execSync("git " + args, {
86
+ cwd: cwd || REPO_DIR,
87
+ stdio: "pipe",
88
+ timeout: 60000,
89
+ env: Object.assign({}, process.env, { GIT_TERMINAL_PROMPT: "0" })
90
+ }).toString().trim();
91
  }
92
 
93
  function ensureRepo() {
94
+ if (fs.existsSync(path.join(REPO_DIR, ".git"))) return;
95
+ execSync("rm -rf " + REPO_DIR, { stdio: "pipe" });
96
+ try {
97
+ git("clone --depth 1 " + repoUrl() + " " + REPO_DIR, "/tmp");
98
+ } catch (e) {
99
+ fs.mkdirSync(REPO_DIR, { recursive: true });
100
+ git("init", REPO_DIR);
101
+ git("remote add origin " + repoUrl(), REPO_DIR);
 
 
 
 
 
102
  }
103
+ try {
104
+ git("config user.email openclaw@hf.space");
105
+ git("config user.name OpenClaw-Sync");
106
+ } catch (e) {}
 
 
 
107
  }
108
 
109
+ function pullLatest() {
110
+ try { git("pull --quiet --no-rebase origin main"); }
111
+ catch (e) {
112
+ try { git("pull --quiet --no-rebase origin master"); } catch (e2) {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
114
  }
115
 
116
+ function commitAndPush(msg) {
117
+ git("add -A");
118
+ var changed = git("diff --cached --name-only");
119
+ if (!changed) return 0;
120
+ var count = changed.split("\n").filter(Boolean).length;
121
+ git('commit -m "' + msg + '"');
122
+ try { git("push --quiet origin HEAD:main"); }
123
+ catch (e) { git("push --quiet origin HEAD:master"); }
124
+ return count;
125
+ }