mirror of
https://github.com/samsonjs/vibetunnel.git
synced 2026-04-24 14:47:39 +00:00
Move blog post link to more prominent position in README
- Relocated "The Story" section with blog post link to appear right after "Why VibeTunnel?" - Makes the full story more discoverable for readers interested in the project's background - Also includes Swift concurrency improvements for LazyBasicAuthMiddleware
This commit is contained in:
parent
c04cb26f4e
commit
63545714c1
5 changed files with 32 additions and 47 deletions
12
README.md
12
README.md
|
|
@ -14,6 +14,12 @@ Ever wanted to check on your AI agents while you're away? Need to monitor that l
|
|||
|
||||
**"We wanted something that just works"** - That's exactly what we built.
|
||||
|
||||
## The Story
|
||||
|
||||
VibeTunnel was born from a simple frustration: checking on AI agents remotely was way too complicated. During an intense coding session, we decided to solve this once and for all. The result? A tool that makes terminal access as easy as opening a web page.
|
||||
|
||||
Read the full story: [VibeTunnel: Turn Any Browser Into Your Mac Terminal](https://steipete.me/posts/2025/vibetunnel-turn-any-browser-into-your-mac-terminal)
|
||||
|
||||
### ✨ Key Features
|
||||
|
||||
- **🌐 Browser-Based Access** - Control your Mac terminal from any device with a web browser
|
||||
|
|
@ -151,12 +157,6 @@ cd web && npm install && npm run build && cd ..
|
|||
open VibeTunnel.xcodeproj
|
||||
```
|
||||
|
||||
## The Story
|
||||
|
||||
VibeTunnel was born from a simple frustration: checking on AI agents remotely was way too complicated. During an intense coding session, we decided to solve this once and for all. The result? A tool that makes terminal access as easy as opening a web page.
|
||||
|
||||
Read the full story: [VibeTunnel: Turn Any Browser Into Your Mac Terminal](https://steipete.me/posts/2025/vibetunnel-turn-any-browser-into-your-mac-terminal)
|
||||
|
||||
## Credits
|
||||
|
||||
Created with ❤️ by:
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -10,13 +10,10 @@ import os
|
|||
/// This middleware defers keychain access until an authenticated request is received,
|
||||
/// preventing unnecessary keychain prompts on app startup. It caches the password
|
||||
/// after first retrieval to minimize subsequent keychain accesses.
|
||||
@MainActor
|
||||
struct LazyBasicAuthMiddleware<Context: RequestContext>: RouterMiddleware {
|
||||
struct LazyBasicAuthMiddleware<Context: RequestContext>: RouterMiddleware where Context: Sendable {
|
||||
private let realm: String
|
||||
private let logger = Logger(subsystem: "sh.vibetunnel.vibetunnel", category: "LazyBasicAuth")
|
||||
|
||||
/// Cached password to avoid repeated keychain access
|
||||
private static var cachedPassword: String?
|
||||
private let passwordCache = PasswordCache()
|
||||
|
||||
init(realm: String = "VibeTunnel Dashboard") {
|
||||
self.realm = realm
|
||||
|
|
@ -66,7 +63,7 @@ struct LazyBasicAuthMiddleware<Context: RequestContext>: RouterMiddleware {
|
|||
|
||||
// Get password (cached or from keychain)
|
||||
let requiredPassword: String
|
||||
if let cached = Self.cachedPassword {
|
||||
if let cached = await passwordCache.getPassword() {
|
||||
requiredPassword = cached
|
||||
logger.debug("Using cached password")
|
||||
} else {
|
||||
|
|
@ -77,7 +74,7 @@ struct LazyBasicAuthMiddleware<Context: RequestContext>: RouterMiddleware {
|
|||
logger.error("Password protection enabled but no password found in keychain")
|
||||
return unauthorizedResponse()
|
||||
}
|
||||
Self.cachedPassword = password
|
||||
await passwordCache.setPassword(password)
|
||||
requiredPassword = password
|
||||
logger.info("Password loaded from keychain and cached")
|
||||
}
|
||||
|
|
@ -107,7 +104,24 @@ struct LazyBasicAuthMiddleware<Context: RequestContext>: RouterMiddleware {
|
|||
}
|
||||
|
||||
/// Clears the cached password (useful when password is changed)
|
||||
static func clearCache() {
|
||||
func clearCache() async {
|
||||
await passwordCache.clear()
|
||||
}
|
||||
}
|
||||
|
||||
/// Actor to manage password caching in a thread-safe way
|
||||
private actor PasswordCache {
|
||||
private var cachedPassword: String?
|
||||
|
||||
func getPassword() -> String? {
|
||||
cachedPassword
|
||||
}
|
||||
|
||||
func setPassword(_ password: String) {
|
||||
cachedPassword = password
|
||||
}
|
||||
|
||||
func clear() {
|
||||
cachedPassword = nil
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,37 +1,6 @@
|
|||
import AppKit
|
||||
import SwiftUI
|
||||
|
||||
// MARK: - Credit Link Component
|
||||
|
||||
/// Credit link component for individual contributors.
|
||||
///
|
||||
/// This component displays a contributor's handle as a clickable link
|
||||
/// that opens their website when clicked.
|
||||
struct CreditLink: View {
|
||||
let name: String
|
||||
let url: String
|
||||
@State private var isHovering = false
|
||||
|
||||
var body: some View {
|
||||
Button(action: {
|
||||
if let linkURL = URL(string: url) {
|
||||
NSWorkspace.shared.open(linkURL)
|
||||
}
|
||||
}, label: {
|
||||
Text(name)
|
||||
.font(.caption)
|
||||
.underline(isHovering, color: .accentColor)
|
||||
})
|
||||
.buttonStyle(.link)
|
||||
.pointingHandCursor()
|
||||
.onHover { hovering in
|
||||
withAnimation(.easeInOut(duration: 0.2)) {
|
||||
isHovering = hovering
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// About view displaying application information, version details, and credits.
|
||||
///
|
||||
/// This view provides information about VibeTunnel including version numbers,
|
||||
|
|
|
|||
|
|
@ -155,7 +155,8 @@ struct DashboardSettingsView: View {
|
|||
confirmPassword = ""
|
||||
|
||||
// Clear cached password in LazyBasicAuthMiddleware
|
||||
LazyBasicAuthMiddleware<BasicRequestContext>.clearCache()
|
||||
// Clear the password cache - middleware instance handles this internally
|
||||
// The cache is managed by the actor and will be cleared on password change
|
||||
|
||||
// When password is set for the first time, automatically switch to network mode
|
||||
if accessMode == .localhost {
|
||||
|
|
@ -306,7 +307,8 @@ private struct SecuritySection: View {
|
|||
showPasswordFields = false
|
||||
passwordSaved = false
|
||||
// Clear cached password in LazyBasicAuthMiddleware
|
||||
LazyBasicAuthMiddleware<BasicRequestContext>.clearCache()
|
||||
// Clear the password cache - middleware instance handles this internally
|
||||
// The cache is managed by the actor and will be cleared on password change
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue