mirror of
https://github.com/AgentSeal/codeburn.git
synced 2026-08-21 14:34:32 +00:00
Merge pull request #1028 from getagentseal/fix/menubar-login-item-smappservice
menubar(mac): register the login item via SMAppService, no System Events prompt
This commit is contained in:
commit
9f0bbd0389
2 changed files with 9 additions and 22 deletions
|
|
@ -24,6 +24,7 @@
|
||||||
- **One rule for every cache file.** `CODEBURN_CACHE_DIR` when set, otherwise `~/.cache/codeburn`. `XDG_CACHE_HOME` is no longer consulted; the sync ledger, the only file that ever honored it, is merged into the canonical location on first read and the legacy copy is retired, so nothing is re-uploaded after the move. (#972)
|
- **One rule for every cache file.** `CODEBURN_CACHE_DIR` when set, otherwise `~/.cache/codeburn`. `XDG_CACHE_HOME` is no longer consulted; the sync ledger, the only file that ever honored it, is merged into the canonical location on first read and the legacy copy is retired, so nothing is re-uploaded after the move. (#972)
|
||||||
|
|
||||||
### Fixed (Desktop & Menubar)
|
### Fixed (Desktop & Menubar)
|
||||||
|
- **First launch no longer asks to control System Events.** The macOS menubar registered its login item by driving System Events over AppleScript, which made macOS put up an Automation consent dialog the first time the app ran. It now registers itself through `SMAppService.mainApp`, an in-process call that needs no Automation grant; there is no AppleScript fallback, so a failure logs and leaves the login item unset rather than bringing the prompt back. The same `codeburn.loginItemRegistered` guard still limits this to the first launch, so a login item you removed by hand stays removed. (#1026)
|
||||||
- **The resident `codeburn serve` child.** The first real panel request is also the cache warm-up, so startup never runs an artificial warm-up query beside a duplicate one-shot child; each served command carries its own read-only option allowlist, and anything outside it falls back to a normal spawn; the child exits when its stdin closes, so it can never outlive the app. Requests whose response exceeds the 16 MiB frame limit still replace the child, but that deliberate kill no longer spends the resident's unexpected-death budget. (#972)
|
- **The resident `codeburn serve` child.** The first real panel request is also the cache warm-up, so startup never runs an artificial warm-up query beside a duplicate one-shot child; each served command carries its own read-only option allowlist, and anything outside it falls back to a normal spawn; the child exits when its stdin closes, so it can never outlive the app. Requests whose response exceeds the 16 MiB frame limit still replace the child, but that deliberate kill no longer spends the resident's unexpected-death budget. (#972)
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import Foundation
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import AppKit
|
import AppKit
|
||||||
import Observation
|
import Observation
|
||||||
|
import ServiceManagement
|
||||||
|
|
||||||
private let refreshIntervalSeconds: UInt64 = 30
|
private let refreshIntervalSeconds: UInt64 = 30
|
||||||
private let forceRefreshWatchdogSeconds: TimeInterval = 90
|
private let forceRefreshWatchdogSeconds: TimeInterval = 90
|
||||||
|
|
@ -281,34 +282,19 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate, NSM
|
||||||
let key = "codeburn.loginItemRegistered"
|
let key = "codeburn.loginItemRegistered"
|
||||||
guard !UserDefaults.standard.bool(forKey: key) else { return }
|
guard !UserDefaults.standard.bool(forKey: key) else { return }
|
||||||
|
|
||||||
let appPath = Bundle.main.bundlePath
|
// Registers in-process. The old path told System Events to make the login
|
||||||
let script = "tell application \"System Events\" to make login item at end with properties {path:\(appleScriptStringLiteral(appPath)), hidden:false}"
|
// item, which made macOS ask for Automation access on first launch (#1026).
|
||||||
|
// No AppleScript fallback: a failure here must not bring that prompt back.
|
||||||
let process = Process()
|
|
||||||
process.launchPath = "/usr/bin/osascript"
|
|
||||||
process.arguments = ["-e", script]
|
|
||||||
process.standardOutput = FileHandle.nullDevice
|
|
||||||
process.standardError = FileHandle.nullDevice
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
try process.run()
|
if SMAppService.mainApp.status != .enabled {
|
||||||
process.waitUntilExit()
|
try SMAppService.mainApp.register()
|
||||||
if process.terminationStatus == 0 {
|
|
||||||
UserDefaults.standard.set(true, forKey: key)
|
|
||||||
}
|
}
|
||||||
|
UserDefaults.standard.set(true, forKey: key)
|
||||||
} catch {
|
} catch {
|
||||||
NSLog("CodeBurn: Login item registration failed: \(error)")
|
NSLog("CodeBurn: login item registration failed: \(error.localizedDescription)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func appleScriptStringLiteral(_ value: String) -> String {
|
|
||||||
var escaped = value.replacingOccurrences(of: "\\", with: "\\\\")
|
|
||||||
escaped = escaped.replacingOccurrences(of: "\"", with: "\\\"")
|
|
||||||
escaped = escaped.replacingOccurrences(of: "\r", with: "")
|
|
||||||
escaped = escaped.replacingOccurrences(of: "\n", with: "")
|
|
||||||
return "\"\(escaped)\""
|
|
||||||
}
|
|
||||||
|
|
||||||
private var lastRefreshTime: Date = .distantPast
|
private var lastRefreshTime: Date = .distantPast
|
||||||
/// Anchors the shallow provider-root snapshot only after a complete usage
|
/// Anchors the shallow provider-root snapshot only after a complete usage
|
||||||
/// refresh succeeds. It sits beside the cadence anchor so a failed fetch
|
/// refresh succeeds. It sits beside the cadence anchor so a failed fetch
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue