1
0
mirror of https://github.com/tw93/Mole.git synced 2026-02-04 14:26:46 +00:00
Files
Mole/windows/lib/core/base.ps1
Bhadra 3255fa2451 fix(windows): fix CI function name and purge return type handling
- Fix Test-IsProtectedPath -> Test-ProtectedPath in CI workflow
- Fix purge.ps1 to handle boolean return from Remove-SafeItem
- Add C:\Program Files and C:\Program Files (x86) to ProtectedPaths
2026-01-08 21:37:07 +05:30

397 lines
12 KiB
PowerShell

# 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.