Files
Tracker/Code/Sync/Win-Setup.ps1
T
2026-02-21 21:55:42 +00:00

315 lines
11 KiB
PowerShell

#Requires -RunAsAdministrator
<#
.SYNOPSIS
JingTian LLM - Windows Sync Setup
.DESCRIPTION
Sets up rclone for automatic file syncing between this Windows machine
and the JingTian Ubuntu server. Run once, then syncing happens automatically.
.NOTES
Run via Setup.bat (double-click) or directly with admin privileges.
#>
param(
[string]$SyncRoot = $PSScriptRoot,
[string]$Password = ""
)
$ErrorActionPreference = "Stop"
# ============================================================================
# Helpers
# ============================================================================
function Write-Step {
param([int]$Number, [string]$Title)
Write-Host ""
Write-Host "[$Number/6] $Title" -ForegroundColor Cyan
Write-Host ("-" * 50)
}
function Read-Config {
param([string]$Path)
$config = @{}
$section = ""
$listItems = @()
$listSection = ""
foreach ($line in Get-Content $Path) {
$line = $line.Trim()
if ($line -eq "" -or $line.StartsWith("#")) { continue }
if ($line -match '^\[(.+)\]$') {
# Save any pending list
if ($listSection -and $listItems.Count -gt 0) {
$config[$listSection] = $listItems
}
$section = $Matches[1]
# Check if this section is a list section (bidirectional)
if ($section -eq "bidirectional") {
$listSection = $section
$listItems = @()
} else {
$listSection = ""
}
continue
}
if ($listSection) {
$listItems += $line
} elseif ($line -match '^(.+?)=(.+)$') {
$key = "$section.$($Matches[1].Trim())"
$config[$key] = $Matches[2].Trim()
}
}
# Save final list section
if ($listSection -and $listItems.Count -gt 0) {
$config[$listSection] = $listItems
}
return $config
}
# ============================================================================
# Main Setup
# ============================================================================
Write-Host ""
Write-Host "========================================" -ForegroundColor Yellow
Write-Host " JingTian LLM - Sync Setup" -ForegroundColor Yellow
Write-Host "========================================" -ForegroundColor Yellow
Write-Host ""
# Determine paths
$configPath = Join-Path $SyncRoot "config.ini"
$rclonePath = Join-Path $SyncRoot "rclone"
$rcloneExe = Join-Path $rclonePath "rclone.exe"
$rcloneConf = Join-Path $rclonePath "rclone.conf"
$logsPath = Join-Path $SyncRoot "logs"
$syncScript = Join-Path $SyncRoot "sync.ps1"
$ignoreFile = Join-Path $SyncRoot ".sync_ignore"
# BenjaminTeam is two levels up from Code/Sync/
$benjaminTeam = (Resolve-Path (Join-Path $SyncRoot "..\..\.." )).Path
Write-Host "Sync root: $SyncRoot"
Write-Host "BenjaminTeam: $benjaminTeam"
Write-Host ""
# Read config
if (-not (Test-Path $configPath)) {
Write-Host "ERROR: config.ini not found at $configPath" -ForegroundColor Red
exit 1
}
$config = Read-Config $configPath
$serverHost = $config["server.host"]
$serverUser = $config["server.user"]
$serverPort = $config["server.port"]
$remotePath = $config["server.remote_path"]
$remoteName = $config["sync.remote_name"]
$interval = $config["sync.interval_minutes"]
Write-Host "Server: ${serverUser}@${serverHost}:${serverPort}" -ForegroundColor Gray
Write-Host "Remote: ${remoteName}:${remotePath}" -ForegroundColor Gray
# --------------------------------------------------------------------------
# Step 1: Install rclone
# --------------------------------------------------------------------------
Write-Step 1 "Installing rclone"
if (Test-Path $rcloneExe) {
$version = & $rcloneExe version 2>&1 | Select-Object -First 1
Write-Host "rclone already installed: $version" -ForegroundColor Green
} else {
Write-Host "Downloading rclone..."
$zipUrl = "https://downloads.rclone.org/rclone-current-windows-amd64.zip"
$zipPath = Join-Path $env:TEMP "rclone.zip"
$extractPath = Join-Path $env:TEMP "rclone-extract"
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
Invoke-WebRequest -Uri $zipUrl -OutFile $zipPath -UseBasicParsing
Write-Host "Extracting..."
if (Test-Path $extractPath) { Remove-Item $extractPath -Recurse -Force }
Expand-Archive -Path $zipPath -DestinationPath $extractPath -Force
# rclone extracts into a subfolder like rclone-v1.68.2-windows-amd64/
$rcloneSubDir = Get-ChildItem $extractPath -Directory | Select-Object -First 1
if (-not (Test-Path $rclonePath)) { New-Item -ItemType Directory -Path $rclonePath -Force | Out-Null }
Copy-Item (Join-Path $rcloneSubDir.FullName "rclone.exe") $rcloneExe -Force
# Cleanup
Remove-Item $zipPath -Force -ErrorAction SilentlyContinue
Remove-Item $extractPath -Recurse -Force -ErrorAction SilentlyContinue
$version = & $rcloneExe version 2>&1 | Select-Object -First 1
Write-Host "Installed: $version" -ForegroundColor Green
}
# --------------------------------------------------------------------------
# Step 2: Configure rclone (password-based SFTP)
# --------------------------------------------------------------------------
Write-Step 2 "Configuring rclone remote"
if ($Password -ne "") {
$plainPassword = $Password
Write-Host "Using provided password" -ForegroundColor Green
} else {
Write-Host "Enter the password for ${serverUser}@${serverHost}:" -ForegroundColor Yellow
$securePassword = Read-Host -AsSecureString
$plainPassword = [Runtime.InteropServices.Marshal]::PtrToStringAuto(
[Runtime.InteropServices.Marshal]::SecureStringToBSTR($securePassword)
)
}
# Obscure the password using rclone
$obscuredPassword = & $rcloneExe obscure $plainPassword 2>&1
$plainPassword = $null # Clear from memory
$rcloneConfContent = @"
[$remoteName]
type = sftp
host = $serverHost
user = $serverUser
port = $serverPort
pass = $obscuredPassword
shell_type = unix
md5sum_command = md5sum
sha1sum_command = sha1sum
"@
# Write without BOM (rclone can't parse BOM)
$utf8NoBom = New-Object System.Text.UTF8Encoding $false
[System.IO.File]::WriteAllText($rcloneConf, $rcloneConfContent, $utf8NoBom)
Write-Host "rclone config written to $rcloneConf" -ForegroundColor Green
# --------------------------------------------------------------------------
# Step 3: Test connection
# --------------------------------------------------------------------------
Write-Step 3 "Testing connection to server"
try {
$testOutput = & $rcloneExe lsd "${remoteName}:${remotePath}" --config $rcloneConf 2>&1
if ($LASTEXITCODE -ne 0) {
Write-Host "ERROR: Connection failed!" -ForegroundColor Red
Write-Host $testOutput
Write-Host ""
Write-Host "Check the server IP, username, and password in config.ini" -ForegroundColor Yellow
exit 1
}
Write-Host "Connection successful! Remote directories:" -ForegroundColor Green
$testOutput | ForEach-Object { Write-Host " $_" -ForegroundColor Gray }
} catch {
Write-Host "ERROR: Connection failed: $_" -ForegroundColor Red
exit 1
}
# --------------------------------------------------------------------------
# Step 4: Generate .sync_ignore
# --------------------------------------------------------------------------
Write-Step 4 "Generating sync exclusion rules"
$ignoreContent = @"
# JingTian Sync Ignore Rules
# rclone filter syntax - one rule per line
# See: https://rclone.org/filtering/
# The _LLM directory (scripts, logs, config - managed via git, not sync)
- _LLM/**
# Dotfiles and hidden items
- .*
# Office temp/lock files
- ~`$*
- *.tmp
- ~*.tmp
# OS generated files
- Thumbs.db
- desktop.ini
- .DS_Store
- ehthumbs.db
# Windows shortcuts
- *.lnk
"@
# Write without BOM (rclone can't parse BOM)
$utf8NoBom = New-Object System.Text.UTF8Encoding $false
[System.IO.File]::WriteAllText($ignoreFile, $ignoreContent, $utf8NoBom)
Write-Host "Sync ignore rules written to $ignoreFile" -ForegroundColor Green
# --------------------------------------------------------------------------
# Step 5: Create logs directory
# --------------------------------------------------------------------------
Write-Step 5 "Setting up logging"
if (-not (Test-Path $logsPath)) {
New-Item -ItemType Directory -Path $logsPath -Force | Out-Null
}
Write-Host "Logs directory: $logsPath" -ForegroundColor Green
# --------------------------------------------------------------------------
# Step 6: Create scheduled task
# --------------------------------------------------------------------------
Write-Step 6 "Creating scheduled task"
$taskName = "JingTian-Sync"
# Remove existing task if present
$existing = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue
if ($existing) {
Write-Host "Removing existing task '$taskName'..." -ForegroundColor Yellow
Unregister-ScheduledTask -TaskName $taskName -Confirm:$false
}
# Build the action - run sync.ps1 via PowerShell
$action = New-ScheduledTaskAction `
-Execute "powershell.exe" `
-Argument "-ExecutionPolicy Bypass -WindowStyle Hidden -File `"$syncScript`" -SyncRoot `"$SyncRoot`"" `
-WorkingDirectory $SyncRoot
# Trigger: every N minutes, indefinitely
$trigger = New-ScheduledTaskTrigger -Once -At (Get-Date) `
-RepetitionInterval (New-TimeSpan -Minutes ([int]$interval)) `
-RepetitionDuration (New-TimeSpan -Days 9999)
# Settings
$settings = New-ScheduledTaskSettingsSet `
-AllowStartIfOnBatteries `
-DontStopIfGoingOnBatteries `
-StartWhenAvailable `
-RunOnlyIfNetworkAvailable `
-MultipleInstances IgnoreNew
# Register as SYSTEM so it runs even when user is logged out
Register-ScheduledTask `
-TaskName $taskName `
-Action $action `
-Trigger $trigger `
-Settings $settings `
-RunLevel Highest `
-User "SYSTEM" `
-Description "JingTian LLM - Sync files with Ubuntu server every $interval minutes" `
| Out-Null
Write-Host "Scheduled task '$taskName' created (every $interval minutes)" -ForegroundColor Green
# ============================================================================
# Done
# ============================================================================
Write-Host ""
Write-Host "========================================" -ForegroundColor Green
Write-Host " Setup Complete!" -ForegroundColor Green
Write-Host "========================================" -ForegroundColor Green
Write-Host ""
Write-Host "Sync will run automatically every $interval minutes." -ForegroundColor White
Write-Host "Logs are saved to: $logsPath" -ForegroundColor White
Write-Host ""
Write-Host "To sync manually, run:" -ForegroundColor Gray
Write-Host " powershell -File `"$syncScript`" -SyncRoot `"$SyncRoot`"" -ForegroundColor Gray
Write-Host ""