mirror of
https://github.com/samsonjs/vibetunnel.git
synced 2026-03-25 09:25:50 +00:00
Implement hash-based vt script version detection
- Add SHA256 hash comparison for vt script updates - Check vt script version on app startup - Show welcome dialog when vt script is outdated - Add update prompts in both Welcome and Settings views - Ensures users always have the latest vt features (like 'vt title') Fixes #245
This commit is contained in:
parent
d9a30e299d
commit
55a852881c
5 changed files with 154 additions and 25 deletions
|
|
@ -41,22 +41,37 @@ struct AdvancedSettingsView: View {
|
|||
// Actual content
|
||||
if cliInstaller.isInstalled {
|
||||
HStack(spacing: 8) {
|
||||
Image(systemName: "checkmark.circle.fill")
|
||||
.foregroundColor(.green)
|
||||
Text("VT installed")
|
||||
.foregroundColor(.secondary)
|
||||
if cliInstaller.isOutdated {
|
||||
Image(systemName: "exclamationmark.triangle.fill")
|
||||
.foregroundColor(.orange)
|
||||
Text("VT update available")
|
||||
.foregroundColor(.secondary)
|
||||
|
||||
Button("Update") {
|
||||
Task {
|
||||
await cliInstaller.install()
|
||||
}
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
.disabled(cliInstaller.isInstalling)
|
||||
} else {
|
||||
Image(systemName: "checkmark.circle.fill")
|
||||
.foregroundColor(.green)
|
||||
Text("VT installed")
|
||||
.foregroundColor(.secondary)
|
||||
|
||||
// Show reinstall button in debug mode
|
||||
if debugMode {
|
||||
Button(action: {
|
||||
cliInstaller.installCLITool()
|
||||
}, label: {
|
||||
Image(systemName: "arrow.clockwise.circle")
|
||||
.font(.system(size: 14))
|
||||
})
|
||||
.buttonStyle(.plain)
|
||||
.foregroundColor(.accentColor)
|
||||
.help("Reinstall CLI tool")
|
||||
// Show reinstall button in debug mode
|
||||
if debugMode {
|
||||
Button(action: {
|
||||
cliInstaller.installCLITool()
|
||||
}, label: {
|
||||
Image(systemName: "arrow.clockwise.circle")
|
||||
.font(.system(size: 14))
|
||||
})
|
||||
.buttonStyle(.plain)
|
||||
.foregroundColor(.accentColor)
|
||||
.help("Reinstall CLI tool")
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -204,6 +204,14 @@ private struct PermissionsSection: View {
|
|||
.padding(.horizontal, 10)
|
||||
.padding(.vertical, 2)
|
||||
.frame(height: 22) // Match small button height
|
||||
.contextMenu {
|
||||
Button("Refresh Status") {
|
||||
permissionManager.forcePermissionRecheck()
|
||||
}
|
||||
Button("Open System Settings...") {
|
||||
permissionManager.requestPermission(.appleScript)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Button("Grant Permission") {
|
||||
permissionManager.requestPermission(.appleScript)
|
||||
|
|
@ -236,6 +244,14 @@ private struct PermissionsSection: View {
|
|||
.padding(.horizontal, 10)
|
||||
.padding(.vertical, 2)
|
||||
.frame(height: 22) // Match small button height
|
||||
.contextMenu {
|
||||
Button("Refresh Status") {
|
||||
permissionManager.forcePermissionRecheck()
|
||||
}
|
||||
Button("Open System Settings...") {
|
||||
permissionManager.requestPermission(.accessibility)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Button("Grant Permission") {
|
||||
permissionManager.requestPermission(.accessibility)
|
||||
|
|
@ -268,6 +284,14 @@ private struct PermissionsSection: View {
|
|||
.padding(.horizontal, 10)
|
||||
.padding(.vertical, 2)
|
||||
.frame(height: 22) // Match small button height
|
||||
.contextMenu {
|
||||
Button("Refresh Status") {
|
||||
permissionManager.forcePermissionRecheck()
|
||||
}
|
||||
Button("Open System Settings...") {
|
||||
permissionManager.requestPermission(.screenRecording)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Button("Grant Permission") {
|
||||
permissionManager.requestPermission(.screenRecording)
|
||||
|
|
|
|||
|
|
@ -53,11 +53,28 @@ struct VTCommandPageView: View {
|
|||
// Install VT Binary button
|
||||
VStack(spacing: 12) {
|
||||
if cliInstaller.isInstalled {
|
||||
HStack {
|
||||
Image(systemName: "checkmark.circle.fill")
|
||||
.foregroundColor(.green)
|
||||
Text("CLI tool is installed")
|
||||
.foregroundColor(.secondary)
|
||||
if cliInstaller.isOutdated {
|
||||
HStack {
|
||||
Image(systemName: "exclamationmark.triangle.fill")
|
||||
.foregroundColor(.orange)
|
||||
Text("CLI tool is outdated")
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
|
||||
Button("Update VT Command Line Tool") {
|
||||
Task {
|
||||
await cliInstaller.install()
|
||||
}
|
||||
}
|
||||
.buttonStyle(.borderedProminent)
|
||||
.disabled(cliInstaller.isInstalling)
|
||||
} else {
|
||||
HStack {
|
||||
Image(systemName: "checkmark.circle.fill")
|
||||
.foregroundColor(.green)
|
||||
Text("CLI tool is installed")
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Button("Install VT Command Line Tool") {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import AppKit
|
||||
import CryptoKit
|
||||
import Foundation
|
||||
import Observation
|
||||
import os.log
|
||||
|
|
@ -36,6 +37,7 @@ final class CLIInstaller {
|
|||
var isInstalled = false
|
||||
var isInstalling = false
|
||||
var lastError: String?
|
||||
var isOutdated = false
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
|
|
@ -79,6 +81,13 @@ final class CLIInstaller {
|
|||
isInstalled = isCorrectlyInstalled
|
||||
|
||||
logger.info("CLIInstaller: vt script installed: \(self.isInstalled)")
|
||||
|
||||
// If installed, check if it's outdated
|
||||
if isInstalled {
|
||||
checkScriptVersion()
|
||||
} else {
|
||||
isOutdated = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -256,4 +265,60 @@ final class CLIInstaller {
|
|||
alert.alertStyle = .critical
|
||||
alert.runModal()
|
||||
}
|
||||
|
||||
// MARK: - Script Version Detection
|
||||
|
||||
/// Calculates SHA256 hash of a file
|
||||
private func calculateHash(for filePath: String) -> String? {
|
||||
guard let data = try? Data(contentsOf: URL(fileURLWithPath: filePath)) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let hash = SHA256.hash(data: data)
|
||||
return hash.compactMap { String(format: "%02x", $0) }.joined()
|
||||
}
|
||||
|
||||
/// Gets the hash of the bundled vt script
|
||||
private func getBundledScriptHash() -> String? {
|
||||
guard let scriptPath = Bundle.main.path(forResource: "vt", ofType: nil) else {
|
||||
logger.error("CLIInstaller: Bundled vt script not found")
|
||||
return nil
|
||||
}
|
||||
|
||||
return calculateHash(for: scriptPath)
|
||||
}
|
||||
|
||||
/// Checks if the installed script is outdated compared to bundled version
|
||||
func checkScriptVersion() {
|
||||
Task { @MainActor in
|
||||
guard let bundledHash = getBundledScriptHash() else {
|
||||
logger.error("CLIInstaller: Failed to get bundled script hash")
|
||||
return
|
||||
}
|
||||
|
||||
// Check both possible installation paths
|
||||
let pathsToCheck = [
|
||||
vtTargetPath,
|
||||
"/opt/homebrew/bin/vt"
|
||||
]
|
||||
|
||||
var installedHash: String?
|
||||
for path in pathsToCheck where FileManager.default.fileExists(atPath: path) {
|
||||
if let hash = calculateHash(for: path) {
|
||||
installedHash = hash
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Update outdated status
|
||||
if let installedHash = installedHash {
|
||||
self.isOutdated = (installedHash != bundledHash)
|
||||
logger.info("CLIInstaller: Script version check - outdated: \(self.isOutdated)")
|
||||
logger.debug("CLIInstaller: Bundled hash: \(bundledHash), Installed hash: \(installedHash)")
|
||||
} else {
|
||||
// Script not installed
|
||||
self.isOutdated = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -194,12 +194,20 @@ final class AppDelegate: NSObject, NSApplicationDelegate, @preconcurrency UNUser
|
|||
// Initialize dock icon visibility through DockIconManager
|
||||
DockIconManager.shared.updateDockVisibility()
|
||||
|
||||
// Show welcome screen when version changes
|
||||
// Check CLI installation status
|
||||
let cliInstaller = CLIInstaller()
|
||||
cliInstaller.checkInstallationStatus()
|
||||
|
||||
// Show welcome screen when version changes OR when vt script is outdated
|
||||
let storedWelcomeVersion = UserDefaults.standard.integer(forKey: AppConstants.UserDefaultsKeys.welcomeVersion)
|
||||
|
||||
// Show welcome if version is different from current
|
||||
if storedWelcomeVersion < AppConstants.currentWelcomeVersion && !isRunningInTests && !isRunningInPreview {
|
||||
showWelcomeScreen()
|
||||
|
||||
// Small delay to allow CLI check to complete
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in
|
||||
// Show welcome if version is different from current OR if vt script is outdated
|
||||
if (storedWelcomeVersion < AppConstants.currentWelcomeVersion || cliInstaller.isOutdated)
|
||||
&& !isRunningInTests && !isRunningInPreview {
|
||||
self?.showWelcomeScreen()
|
||||
}
|
||||
}
|
||||
|
||||
// Skip all service initialization during tests
|
||||
|
|
|
|||
Loading…
Reference in a new issue