From 6a61db431fb35b105411499afb9d4487674a6783 Mon Sep 17 00:00:00 2001 From: Bob Date: Thu, 28 May 2026 15:09:13 +0000 Subject: [PATCH 1/2] feat(macos): support native title exclusion flags --- aw_watcher_window/__init__.py | 5 ++- aw_watcher_window/macos.swift | 56 ++++++++++++++++++++++++++++++++-- aw_watcher_window/macos_cli.py | 15 +++++++++ aw_watcher_window/main.py | 7 +++-- tests/test_main.py | 44 ++++++++++++++++++++++++++ 5 files changed, 121 insertions(+), 6 deletions(-) create mode 100644 aw_watcher_window/macos_cli.py create mode 100644 tests/test_main.py diff --git a/aw_watcher_window/__init__.py b/aw_watcher_window/__init__.py index 21db616e..68e28ecb 100644 --- a/aw_watcher_window/__init__.py +++ b/aw_watcher_window/__init__.py @@ -1,3 +1,6 @@ -from .main import main +def main(): + from .main import main as _main + + return _main() __all__ = ["main"] diff --git a/aw_watcher_window/macos.swift b/aw_watcher_window/macos.swift index 88d10470..e9748ed0 100644 --- a/aw_watcher_window/macos.swift +++ b/aw_watcher_window/macos.swift @@ -123,6 +123,8 @@ var baseurl = "http://localhost:5600" var clientHostname = ProcessInfo.processInfo.hostName var clientName = "aw-watcher-window" var bucketName = "\(clientName)_\(clientHostname)" +var excludeTitle = false +var excludeTitlePatterns: [NSRegularExpression] = [] let main = MainThing() var oldHeartbeat: Heartbeat? @@ -140,6 +142,49 @@ encoder.dateEncodingStrategy = .custom({ date, encoder in start() RunLoop.main.run() +func compileExcludeTitlePattern(_ pattern: String) -> NSRegularExpression { + do { + return try NSRegularExpression(pattern: pattern, options: [.caseInsensitive]) + } catch { + error("Invalid regex pattern: \(pattern)") + exit(1) + } +} + +func parseOptionalArguments(_ arguments: ArraySlice) { + var index = arguments.startIndex + while index < arguments.endIndex { + let argument = arguments[index] + + if argument == "--exclude-title" { + excludeTitle = true + index = arguments.index(after: index) + continue + } + + if argument == "--exclude-titles" { + let nextIndex = arguments.index(after: index) + guard nextIndex < arguments.endIndex else { + error("Missing value for --exclude-titles") + exit(1) + } + excludeTitlePatterns.append(compileExcludeTitlePattern(arguments[nextIndex])) + index = arguments.index(after: nextIndex) + continue + } + + error("Unknown argument: \(argument)") + exit(1) + } +} + +func titleShouldBeExcluded(_ title: String) -> Bool { + let range = NSRange(title.startIndex.. ") + // Check that we get the 4 required arguments plus any optional flags + if arguments.count < 5 { + print("Usage: aw-watcher-window [--exclude-title] [--exclude-titles ...]") exit(1) } @@ -158,6 +203,7 @@ func start() { bucketName = arguments[2] clientHostname = arguments[3] clientName = arguments[4] + parseOptionalArguments(arguments.dropFirst(5)) guard checkAccess() else { DispatchQueue.main.asyncAfter(deadline: .now() + 10) { @@ -388,6 +434,10 @@ class MainThing { } } + if excludeTitle || titleShouldBeExcluded(data.title) { + data.title = "excluded" + } + let heartbeat = Heartbeat(timestamp: nowTime, data: data) sendHeartbeat(heartbeat) } diff --git a/aw_watcher_window/macos_cli.py b/aw_watcher_window/macos_cli.py new file mode 100644 index 00000000..1ba4a3e7 --- /dev/null +++ b/aw_watcher_window/macos_cli.py @@ -0,0 +1,15 @@ +def build_swift_command( + binpath, + server_address, + bucket_id, + client_hostname, + client_name, + exclude_title=False, + exclude_titles=None, +): + command = [binpath, server_address, bucket_id, client_hostname, client_name] + if exclude_title: + command.append("--exclude-title") + for title in exclude_titles or []: + command.extend(["--exclude-titles", title]) + return command diff --git a/aw_watcher_window/main.py b/aw_watcher_window/main.py index b00f6780..e271c703 100644 --- a/aw_watcher_window/main.py +++ b/aw_watcher_window/main.py @@ -14,6 +14,7 @@ from .config import parse_args from .exceptions import FatalError from .lib import get_current_window +from .macos_cli import build_swift_command from .macos_permissions import background_ensure_permissions logger = logging.getLogger(__name__) @@ -80,13 +81,15 @@ def main(): try: p = subprocess.Popen( - [ + build_swift_command( binpath, client.server_address, bucket_id, client.client_hostname, client.client_name, - ] + exclude_title=args.exclude_title, + exclude_titles=args.exclude_titles, + ) ) # terminate swift process when this process dies signal.signal(signal.SIGTERM, lambda *_: kill_process(p.pid)) diff --git a/tests/test_main.py b/tests/test_main.py new file mode 100644 index 00000000..96e04b11 --- /dev/null +++ b/tests/test_main.py @@ -0,0 +1,44 @@ +from aw_watcher_window.macos_cli import build_swift_command + + +def test_build_swift_command_omits_optional_filters(): + command = build_swift_command( + "/tmp/aw-watcher-window-macos", + "http://localhost:5600", + "bucket", + "host.localdomain", + "aw-watcher-window", + ) + + assert command == [ + "/tmp/aw-watcher-window-macos", + "http://localhost:5600", + "bucket", + "host.localdomain", + "aw-watcher-window", + ] + + +def test_build_swift_command_passes_title_filters(): + command = build_swift_command( + "/tmp/aw-watcher-window-macos", + "http://localhost:5600", + "bucket", + "host.localdomain", + "aw-watcher-window", + exclude_title=True, + exclude_titles=["Zoom", "Slack.*huddle"], + ) + + assert command == [ + "/tmp/aw-watcher-window-macos", + "http://localhost:5600", + "bucket", + "host.localdomain", + "aw-watcher-window", + "--exclude-title", + "--exclude-titles", + "Zoom", + "--exclude-titles", + "Slack.*huddle", + ] From 5e51a998eb88b856515abf48c47dc65186936157 Mon Sep 17 00:00:00 2001 From: Bob Date: Thu, 28 May 2026 15:20:53 +0000 Subject: [PATCH 2/2] fix(macos/swift): rename catch binding to avoid shadowing error() func Swift's implicit 'error' variable in catch blocks shadows the custom error() function defined in the same file, causing the compiler error: 'cannot call value of non-function type any Error'. --- aw_watcher_window/macos.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aw_watcher_window/macos.swift b/aw_watcher_window/macos.swift index e9748ed0..5c0f22b4 100644 --- a/aw_watcher_window/macos.swift +++ b/aw_watcher_window/macos.swift @@ -145,8 +145,8 @@ RunLoop.main.run() func compileExcludeTitlePattern(_ pattern: String) -> NSRegularExpression { do { return try NSRegularExpression(pattern: pattern, options: [.caseInsensitive]) - } catch { - error("Invalid regex pattern: \(pattern)") + } catch let regexError { + error("Invalid regex pattern: \(pattern) — \(regexError.localizedDescription)") exit(1) } }