When to use this
Run this cleanup if your fleet was previously deployed with the script-based Detect path (an MDM Script + therunlayer-scan PPPC profile) and you are migrating to the macOS .pkg flow (see the per-MDM guides linked from Deploy AI Watch). The two paths use different binary identifiers, paths, and PPPC pins — leaving the old artifacts in place causes:
- Duplicate scans (per-user
com.runlayer.scanLaunchAgent + system-widecom.runlayer.aiwatchLaunchAgent both fire). - Dangling TCC grants for
/usr/local/bin/runlayer-scanthat survive even after the script is unscoped. - Confusing log output split across
/var/log/runlayer/ai_watch_detect.log(old) and the new macOS unified log underprocess == "aiwatch".
0.31.14 or newer retires the on-device agent for you: the .pkg preinstall and the uninstall package both boot out every user’s com.runlayer.scan LaunchAgent and remove its plist and the runlayer-scan wrapper. What remains for you is the MDM side, the legacy org API key in Runlayer, and optional user-space tidy-up.
This page cleans up the legacy script-based Detect rollout. To remove the current AI Watch package itself, use Remove AI Watch.
What gets left behind
The script-based deployment installs and configures the following on each enrolled device. Removed by says who cleans it up./var/log/runlayer/ is shared with the Enforce hook and sync MDM scripts; only ai_watch_detect.log belongs to the legacy Detect rollout.
Order of operations
- Delete the legacy MDM artifacts (Script + old PPPC profile) so devices stop receiving the script-based deployment. On Iru/Kandji delete the Custom Script library item first: its Audit Script reinstalls the agent on every run until it is gone.
- Deploy the
.pkg(0.31.14or newer) via the guide for your MDM platform — see Deploy AI Watch. The installer retires the legacy agent and wrapper on every device it reaches. Wait one MDM check-in cycle after step 1 so no device gets the legacy script re-pushed after the package lands. - Revoke the legacy org API key in Settings → Organization API keys. Every legacy device stored this key; revoking it is what ends access, whether or not a given device is ever cleaned.
- Optional: tidy user space with the script below. It removes the uv-installed CLI, its shim, the stored key entry, and the old logs, and also covers devices still on a
.pkgolder than0.31.14. - Verify with the MDM verification script.
MDM-side cleanup
For each MDM, remove or unscope the two script-based artifacts. Names below match the artifacts suggested in the original setup guides; yours may differ. The PPPC profile shows up in every MDM under its display nameRunlayer AI Watch - Full Disk Access (identifier com.runlayer.scan.pppc).
- Jamf Pro
- Iru/Kandji
- SimpleMDM
- Mosyle
- Other MDM
- Computers → Policies → open the “Deploy AI Watch Detect” Policy (earliest adopters: “Deploy Runlayer Detect”) → set Scope to none, then delete it. Its trigger was Recurring Check-in or Login with frequency Ongoing, so an unscoped-but-present policy keeps logging failures once the script is gone.
- Settings → Computer Management → Scripts → delete the “AI Watch Detect” Script (“Runlayer Detect” for earliest adopters).
- Computers → Configuration Profiles → delete “Runlayer AI Watch - Full Disk Access” (
com.runlayer.scan.pppc).
EXPECT_TIDY=0 until the tidy Policy has run, or the group targets every legacy device.Per-device tidy script
Optional. Push it as a one-time root Custom Script to devices that previously ran the script-based deployment. It removes what the.pkg leaves alone — the uv-installed Runlayer CLI, its shim, the stored ai_watch_mdm key entry, and the old logs — and also retires the agent and wrapper on devices still running a .pkg older than 0.31.14. It walks every home under /Users, so it works at the login window and on shared Macs. It is idempotent, a no-op on devices that never had the old deployment, and always exits 0.
The key entry is removed under every host stored in ~/.runlayer/config.yaml. The name ai_watch_mdm was only ever written by the legacy script, so there is no tenant URL to fill in.
The script intentionally leaves the following in place:
uvitself (/usr/local/bin/uv,/usr/local/bin/uvx) and its download cache~/.cache/uv— general-purpose tooling that may be in use for other workflows. Only therunlayeruv-tool install is removed.~/.runlayer/config.yaml— only theorg_api_keys.ai_watch_mdmentry for your host is removed. Other host entries and any personalrunlayer logincredentials are kept. The file is not deleted, even if it ends up empty.- Other files in
/var/log/runlayer/— the Enforce hook and sync MDM scripts log there too. Onlyai_watch_detect.logis removed; the directory goes only if it is empty afterwards. - OS Keychain entries — the script-based deployment never wrote to the keychain. Any
runlayerkeychain entries belong to an interactiverunlayer loginsession and are left untouched.
Verification
Audit variant (exit 1 on leftovers)
Push this as a root Custom Script after the.pkg has rolled out. It checks every home under /Users, prints one line per check, and exits 0 when the agent, its plist, and the wrapper are gone and 1 when any is left, so your MDM’s script status matches reality. Use it as the Audit Script on Iru/Kandji, as an Extension Attribute on Jamf Pro, and as a plain one-time script elsewhere.
The checks come in two groups. Agent checks (plist, loaded agent, wrapper) are what the .pkg guarantees and always count. User-space checks (uv tool, stored key entry) are only cleared by the tidy script, so by default they print as NOTE and do not fail the audit. Once you have deployed the tidy script, set EXPECT_TIDY=1 at the top so they count too; that is what makes an Iru Audit + Remediation pairing retry the tidy script.
<result> with the exit code, and build a Smart Group on a non-zero value to re-target stragglers with the tidy script.
Spot-check a test device
After the.pkg is deployed, run these in Terminal as the signed-in user (not from MDM: id -u would be root’s) to confirm there is exactly one Runlayer scan agent. Every line exits 0; read the printed message, not the exit status.
com.runlayer.aiwatch. Empty output is also clean: it means no TCC decision was logged in the last hour. Any remaining com.runlayer.scan entry means the device still has the old PPPC profile or wrapper; re-run the audit script against it.
Rollback
If you need to revert to the script-based deployment temporarily, re-scope the original Script and PPPC profile in your MDM; the legacy script reinstalls the agent and wrapper on its next run. The.pkg and the old wrapper can coexist for short periods (they use distinct identifiers and paths), but scheduled scans then run twice per cycle, and the next .pkg install or self-update retires the legacy agent again — use only as a short-term safety net during migration.