mirror of
https://github.com/tw93/Mole.git
synced 2026-02-08 08:34:21 +00:00
chore: restructure windows branch (move windows/ content to root, remove macos files)
This commit is contained in:
396
lib/core/base.ps1
Normal file
396
lib/core/base.ps1
Normal file
@@ -0,0 +1,396 @@
|
||||
# Mole - Base Definitions and Utilities
|
||||
# Core definitions, constants, and basic utility functions used by all modules
|
||||
|
||||
#Requires -Version 5.1
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
# Prevent multiple sourcing
|
||||
if ((Get-Variable -Name 'MOLE_BASE_LOADED' -Scope Script -ErrorAction SilentlyContinue) -and $script:MOLE_BASE_LOADED) { return }
|
||||
$script:MOLE_BASE_LOADED = $true
|
||||
|
||||
# ============================================================================
|
||||
# Color Definitions (ANSI escape codes for modern terminals)
|
||||
# ============================================================================
|
||||
$script:ESC = [char]27
|
||||
$script:Colors = @{
|
||||
Green = "$ESC[0;32m"
|
||||
Blue = "$ESC[0;34m"
|
||||
Cyan = "$ESC[0;36m"
|
||||
Yellow = "$ESC[0;33m"
|
||||
Purple = "$ESC[0;35m"
|
||||
PurpleBold = "$ESC[1;35m"
|
||||
Red = "$ESC[0;31m"
|
||||
Gray = "$ESC[0;90m"
|
||||
White = "$ESC[0;37m"
|
||||
NC = "$ESC[0m" # No Color / Reset
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Icon Definitions
|
||||
# ============================================================================
|
||||
$script:Icons = @{
|
||||
Confirm = [char]0x25CE # ◎
|
||||
Admin = [char]0x2699 # ⚙
|
||||
Success = [char]0x2713 # ✓
|
||||
Error = [char]0x263B # ☻
|
||||
Warning = [char]0x25CF # ●
|
||||
Empty = [char]0x25CB # ○
|
||||
Solid = [char]0x25CF # ●
|
||||
List = [char]0x2022 # •
|
||||
Arrow = [char]0x27A4 # ➤
|
||||
DryRun = [char]0x2192 # →
|
||||
NavUp = [char]0x2191 # ↑
|
||||
NavDown = [char]0x2193 # ↓
|
||||
Folder = [char]0x25A0 # ■ (folder substitute)
|
||||
File = [char]0x25A1 # □ (file substitute)
|
||||
Trash = [char]0x2718 # ✘ (trash substitute)
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Global Configuration Constants
|
||||
# ============================================================================
|
||||
$script:Config = @{
|
||||
TempFileAgeDays = 7 # Temp file retention (days)
|
||||
OrphanAgeDays = 60 # Orphaned data retention (days)
|
||||
MaxParallelJobs = 15 # Parallel job limit
|
||||
LogAgeDays = 7 # Log retention (days)
|
||||
CrashReportAgeDays = 7 # Crash report retention (days)
|
||||
MaxIterations = 100 # Max iterations for scans
|
||||
ConfigPath = "$env:USERPROFILE\.config\mole"
|
||||
CachePath = "$env:USERPROFILE\.cache\mole"
|
||||
WhitelistFile = "$env:USERPROFILE\.config\mole\whitelist.txt"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Default Whitelist Patterns (paths to never clean)
|
||||
# ============================================================================
|
||||
$script:DefaultWhitelistPatterns = @(
|
||||
"$env:LOCALAPPDATA\Microsoft\Windows\Explorer" # Windows Explorer cache
|
||||
"$env:LOCALAPPDATA\Microsoft\Windows\Fonts" # User fonts
|
||||
"$env:APPDATA\Microsoft\Windows\Recent" # Recent files (used by shell)
|
||||
"$env:LOCALAPPDATA\Packages\*" # UWP app data
|
||||
"$env:USERPROFILE\.vscode\extensions" # VS Code extensions
|
||||
"$env:USERPROFILE\.nuget" # NuGet packages
|
||||
"$env:USERPROFILE\.cargo" # Rust packages
|
||||
"$env:USERPROFILE\.rustup" # Rust toolchain
|
||||
"$env:USERPROFILE\.m2\repository" # Maven repository
|
||||
"$env:USERPROFILE\.gradle\caches\modules-2\files-*" # Gradle modules
|
||||
"$env:USERPROFILE\.ollama\models" # Ollama AI models
|
||||
"$env:LOCALAPPDATA\JetBrains" # JetBrains IDEs
|
||||
)
|
||||
|
||||
# ============================================================================
|
||||
# Protected System Paths (NEVER touch these)
|
||||
# ============================================================================
|
||||
$script:ProtectedPaths = @(
|
||||
"C:\Windows"
|
||||
"C:\Windows\System32"
|
||||
"C:\Windows\SysWOW64"
|
||||
"C:\Program Files"
|
||||
"C:\Program Files (x86)"
|
||||
"C:\Program Files\Windows Defender"
|
||||
"C:\Program Files (x86)\Windows Defender"
|
||||
"C:\ProgramData\Microsoft\Windows Defender"
|
||||
"$env:SYSTEMROOT"
|
||||
"$env:WINDIR"
|
||||
)
|
||||
|
||||
# ============================================================================
|
||||
# System Utilities
|
||||
# ============================================================================
|
||||
|
||||
function Test-IsAdmin {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Check if running with administrator privileges
|
||||
#>
|
||||
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
|
||||
$principal = New-Object Security.Principal.WindowsPrincipal($identity)
|
||||
return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
|
||||
}
|
||||
|
||||
function Get-FreeSpace {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Get free disk space on system drive
|
||||
.OUTPUTS
|
||||
Human-readable string (e.g., "100GB")
|
||||
#>
|
||||
param([string]$Drive = $env:SystemDrive)
|
||||
|
||||
$disk = Get-WmiObject Win32_LogicalDisk -Filter "DeviceID='$Drive'" -ErrorAction SilentlyContinue
|
||||
if ($disk) {
|
||||
return Format-ByteSize -Bytes $disk.FreeSpace
|
||||
}
|
||||
return "Unknown"
|
||||
}
|
||||
|
||||
function Get-WindowsVersion {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Get Windows version information
|
||||
#>
|
||||
$os = Get-WmiObject Win32_OperatingSystem
|
||||
return @{
|
||||
Name = $os.Caption
|
||||
Version = $os.Version
|
||||
Build = $os.BuildNumber
|
||||
Arch = $os.OSArchitecture
|
||||
}
|
||||
}
|
||||
|
||||
function Get-CPUCores {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Get number of CPU cores
|
||||
#>
|
||||
return (Get-WmiObject Win32_Processor).NumberOfLogicalProcessors
|
||||
}
|
||||
|
||||
function Get-OptimalParallelJobs {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Get optimal number of parallel jobs based on CPU cores
|
||||
#>
|
||||
param(
|
||||
[ValidateSet('scan', 'io', 'compute', 'default')]
|
||||
[string]$OperationType = 'default'
|
||||
)
|
||||
|
||||
$cores = Get-CPUCores
|
||||
switch ($OperationType) {
|
||||
'scan' { return [Math]::Min($cores * 2, 32) }
|
||||
'io' { return [Math]::Min($cores * 2, 32) }
|
||||
'compute' { return $cores }
|
||||
default { return [Math]::Min($cores + 2, 20) }
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Path Utilities
|
||||
# ============================================================================
|
||||
|
||||
function Test-ProtectedPath {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Check if a path is protected and should never be modified
|
||||
#>
|
||||
param([string]$Path)
|
||||
|
||||
$normalizedPath = [System.IO.Path]::GetFullPath($Path).TrimEnd('\')
|
||||
|
||||
foreach ($protected in $script:ProtectedPaths) {
|
||||
$normalizedProtected = [System.IO.Path]::GetFullPath($protected).TrimEnd('\')
|
||||
if ($normalizedPath -eq $normalizedProtected -or
|
||||
$normalizedPath.StartsWith("$normalizedProtected\", [StringComparison]::OrdinalIgnoreCase)) {
|
||||
return $true
|
||||
}
|
||||
}
|
||||
return $false
|
||||
}
|
||||
|
||||
function Test-Whitelisted {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Check if path matches a whitelist pattern
|
||||
#>
|
||||
param([string]$Path)
|
||||
|
||||
# Check default patterns
|
||||
foreach ($pattern in $script:DefaultWhitelistPatterns) {
|
||||
$expandedPattern = [Environment]::ExpandEnvironmentVariables($pattern)
|
||||
if ($Path -like $expandedPattern) {
|
||||
return $true
|
||||
}
|
||||
}
|
||||
|
||||
# Check user whitelist file
|
||||
if (Test-Path $script:Config.WhitelistFile) {
|
||||
$userPatterns = Get-Content $script:Config.WhitelistFile -ErrorAction SilentlyContinue
|
||||
foreach ($pattern in $userPatterns) {
|
||||
$pattern = $pattern.Trim()
|
||||
if ($pattern -and -not $pattern.StartsWith('#')) {
|
||||
if ($Path -like $pattern) {
|
||||
return $true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $false
|
||||
}
|
||||
|
||||
function Resolve-SafePath {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Resolve and validate a path for safe operations
|
||||
#>
|
||||
param([string]$Path)
|
||||
|
||||
try {
|
||||
$resolved = [System.IO.Path]::GetFullPath($Path)
|
||||
return $resolved
|
||||
}
|
||||
catch {
|
||||
return $null
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Formatting Utilities
|
||||
# ============================================================================
|
||||
|
||||
function Format-ByteSize {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Convert bytes to human-readable format
|
||||
#>
|
||||
param([long]$Bytes)
|
||||
|
||||
if ($Bytes -ge 1TB) {
|
||||
return "{0:N2}TB" -f ($Bytes / 1TB)
|
||||
}
|
||||
elseif ($Bytes -ge 1GB) {
|
||||
return "{0:N2}GB" -f ($Bytes / 1GB)
|
||||
}
|
||||
elseif ($Bytes -ge 1MB) {
|
||||
return "{0:N1}MB" -f ($Bytes / 1MB)
|
||||
}
|
||||
elseif ($Bytes -ge 1KB) {
|
||||
return "{0:N0}KB" -f ($Bytes / 1KB)
|
||||
}
|
||||
else {
|
||||
return "{0}B" -f $Bytes
|
||||
}
|
||||
}
|
||||
|
||||
function Format-Number {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Format a number with thousands separators
|
||||
#>
|
||||
param([long]$Number)
|
||||
return $Number.ToString("N0")
|
||||
}
|
||||
|
||||
function Format-TimeSpan {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Format a timespan to human-readable string
|
||||
#>
|
||||
param([TimeSpan]$Duration)
|
||||
|
||||
if ($Duration.TotalHours -ge 1) {
|
||||
return "{0:N1}h" -f $Duration.TotalHours
|
||||
}
|
||||
elseif ($Duration.TotalMinutes -ge 1) {
|
||||
return "{0:N0}m" -f $Duration.TotalMinutes
|
||||
}
|
||||
else {
|
||||
return "{0:N0}s" -f $Duration.TotalSeconds
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Environment Detection
|
||||
# ============================================================================
|
||||
|
||||
function Get-UserHome {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Get the current user's home directory
|
||||
#>
|
||||
return $env:USERPROFILE
|
||||
}
|
||||
|
||||
function Get-TempPath {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Get the system temp path
|
||||
#>
|
||||
return [System.IO.Path]::GetTempPath()
|
||||
}
|
||||
|
||||
function Get-ConfigPath {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Get Mole config directory, creating it if needed
|
||||
#>
|
||||
$path = $script:Config.ConfigPath
|
||||
if (-not (Test-Path $path)) {
|
||||
New-Item -ItemType Directory -Path $path -Force | Out-Null
|
||||
}
|
||||
return $path
|
||||
}
|
||||
|
||||
function Get-CachePath {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Get Mole cache directory, creating it if needed
|
||||
#>
|
||||
$path = $script:Config.CachePath
|
||||
if (-not (Test-Path $path)) {
|
||||
New-Item -ItemType Directory -Path $path -Force | Out-Null
|
||||
}
|
||||
return $path
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Temporary File Management
|
||||
# ============================================================================
|
||||
|
||||
$script:TempFiles = [System.Collections.ArrayList]::new()
|
||||
$script:TempDirs = [System.Collections.ArrayList]::new()
|
||||
|
||||
function New-TempFile {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Create a tracked temporary file
|
||||
#>
|
||||
param([string]$Prefix = "winmole")
|
||||
|
||||
$tempPath = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), "$Prefix-$([Guid]::NewGuid().ToString('N').Substring(0,8)).tmp")
|
||||
New-Item -ItemType File -Path $tempPath -Force | Out-Null
|
||||
[void]$script:TempFiles.Add($tempPath)
|
||||
return $tempPath
|
||||
}
|
||||
|
||||
function New-TempDirectory {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Create a tracked temporary directory
|
||||
#>
|
||||
param([string]$Prefix = "winmole")
|
||||
|
||||
$tempPath = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), "$Prefix-$([Guid]::NewGuid().ToString('N').Substring(0,8))")
|
||||
New-Item -ItemType Directory -Path $tempPath -Force | Out-Null
|
||||
[void]$script:TempDirs.Add($tempPath)
|
||||
return $tempPath
|
||||
}
|
||||
|
||||
function Clear-TempFiles {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Clean up all tracked temporary files and directories
|
||||
#>
|
||||
foreach ($file in $script:TempFiles) {
|
||||
if (Test-Path $file) {
|
||||
Remove-Item $file -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
$script:TempFiles.Clear()
|
||||
|
||||
foreach ($dir in $script:TempDirs) {
|
||||
if (Test-Path $dir) {
|
||||
Remove-Item $dir -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
$script:TempDirs.Clear()
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Exports (functions and variables are available via dot-sourcing)
|
||||
# ============================================================================
|
||||
# Variables: Colors, Icons, Config, ProtectedPaths, DefaultWhitelistPatterns
|
||||
# Functions: Test-IsAdmin, Get-FreeSpace, Get-WindowsVersion, etc.
|
||||
Reference in New Issue
Block a user