renminwansui1976 commited on
Commit
ef1db83
·
unverified ·
1 Parent(s): 16d8f48

更新 main.rs

Browse files
Files changed (1) hide show
  1. src/main.rs +41 -16
src/main.rs CHANGED
@@ -14,13 +14,18 @@ use tokio::process::{Child, Command};
14
  use tokio::time::{interval, timeout};
15
  use walkdir::WalkDir;
16
 
17
- const WORKSPACE_DIR: &str = "/home/user/.openclaw/workspace";
 
 
 
18
  const STATE_FILE: &str = ".hf-sync-state.json";
19
  const FINAL_WAIT_TIMEOUT: Duration = Duration::from_secs(20);
20
 
21
  #[derive(Debug, Clone)]
22
  struct Config {
23
  token: String,
 
 
24
  dataset_id: String,
25
  sync_interval: Duration,
26
  workspace: PathBuf,
@@ -144,7 +149,13 @@ async fn run() -> Result<()> {
144
 
145
  fn load_config() -> Result<Config> {
146
  let token = env::var("HF_TOKEN").context("HF_TOKEN is required")?;
147
- let dataset_id = env::var("HF_DATASET_ID").context("HF_DATASET_ID is required")?;
 
 
 
 
 
 
148
  validate_dataset_id(&dataset_id)?;
149
 
150
  let sync_interval_secs: u64 = env::var("SYNC_INTERVAL")
@@ -169,7 +180,9 @@ fn validate_dataset_id(dataset_id: &str) -> Result<()> {
169
  let owner = parts.next().unwrap_or_default();
170
  let repo = parts.next().unwrap_or_default();
171
  if owner.is_empty() || repo.is_empty() || parts.next().is_some() {
172
- return Err(anyhow!("HF_DATASET_ID must be in form owner/name"));
 
 
173
  }
174
  Ok(())
175
  }
@@ -204,11 +217,24 @@ async fn ensure_dataset_exists(client: &reqwest::Client, cfg: &Config) -> Result
204
  Ok(())
205
  }
206
  StatusCode::NOT_FOUND => {
207
- eprintln!(
208
- "dataset {} not found, creating private dataset",
209
- cfg.dataset_id
210
- );
211
- create_private_dataset(client, cfg).await
 
 
 
 
 
 
 
 
 
 
 
 
 
212
  }
213
  status => {
214
  let body = response.text().await.unwrap_or_default();
@@ -221,7 +247,7 @@ async fn create_private_dataset(client: &reqwest::Client, cfg: &Config) -> Resul
221
  let (owner, name) = cfg
222
  .dataset_id
223
  .split_once('/')
224
- .ok_or_else(|| anyhow!("HF_DATASET_ID must be in form owner/name"))?;
225
 
226
  let me_url = "https://huggingface.co/api/whoami-v2";
227
  let username = client
@@ -263,7 +289,7 @@ async fn create_private_dataset(client: &reqwest::Client, cfg: &Config) -> Resul
263
  }
264
 
265
  async fn pull_workspace(client: &reqwest::Client, cfg: &Config) -> Result<()> {
266
- eprintln!("pulling workspace from Hugging Face dataset");
267
  let remote_files = list_remote_files(client, cfg).await?;
268
 
269
  for file in &remote_files {
@@ -287,7 +313,7 @@ async fn pull_workspace(client: &reqwest::Client, cfg: &Config) -> Result<()> {
287
  f.write_all(&bytes).await?;
288
  }
289
 
290
- eprintln!("pull complete with {} files", remote_files.len());
291
  Ok(())
292
  }
293
 
@@ -311,7 +337,7 @@ async fn list_remote_files(client: &reqwest::Client, cfg: &Config) -> Result<Vec
311
  }
312
 
313
  async fn push_workspace(client: &reqwest::Client, cfg: &Config) -> Result<()> {
314
- eprintln!("pushing workspace updates to Hugging Face");
315
  let state_path = cfg.workspace.join(STATE_FILE);
316
  let mut state = load_state(&state_path).await?;
317
 
@@ -433,7 +459,6 @@ async fn load_state(path: &Path) -> Result<SyncState> {
433
  if !path.exists() {
434
  return Ok(SyncState::default());
435
  }
436
-
437
  let raw = tokio::fs::read(path).await?;
438
  let state = serde_json::from_slice(&raw).context("failed to parse sync state file")?;
439
  Ok(state)
@@ -449,7 +474,7 @@ fn spawn_child_from_args() -> Result<Child> {
449
  let args: Vec<String> = env::args().skip(1).collect();
450
  if args.is_empty() {
451
  return Err(anyhow!(
452
- "no command provided. pass the main process command as entrypoint arguments"
453
  ));
454
  }
455
 
@@ -470,7 +495,7 @@ fn forward_sigterm(child: &mut Child) {
470
  let ret = unsafe { libc::kill(id as libc::pid_t, libc::SIGTERM) };
471
  if ret != 0 {
472
  eprintln!(
473
- "failed to forward SIGTERM: errno={}",
474
  std::io::Error::last_os_error()
475
  );
476
  }
@@ -495,7 +520,7 @@ async fn wait_for_child_shutdown(child: &mut Child) {
495
  let ret = unsafe { libc::kill(id as libc::pid_t, libc::SIGKILL) };
496
  if ret != 0 {
497
  eprintln!(
498
- "failed to SIGKILL child: errno={}",
499
  std::io::Error::last_os_error()
500
  );
501
  }
 
14
  use tokio::time::{interval, timeout};
15
  use walkdir::WalkDir;
16
 
17
+ /// The OpenClaw config directory we sync to/from HuggingFace.
18
+ /// Must match the path OpenClaw itself writes to (HOME=/home/node).
19
+ const WORKSPACE_DIR: &str = "/home/node/.openclaw";
20
+
21
  const STATE_FILE: &str = ".hf-sync-state.json";
22
  const FINAL_WAIT_TIMEOUT: Duration = Duration::from_secs(20);
23
 
24
  #[derive(Debug, Clone)]
25
  struct Config {
26
  token: String,
27
+ /// HuggingFace dataset repo in the form "owner/name".
28
+ /// Read from OPENCLAW_DATASET_REPO (canonical HuggingClaw variable).
29
  dataset_id: String,
30
  sync_interval: Duration,
31
  workspace: PathBuf,
 
149
 
150
  fn load_config() -> Result<Config> {
151
  let token = env::var("HF_TOKEN").context("HF_TOKEN is required")?;
152
+
153
+ // Accept OPENCLAW_DATASET_REPO (canonical HuggingClaw name) with
154
+ // HF_DATASET_ID as a legacy fallback so old Space configs keep working.
155
+ let dataset_id = env::var("OPENCLAW_DATASET_REPO")
156
+ .or_else(|_| env::var("HF_DATASET_ID"))
157
+ .context("OPENCLAW_DATASET_REPO (or HF_DATASET_ID) is required")?;
158
+
159
  validate_dataset_id(&dataset_id)?;
160
 
161
  let sync_interval_secs: u64 = env::var("SYNC_INTERVAL")
 
180
  let owner = parts.next().unwrap_or_default();
181
  let repo = parts.next().unwrap_or_default();
182
  if owner.is_empty() || repo.is_empty() || parts.next().is_some() {
183
+ return Err(anyhow!(
184
+ "OPENCLAW_DATASET_REPO must be in the form owner/name"
185
+ ));
186
  }
187
  Ok(())
188
  }
 
217
  Ok(())
218
  }
219
  StatusCode::NOT_FOUND => {
220
+ // Only auto-create if the user has opted in.
221
+ let auto_create = env::var("AUTO_CREATE_DATASET")
222
+ .map(|v| matches!(v.to_lowercase().as_str(), "1" | "true" | "yes" | "on"))
223
+ .unwrap_or(false);
224
+
225
+ if auto_create {
226
+ eprintln!(
227
+ "dataset {} not found, AUTO_CREATE_DATASET=true — creating private dataset",
228
+ cfg.dataset_id
229
+ );
230
+ create_private_dataset(client, cfg).await
231
+ } else {
232
+ Err(anyhow!(
233
+ "dataset {} not found. Create it on huggingface.co/new-dataset \
234
+ or set AUTO_CREATE_DATASET=true to create it automatically.",
235
+ cfg.dataset_id
236
+ ))
237
+ }
238
  }
239
  status => {
240
  let body = response.text().await.unwrap_or_default();
 
247
  let (owner, name) = cfg
248
  .dataset_id
249
  .split_once('/')
250
+ .ok_or_else(|| anyhow!("OPENCLAW_DATASET_REPO must be in the form owner/name"))?;
251
 
252
  let me_url = "https://huggingface.co/api/whoami-v2";
253
  let username = client
 
289
  }
290
 
291
  async fn pull_workspace(client: &reqwest::Client, cfg: &Config) -> Result<()> {
292
+ eprintln!("pulling workspace from HuggingFace dataset: {}", cfg.dataset_id);
293
  let remote_files = list_remote_files(client, cfg).await?;
294
 
295
  for file in &remote_files {
 
313
  f.write_all(&bytes).await?;
314
  }
315
 
316
+ eprintln!("pull complete: {} files restored", remote_files.len());
317
  Ok(())
318
  }
319
 
 
337
  }
338
 
339
  async fn push_workspace(client: &reqwest::Client, cfg: &Config) -> Result<()> {
340
+ eprintln!("pushing workspace updates to HuggingFace");
341
  let state_path = cfg.workspace.join(STATE_FILE);
342
  let mut state = load_state(&state_path).await?;
343
 
 
459
  if !path.exists() {
460
  return Ok(SyncState::default());
461
  }
 
462
  let raw = tokio::fs::read(path).await?;
463
  let state = serde_json::from_slice(&raw).context("failed to parse sync state file")?;
464
  Ok(state)
 
474
  let args: Vec<String> = env::args().skip(1).collect();
475
  if args.is_empty() {
476
  return Err(anyhow!(
477
+ "no command provided pass the main process command as entrypoint arguments"
478
  ));
479
  }
480
 
 
495
  let ret = unsafe { libc::kill(id as libc::pid_t, libc::SIGTERM) };
496
  if ret != 0 {
497
  eprintln!(
498
+ "failed to forward SIGTERM: {}",
499
  std::io::Error::last_os_error()
500
  );
501
  }
 
520
  let ret = unsafe { libc::kill(id as libc::pid_t, libc::SIGKILL) };
521
  if ret != 0 {
522
  eprintln!(
523
+ "failed to SIGKILL child: {}",
524
  std::io::Error::last_os_error()
525
  );
526
  }