feat(platform): Phase 115 bridges — CSV mesh, archive health, webhook URL (v0.8.79)

- exportHookFailureDigest / getHookFailureDigestCsvExport with signatureValid
- GUILD_SYNC slices: hookFailureDigestCsvExport, archiveHealthPrefs, modDigestGroupDmMeta
- refreshArchivePeerHealth + archive health prefs on guild focus/join/open
- dryRunDigestWebhookRelay + digest relay webhook URL plugin sync
- syncModDigestGroupDmFromMesh for mod digest group DM participant sync

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Raven Scott
2026-05-22 07:11:30 -04:00
co-authored by Cursor
parent c844d4f200
commit 9e5390de96
+142 -6
View File
@@ -292,6 +292,7 @@ class PearcordPlatform extends EventEmitter {
this._devicePairSwarm = null
this._discoveryWired = false
this._archivePeerHealthRefreshedAt = null
this._archiveHealthRefreshTimer = null
this.mode = 'home'
this.activeChannelId = null
this.onboarded = false
@@ -1199,6 +1200,8 @@ class PearcordPlatform extends EventEmitter {
let auditExportArchives = []
let archivePeerSeen = []
let hookFailureDigest = null
let hookFailureDigestCsvExport = null
let archiveHealthPrefs = null
await this._initDeliveryReceipts(guild.id)
if (this.deliveryReceipts) {
auditExportSchedule = await this.deliveryReceipts.exportAuditExportScheduleSlice()
@@ -1209,6 +1212,9 @@ class PearcordPlatform extends EventEmitter {
await this.deliveryReceipts.exportAutomationDigestExportSnapshotsSlice(2)
auditExportArchives = await this.deliveryReceipts.exportAuditExportArchivesSlice(3)
archivePeerSeen = await this.deliveryReceipts.exportArchivePeerSeenSlice(16)
archiveHealthPrefs = await this.deliveryReceipts.exportArchiveHealthPrefsSlice()
hookFailureDigestCsvExport =
await this.deliveryReceipts.exportHookFailureDigestCsvSlice()
digestRelayHandoff = await this.deliveryReceipts.exportDigestRelayHandoffSlice()
const digestSnap = await this.deliveryReceipts.getHookFailureDigestSnapshot()
if (digestSnap) {
@@ -1293,6 +1299,8 @@ class PearcordPlatform extends EventEmitter {
auditExportArchives,
archivePeerSeen,
hookFailureDigest,
hookFailureDigestCsvExport,
archiveHealthPrefs,
modDigestGroupDmMeta,
workerReleaseHint,
automodConfig: automodConfig
@@ -1427,6 +1435,20 @@ class PearcordPlatform extends EventEmitter {
payload.digestRelayHandoff
)
}
if (payload.hookFailureDigestCsvExport) {
await this._initDeliveryReceipts(payload.guildId)
await this.deliveryReceipts.ingestHookFailureDigestCsvSlice(
payload.guildId,
payload.hookFailureDigestCsvExport
)
}
if (payload.archiveHealthPrefs) {
await this._initDeliveryReceipts(payload.guildId)
await this.deliveryReceipts.ingestArchiveHealthPrefsSlice(
payload.guildId,
payload.archiveHealthPrefs
)
}
if (payload.auditExportSchedule) {
await this._initDeliveryReceipts(payload.guildId)
await this.deliveryReceipts.ingestAuditExportScheduleSlice(
@@ -2725,7 +2747,8 @@ class PearcordPlatform extends EventEmitter {
guildMute: false,
muteNotifications: false,
digestInboxChannelId: null,
digestRelayEnabled: false
digestRelayEnabled: false,
digestRelayWebhookUrl: null
}
}
await this._initDeliveryReceipts(this.guild.guild.id)
@@ -2741,10 +2764,34 @@ class PearcordPlatform extends EventEmitter {
muteNotifications: !!guild.muteNotifications || userMute,
digestInboxChannelId: guild.digestInboxChannelId || null,
digestRelayEnabled: !!guild.digestRelayEnabled,
activeRelayPluginId: guild.activeRelayPluginId || null
activeRelayPluginId: guild.activeRelayPluginId || null,
digestRelayWebhookUrl: guild.digestRelayWebhookUrl || null
}
}
async getArchiveHealthPrefs () {
if (!this.guild?.guild) {
return { autoRefreshOnGuildFocus: true, updatedAt: 0 }
}
await this._initDeliveryReceipts(this.guild.guild.id)
return this.deliveryReceipts.getArchiveHealthPrefs()
}
async setArchiveHealthPrefs (partial = {}) {
if (!this.guild?.guild) throw new Error('no guild')
const roles = await this._memberRoles()
if (!roleHasPermission(roles, PERMISSION.MANAGE_GUILD)) {
throw new Error('no permission to manage archive health prefs')
}
await this._initDeliveryReceipts(this.guild.guild.id)
const prefs = await this.deliveryReceipts.setArchiveHealthPrefs({
autoRefreshOnGuildFocus: partial.autoRefreshOnGuildFocus
})
setTimeout(() => this._pushGuildSyncToMesh().catch(() => {}), 400)
this._restartArchiveHealthRefreshTimer()
return prefs
}
async setAutomationDigestNotifyPrefs (partial = {}) {
if (!this.guild?.guild) throw new Error('no guild')
const userId = this.identity.user?.id
@@ -2770,7 +2817,13 @@ class PearcordPlatform extends EventEmitter {
if (partial.activeRelayPluginId !== undefined) {
patch.activeRelayPluginId = partial.activeRelayPluginId || null
}
if (partial.digestRelayWebhookUrl !== undefined) {
patch.digestRelayWebhookUrl = partial.digestRelayWebhookUrl || null
}
await this.deliveryReceipts.setAutomationDigestNotifyPrefs(patch)
if (patch.digestRelayWebhookUrl !== undefined) {
await this._syncDigestRelayWebhookUrlToPlugin(patch.digestRelayWebhookUrl).catch(() => {})
}
setTimeout(() => this._pushGuildSyncToMesh().catch(() => {}), 400)
} else {
if (partial.userMute != null || partial.muteNotifications != null) {
@@ -2867,7 +2920,8 @@ class PearcordPlatform extends EventEmitter {
const plugin = await this.deliveryReceipts.getActiveDigestRelayPlugin()
if (plugin) {
handoff = applyRelayPluginToHandoff(handoff, plugin, {
relaySecret: this.guild.guild.id
relaySecret: this.guild.guild.id,
webhookUrl: prefs.digestRelayWebhookUrl || plugin.config?.webhookUrl || null
})
}
await this.deliveryReceipts.appendDigestRelayHandoff(handoff)
@@ -2978,6 +3032,15 @@ class PearcordPlatform extends EventEmitter {
})
signatureValid = verified.ok
body = body.csv
await this.deliveryReceipts.recordHookFailureDigestCsvExport({
csv: body,
signature,
signatureAlg,
exportedAt: Date.now()
})
if (opts.recordMesh !== false) {
setTimeout(() => this._pushGuildSyncToMesh().catch(() => {}), 400)
}
}
return {
format,
@@ -2991,6 +3054,18 @@ class PearcordPlatform extends EventEmitter {
}
}
async getHookFailureDigestCsvExport () {
if (!this.guild?.guild) return null
await this._initDeliveryReceipts(this.guild.guild.id)
const row = await this.deliveryReceipts.getHookFailureDigestCsvExport()
if (!row?.signature) return row
const verified = verifyHookFailureDigestCsv(row.csvBody, row.signature, {
guildId: this.guild.guild.id,
relaySecret: this.guild.guild.id
})
return { ...row, signatureValid: verified.ok }
}
async setGuildIconImage ({ data, filename, mimeType }) {
const user = this.identity.user
if (!user || !this.guild?.guild) throw new Error('no guild')
@@ -3892,11 +3967,17 @@ class PearcordPlatform extends EventEmitter {
guildName: this.guild.guild.name,
exported: { exportedAt: Date.now(), body: '', format: 'text' }
})
const digPrefs = await this.deliveryReceipts.getAutomationDigestNotifyPrefs()
const stamped = plugin
? applyRelayPluginToHandoff(handoff, plugin, { relaySecret: this.guild.guild.id })
? applyRelayPluginToHandoff(handoff, plugin, {
relaySecret: this.guild.guild.id,
webhookUrl: digPrefs.digestRelayWebhookUrl || null
})
: handoff
if (plugin?.transport === 'webhook-bridge') {
result.webhookRelayPreview = dryRunWebhookRelayPost(stamped, {})
result.webhookRelayPreview = dryRunWebhookRelayPost(stamped, {
webhookUrl: digPrefs.digestRelayWebhookUrl || null
})
}
}
return { sample, result }
@@ -4029,6 +4110,8 @@ class PearcordPlatform extends EventEmitter {
await this.discovery?.touchGuild(guildRecord.id).catch(() => {})
await this._ensureGuildModules()
this._startAuditExportScheduleTimer()
await this._maybeArchiveHealthRefreshOnFocus()
this._restartArchiveHealthRefreshTimer()
}
async createGuild ({ name }) {
@@ -4077,10 +4160,43 @@ class PearcordPlatform extends EventEmitter {
}).catch(() => {})
await this._ensureGuildModules()
this._startAuditExportScheduleTimer()
await this._maybeArchiveHealthRefreshOnFocus()
this._restartArchiveHealthRefreshTimer()
this.emit('guild', result)
return result
}
_stopArchiveHealthRefreshTimer () {
if (this._archiveHealthRefreshTimer) {
clearInterval(this._archiveHealthRefreshTimer)
this._archiveHealthRefreshTimer = null
}
}
_restartArchiveHealthRefreshTimer () {
this._stopArchiveHealthRefreshTimer()
if (this.mode !== 'guild' || !this.guild?.guild) return
this._initDeliveryReceipts(this.guild.guild.id)
.then(() => this.deliveryReceipts.getArchiveHealthPrefs())
.then((prefs) => {
if (!prefs.autoRefreshOnGuildFocus) return
this._archiveHealthRefreshTimer = setInterval(() => {
this.refreshArchivePeerHealth().catch(() => {})
}, 30000)
})
.catch(() => {})
}
async _maybeArchiveHealthRefreshOnFocus () {
if (!this.guild?.guild) return null
await this._initDeliveryReceipts(this.guild.guild.id)
const prefs = await this.deliveryReceipts.getArchiveHealthPrefs()
if (!prefs.autoRefreshOnGuildFocus) return null
const last = this._archivePeerHealthRefreshedAt || 0
if (Date.now() - last < 5000) return null
return this.refreshArchivePeerHealth().catch(() => null)
}
async listGuilds () {
return this.db.find(COLLECTIONS.GUILDS, {})
}
@@ -4114,6 +4230,8 @@ class PearcordPlatform extends EventEmitter {
await this.discovery?.recordGuild(guild).catch(() => {})
await this._ensureGuildModules()
this._startAuditExportScheduleTimer()
await this._maybeArchiveHealthRefreshOnFocus()
this._restartArchiveHealthRefreshTimer()
return guild
}
@@ -5857,12 +5975,24 @@ class PearcordPlatform extends EventEmitter {
})
}
}
const prefs = await this.deliveryReceipts.getAutomationDigestNotifyPrefs()
const preview = dryRunWebhookRelayPost(handoff, {
webhookUrl: opts.webhookUrl || null
webhookUrl: opts.webhookUrl || prefs.digestRelayWebhookUrl || handoff.webhookUrl || null
})
return { handoff, preview }
}
async _syncDigestRelayWebhookUrlToPlugin (url) {
if (!this.guild?.guild) return null
const plugins = await this.deliveryReceipts.listDigestRelayPlugins()
const webhook = plugins.find((p) => p.pluginId === 'pearcord/digest-webhook-bridge')
if (!webhook) return null
return this.deliveryReceipts.upsertDigestRelayPlugin({
...webhook,
config: { ...(webhook.config || {}), webhookUrl: url || null }
})
}
async syncModDigestGroupDmFromMesh (slice = {}) {
if (!this.guild?.guild || !slice?.channelId) return null
const roles = await this._memberRoles()
@@ -7902,6 +8032,12 @@ class PearcordPlatform extends EventEmitter {
digestRelayHandoff,
auditArchiveFetchPeers,
archivePeerHealthRefresh: this.getArchivePeerHealthRefreshStatus(),
archiveHealthPrefs: this.guild?.guild
? await this.getArchiveHealthPrefs().catch(() => ({
autoRefreshOnGuildFocus: true,
updatedAt: 0
}))
: { autoRefreshOnGuildFocus: true, updatedAt: 0 },
workerReleaseHint,
workerInstallPlan,
auditLogFilterOptions,