← Back to PRs

#21075: fix(media): use sips on Node.js + darwin to prevent Photos TCC prompt

by irchelper open 2026-02-19 16:37 View on GitHub →
size: S
## Problem On macOS 15, when OpenClaw receives a media file (e.g. image from Telegram), the Node.js gateway process triggers a macOS TCC Photos permission dialog: > "node" wants to access your Photos library **Root cause:** `prefersSips()` in `image-ops.ts` only selects `sips` when running under Bun on darwin. When running under Node.js (the common case for the npm-installed gateway), it falls back to `sharp`. The `sharp` package loads Apple's native HEIC/ImageIO codec libraries, which macOS 15 associates with the Photos framework, triggering the TCC permission request. ## Fix Remove the `isBun()` guard (and the now-unused `isBun()` helper). On any darwin platform, `sips` is already available at `/usr/bin/sips` and handles JPEG/PNG/HEIC correctly without touching Photos framework APIs. Export `prefersSips()` so it can be unit-tested directly. ```ts // Before process.env.OPENCLAW_IMAGE_BACKEND !== "sharp" && isBun() && process.platform === "darwin" // After process.env.OPENCLAW_IMAGE_BACKEND !== "sharp" && process.platform === "darwin" ``` ## Behavior | Runtime | Before | After | |---------|--------|-------| | Bun + darwin | sips ✅ | sips ✅ | | Node.js + darwin | sharp ❌ (TCC prompt) | sips ✅ | | Linux/Windows | sharp ✅ | sharp ✅ | `OPENCLAW_IMAGE_BACKEND=sharp` still overrides to sharp on all platforms for users who explicitly want it. ## Tests (15 cases) All branches of `prefersSips()` are now directly unit-tested via `vi.stubEnv` + `vi.spyOn(process, 'platform', 'get')`: | # | Condition | Expected | |---|-----------|----------| | 1 | `OPENCLAW_IMAGE_BACKEND=sips` + darwin | `true` | | 1 | `OPENCLAW_IMAGE_BACKEND=sips` + linux | `true` | | 1 | `OPENCLAW_IMAGE_BACKEND=sips` + win32 | `true` | | 2 | `OPENCLAW_IMAGE_BACKEND=sharp` + darwin | `false` (override wins) | | 2 | `OPENCLAW_IMAGE_BACKEND=sharp` + linux | `false` | | 5 | `OPENCLAW_IMAGE_BACKEND=sharp` + darwin + Bun-like runtime | `false` (regression guard) | | 3 | no env + darwin (Node.js) | `true` ← **core fix** | | 3 | no env + darwin (Bun) | `true` (no regression) | | 3 | empty string env + darwin | `true` | | 4 | no env + linux | `false` | | 4 | no env + win32 | `false` | | 4 | empty string env + linux | `false` | <!-- greptile_comment --> <h3>Greptile Summary</h3> Removes the Bun-only guard from `prefersSips()` so that Node.js on darwin also uses the native `sips` tool instead of `sharp` for image operations. This prevents macOS 15 from showing a TCC Photos permission prompt when the gateway processes media files, since `sharp` loads Apple's HEIC/ImageIO codec libraries that trigger the Photos framework association. - Removes `isBun()` helper function from `src/media/image-ops.ts:22-25` - Updates `prefersSips()` logic to check only platform (darwin) and env override, not runtime - Adds test documentation explaining the behavior change - `OPENCLAW_IMAGE_BACKEND=sharp` env var still allows explicit sharp usage on all platforms <h3>Confidence Score: 5/5</h3> - This PR is safe to merge with no risk - The change is minimal and well-contained: removes a runtime check that was incorrectly limiting `sips` usage to Bun only. The fix correctly addresses the TCC permission prompt issue by allowing Node.js on darwin to use the native `sips` tool. The logic is straightforward, backward compatible (env override still works), and the test documents the expected behavior. - No files require special attention <sub>Last reviewed commit: ff6daf8</sub> <!-- greptile_other_comments_section --> <!-- /greptile_comment --> ## Incidental fix Also fixes a pre-existing TypeScript error (`TS2339`) in `src/cli/update-cli.test.ts:284` introduced by #21071: wraps `runDaemonInstall.mockResolvedValue` with `vi.mocked()` to match the pattern used elsewhere in the same file.

Most Similar PRs