Skip to content

Conversation

@alexbartok
Copy link
Contributor

@alexbartok alexbartok commented Dec 29, 2025

Sorry, one more while I'm at it ;-)


Add Launch at Login Functionality

Summary

Adds the ability to automatically start BrewServicesManager when the user logs in to macOS. This feature uses the modern SMAppService API from the ServiceManagement framework, providing a native and reliable launch-at-login experience.

Changes

New Files

  • LaunchAtLoginError.swift - Error type for handling launch-at-login failures with localized descriptions and recovery suggestions
  • BrewServicesManager.entitlements - Entitlements file for code signing with documentation explaining why app sandbox is disabled to preserve Homebrew access

Modified Files

  • AppSettings.swift - Added launch-at-login state management with:
    • UserDefaults persistence for the setting
    • System state synchronization on app launch (optimized to prevent redundant API calls)
    • Automatic registration/unregistration with SMAppService
    • Graceful error handling with state reversion on failure
  • SettingsView.swift - Added new "Launch" settings card with:
    • Toggle control for enabling/disabling launch at login
    • Inline error display with recovery suggestions
    • Direct link to System Settings when user approval is needed
  • project.pbxproj - Configured CODE_SIGN_ENTITLEMENTS for both Debug and Release builds
  • project.pbxproj - Updated deployment target to 15.0 (from 15.7) and configured CODE_SIGN_ENTITLEMENTS for both Debug and Release builds
  • README.md - Updated feature list and settings documentation

Implementation Details

Architecture

The implementation follows existing patterns in the codebase:

  • Integrated directly into AppSettings (@observable class) rather than creating a separate manager
  • Uses UserDefaults for persistence with didSet observers for automatic updates
  • Errors are stored as optional properties and displayed inline in the UI

Key Features

  • State Synchronization: Automatically syncs with macOS login items on app launch to handle manual changes made through System Settings. Uses a synchronization flag to prevent redundant system API calls during state updates.
  • Error Handling: Non-blocking errors with user-friendly messages and recovery suggestions
  • No Sandboxing Required: Uses minimal entitlements (no app sandbox) to preserve full Homebrew access while still supporting ServiceManagement

Technical Decisions

Why No App Sandbox?

Initially implemented with full app sandboxing (as commonly recommended for ServiceManagement), but this broke Homebrew detection and command execution. Testing revealed that SMAppService works perfectly without app sandboxing on modern macOS. The entitlements file includes only the minimal com.apple.application-identifier key, with XML comments documenting why sandboxing is disabled (to preserve shell access for Homebrew commands, sudo privilege escalation, and lsof port detection).

Error State Handling

.notFound status (which occurs when running from Xcode or when not yet registered) is treated as a normal state rather than an error, preventing unnecessary error messages on first launch.

Testing

Manual Testing Checklist

  • Toggle "Launch at login" ON → verify app appears in System Settings > General > Login Items
  • Toggle OFF → verify app is removed from Login Items
  • Quit and relaunch → verify setting persists correctly
  • Log out and back in with setting enabled → verify app launches automatically
  • Manually remove from System Settings → relaunch app → verify toggle syncs to OFF
  • Verify Homebrew functionality still works (not broken by entitlements)
  • Build succeeds for both Debug and Release configurations

Notes

  • The app must be properly code-signed for this feature to work
  • Users may see a macOS permission prompt on first toggle (this is expected behavior)
  • If approval is required, the UI provides a direct link to System Settings

alexbartok and others added 12 commits December 28, 2025 14:58
Add ability to detect and display listening ports for running services using lsof.

Features:
- Detect TCP/UDP listening ports for services and their child processes
- Display ports in service info panel with protocol and port number
- Show port summary in service actions popover (first 3 ports)
- Automatically detect child processes to catch ports from worker processes

Implementation:
- PortDetector actor uses lsof to find listening ports by PID
- Recursively finds all descendant PIDs to include child process ports
- ServicePort model represents detected ports with protocol type
- Ports displayed in both detail panel and popover menu

Technical details:
- Uses `lsof -nP -iTCP -sTCP:LISTEN -a -p {pids}` for detection
- Uses `ps -o pid= -g {pid}` to find child processes
- Port detection is non-blocking and gracefully handles errors
- No thousands separators in port number display

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
Add ability to configure custom URLs for each service with auto-detection based on detected ports.

Features:
- Configure custom URLs per service (e.g., http://localhost:8384 for Syncthing)
- Auto-suggest URLs based on detected listening ports
- Display links as inline icon buttons in service rows (up to 2 visible)
- Full link management UI with add/edit/delete functionality
- Persistent storage across app restarts
- Service-agnostic design (no hardcoded service-specific URLs)

Implementation:
- ServiceLinksStore manages links with JSON persistence to Application Support
- Links displayed inline in service rows and in popover menu
- ServiceLinksManagementView provides full CRUD interface
- Smart URL suggestions for common HTTP/HTTPS ports
- Opens links in default browser via NSWorkspace

Technical details:
- Uses @observable pattern for reactive state management
- Persists to ~/Library/Application Support/BrewServicesManager/{bundleId}/service-links.json
- Inline overlay-based forms (avoids .sheet() issues in menu bar extras)
- Supports multiple links per service with optional custom labels
- Auto-focuses text fields for better UX

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
Improve visual consistency in the popover header section:
- Use consistent spacing for all HStack elements in header
- Align status and port information properly with action icons
- Match line heights between header section and menu items

Changes:
- Change VStack spacing from tightSpacing to compactSpacing for better vertical rhythm
- Add explicit tightSpacing to status and operation HStacks
- Ensures network icon and status icon align consistently

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
Fix system authentication dialog interaction issue in MenuBarExtra context.

Problem:
When switching to system domain or performing privileged operations, the
osascript authentication dialog appears but cannot receive mouse clicks
because the MenuBarExtra interferes with input handling.

Solution:
Call NSApp.activate(ignoringOtherApps: true) before showing the authentication
dialog. This brings the app to the foreground and ensures the system dialog
can properly receive user input.

Changes:
- Import AppKit in PrivilegeEscalator
- Add NSApp.activate() call on MainActor before executing osascript
- Dialog now properly accepts clicks and keyboard input

This complements the earlier fix for SwiftUI dialogs (using inline overlays
instead of .sheet()) to fully resolve dialog interaction issues in menu bar
extras.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
Replaces command-line osascript with NSAppleScript API running on MainActor.
This fixes the issue where the system authentication dialog could not be
interacted with using the mouse in MenuBarExtra apps.

The previous approach of activating the app didn't work because osascript
ran as a separate process, causing focus issues. NSAppleScript executes
in-process on the main thread, properly associating the auth dialog with
the app's event loop.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
Adds comprehensive documentation for the new features:
- Port detection: automatic discovery of listening ports
- Service links: user-configurable URLs for web interfaces
- Troubleshooting section for port detection issues
- Documentation for the authentication dialog fix

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
Removes hardcoded DEVELOPMENT_TEAM settings to allow contributors to use
their own Apple Developer accounts. Contributors will need to select their
team in Xcode's Signing & Capabilities editor when building.

This is standard practice for open source macOS projects.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
Adds clear instructions for configuring code signing after removing
hardcoded development team IDs. Documents the requirement for an Apple
Developer account and provides step-by-step setup instructions.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
- Fix data race in ServiceLinksStore save() method
- Fix child process detection using pgrep instead of ps -g
- Improve URL validation with blacklist approach
- Simplify redundant port range logic
- Make detectedPorts immutable in BrewServiceInfoEntry
- Fix misleading PostgreSQL example in README
- Remove thousands separator from port suggestions

Addresses feedback from Copilot PR review on:
- Data race concerns with Swift concurrency
- Incorrect ps command semantics
- Security concerns with URL validation
- Code quality improvements

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
- Remove thousands separators from port numbers in UI
- Trigger port detection when actions popover opens
- Ensure ports appear immediately without requiring View Info click

Port numbers now display without locale formatting (8080 not 8,080)
and are detected eagerly when user opens the actions menu.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
Adds the ability to automatically start the app when the user logs in
to macOS, using the modern SMAppService API from ServiceManagement
framework.

Changes:
- Add LaunchAtLoginError.swift for error handling
- Extend AppSettings with launch-at-login state management and system
  synchronization
- Update SettingsView with toggle UI and inline error display
- Add entitlements file (without app sandbox to preserve Homebrew access)
- Configure CODE_SIGN_ENTITLEMENTS in Xcode project
- Update README with new feature documentation

The implementation:
- Syncs with system state on app launch to handle manual changes
- Handles errors gracefully with user-friendly messages
- Provides direct link to System Settings when approval is needed
- Works without requiring full app sandboxing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
@validatedev
Copy link
Owner

You're amazing thank you! I'll check this PR tomorrow

@validatedev validatedev added the enhancement New feature or request label Dec 30, 2025
@validatedev validatedev assigned validatedev and Copilot and unassigned Copilot Dec 30, 2025
@validatedev validatedev self-requested a review December 30, 2025 21:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants