8900 commited on
Commit
a7e5574
Β·
verified Β·
1 Parent(s): f844586

Update hf-sync-manager.mjs

Browse files
Files changed (1) hide show
  1. hf-sync-manager.mjs +128 -46
hf-sync-manager.mjs CHANGED
@@ -6,27 +6,24 @@ import fs from "node:fs";
6
  import path from "node:path";
7
  import { execSync } from "node:child_process";
8
 
9
- // –– config β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
10
-
11
  var HOME = process.env.OPENCLAW_HOME || process.env.HOME || "/home/user";
12
  var OPENCLAW_DIR = path.join(HOME, ".openclaw");
13
  var WORKSPACE = path.join(OPENCLAW_DIR, "workspace");
14
  var HF_TOKEN = (process.env.HF_TOKEN || "").trim();
15
  var DATASET_ID = (process.env.HF_DATASET_ID || "").trim();
16
  var REPO_DIR = "/tmp/oc-dataset";
17
- var INTERVAL = 30 * 60 * 1000; // 30 min
18
-
19
- var RUNNING_FLAG = "/tmp/hf-sync-running";
20
 
21
  var EXCLUDE = ["sessions", "qmd", "canvas", ".git"];
22
  var EXCLUDE_EXT = [".bak", ".tmp", ".log"];
23
 
24
- // –– helpers ———————————————–
25
 
26
  function log(msg) { console.log("[hf-sync] " + msg); }
27
 
28
  function sleep(ms) {
29
- return new Promise(function(r) { setTimeout(r, ms); });
30
  }
31
 
32
  function repoUrl() {
@@ -43,14 +40,6 @@ env: Object.assign({}, process.env, { GIT_TERMINAL_PROMPT: "0" })
43
  }).toString().trim();
44
  }
45
 
46
- function isValidJson(p) {
47
- try {
48
- if (!fs.existsSync(p)) return false;
49
- JSON.parse(fs.readFileSync(p, "utf-8").trim());
50
- return true;
51
- } catch (e) { return false; }
52
- }
53
-
54
  function shouldExclude(name) {
55
  var i;
56
  for (i = 0; i < EXCLUDE.length; i++) {
@@ -62,20 +51,19 @@ if (name.indexOf(EXCLUDE_EXT[i]) >= 0) return true;
62
  return false;
63
  }
64
 
65
- // –– git repo setup ––––––––––––––––––––
66
 
67
  function ensureRepo() {
68
  if (fs.existsSync(path.join(REPO_DIR, ".git"))) return;
69
  execSync("rm -rf " + REPO_DIR, { stdio: "pipe" });
70
  try {
71
  execSync("git clone --depth 1 " + repoUrl() + " " + REPO_DIR, {
72
- stdio: "pipe",
73
- timeout: 120000,
74
- env: Object.assign({}, process.env, { GIT_TERMINAL_PROMPT: "0" })
75
  });
76
  log("Repo cloned");
77
  } catch (e) {
78
- log("Clone failed, creating empty repo: " + e.message);
79
  fs.mkdirSync(REPO_DIR, { recursive: true });
80
  execSync("git init " + REPO_DIR, { stdio: "pipe" });
81
  git("remote add origin " + repoUrl());
@@ -113,22 +101,19 @@ return 0;
113
  }
114
  }
115
 
116
- // –– recursive mirror: local -> repo ———————–
117
 
118
- function mirrorToRepo(srcDir, dstDir) {
119
  if (!fs.existsSync(srcDir)) return 0;
120
  fs.mkdirSync(dstDir, { recursive: true });
121
-
122
- var items = fs.readdirSync(srcDir);
123
  var count = 0;
124
-
125
- items.forEach(function(name) {
126
  if (shouldExclude(name)) return;
127
- var src = path.join(srcDir, name);
128
- var dst = path.join(dstDir, name);
129
  try {
130
  if (fs.statSync(src).isDirectory()) {
131
- count += mirrorToRepo(src, dst);
132
  } else {
133
  fs.copyFileSync(src, dst);
134
  count++;
@@ -137,25 +122,19 @@ count++;
137
  log("Copy error [" + name + "]: " + e.message);
138
  }
139
  });
140
-
141
  return count;
142
  }
143
 
144
- // –– recursive mirror: repo -> local ———————–
145
-
146
- function mirrorFromRepo(srcDir, dstDir) {
147
  if (!fs.existsSync(srcDir)) return 0;
148
  fs.mkdirSync(dstDir, { recursive: true });
149
-
150
- var items = fs.readdirSync(srcDir);
151
  var count = 0;
152
-
153
- items.forEach(function(name) {
154
- var src = path.join(srcDir, name);
155
- var dst = path.join(dstDir, name);
156
  try {
157
  if (fs.statSync(src).isDirectory()) {
158
- count += mirrorFromRepo(src, dst);
159
  } else {
160
  fs.copyFileSync(src, dst);
161
  count++;
@@ -164,11 +143,10 @@ count++;
164
  log("Restore error [" + name + "]: " + e.message);
165
  }
166
  });
167
-
168
  return count;
169
  }
170
 
171
- // –– seed workspace default files –––––––––––––
172
 
173
  function seedWorkspaceIfNeeded() {
174
  fs.mkdirSync(WORKSPACE, { recursive: true });
@@ -209,9 +187,9 @@ fs.writeFileSync(agents, [
209
  "",
210
  "## Memory rules",
211
  "",
212
- "- Write important facts to MEMORY.md when asked to remember something",
213
  "- Log daily context to memory/YYYY-MM-DD.md",
214
- "- Do not ask for information the user has already provided"
215
  ].join("\n") + "\n", "utf-8");
216
  log("Seeded workspace/AGENTS.md");
217
  }
@@ -228,6 +206,110 @@ log("Seeded workspace/MEMORY.md");
228
  }
229
  }
230
 
231
- // ============================================================
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
232
 
233
- main().catch(function(e) { console.error("[hf-sync] Fatal: " + e.message); });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  import path from "node:path";
7
  import { execSync } from "node:child_process";
8
 
 
 
9
  var HOME = process.env.OPENCLAW_HOME || process.env.HOME || "/home/user";
10
  var OPENCLAW_DIR = path.join(HOME, ".openclaw");
11
  var WORKSPACE = path.join(OPENCLAW_DIR, "workspace");
12
  var HF_TOKEN = (process.env.HF_TOKEN || "").trim();
13
  var DATASET_ID = (process.env.HF_DATASET_ID || "").trim();
14
  var REPO_DIR = "/tmp/oc-dataset";
15
+ var INTERVAL = 30 * 60 * 1000;
16
+ var RUNNING_FLAG = "/tmp/.hf-sync-running";
 
17
 
18
  var EXCLUDE = ["sessions", "qmd", "canvas", ".git"];
19
  var EXCLUDE_EXT = [".bak", ".tmp", ".log"];
20
 
21
+ // –––––––––––––––––––––––––––––
22
 
23
  function log(msg) { console.log("[hf-sync] " + msg); }
24
 
25
  function sleep(ms) {
26
+ return new Promise(function(resolve) { setTimeout(resolve, ms); });
27
  }
28
 
29
  function repoUrl() {
 
40
  }).toString().trim();
41
  }
42
 
 
 
 
 
 
 
 
 
43
  function shouldExclude(name) {
44
  var i;
45
  for (i = 0; i < EXCLUDE.length; i++) {
 
51
  return false;
52
  }
53
 
54
+ // –––––––––––––––––––––––––––––
55
 
56
  function ensureRepo() {
57
  if (fs.existsSync(path.join(REPO_DIR, ".git"))) return;
58
  execSync("rm -rf " + REPO_DIR, { stdio: "pipe" });
59
  try {
60
  execSync("git clone --depth 1 " + repoUrl() + " " + REPO_DIR, {
61
+ stdio: "pipe", timeout: 120000,
62
+ env: Object.assign({}, process.env, { GIT_TERMINAL_PROMPT: "0" })
 
63
  });
64
  log("Repo cloned");
65
  } catch (e) {
66
+ log("Clone failed, init empty: " + e.message);
67
  fs.mkdirSync(REPO_DIR, { recursive: true });
68
  execSync("git init " + REPO_DIR, { stdio: "pipe" });
69
  git("remote add origin " + repoUrl());
 
101
  }
102
  }
103
 
104
+ // –––––––––––––––––––––––––––––
105
 
106
+ function mirrorDirToRepo(srcDir, dstDir) {
107
  if (!fs.existsSync(srcDir)) return 0;
108
  fs.mkdirSync(dstDir, { recursive: true });
 
 
109
  var count = 0;
110
+ fs.readdirSync(srcDir).forEach(function(name) {
 
111
  if (shouldExclude(name)) return;
112
+ var src = path.join(srcDir, name);
113
+ var dst = path.join(dstDir, name);
114
  try {
115
  if (fs.statSync(src).isDirectory()) {
116
+ count += mirrorDirToRepo(src, dst);
117
  } else {
118
  fs.copyFileSync(src, dst);
119
  count++;
 
122
  log("Copy error [" + name + "]: " + e.message);
123
  }
124
  });
 
125
  return count;
126
  }
127
 
128
+ function mirrorDirFromRepo(srcDir, dstDir) {
 
 
129
  if (!fs.existsSync(srcDir)) return 0;
130
  fs.mkdirSync(dstDir, { recursive: true });
 
 
131
  var count = 0;
132
+ fs.readdirSync(srcDir).forEach(function(name) {
133
+ var src = path.join(srcDir, name);
134
+ var dst = path.join(dstDir, name);
 
135
  try {
136
  if (fs.statSync(src).isDirectory()) {
137
+ count += mirrorDirFromRepo(src, dst);
138
  } else {
139
  fs.copyFileSync(src, dst);
140
  count++;
 
143
  log("Restore error [" + name + "]: " + e.message);
144
  }
145
  });
 
146
  return count;
147
  }
148
 
149
+ // –––––––––––––––––––––––––––––
150
 
151
  function seedWorkspaceIfNeeded() {
152
  fs.mkdirSync(WORKSPACE, { recursive: true });
 
187
  "",
188
  "## Memory rules",
189
  "",
190
+ "- Write important facts to MEMORY.md when asked",
191
  "- Log daily context to memory/YYYY-MM-DD.md",
192
+ "- Do not ask for information already provided"
193
  ].join("\n") + "\n", "utf-8");
194
  log("Seeded workspace/AGENTS.md");
195
  }
 
206
  }
207
  }
208
 
209
+ // –––––––––––––––––––––––––––––
210
+
211
+ function bootRestore() {
212
+ if (!HF_TOKEN || !DATASET_ID) {
213
+ log("No HF_TOKEN/HF_DATASET_ID - skipping Dataset restore");
214
+ seedWorkspaceIfNeeded();
215
+ return;
216
+ }
217
+
218
+ log("Boot restore from " + DATASET_ID + "…");
219
+ try {
220
+ ensureRepo();
221
+ pull();
222
+ } catch (e) {
223
+ log("Repo init failed (" + e.message + ") - starting fresh");
224
+ seedWorkspaceIfNeeded();
225
+ return;
226
+ }
227
+
228
+ var repoOC = path.join(REPO_DIR, "openclaw");
229
+ if (fs.existsSync(repoOC)) {
230
+ var n = mirrorDirFromRepo(repoOC, OPENCLAW_DIR);
231
+ log("Boot restore done: " + n + " file(s) -> " + OPENCLAW_DIR);
232
+ } else {
233
+ log("No data in Dataset yet - starting fresh");
234
+ }
235
+
236
+ seedWorkspaceIfNeeded();
237
+ log("Boot restore complete");
238
+ }
239
+
240
+ function runSync() {
241
+ if (!HF_TOKEN || !DATASET_ID) return;
242
+
243
+ var ts = new Date().toISOString().replace("T", " ").substring(0, 19);
244
+ log("Sync at " + ts + "…");
245
+
246
+ try {
247
+ ensureRepo();
248
+ pull();
249
+
250
+ var repoOC = path.join(REPO_DIR, "openclaw");
251
+ if (fs.existsSync(repoOC)) {
252
+ execSync("rm -rf " + repoOC, { stdio: "pipe" });
253
+ }
254
+ fs.mkdirSync(repoOC, { recursive: true });
255
+
256
+ var n = mirrorDirToRepo(OPENCLAW_DIR, repoOC);
257
+ log("Mirrored " + n + " file(s)");
258
+
259
+ var pushed = push();
260
+ if (pushed > 0) {
261
+ log("Sync done: " + pushed + " file(s) pushed");
262
+ } else {
263
+ log("Sync done: no changes");
264
+ }
265
+
266
+ } catch (e) {
267
+ log("Sync failed (retry in 30 min): " + e.message);
268
+ }
269
+ }
270
+
271
+ // –––––––––––––––––––––––––––––
272
+
273
+ (async function() {
274
+ log("Starting…");
275
+ log("OPENCLAW_DIR : " + OPENCLAW_DIR);
276
+ log("DATASET : " + (DATASET_ID || "NOT SET"));
277
 
278
+ fs.mkdirSync(OPENCLAW_DIR, { recursive: true });
279
+ fs.mkdirSync(WORKSPACE, { recursive: true });
280
+
281
+ try {
282
+ execSync("git config --global user.email openclaw@hf.space", { stdio: "pipe" });
283
+ execSync("git config --global user.name OpenClaw-Sync", { stdio: "pipe" });
284
+ execSync("git config --global http.postBuffer 52428800", { stdio: "pipe" });
285
+ execSync("git config --global pull.rebase false", { stdio: "pipe" });
286
+ } catch (e) { /* non-fatal */ }
287
+
288
+ var isGatewayRestart = fs.existsSync(RUNNING_FLAG);
289
+
290
+ if (isGatewayRestart) {
291
+ log("Gateway restart - skipping boot restore (data on disk)");
292
+ } else {
293
+ log("Container start - running full boot restore");
294
+ bootRestore();
295
+ }
296
+
297
+ fs.writeFileSync(RUNNING_FLAG, String(process.pid), "utf-8");
298
+
299
+ process.on("exit", function() { try { fs.unlinkSync(RUNNING_FLAG); } catch (e) { /* ok */ } });
300
+ process.on("SIGTERM", function() { try { fs.unlinkSync(RUNNING_FLAG); } catch (e) { /* ok */ } process.exit(0); });
301
+ process.on("SIGINT", function() { try { fs.unlinkSync(RUNNING_FLAG); } catch (e) { /* ok */ } process.exit(0); });
302
+
303
+ if (!HF_TOKEN || !DATASET_ID) {
304
+ log("Sync disabled: set HF_TOKEN and HF_DATASET_ID in Secrets");
305
+ return;
306
+ }
307
+
308
+ log("Sync loop starting (first run in 10 min)…");
309
+ await sleep(10 * 60 * 1000);
310
+
311
+ while (true) {
312
+ runSync();
313
+ await sleep(INTERVAL);
314
+ }
315
+ })().catch(function(e) { console.error("[hf-sync] Fatal: " + e.message); });