From 157599be4a7cfa1eff915aa7c851e26d5c63635b Mon Sep 17 00:00:00 2001 From: opencode-agent Date: Fri, 13 Feb 2026 11:17:19 +0000 Subject: [PATCH] feat: add Windows sync script - Phase 1: One-way sync (Windows -> Server) for main folders - Phase 2: Bidirectional sync using rclone bisync for _LLM_Sync - Auto-detects rclone and BenjaminTeam folder paths - Comprehensive logging to %LOCALAPPDATA%\JingTian\logs - Supports verbose output with -VerboseOutput flag --- windows/sync.ps1 | 235 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 235 insertions(+) create mode 100644 windows/sync.ps1 diff --git a/windows/sync.ps1 b/windows/sync.ps1 new file mode 100644 index 0000000..d762761 --- /dev/null +++ b/windows/sync.ps1 @@ -0,0 +1,235 @@ +<# +.SYNOPSIS + JingTian rclone Sync Script + +.DESCRIPTION + Syncs the local BenjaminTeam folder with the remote Ubuntu server. + - Main folders: One-way sync (Windows -> Server) + - _LLM_Sync folder: Bidirectional sync + +.PARAMETER LocalPath + Path to the local BenjaminTeam folder. + +.PARAMETER RclonePath + Path to rclone.exe. Default: auto-detect + +.PARAMETER LogDir + Directory for log files. Default: %LOCALAPPDATA%\JingTian\logs + +.PARAMETER Verbose + Show detailed output during sync. +#> + +param( + [string]$LocalPath = "", + [string]$RclonePath = "", + [string]$LogDir = "$env:LOCALAPPDATA\JingTian\logs", + [switch]$VerboseOutput +) + +# Configuration +$RemoteName = "jingtian-server" +$RemotePath = "/data/jingtian/BenjaminTeam" +$LLMSyncFolder = "_LLM_Sync" + +# Find rclone if not specified +if (-not $RclonePath -or -not (Test-Path $RclonePath)) { + $possiblePaths = @( + "$env:LOCALAPPDATA\rclone\rclone.exe", + "C:\rclone\rclone.exe", + "rclone.exe" + ) + foreach ($path in $possiblePaths) { + if (Test-Path $path) { + $RclonePath = $path + break + } + } +} + +if (-not (Test-Path $RclonePath)) { + Write-Error "rclone not found. Please run setup.ps1 first." + exit 1 +} + +# Find LocalPath if not specified (check common locations) +if (-not $LocalPath -or -not (Test-Path $LocalPath)) { + $searchPaths = @( + "$env:USERPROFILE\Documents\BenjaminTeam", + "$env:USERPROFILE\Documents\Benjamin Team", + "D:\BenjaminTeam", + "C:\BenjaminTeam" + ) + foreach ($path in $searchPaths) { + if (Test-Path $path) { + $LocalPath = $path + break + } + } +} + +if (-not (Test-Path $LocalPath)) { + Write-Error "BenjaminTeam folder not found. Please specify -LocalPath" + exit 1 +} + +# Setup logging +$timestamp = Get-Date -Format "yyyy-MM-dd_HH-mm-ss" +$logFile = Join-Path $LogDir "sync.log" +$detailLogFile = Join-Path $LogDir "sync_$timestamp.log" + +if (-not (Test-Path $LogDir)) { + New-Item -ItemType Directory -Path $LogDir -Force | Out-Null +} + +function Write-Log { + param([string]$Message, [string]$Level = "INFO") + $ts = Get-Date -Format "yyyy-MM-dd HH:mm:ss" + $logLine = "[$ts] [$Level] $Message" + Add-Content -Path $logFile -Value $logLine + + if ($VerboseOutput) { + switch ($Level) { + "ERROR" { Write-Host $logLine -ForegroundColor Red } + "WARN" { Write-Host $logLine -ForegroundColor Yellow } + "OK" { Write-Host $logLine -ForegroundColor Green } + default { Write-Host $logLine } + } + } +} + +# ============================================================ +# MAIN SYNC LOGIC +# ============================================================ + +Write-Log "========== Sync Started ==========" +Write-Log "Local: $LocalPath" +Write-Log "Remote: ${RemoteName}:${RemotePath}" + +$syncErrors = @() +$startTime = Get-Date + +# ------------------------------------------------------------ +# Phase 1: One-way sync (Windows -> Server) for main folders +# Excludes _LLM_Sync which is handled separately +# ------------------------------------------------------------ + +Write-Log "Phase 1: Syncing main folders (Windows -> Server)..." + +$rcloneArgs = @( + "sync", + $LocalPath, + "${RemoteName}:${RemotePath}", + "--exclude", "$LLMSyncFolder/**", + "--transfers", "4", + "--checkers", "8", + "--contimeout", "60s", + "--timeout", "300s", + "--retries", "3", + "--low-level-retries", "10", + "--stats", "1m", + "--stats-one-line", + "--log-file", $detailLogFile, + "--log-level", "INFO" +) + +if ($VerboseOutput) { + $rcloneArgs += "--progress" +} + +try { + $result = & $RclonePath @rcloneArgs 2>&1 + if ($LASTEXITCODE -eq 0) { + Write-Log "Phase 1 complete: Main folders synced successfully" "OK" + } else { + Write-Log "Phase 1 warning: rclone exited with code $LASTEXITCODE" "WARN" + $syncErrors += "Main folder sync had warnings (exit code: $LASTEXITCODE)" + } +} catch { + Write-Log "Phase 1 error: $_" "ERROR" + $syncErrors += "Main folder sync failed: $_" +} + +# ------------------------------------------------------------ +# Phase 2: Bidirectional sync for _LLM_Sync folder +# Uses rclone bisync for true bidirectional sync +# ------------------------------------------------------------ + +Write-Log "Phase 2: Syncing _LLM_Sync folder (bidirectional)..." + +$llmLocalPath = Join-Path $LocalPath $LLMSyncFolder +$llmRemotePath = "${RemoteName}:${RemotePath}/${LLMSyncFolder}" + +# Ensure local _LLM_Sync exists +if (-not (Test-Path $llmLocalPath)) { + New-Item -ItemType Directory -Path $llmLocalPath -Force | Out-Null + Write-Log "Created local _LLM_Sync folder" +} + +# Check if bisync has been initialized (look for .rclone-bisync directory) +$bisyncStateDir = "$env:USERPROFILE\.cache\rclone\bisync" +$bisyncStateFile = Join-Path $bisyncStateDir "*.lst" + +# For first run, we need to use --resync to initialize +$needsResync = $false +if (-not (Test-Path $bisyncStateDir) -or -not (Get-ChildItem $bisyncStateFile -ErrorAction SilentlyContinue)) { + Write-Log "First bisync run detected, will initialize with --resync" + $needsResync = $true +} + +$bisyncArgs = @( + "bisync", + $llmLocalPath, + $llmRemotePath, + "--transfers", "4", + "--checkers", "8", + "--contimeout", "60s", + "--timeout", "300s", + "--retries", "3", + "--log-file", $detailLogFile, + "--log-level", "INFO" +) + +if ($needsResync) { + $bisyncArgs += "--resync" + $bisyncArgs += "--resync-mode", "newer" +} + +if ($VerboseOutput) { + $bisyncArgs += "--progress" +} + +try { + $result = & $RclonePath @bisyncArgs 2>&1 + if ($LASTEXITCODE -eq 0) { + Write-Log "Phase 2 complete: _LLM_Sync folder synced successfully" "OK" + } else { + # bisync can return non-zero for various reasons, check output + Write-Log "Phase 2 warning: bisync exited with code $LASTEXITCODE" "WARN" + $syncErrors += "_LLM_Sync bisync had warnings (exit code: $LASTEXITCODE)" + } +} catch { + Write-Log "Phase 2 error: $_" "ERROR" + $syncErrors += "_LLM_Sync sync failed: $_" +} + +# ------------------------------------------------------------ +# Summary +# ------------------------------------------------------------ + +$endTime = Get-Date +$duration = $endTime - $startTime + +Write-Log "========== Sync Completed ==========" +Write-Log "Duration: $($duration.TotalSeconds.ToString('F1')) seconds" + +if ($syncErrors.Count -gt 0) { + Write-Log "Completed with $($syncErrors.Count) warning(s):" "WARN" + foreach ($err in $syncErrors) { + Write-Log " - $err" "WARN" + } + exit 1 +} else { + Write-Log "All syncs completed successfully" "OK" + exit 0 +}