|
|
||
|---|---|---|
| .. | ||
| import-existing-names.swift | ||
| migrate-photos.ts | ||
| package.json | ||
| README.md | ||
| restore-contacts.swift | ||
| tsconfig.json | ||
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
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:
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:
{"identifier":"ABC-123","givenName":"Molly","familyName":"Smith","phoneNumbers":["+14155551234"],"emailAddresses":[],"note":"","capturedAt":"2026-04-24T09:47:12Z"}