feat(platform): Phase 102 archive fetch, dashboard, trial asserts

- fetchAuditExportArchiveFromMesh via RPC 72 request/response waiters
- Respond to audit-export-archive-request when local archive has entries
- getAutomationScheduleDashboard on view state
- runIntegrationWorkerTrial fans out MESSAGE_CREATE and requires sawMessageCreate
- Merge archive ingest resolves pending mesh fetch waiters

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Raven Scott
2026-05-22 04:57:16 -04:00
co-authored by Cursor
parent a5008a3621
commit 09b98b0efb
+108 -4
View File
@@ -46,6 +46,7 @@ const {
formatAuditExport,
formatHookFailureDigestExport,
mergeHookFailureDigests,
buildAutomationScheduleDashboard,
HookFailureAlertStore
} = require('pearcord-delivery-receipts')
const {
@@ -255,6 +256,7 @@ class PearcordPlatform extends EventEmitter {
this.hookFailureAlerts = null
this.workerReleaseHints = null
this._auditScheduleTimer = null
this._auditArchiveFetchWaiters = new Map()
this.appVersion = opts.appVersion || process.env.PEARCORD_APP_VERSION || '0.0.0'
this.discovery = null
this.contacts = null
@@ -1614,6 +1616,9 @@ class PearcordPlatform extends EventEmitter {
guildInstance.on('audit-export-archive', (payload) => {
this._onAuditExportArchiveGossip(payload).catch(() => {})
})
guildInstance.on('audit-export-archive-request', (payload) => {
this._onAuditExportArchiveRequestGossip(payload).catch(() => {})
})
guildInstance.on('audit', async (p) => {
if (p?.guildId && p?.id) {
const existing = await this.db.get(COLLECTIONS.AUDIT_LOG, { id: p.id })
@@ -2018,7 +2023,10 @@ class PearcordPlatform extends EventEmitter {
if (this.guild?.guild?.id !== payload.guildId) return null
await this._initDeliveryReceipts(payload.guildId)
const row = await this.deliveryReceipts.ingestAuditExportArchive(payload)
if (row) this.emit('audit-export-archive', row)
if (row) {
this._resolveAuditArchiveFetchWaiter(row)
this.emit('audit-export-archive', row)
}
return row
}
@@ -2028,6 +2036,70 @@ class PearcordPlatform extends EventEmitter {
return this.deliveryReceipts.listAuditExportArchives(limit)
}
async _onAuditExportArchiveRequestGossip (payload) {
if (!payload?.guildId || !payload?.archiveId) return null
if (this.guild?.guild?.id !== payload.guildId) return null
await this._initDeliveryReceipts(payload.guildId)
const row = await this.deliveryReceipts.getAuditExportArchive(payload.archiveId)
if (!row?.entries?.length) return null
if (this.guild.gossipAuditExportArchive) {
this.guild.gossipAuditExportArchive(row)
}
return row
}
_resolveAuditArchiveFetchWaiter (archive) {
if (!archive?.id) return
for (const [key, handlers] of this._auditArchiveFetchWaiters) {
if (key === archive.id || key.startsWith(`${archive.id}:`)) {
for (const fn of handlers) fn(archive)
this._auditArchiveFetchWaiters.delete(key)
}
}
}
async fetchAuditExportArchiveFromMesh (archiveId, opts = {}) {
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 fetch audit export archive')
}
await this._initDeliveryReceipts(this.guild.guild.id)
let row = await this.deliveryReceipts.getAuditExportArchive(archiveId)
if (row?.entries?.length) {
return this.exportAuditExportArchive(archiveId, opts)
}
const timeoutMs = Math.min(15000, Math.max(2000, Number(opts.timeoutMs) || 8000))
const requestId = id()
const waitPromise = new Promise((resolve, reject) => {
const timer = setTimeout(() => {
this._auditArchiveFetchWaiters.delete(archiveId)
reject(new Error('audit archive fetch timed out'))
}, timeoutMs)
const handlers = this._auditArchiveFetchWaiters.get(archiveId) || []
handlers.push((arch) => {
clearTimeout(timer)
resolve(arch)
})
this._auditArchiveFetchWaiters.set(archiveId, handlers)
})
if (this.guild.gossipAuditExportArchiveRequest) {
this.guild.gossipAuditExportArchiveRequest({
guildId: this.guild.guild.id,
archiveId,
requestId,
requestedBy: this.identity.user?.id || null,
at: Date.now()
})
}
const fetched = await waitPromise.catch(async () => {
row = await this.deliveryReceipts.getAuditExportArchive(archiveId)
if (row?.entries?.length) return row
throw new Error('audit export archive not found on mesh')
})
return this.exportAuditExportArchive(fetched.id || archiveId, opts)
}
async exportAuditExportArchive (archiveId, opts = {}) {
if (!this.guild?.guild) throw new Error('no guild')
const roles = await this._memberRoles()
@@ -2072,23 +2144,52 @@ class PearcordPlatform extends EventEmitter {
const worker = this.createIntegrationWorker()
const events = []
const receipts = []
worker.on('event', (ev) => events.push(ev))
const eventTypes = []
worker.on('event', (ev) => {
events.push(ev)
if (ev?.type) eventTypes.push(ev.type)
})
worker.on('receipt', (r) => receipts.push(r))
await worker.start({ templateId })
const ms = Math.min(5000, Math.max(200, Number(holdMs) || 800))
await this._fanoutAppEvent(APP_EVENTS.MESSAGE_CREATE, {
message: {
id: `trial-${Date.now()}`,
channelId: this.activeChannelId || 'trial-ch',
content: '(integration worker trial)',
authorId: this.identity.user?.id || 'trial'
}
}).catch(() => {})
const ms = Math.min(5000, Math.max(400, Number(holdMs) || 800))
await new Promise((resolve) => setTimeout(resolve, ms))
await worker.stop()
const sawMessageCreate = eventTypes.includes(APP_EVENTS.MESSAGE_CREATE)
return {
ok: true,
ok: sawMessageCreate,
templateId,
templateName: tpl.name,
holdMs: ms,
eventsSeen: events.length,
receiptsSeen: receipts.length,
eventTypes: [...new Set(eventTypes)],
sawMessageCreate,
guildId: this.guild.guild.id
}
}
async getAutomationScheduleDashboard () {
if (!this.guild?.guild) {
return buildAutomationScheduleDashboard({})
}
const auditExportSchedule = await this.getAuditExportSchedule()
const digestExportSchedule = await this.getDigestExportSchedule()
const hookFailureDigest = await this.getHookFailureDigest()
return buildAutomationScheduleDashboard({
auditExportSchedule,
digestExportSchedule,
hookFailureDigest
})
}
async exportHookFailureDigest (opts = {}) {
if (!this.guild?.guild) throw new Error('no guild')
const roles = await this._memberRoles()
@@ -6370,6 +6471,7 @@ class PearcordPlatform extends EventEmitter {
let auditExportSchedule = null
let digestExportSchedule = null
let auditExportArchives = []
let automationScheduleDashboard = null
let workerReleaseHint = null
let workerInstallPlan = null
if (guild) {
@@ -6377,6 +6479,7 @@ class PearcordPlatform extends EventEmitter {
auditExportSchedule = await this.getAuditExportSchedule()
digestExportSchedule = await this.getDigestExportSchedule()
auditExportArchives = await this.listAuditExportArchives(6)
automationScheduleDashboard = await this.getAutomationScheduleDashboard()
workerReleaseHint = await this.getWorkerReleaseHint()
this._lastWorkerReleaseHintView = workerReleaseHint
if (workerReleaseHint?.updateAvailable) {
@@ -6549,6 +6652,7 @@ class PearcordPlatform extends EventEmitter {
auditExportSchedule,
digestExportSchedule,
auditExportArchives,
automationScheduleDashboard,
workerReleaseHint,
workerInstallPlan,
auditLogFilterOptions,