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._devicePairSwarm = null
this._discoveryWired = false this._discoveryWired = false
this._archivePeerHealthRefreshedAt = null this._archivePeerHealthRefreshedAt = null
this._archiveHealthRefreshTimer = null
this.mode = 'home' this.mode = 'home'
this.activeChannelId = null this.activeChannelId = null
this.onboarded = false this.onboarded = false
@@ -1199,6 +1200,8 @@ class PearcordPlatform extends EventEmitter {
let auditExportArchives = [] let auditExportArchives = []
let archivePeerSeen = [] let archivePeerSeen = []
let hookFailureDigest = null let hookFailureDigest = null
let hookFailureDigestCsvExport = null
let archiveHealthPrefs = null
await this._initDeliveryReceipts(guild.id) await this._initDeliveryReceipts(guild.id)
if (this.deliveryReceipts) { if (this.deliveryReceipts) {
auditExportSchedule = await this.deliveryReceipts.exportAuditExportScheduleSlice() auditExportSchedule = await this.deliveryReceipts.exportAuditExportScheduleSlice()
@@ -1209,6 +1212,9 @@ class PearcordPlatform extends EventEmitter {
await this.deliveryReceipts.exportAutomationDigestExportSnapshotsSlice(2) await this.deliveryReceipts.exportAutomationDigestExportSnapshotsSlice(2)
auditExportArchives = await this.deliveryReceipts.exportAuditExportArchivesSlice(3) auditExportArchives = await this.deliveryReceipts.exportAuditExportArchivesSlice(3)
archivePeerSeen = await this.deliveryReceipts.exportArchivePeerSeenSlice(16) archivePeerSeen = await this.deliveryReceipts.exportArchivePeerSeenSlice(16)
archiveHealthPrefs = await this.deliveryReceipts.exportArchiveHealthPrefsSlice()
hookFailureDigestCsvExport =
await this.deliveryReceipts.exportHookFailureDigestCsvSlice()
digestRelayHandoff = await this.deliveryReceipts.exportDigestRelayHandoffSlice() digestRelayHandoff = await this.deliveryReceipts.exportDigestRelayHandoffSlice()
const digestSnap = await this.deliveryReceipts.getHookFailureDigestSnapshot() const digestSnap = await this.deliveryReceipts.getHookFailureDigestSnapshot()
if (digestSnap) { if (digestSnap) {
@@ -1293,6 +1299,8 @@ class PearcordPlatform extends EventEmitter {
auditExportArchives, auditExportArchives,
archivePeerSeen, archivePeerSeen,
hookFailureDigest, hookFailureDigest,
hookFailureDigestCsvExport,
archiveHealthPrefs,
modDigestGroupDmMeta, modDigestGroupDmMeta,
workerReleaseHint, workerReleaseHint,
automodConfig: automodConfig automodConfig: automodConfig
@@ -1427,6 +1435,20 @@ class PearcordPlatform extends EventEmitter {
payload.digestRelayHandoff 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) { if (payload.auditExportSchedule) {
await this._initDeliveryReceipts(payload.guildId) await this._initDeliveryReceipts(payload.guildId)
await this.deliveryReceipts.ingestAuditExportScheduleSlice( await this.deliveryReceipts.ingestAuditExportScheduleSlice(
@@ -2725,7 +2747,8 @@ class PearcordPlatform extends EventEmitter {
guildMute: false, guildMute: false,
muteNotifications: false, muteNotifications: false,
digestInboxChannelId: null, digestInboxChannelId: null,
digestRelayEnabled: false digestRelayEnabled: false,
digestRelayWebhookUrl: null
} }
} }
await this._initDeliveryReceipts(this.guild.guild.id) await this._initDeliveryReceipts(this.guild.guild.id)
@@ -2741,10 +2764,34 @@ class PearcordPlatform extends EventEmitter {
muteNotifications: !!guild.muteNotifications || userMute, muteNotifications: !!guild.muteNotifications || userMute,
digestInboxChannelId: guild.digestInboxChannelId || null, digestInboxChannelId: guild.digestInboxChannelId || null,
digestRelayEnabled: !!guild.digestRelayEnabled, 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 = {}) { async setAutomationDigestNotifyPrefs (partial = {}) {
if (!this.guild?.guild) throw new Error('no guild') if (!this.guild?.guild) throw new Error('no guild')
const userId = this.identity.user?.id const userId = this.identity.user?.id
@@ -2770,7 +2817,13 @@ class PearcordPlatform extends EventEmitter {
if (partial.activeRelayPluginId !== undefined) { if (partial.activeRelayPluginId !== undefined) {
patch.activeRelayPluginId = partial.activeRelayPluginId || null patch.activeRelayPluginId = partial.activeRelayPluginId || null
} }
if (partial.digestRelayWebhookUrl !== undefined) {
patch.digestRelayWebhookUrl = partial.digestRelayWebhookUrl || null
}
await this.deliveryReceipts.setAutomationDigestNotifyPrefs(patch) await this.deliveryReceipts.setAutomationDigestNotifyPrefs(patch)
if (patch.digestRelayWebhookUrl !== undefined) {
await this._syncDigestRelayWebhookUrlToPlugin(patch.digestRelayWebhookUrl).catch(() => {})
}
setTimeout(() => this._pushGuildSyncToMesh().catch(() => {}), 400) setTimeout(() => this._pushGuildSyncToMesh().catch(() => {}), 400)
} else { } else {
if (partial.userMute != null || partial.muteNotifications != null) { if (partial.userMute != null || partial.muteNotifications != null) {
@@ -2867,7 +2920,8 @@ class PearcordPlatform extends EventEmitter {
const plugin = await this.deliveryReceipts.getActiveDigestRelayPlugin() const plugin = await this.deliveryReceipts.getActiveDigestRelayPlugin()
if (plugin) { if (plugin) {
handoff = applyRelayPluginToHandoff(handoff, 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) await this.deliveryReceipts.appendDigestRelayHandoff(handoff)
@@ -2978,6 +3032,15 @@ class PearcordPlatform extends EventEmitter {
}) })
signatureValid = verified.ok signatureValid = verified.ok
body = body.csv body = body.csv
await this.deliveryReceipts.recordHookFailureDigestCsvExport({
csv: body,
signature,
signatureAlg,
exportedAt: Date.now()
})
if (opts.recordMesh !== false) {
setTimeout(() => this._pushGuildSyncToMesh().catch(() => {}), 400)
}
} }
return { return {
format, 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 }) { async setGuildIconImage ({ data, filename, mimeType }) {
const user = this.identity.user const user = this.identity.user
if (!user || !this.guild?.guild) throw new Error('no guild') if (!user || !this.guild?.guild) throw new Error('no guild')
@@ -3892,11 +3967,17 @@ class PearcordPlatform extends EventEmitter {
guildName: this.guild.guild.name, guildName: this.guild.guild.name,
exported: { exportedAt: Date.now(), body: '', format: 'text' } exported: { exportedAt: Date.now(), body: '', format: 'text' }
}) })
const digPrefs = await this.deliveryReceipts.getAutomationDigestNotifyPrefs()
const stamped = plugin const stamped = plugin
? applyRelayPluginToHandoff(handoff, plugin, { relaySecret: this.guild.guild.id }) ? applyRelayPluginToHandoff(handoff, plugin, {
relaySecret: this.guild.guild.id,
webhookUrl: digPrefs.digestRelayWebhookUrl || null
})
: handoff : handoff
if (plugin?.transport === 'webhook-bridge') { if (plugin?.transport === 'webhook-bridge') {
result.webhookRelayPreview = dryRunWebhookRelayPost(stamped, {}) result.webhookRelayPreview = dryRunWebhookRelayPost(stamped, {
webhookUrl: digPrefs.digestRelayWebhookUrl || null
})
} }
} }
return { sample, result } return { sample, result }
@@ -4029,6 +4110,8 @@ class PearcordPlatform extends EventEmitter {
await this.discovery?.touchGuild(guildRecord.id).catch(() => {}) await this.discovery?.touchGuild(guildRecord.id).catch(() => {})
await this._ensureGuildModules() await this._ensureGuildModules()
this._startAuditExportScheduleTimer() this._startAuditExportScheduleTimer()
await this._maybeArchiveHealthRefreshOnFocus()
this._restartArchiveHealthRefreshTimer()
} }
async createGuild ({ name }) { async createGuild ({ name }) {
@@ -4077,10 +4160,43 @@ class PearcordPlatform extends EventEmitter {
}).catch(() => {}) }).catch(() => {})
await this._ensureGuildModules() await this._ensureGuildModules()
this._startAuditExportScheduleTimer() this._startAuditExportScheduleTimer()
await this._maybeArchiveHealthRefreshOnFocus()
this._restartArchiveHealthRefreshTimer()
this.emit('guild', result) this.emit('guild', result)
return 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 () { async listGuilds () {
return this.db.find(COLLECTIONS.GUILDS, {}) return this.db.find(COLLECTIONS.GUILDS, {})
} }
@@ -4114,6 +4230,8 @@ class PearcordPlatform extends EventEmitter {
await this.discovery?.recordGuild(guild).catch(() => {}) await this.discovery?.recordGuild(guild).catch(() => {})
await this._ensureGuildModules() await this._ensureGuildModules()
this._startAuditExportScheduleTimer() this._startAuditExportScheduleTimer()
await this._maybeArchiveHealthRefreshOnFocus()
this._restartArchiveHealthRefreshTimer()
return guild return guild
} }
@@ -5857,12 +5975,24 @@ class PearcordPlatform extends EventEmitter {
}) })
} }
} }
const prefs = await this.deliveryReceipts.getAutomationDigestNotifyPrefs()
const preview = dryRunWebhookRelayPost(handoff, { const preview = dryRunWebhookRelayPost(handoff, {
webhookUrl: opts.webhookUrl || null webhookUrl: opts.webhookUrl || prefs.digestRelayWebhookUrl || handoff.webhookUrl || null
}) })
return { handoff, preview } 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 = {}) { async syncModDigestGroupDmFromMesh (slice = {}) {
if (!this.guild?.guild || !slice?.channelId) return null if (!this.guild?.guild || !slice?.channelId) return null
const roles = await this._memberRoles() const roles = await this._memberRoles()
@@ -7902,6 +8032,12 @@ class PearcordPlatform extends EventEmitter {
digestRelayHandoff, digestRelayHandoff,
auditArchiveFetchPeers, auditArchiveFetchPeers,
archivePeerHealthRefresh: this.getArchivePeerHealthRefreshStatus(), archivePeerHealthRefresh: this.getArchivePeerHealthRefreshStatus(),
archiveHealthPrefs: this.guild?.guild
? await this.getArchiveHealthPrefs().catch(() => ({
autoRefreshOnGuildFocus: true,
updatedAt: 0
}))
: { autoRefreshOnGuildFocus: true, updatedAt: 0 },
workerReleaseHint, workerReleaseHint,
workerInstallPlan, workerInstallPlan,
auditLogFilterOptions, auditLogFilterOptions,