89198ab24e
Five extractions so the Amethyst UI and the new amy CLI share one implementation for each piece of Marmot/identity behaviour instead of reimplementing it per platform. quartz additions: - MarmotGroupData.bootstrap(..) + withMergedRelays(..): factory for the metadata shape every inviter stamps on a fresh group and the outbox- merge rule every admin commit carries forward. - KeyPackageFetcher: (a) pure fetchRelaysFor union of target kind:10051, target outbox, my outbox; (b) one-shot fetchKeyPackage; (c) publish- side resolveKeyPackagePublishRelays. - resolveUserHexOrNull: single entry point for npub / nprofile / 64-hex / NIP-05 identifiers. Uses the existing Nip19Parser + Nip05Client infra so NIP-05 resolution is live over HTTP. commons additions: - MarmotManager.leafIndexOf(..): pubkey -> leaf index lookup used by every removeMember caller. - MarmotManager.addMember(nostrGroupId, keyPackageEvent, relays): convenience overload that lifts the base64 decode into the manager. - MarmotManager.buildTextMessage(..) returning a TextMessageBundle; optionally persists the outbound inner event (CLI needs persist, Amethyst relies on LocalCache loopback). - MarmotIngest.ingest(event): routes kind:1059 / kind:445 through unwrap -> processWelcome / processGroupEvent -> persist the decrypted application message. Deliberately platform-agnostic. Retrofits: - Account.addMarmotGroupMember and fetchKeyPackageAndAddMember now take a KeyPackageEvent directly; Account.keyPackagePublishRelays delegates to KeyPackageFetcher. - AccountViewModel.updateMarmotGroupMetadata uses bootstrap + merged. - AccountViewModel.sendMarmotGroupMessage builds the kind:9 template via buildTextMessage(persistOwn = false) to avoid double-persisting. - AccountSessionManager's NIP-05 login branch delegates to resolveUserHexOrNull; no more hand-rolled Nip05Id / nip05Client.get. - CLI Context exposes nip05Client and requireUserHex; syncIncoming now calls MarmotManager.ingest; all command sites use KeyPackageFetcher + the shared identifier resolver. Npubs util deleted. https://claude.ai/code/session_01M6dCKAF5Y1VyHGZPjzwDXq