72 lines
2.8 KiB
Markdown
72 lines
2.8 KiB
Markdown
|
|
# mac-sync scripts
|
||
|
|
|
||
|
|
## restore-contacts.swift
|
||
|
|
|
||
|
|
Restores the macOS Contacts store from a full vCard backup produced by
|
||
|
|
`ContactsBackup.dumpFullVCard`. These backups are written by the mac-sync app
|
||
|
|
every time the contact-renderer poller runs its first iteration for a session.
|
||
|
|
|
||
|
|
### Backup locations
|
||
|
|
|
||
|
|
```
|
||
|
|
~/Library/Application Support/com.lilith.mac-sync/contact-backups/
|
||
|
|
full-YYYYMMDD-HHmmss.vcf # full store snapshot
|
||
|
|
rollback-YYYYMMDD-HHmmss.jsonl # per-contact pre-state for selective restore
|
||
|
|
diff-YYYYMMDD-HHmmss.jsonl # dry-run report of "what would change"
|
||
|
|
```
|
||
|
|
|
||
|
|
### Restore full store
|
||
|
|
|
||
|
|
```bash
|
||
|
|
swift scripts/restore-contacts.swift \
|
||
|
|
~/Library/Application\ Support/com.lilith.mac-sync/contact-backups/full-20260424-094712.vcf
|
||
|
|
```
|
||
|
|
|
||
|
|
The script imports every contact in the .vcf. It does NOT wipe the store first
|
||
|
|
— restored contacts are added with new identifiers. For a clean rollback after
|
||
|
|
a bad write run, delete the lilith-sync-managed contacts first (search Contacts
|
||
|
|
for the airport prefix `[LAX]`/`[???]`/etc or the emoji glyphs 🔥 👸 🌹), then
|
||
|
|
run this script.
|
||
|
|
|
||
|
|
### Permissions
|
||
|
|
|
||
|
|
The Terminal (or whichever shell host you're in) must have Contacts access:
|
||
|
|
- System Settings → Privacy & Security → Contacts → enable Terminal
|
||
|
|
- Or: swift will prompt on first invocation; click Allow.
|
||
|
|
|
||
|
|
### macOS 14+ notes entitlement
|
||
|
|
|
||
|
|
Reading and writing `CNContact.note` requires the
|
||
|
|
`com.apple.developer.contacts.notes` entitlement (already in
|
||
|
|
`src/client/Resources/LilithMacSync.entitlements`). This is a restricted
|
||
|
|
entitlement — Apple gates it behind a provisioning profile request. If the
|
||
|
|
signing cert doesn't grant it, the ContactsWriter notes merge silently no-ops:
|
||
|
|
`givenName` + `familyName` still land, but the `<lilith-sync>` block never
|
||
|
|
appears in Contacts.app. Symptom to watch: first canary contact shows the
|
||
|
|
airport prefix + emojis but an empty notes field.
|
||
|
|
|
||
|
|
Workaround if the entitlement isn't available: drop the notes branch by
|
||
|
|
setting `ContactsWriter.mergeNote` to return an empty string — the first/last
|
||
|
|
name rendering is the bulk of the value anyway.
|
||
|
|
|
||
|
|
### Dry-run diff review
|
||
|
|
|
||
|
|
Before any real write run, the contact-renderer poller writes a dry-run diff:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
jq -c '.' ~/Library/Application\ Support/com.lilith.mac-sync/contact-backups/diff-*.jsonl | less
|
||
|
|
```
|
||
|
|
|
||
|
|
Each line is `{handle, before: {...}, after: {...}}`. Skim it before lifting
|
||
|
|
the `MAC_SYNC_CONTACTS_WRITE=1` gate.
|
||
|
|
|
||
|
|
### Selective rollback
|
||
|
|
|
||
|
|
To restore a single contact from the rollback JSONL (without re-importing the
|
||
|
|
whole .vcf), extract the pre-state block and apply it manually via Contacts.app
|
||
|
|
or via a one-off swift snippet using `CNMutableContact`. The JSONL schema:
|
||
|
|
|
||
|
|
```json
|
||
|
|
{"identifier":"ABC-123","givenName":"Molly","familyName":"Smith","phoneNumbers":["+14155551234"],"emailAddresses":[],"note":"","capturedAt":"2026-04-24T09:47:12Z"}
|
||
|
|
```
|