Skip to content

fix(macos): give Swift helper a bundle identity so its permission entry is created automatically - #150

Open
StreamOfRon wants to merge 4 commits into
ActivityWatch:masterfrom
StreamOfRon:fix/macos-helper-identity
Open

StreamOfRon wants to merge 4 commits into
ActivityWatch:masterfrom
StreamOfRon:fix/macos-helper-identity

Conversation

@StreamOfRon

Copy link
Copy Markdown

Fixes #149.

Stacked on #148 (one-time prompt, currently unmerged) — this PR's diff includes that commit until #148 lands, then it will shrink to the identity commit alone.

Problem: the helper is a bare unix executable with no bundle identity, so when it prompts for Accessibility access, macOS does not add a usable entry to the permissions list — users must click +, dig into Contents/Frameworks/aw_watcher_window/, and select the binary by hand.

Change (4 files, +45/-4 on top of #148):

  • New aw_watcher_window/Helper-Info.plist: CFBundleIdentifier net.activitywatch.aw-watcher-window-macos, CFBundleName "ActivityWatch Window Helper".
  • Makefile: embed it via __TEXT,__info_plist in the build-swift rule (compile input switched from $^ to $< so the plist is not passed to swiftc).
  • macos.swift: one-time log on first silent trust-check failure naming the helper path and the Settings location (greptile P2 on fix(macos): prompt for accessibility access only once per launch #148 — silent-wait state is now diagnosable without re-prompting).
  • macos_permissions.py: alert copy names "ActivityWatch Window Helper" + Settings path instead of generic "grant it permissions / remove and re-add".

Verified: make build-swift clean (0 errors); section present via otool -s __TEXT __info_plist; adhoc re-sign derives Identifier=net.activitywatch.aw-watcher-window-macos (previously a hash-suffixed id); pytest tests/ 78/78 pass.

Caveats: nothing self-grants TCC — the user still approves once. On adhoc local builds grants are cdhash-keyed, so rebuilding invalidates them; only Developer ID signing gives persistence. End-to-end auto-add proof needs a tccutil reset + relaunch on a live machine (left as manual verification).

Ron Miller added 2 commits September 27, 2026 12:13
The helper retried checkAccess() with prompt:true every 10s, so a
helper whose grant hadn't stuck (adhoc signature, or grant applied to
the top-level app instead of the helper) re-prompted forever. Prompt
once, then poll silently so a later manual grant still takes effect.
…ry is created automatically

Embed Helper-Info.plist (CFBundleIdentifier net.activitywatch.aw-watcher-window-macos,
CFBundleName 'ActivityWatch Window Helper') into aw-watcher-window-macos via
__TEXT,__info_plist so TCC lists the helper by name when it prompts, instead
of requiring users to manually locate the bare helper binary in the app bundle.

Also log the missing-access state once per launch (naming the helper and the
Settings location) and update the Python permission alert to match, so the
silent-wait state is diagnosable without reintroducing repeated prompts.
@greptile-apps

greptile-apps Bot commented Sep 27, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 4/5

[Medium risk] Adjusts macOS accessibility permission handling for the window watcher.

The PR should not merge until the permission alert directs script-strategy users to the process that actually needs access.

Findings

  1. P1 Alert names the wrong process ▶
  2. P2 Helper identity goes unchecked ▶

Summary

The PR embeds an identity plist in the macOS Swift helper and changes permission prompting and guidance.

  • The Python alert now names the Swift helper even when a script strategy needs access for a different process.
  • The binary-identity change lacks a check on the packaged artifact.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[macOS watcher starts] --> B[Python permission check and alert]
  A --> C{Capture strategy}
  C -->|Swift| D[Swift helper checks its own access]
  C -->|JXA or AppleScript| E[Script capture through launcher]
  B --> F[Alert names Swift helper for both strategies]
Loading

Reviews (1) · Last reviewed commit: "fix(macos): give Swift helper a bundle i..."

Comment thread aw_watcher_window/macos_permissions.py Outdated
Comment on lines +28 to +29
"To let ActivityWatch capture window titles, enable "
'"ActivityWatch Window Helper" (aw-watcher-window-macos) under '

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Alert names the wrong process

When the jxa or applescript strategy is selected, this alert still runs, but the Swift helper is not launched. The alert now tells users to enable that helper even though the permission check runs in the Python-side process. Granting the named helper does not resolve the missing access for script capture, so window data remains unavailable. The guidance needs to name the process that requires access for the selected strategy.

Knowledge Base Used:

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 1a9671f: the alert now takes the capture strategy and names the Swift helper for swift, aw-watcher-window itself for jxa/applescript.

Comment thread Makefile
swiftc -target "$(shell uname -m)-apple-macosx$(MACOSX_DEPLOYMENT_TARGET)" $^ -o $@
aw_watcher_window/aw-watcher-window-macos: aw_watcher_window/macos.swift aw_watcher_window/Helper-Info.plist
swiftc -target "$(shell uname -m)-apple-macosx$(MACOSX_DEPLOYMENT_TARGET)" \
-Xlinker -sectcreate -Xlinker __TEXT -Xlinker __info_plist -Xlinker aw_watcher_window/Helper-Info.plist \

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Helper identity goes unchecked

This plist section is central to the permission-entry fix, but the macOS build check verifies only the deployment target, and the package smoke test does not launch the helper. Without an assertion that the packaged helper retains the __info_plist section and identifier, a later linker or packaging change could silently undo the fix.

Knowledge Base Used:

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 1a9671f: new 'Check macOS helper bundle identity' CI step (after Package) asserts the packaged helper retains the __TEXT,__info_plist section and bundle identifier.

Ron Miller added 2 commits September 27, 2026 12:42
…dentity

P1: the permission alert now names the process that actually needs access
for the selected strategy (Swift helper for swift, aw-watcher-window
itself for jxa/applescript) instead of always naming the Swift helper.

P2: CI asserts the packaged helper retains the __TEXT,__info_plist
section and bundle identifier, so a linker or packaging change cannot
silently undo the permission-entry fix.
- Fix garbled alert copy (duplicated-word typo in the fallback sentence).
- Co-locate hasLoggedMissingAccess with hasPromptedForAccess; both guard
  checkAccess().
- Makefile: compile the sole Swift source by explicit name instead of $<,
  with a note to keep UPX/strip off the helper so the __TEXT,__info_plist
  section survives packaging.
- CI: assert the bundle identifier inside the decoded plist section rather
  than a bare strings match anywhere in the binary.
@TimeToBuildBob

Copy link
Copy Markdown
Contributor

Thanks @StreamOfRon — embedding the identity is the right approach to #149, and the -sectcreate __TEXT __info_plist route is the standard one for CLI binaries. Notes:

Looks right

  • Plist is minimal and valid; bundle ID net.activitywatch.aw-watcher-window-macos is stable and not version-derived, which matters because TCC keys on it (with a Developer ID signature the designated requirement uses this identifier, so grants should survive upgrades; adhoc builds stay cdhash-keyed, as the PR says).
  • Alert copy now names the right process per strategy (greptile P1 addressed), and the new CI step asserts the section survives packaging.

Needs attention

  1. Merge conflict with fix(macos): keep foreground tracking when browser context fails #146 in the Makefile rule (real, silent). fix(macos): keep foreground tracking when browser context fails #146 adds macos_state.swift and compiles via $^. This PR hardcodes macos.swift and switches off $^. Whichever lands second must reconcile: combining them naively either drops macos_state.swift (link errors) or passes the plist to swiftc ($^ includes it). Suggested merged rule: prerequisites macos.swift macos_state.swift Helper-Info.plist, compile $(filter %.swift,$^), plus the -sectcreate flags. That also stops the "sole Swift source" assumption in the new Makefile comment from going stale — it's already false once fix(macos): keep foreground tracking when browser context fails #146 lands.
  2. upx=True in aw-watcher-window.spec (EXE and COLLECT). The new comment says to keep UPX off the helper, but the spec currently enables it globally. PyInstaller generally skips UPX on macOS binaries, but the CI step you added is exactly what would catch it — which leads to:
  3. The new Check macOS helper bundle identity step has never run. Checks on the head are action_required (fork-PR workflow approval), so a maintainer needs to approve the run. The otool/awk/sed word-swap pipeline is clever but fragile; I'd verify it passes on a real runner before relying on it. A simpler assertion would be codesign -dv "$HELPER" 2>&1 | grep -q 'Identifier=net.activitywatch.aw-watcher-window-macos' (needs the helper signed/adhoc-signed first, which the build already does).
  4. Stacked on fix(macos): prompt for accessibility access only once per launch #148 — it will need a rebase once that lands.

macOS maintainer smoke test (cannot be done from Linux)

  • Fresh tccutil reset Accessibility, launch from the packaged app, confirm the system prompt appears and "ActivityWatch Window Helper" is listed automatically in System Settings.
  • Toggle on → tracking starts without restart.
  • Rebuild/upgrade with the same Developer ID → grant persists.
  • --strategy jxa / applescript: alert names aw-watcher-window, not the helper.
  • otool -s __TEXT __info_plist on the packaged helper inside the PyInstaller bundle.

Merge order: #148 → #150 (rebase, fix the Makefile rule against #146 or vice versa) → #146. Or #146 first, then #150 rebased onto it with the rule above. Leaving the merge to a maintainer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[macos] Swift helper never appears in the Accessibility list on its own; users must manually dig out the helper binary

2 participants