Phase 2.
This commit is contained in:
@@ -104,6 +104,7 @@ function Import-SetupConfiguration {
|
||||
}
|
||||
|
||||
$Configuration = @{
|
||||
AppCatalog = if (Test-Path -LiteralPath (Join-Path $PSScriptRoot 'Config\app-catalog.json') -PathType Leaf) { Import-SetupConfiguration -Name 'app-catalog.json' } else { $null }
|
||||
CorePackages = Import-SetupConfiguration -Name 'apps-core.json'
|
||||
OptionalApps = Import-SetupConfiguration -Name 'apps-optional.json'
|
||||
PortableApps = Import-SetupConfiguration -Name 'portable-apps.json'
|
||||
@@ -112,6 +113,19 @@ $Configuration = @{
|
||||
Profiles = Import-SetupConfiguration -Name 'workstation-profiles.json'
|
||||
Automotive = Import-SetupConfiguration -Name 'automotive-resources.json'
|
||||
}
|
||||
|
||||
function Get-ProfileCatalogItems {
|
||||
param(
|
||||
[Parameter(Mandatory)][object[]]$Items,
|
||||
[Parameter(Mandatory)][string]$Channel
|
||||
)
|
||||
$profileFolder = $Profile.Folder
|
||||
return @($Items | Where-Object {
|
||||
$_.Channel -eq $Channel -and
|
||||
((-not $_.PSObject.Properties['Profiles']) -or @($_.Profiles) -contains $profileFolder)
|
||||
})
|
||||
}
|
||||
|
||||
$Profile = @($Configuration.Profiles.Profiles | Where-Object { $_.Name -eq $WorkstationProfile } | Select-Object -First 1)
|
||||
if ($Profile.Count -ne 1) {
|
||||
throw "Workstation profile '$WorkstationProfile' is not defined in Config\workstation-profiles.json."
|
||||
@@ -313,8 +327,10 @@ function Invoke-ReliableDownload {
|
||||
[Parameter(Mandatory)][string]$Uri,
|
||||
[Parameter(Mandatory)][string]$Destination,
|
||||
[hashtable]$Headers = @{},
|
||||
[switch]$UseCurl
|
||||
[switch]$UseCurl,
|
||||
[switch]$AllowInsecureHttp
|
||||
)
|
||||
Test-DownloadUri -Uri $Uri -AllowInsecureHttp:$AllowInsecureHttp
|
||||
$partial = "$Destination.partial"
|
||||
$lastError = $null
|
||||
for ($attempt = 1; $attempt -le $DownloadRetryCount; $attempt++) {
|
||||
@@ -519,6 +535,48 @@ function Get-HWiNFOPortableUri {
|
||||
return $portableUri
|
||||
}
|
||||
|
||||
function Test-DownloadUri {
|
||||
param(
|
||||
[Parameter(Mandatory)][string]$Uri,
|
||||
[switch]$AllowInsecureHttp
|
||||
)
|
||||
$parsedUri = [Uri]$Uri
|
||||
if ($parsedUri.Scheme -eq 'https') { return }
|
||||
if ($parsedUri.Scheme -eq 'http' -and $AllowInsecureHttp) {
|
||||
Write-Log "Using explicitly approved HTTP download: $Uri" 'WARN'
|
||||
return
|
||||
}
|
||||
throw "Insecure download blocked: $Uri. Use HTTPS or explicitly approve this documented vendor exception."
|
||||
}
|
||||
|
||||
function Test-DownloadedFile {
|
||||
param(
|
||||
[Parameter(Mandatory)][string]$Path,
|
||||
[Parameter(Mandatory)][string]$Name,
|
||||
[int64]$MinimumBytes = 65536,
|
||||
[string]$ExpectedSHA256,
|
||||
[switch]$RequireAuthenticode
|
||||
)
|
||||
$file = Get-Item -LiteralPath $Path -ErrorAction Stop
|
||||
if ($file.Length -lt $MinimumBytes) {
|
||||
throw "$Name download is unexpectedly small: $($file.Length) bytes; minimum is $MinimumBytes."
|
||||
}
|
||||
if (-not [string]::IsNullOrWhiteSpace($ExpectedSHA256) -and $ExpectedSHA256 -notmatch '(?i)^TO_BE_FILLED') {
|
||||
$actualHash = (Get-FileHash -LiteralPath $Path -Algorithm SHA256 -ErrorAction Stop).Hash
|
||||
if ($actualHash -ne $ExpectedSHA256.ToUpperInvariant()) {
|
||||
throw "$Name SHA256 mismatch. Expected $ExpectedSHA256, received $actualHash."
|
||||
}
|
||||
Write-Log "$Name SHA256 verified: $actualHash" 'OK'
|
||||
}
|
||||
if ($file.Extension -in @('.exe', '.dll')) {
|
||||
$signature = Get-AuthenticodeSignature -LiteralPath $Path
|
||||
if ($signature.Status -ne 'Valid') {
|
||||
if ($RequireAuthenticode) { throw "$Name Authenticode signature is not valid: $($signature.Status)." }
|
||||
Write-Log "$Name has no valid Authenticode signature: $($signature.Status)." 'WARN'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Install-PortableArchive {
|
||||
param(
|
||||
[Parameter(Mandatory)][string]$Name,
|
||||
@@ -528,7 +586,10 @@ function Install-PortableArchive {
|
||||
[string]$ArchiveName = '',
|
||||
[switch]$FlattenSingleDirectory,
|
||||
[hashtable]$Headers = @{},
|
||||
[switch]$DownloadWithCurl
|
||||
[switch]$DownloadWithCurl,
|
||||
[string]$ExpectedSHA256,
|
||||
[switch]$AllowInsecureHttp,
|
||||
[switch]$RequireAuthenticode
|
||||
)
|
||||
$downloadName = if ($ArchiveName) { $ArchiveName } else { [IO.Path]::GetFileName(([Uri]$Uri).AbsolutePath) }
|
||||
if (-not $downloadName) { $downloadName = ($Name -replace '[^A-Za-z0-9._-]','_') + '.zip' }
|
||||
@@ -538,6 +599,7 @@ function Install-PortableArchive {
|
||||
Remove-Item -LiteralPath $staging -Recurse -Force -ErrorAction SilentlyContinue
|
||||
$null = New-Item -ItemType Directory -Path $staging -Force
|
||||
try {
|
||||
Test-DownloadUri -Uri $Uri -AllowInsecureHttp:$AllowInsecureHttp
|
||||
Invoke-ReliableDownload -Name $Name -Uri $Uri -Destination $archive -Headers $Headers -UseCurl:$DownloadWithCurl
|
||||
}
|
||||
catch {
|
||||
@@ -547,7 +609,7 @@ function Install-PortableArchive {
|
||||
}
|
||||
$archiveSize = (Get-Item -LiteralPath $archive).Length
|
||||
Write-Log "$Name portable archive downloaded: $archiveSize bytes"
|
||||
if ($archiveSize -lt 64KB) { throw "$Name download is unexpectedly small." }
|
||||
Test-DownloadedFile -Path $archive -Name $Name -MinimumBytes 65536 -ExpectedSHA256 $ExpectedSHA256
|
||||
if ([IO.Path]::GetExtension($archive) -ne '.zip') { throw "$Name archive must be ZIP for safe native extraction." }
|
||||
Expand-Archive -LiteralPath $archive -DestinationPath $staging -Force
|
||||
|
||||
@@ -579,7 +641,10 @@ function Install-PortableExecutable {
|
||||
[Parameter(Mandatory)][string]$Name,
|
||||
[Parameter(Mandatory)][string]$Uri,
|
||||
[Parameter(Mandatory)][string]$Destination,
|
||||
[Parameter(Mandatory)][string]$FileName
|
||||
[Parameter(Mandatory)][string]$FileName,
|
||||
[string]$ExpectedSHA256,
|
||||
[switch]$AllowInsecureHttp,
|
||||
[switch]$RequireAuthenticode
|
||||
)
|
||||
$null = New-Item -ItemType Directory -Path $Destination -Force
|
||||
$target = Join-Path $Destination $FileName
|
||||
@@ -587,8 +652,9 @@ function Install-PortableExecutable {
|
||||
$backup = "$target.previous"
|
||||
Remove-Item -LiteralPath $temporary -Force -ErrorAction SilentlyContinue
|
||||
try {
|
||||
Test-DownloadUri -Uri $Uri -AllowInsecureHttp:$AllowInsecureHttp
|
||||
Invoke-ReliableDownload -Name $Name -Uri $Uri -Destination $temporary
|
||||
if ((Get-Item -LiteralPath $temporary).Length -lt 64KB) { throw "$Name download is unexpectedly small." }
|
||||
Test-DownloadedFile -Path $temporary -Name $Name -MinimumBytes 65536 -ExpectedSHA256 $ExpectedSHA256 -RequireAuthenticode:$RequireAuthenticode
|
||||
Remove-Item -LiteralPath $backup -Force -ErrorAction SilentlyContinue
|
||||
if (Test-Path -LiteralPath $target) { Move-Item -LiteralPath $target -Destination $backup -Force }
|
||||
Move-Item -LiteralPath $temporary -Destination $target -Force
|
||||
@@ -892,8 +958,17 @@ Complete-SetupPhase -Phase 'SharedPartitions' -Details "DataRoot=$SharedDataRoot
|
||||
# 3. Portable applications are installed after WinGet so archive prerequisites are available.
|
||||
|
||||
# 4. Core applications. WinGet uses package manifests and each vendor's installer.
|
||||
$CorePackages = $Configuration.CorePackages
|
||||
$OptionalPackages = $Configuration.OptionalApps.Packages
|
||||
$catalogPackages = if ($Configuration.AppCatalog) { @($Configuration.AppCatalog.Packages) } else { @() }
|
||||
$CorePackages = if ($catalogPackages.Count -gt 0) {
|
||||
Get-ProfileCatalogItems -Items $catalogPackages -Channel 'Core'
|
||||
} else {
|
||||
@($Configuration.CorePackages)
|
||||
}
|
||||
$OptionalPackages = if ($catalogPackages.Count -gt 0) {
|
||||
Get-ProfileCatalogItems -Items $catalogPackages -Channel 'Optional'
|
||||
} else {
|
||||
@($Configuration.OptionalApps.Packages)
|
||||
}
|
||||
|
||||
if (-not $SkipWinget) {
|
||||
Invoke-Safe 'WinGet' 'Refresh package sources' {
|
||||
@@ -948,11 +1023,13 @@ if (-not $SkipDownloads) {
|
||||
$createShortcut = $null -eq $configuredApp.Shortcut -or [bool]$configuredApp.Shortcut
|
||||
$flattenSingleDirectory = if ($configuredApp.PSObject.Properties['Flatten']) { [bool]$configuredApp.Flatten } else { -not $isDirectExe }
|
||||
$downloadWithCurl = [bool]($configuredApp.PSObject.Properties['DownloadWithCurl'] -and $configuredApp.DownloadWithCurl)
|
||||
$expectedSHA256 = if ($configuredApp.PSObject.Properties['ExpectedSHA256']) { [string]$configuredApp.ExpectedSHA256 } else { '' }
|
||||
$requireAuthenticode = [bool]($configuredApp.PSObject.Properties['RequireAuthenticode'] -and $configuredApp.RequireAuthenticode)
|
||||
if (-not $ForcePortableUpdates -and (Test-PortableInstallation -Destination $destination -ExpectedExecutable $configuredApp.Executable)) {
|
||||
$PortableDefinitions.Add([pscustomobject]@{
|
||||
Name=$configuredApp.Name; Type=if ($isDirectExe) { 'Exe' } else { 'Zip' }; Uri=''; Folder=$configuredApp.Folder;
|
||||
Executable=$configuredApp.Executable; Archive=''; Flatten=$flattenSingleDirectory; Shortcut=$createShortcut;
|
||||
DownloadWithCurl=$downloadWithCurl
|
||||
DownloadWithCurl=$downloadWithCurl; ExpectedSHA256=$expectedSHA256; RequireAuthenticode=$requireAuthenticode
|
||||
})
|
||||
Add-Result 'PortableResolve' $configuredApp.Name 'Skipped' "Validated installation already exists: $destination"
|
||||
Write-Log "$($configuredApp.Name) release lookup skipped because its portable installation is already validated." 'INFO'
|
||||
@@ -963,7 +1040,8 @@ if (-not $SkipDownloads) {
|
||||
$asset = Get-ChromeForTestingAsset -Platform $configuredApp.Platform
|
||||
$PortableDefinitions.Add([pscustomobject]@{
|
||||
Name=$configuredApp.Name; Type='Zip'; Uri=$asset.url; Folder=$configuredApp.Folder;
|
||||
Executable=$configuredApp.Executable; Archive=$configuredApp.Archive; Flatten=[bool]$configuredApp.Flatten; Shortcut=$createShortcut
|
||||
Executable=$configuredApp.Executable; Archive=$configuredApp.Archive; Flatten=[bool]$configuredApp.Flatten; Shortcut=$createShortcut;
|
||||
ExpectedSHA256=$expectedSHA256; RequireAuthenticode=$requireAuthenticode
|
||||
})
|
||||
} | Out-Null
|
||||
}
|
||||
@@ -972,7 +1050,8 @@ if (-not $SkipDownloads) {
|
||||
$asset = Get-GitHubReleaseAsset -Repository $configuredApp.Repository -AssetRegex $configuredApp.Regex
|
||||
$PortableDefinitions.Add([pscustomobject]@{
|
||||
Name=$configuredApp.Name; Type=if ($isDirectExe) { 'Exe' } else { 'Zip' }; Uri=$asset.browser_download_url; Folder=$configuredApp.Folder;
|
||||
Executable=$configuredApp.Executable; Archive=if ($isDirectExe) { '' } else { $asset.name }; Flatten=(-not $isDirectExe); Shortcut=$createShortcut
|
||||
Executable=$configuredApp.Executable; Archive=if ($isDirectExe) { '' } else { $asset.name }; Flatten=(-not $isDirectExe); Shortcut=$createShortcut;
|
||||
ExpectedSHA256=$expectedSHA256; RequireAuthenticode=$requireAuthenticode
|
||||
})
|
||||
} | Out-Null
|
||||
}
|
||||
@@ -980,7 +1059,7 @@ if (-not $SkipDownloads) {
|
||||
$PortableDefinitions.Add([pscustomobject]@{
|
||||
Name=$configuredApp.Name; Type=if ($isDirectExe) { 'Exe' } else { 'Zip' }; Uri=$configuredApp.Uri; Folder=$configuredApp.Folder;
|
||||
Executable=$configuredApp.Executable; Archive=$configuredApp.Archive; Flatten=$flattenSingleDirectory; Shortcut=$createShortcut;
|
||||
DownloadWithCurl=$downloadWithCurl
|
||||
DownloadWithCurl=$downloadWithCurl; ExpectedSHA256=$expectedSHA256; RequireAuthenticode=$requireAuthenticode
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -994,14 +1073,15 @@ if (-not $SkipDownloads) {
|
||||
else {
|
||||
Invoke-Safe 'PortableInstall' $app.Name {
|
||||
if ($app.Type -eq 'Exe') {
|
||||
Install-PortableExecutable -Name $app.Name -Uri $app.Uri -Destination $destination -FileName $app.Executable
|
||||
Install-PortableExecutable -Name $app.Name -Uri $app.Uri -Destination $destination -FileName $app.Executable `
|
||||
-ExpectedSHA256 $app.ExpectedSHA256 -RequireAuthenticode:$app.RequireAuthenticode
|
||||
}
|
||||
else {
|
||||
$requestHeaders = if ($app.PSObject.Properties['Headers']) { $app.Headers } else { @{} }
|
||||
$downloadWithCurl = $app.PSObject.Properties['DownloadWithCurl'] -and $app.DownloadWithCurl
|
||||
Install-PortableArchive -Name $app.Name -Uri $app.Uri -Destination $destination `
|
||||
-ExpectedExecutable $app.Executable -ArchiveName $app.Archive -FlattenSingleDirectory:$app.Flatten `
|
||||
-Headers $requestHeaders -DownloadWithCurl:$downloadWithCurl
|
||||
-Headers $requestHeaders -DownloadWithCurl:$downloadWithCurl -ExpectedSHA256 $app.ExpectedSHA256
|
||||
}
|
||||
} | Out-Null
|
||||
}
|
||||
@@ -1040,7 +1120,13 @@ if (-not $SkipDownloads) {
|
||||
|
||||
# Extensions are installed only after portable Code.exe and its data directory exist.
|
||||
# They support offline binary inspection and embedded-module development only.
|
||||
$vsCodeExtensions = @($Configuration.OptionalApps.VSCodeExtensions)
|
||||
$vsCodeExtensions = if ($Configuration.AppCatalog -and @($Configuration.AppCatalog.VSCodeExtensions).Count -gt 0) {
|
||||
@($Configuration.AppCatalog.VSCodeExtensions | Where-Object {
|
||||
(-not $_.PSObject.Properties['Profiles']) -or @($_.Profiles) -contains $Profile.Folder
|
||||
})
|
||||
} else {
|
||||
@($Configuration.OptionalApps.VSCodeExtensions)
|
||||
}
|
||||
try {
|
||||
$installedVSCodeExtensions = Get-InstalledVSCodeExtensions -CodeExecutable (Join-Path $vsCodeRoot 'Code.exe')
|
||||
Write-Log "Found $($installedVSCodeExtensions.Count) installed VS Code extensions."
|
||||
@@ -1471,7 +1557,9 @@ Multi-PROG for the selected device, and preserve multiple verified original back
|
||||
$multiProgSoftwareRoot = Join-Path $ProfileInstallerRoot 'Xhorse\Multi-PROG'
|
||||
$null = New-Item -ItemType Directory -Path $multiProgSoftwareRoot -Force
|
||||
$installerPath = Join-Path $multiProgSoftwareRoot 'Multi-PROG.exe'
|
||||
Invoke-ReliableDownload -Name 'Xhorse Multi-PROG installer' -Uri $Configuration.Automotive.Installers[0].Uri -Destination $installerPath
|
||||
$multiProg = $Configuration.Automotive.Installers | Where-Object { $_.Name -eq 'Multi-PROG' } | Select-Object -First 1
|
||||
if (-not $multiProg) { throw 'Multi-PROG installer is not defined in automotive-resources.json.' }
|
||||
Invoke-ReliableDownload -Name 'Xhorse Multi-PROG installer' -Uri $multiProg.Uri -Destination $installerPath -AllowInsecureHttp
|
||||
if ((Get-Item -LiteralPath $installerPath).Length -lt 1MB) { throw 'Multi-PROG installer download is unexpectedly small.' }
|
||||
Unblock-File -LiteralPath $installerPath -ErrorAction SilentlyContinue
|
||||
Write-Log 'Multi-PROG installer downloaded. Launching interactive setup; connect the Multi-PROG dongle only when the wizard prompts for it.' 'WARN'
|
||||
@@ -1483,11 +1571,13 @@ Multi-PROG for the selected device, and preserve multiple verified original back
|
||||
$null = New-Item -ItemType Directory -Path $mvciSoftwareRoot -Force
|
||||
$installerPath = Join-Path $mvciSoftwareRoot 'MVCI_PRO-J2534.exe'
|
||||
# Xhorse's MVCI PRO CDN rejects HTTPS (403); HTTP is the vendor's only working transport for this asset.
|
||||
Invoke-ReliableDownload -Name 'Xhorse MVCI PRO installer' -Uri $Configuration.Automotive.Installers[1].Uri -Destination $installerPath
|
||||
$mvci = $Configuration.Automotive.CommunicationSoftware | Where-Object { $_.Name -eq 'MVCI PRO J2534' } | Select-Object -First 1
|
||||
if (-not $mvci) { throw 'MVCI PRO J2534 is not defined in automotive-resources.json.' }
|
||||
Invoke-ReliableDownload -Name 'Xhorse MVCI PRO installer' -Uri $mvci.Uri -Destination $installerPath -AllowInsecureHttp:$mvci.AllowInsecureHttp
|
||||
if ((Get-Item -LiteralPath $installerPath).Length -lt 1MB) { throw 'MVCI PRO installer download is unexpectedly small.' }
|
||||
$manualPath = Join-Path $mvciSoftwareRoot 'MVCI-PRO-User-Manual.pdf'
|
||||
try {
|
||||
Invoke-ReliableDownload -Name 'Xhorse MVCI PRO user manual' -Uri 'http://dl.xhorse.com/p/vd06/userManual.pdf' -Destination $manualPath
|
||||
Invoke-ReliableDownload -Name 'Xhorse MVCI PRO user manual' -Uri 'http://dl.xhorse.com/p/vd06/userManual.pdf' -Destination $manualPath -AllowInsecureHttp
|
||||
}
|
||||
catch {
|
||||
Write-Log "MVCI PRO user manual download failed (non-critical): $($_.Exception.Message)" 'WARN'
|
||||
@@ -1508,7 +1598,8 @@ Multi-PROG for the selected device, and preserve multiple verified original back
|
||||
$mvciRoot = Join-Path $ProfileInstallerRoot $mvci.Folder
|
||||
$null = New-Item -ItemType Directory -Path $mvciRoot -Force
|
||||
$mvciPath = Join-Path $mvciRoot $mvci.FileName
|
||||
Invoke-ReliableDownload -Name $mvci.Name -Uri $mvci.Uri -Destination $mvciPath
|
||||
if ($mvci.RequiresVpn) { Write-Log "$($mvci.Name) requires the vendor/VPN network path before downloading." 'WARN' }
|
||||
Invoke-ReliableDownload -Name $mvci.Name -Uri $mvci.Uri -Destination $mvciPath -AllowInsecureHttp:$mvci.AllowInsecureHttp
|
||||
if ((Get-Item -LiteralPath $mvciPath).Length -lt [int64]$mvci.MinimumBytes) { throw 'MVCI PRO installer is unexpectedly small.' }
|
||||
Unblock-File -LiteralPath $mvciPath -ErrorAction SilentlyContinue
|
||||
Write-Log 'MVCI PRO J2534 installer downloaded. Launching interactive setup for this Windows installation; connect the interface only when prompted.' 'WARN'
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"SchemaVersion": "2.0",
|
||||
"Packages": [
|
||||
{ "Id": "7zip.7zip", "Name": "7-Zip", "Channel": "Core", "Profiles": ["DailyTech-Tuning", "ODIS-XENTRY", "PIWIS-ISTA"] },
|
||||
{ "Id": "voidtools.Everything", "Name": "Everything Search", "Channel": "Core", "Profiles": ["DailyTech-Tuning", "ODIS-XENTRY", "PIWIS-ISTA"] },
|
||||
{ "Id": "Notepad++.Notepad++", "Name": "Notepad++", "Channel": "Core", "Profiles": ["DailyTech-Tuning", "ODIS-XENTRY", "PIWIS-ISTA"] },
|
||||
{ "Id": "Microsoft.VisualStudioCode", "Name": "Visual Studio Code", "Channel": "Core", "Profiles": ["DailyTech-Tuning", "ODIS-XENTRY", "PIWIS-ISTA"] },
|
||||
{ "Id": "Git.Git", "Name": "Git", "Channel": "Core", "Profiles": ["DailyTech-Tuning", "ODIS-XENTRY", "PIWIS-ISTA"] },
|
||||
{ "Id": "Python.Python.3.13", "Name": "Python 3.13", "Channel": "Core", "Profiles": ["DailyTech-Tuning", "ODIS-XENTRY", "PIWIS-ISTA"] },
|
||||
{ "Id": "Microsoft.PowerShell", "Name": "PowerShell 7", "Channel": "Core", "Profiles": ["DailyTech-Tuning", "ODIS-XENTRY", "PIWIS-ISTA"] },
|
||||
{ "Id": "Microsoft.PowerToys", "Name": "Microsoft PowerToys", "Channel": "Core", "Profiles": ["DailyTech-Tuning", "ODIS-XENTRY", "PIWIS-ISTA"] },
|
||||
{ "Id": "PuTTY.PuTTY", "Name": "PuTTY", "Channel": "Core", "Profiles": ["DailyTech-Tuning", "ODIS-XENTRY", "PIWIS-ISTA"] },
|
||||
{ "Id": "WinSCP.WinSCP", "Name": "WinSCP", "Channel": "Core", "Profiles": ["DailyTech-Tuning", "ODIS-XENTRY", "PIWIS-ISTA"] },
|
||||
{ "Id": "WiresharkFoundation.Wireshark", "Name": "Wireshark", "Channel": "Core", "Profiles": ["DailyTech-Tuning", "ODIS-XENTRY", "PIWIS-ISTA"] },
|
||||
{ "Id": "Rufus.Rufus", "Name": "Rufus", "Channel": "Core", "Profiles": ["DailyTech-Tuning", "ODIS-XENTRY", "PIWIS-ISTA"] },
|
||||
{ "Id": "Ventoy.Ventoy", "Name": "Ventoy", "Channel": "Core", "Profiles": ["DailyTech-Tuning", "ODIS-XENTRY", "PIWIS-ISTA"] },
|
||||
{ "Id": "CrystalDewWorld.CrystalDiskInfo", "Name": "CrystalDiskInfo", "Channel": "Core", "Profiles": ["DailyTech-Tuning", "ODIS-XENTRY", "PIWIS-ISTA"] },
|
||||
{ "Id": "CPUID.CPU-Z", "Name": "CPU-Z", "Channel": "Core", "Profiles": ["DailyTech-Tuning", "ODIS-XENTRY", "PIWIS-ISTA"] },
|
||||
{ "Id": "TechPowerUp.GPU-Z", "Name": "GPU-Z", "Channel": "Core", "Profiles": ["DailyTech-Tuning", "ODIS-XENTRY", "PIWIS-ISTA"] },
|
||||
{ "Id": "GnuWin32.Make", "Name": "GNU Make", "Channel": "Optional", "Profiles": ["DailyTech-Tuning"] },
|
||||
{ "Id": "Kitware.CMake", "Name": "CMake", "Channel": "Optional", "Profiles": ["DailyTech-Tuning"] },
|
||||
{ "Id": "OpenJS.NodeJS.LTS", "Name": "Node.js LTS", "Channel": "Optional", "Profiles": ["DailyTech-Tuning"] },
|
||||
{ "Id": "Postman.Postman", "Name": "Postman", "Channel": "Optional", "Profiles": ["DailyTech-Tuning"] },
|
||||
{ "Id": "Microsoft.WindowsTerminal", "Name": "Windows Terminal", "Channel": "Core", "Profiles": ["DailyTech-Tuning", "ODIS-XENTRY", "PIWIS-ISTA"] },
|
||||
{ "Id": "Oracle.VirtualBox", "Name": "VirtualBox", "Channel": "Optional", "Profiles": ["DailyTech-Tuning"] },
|
||||
{ "Id": "VideoLAN.VLC", "Name": "VLC", "Channel": "Optional", "Profiles": ["DailyTech-Tuning", "ODIS-XENTRY", "PIWIS-ISTA"] },
|
||||
{ "Id": "SumatraPDF.SumatraPDF", "Name": "SumatraPDF", "Channel": "Optional", "Profiles": ["DailyTech-Tuning", "ODIS-XENTRY", "PIWIS-ISTA"] }
|
||||
],
|
||||
"VSCodeExtensions": [
|
||||
{ "Id": "ms-vscode.hexeditor", "Name": "Hex Editor", "Profiles": ["DailyTech-Tuning", "ODIS-XENTRY", "PIWIS-ISTA"] },
|
||||
{ "Id": "ms-vscode.vscode-serial-monitor", "Name": "Serial Monitor", "Profiles": ["DailyTech-Tuning", "ODIS-XENTRY", "PIWIS-ISTA"] },
|
||||
{ "Id": "ms-python.python", "Name": "Python", "Profiles": ["DailyTech-Tuning", "ODIS-XENTRY", "PIWIS-ISTA"] },
|
||||
{ "Id": "ms-vscode.cpptools", "Name": "C/C++", "Profiles": ["DailyTech-Tuning"] },
|
||||
{ "Id": "ms-vscode.cmake-tools", "Name": "CMake Tools", "Profiles": ["DailyTech-Tuning"] },
|
||||
{ "Id": "ms-vscode.makefile-tools", "Name": "Makefile Tools", "Profiles": ["DailyTech-Tuning"] },
|
||||
{ "Id": "ms-vscode.powershell", "Name": "PowerShell", "Profiles": ["DailyTech-Tuning", "ODIS-XENTRY", "PIWIS-ISTA"] },
|
||||
{ "Id": "redhat.vscode-yaml", "Name": "YAML", "Profiles": ["DailyTech-Tuning", "ODIS-XENTRY", "PIWIS-ISTA"] },
|
||||
{ "Id": "redhat.vscode-xml", "Name": "XML", "Profiles": ["DailyTech-Tuning", "ODIS-XENTRY", "PIWIS-ISTA"] },
|
||||
{ "Id": "tamasfe.even-better-toml", "Name": "Even Better TOML", "Profiles": ["DailyTech-Tuning", "ODIS-XENTRY", "PIWIS-ISTA"] }
|
||||
]
|
||||
}
|
||||
@@ -7,11 +7,10 @@
|
||||
],
|
||||
"Installers": [
|
||||
{ "Name": "Multi-PROG", "FileName": "Multi-PROG.exe", "Folder": "Xhorse\\Multi-PROG", "Uri": "http://dl.xhorse.com/product/multiProg/Multi-PROG.exe" },
|
||||
{ "Name": "MVCI PRO J2534", "FileName": "MVCI_PRO-J2534.exe", "Folder": "Xhorse\\MVCI-PRO", "Uri": "http://dl.xhorse.com/product/mvci/MVCI_PRO-J2534.exe" },
|
||||
{ "Name": "VVDI-MLB", "FileName": "VVDI-MLB.exe", "Folder": "Xhorse\\VVDI-MLB", "Uri": "http://dl.xhorse.com/product/vvdi-mlb/VVDI-MLB.exe" }
|
||||
],
|
||||
"CommunicationSoftware": [
|
||||
{ "Name": "MVCI PRO J2534", "FileName": "MVCI_PRO-J2534.exe", "Folder": "Xhorse\\MVCI-PRO", "Uri": "http://dl.xhorse.com/product/mvci/MVCI_PRO-J2534.exe", "MinimumBytes": 1048576 },
|
||||
{ "Name": "MVCI PRO J2534", "FileName": "MVCI_PRO-J2534.exe", "Folder": "Xhorse\\MVCI-PRO", "Uri": "http://dl.xhorse.com/product/mvci/MVCI_PRO-J2534.exe", "MinimumBytes": 1048576, "AllowInsecureHttp": true, "RequiresVpn": true },
|
||||
{ "Name": "Autel Maxi PC Suite", "FileName": "MaxiPCSuite_V1.50.zip", "Folder": "Autel\\Maxi-PC-Suite", "Uri": "https://autel.us/wp-content/uploads/MaxiPCSuite_V1.50.zip", "DownloadPage": "https://autel.us/software-downloads/", "MinimumBytes": 1048576 }
|
||||
],
|
||||
"References": [
|
||||
|
||||
Reference in New Issue
Block a user