feat(platform): Phase 491 automation span metadata (v0.8.454)

Add automation.hook, automation.manifest, and automation.hook.dryRun spans.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Raven Scott
2026-05-24 06:50:48 -04:00
co-authored by Cursor
parent 8496ff0a97
commit 1aebd555c5
2 changed files with 117 additions and 46 deletions
+2
View File
@@ -39,6 +39,8 @@ Expose a single `EventEmitter` API the UI drives over IPC (`apps/pearcord/index.
**v0.8.430 (Phase 467):** `voice.leave` span `end` includes `leavingIsStage`; `screen.start` span `end` includes `label`. Bundle: `npm run test:phase467-voice-stage`. See [VOICE.md](../../docs/VOICE.md), [STAGE_CHANNELS.md](../../docs/STAGE_CHANNELS.md).
**v0.8.454 (Phase 491):** `automation.hook` / `automation.manifest` / `automation.hook.dryRun` spans + errors. UI automation compositor + hints. Bundle: `npm run test:phase491-automation`. See [AUTOMATION_EXPORT.md](../../docs/AUTOMATION_EXPORT.md).
**v0.8.453 (Phase 490):** `bot.token`/`bot.verify`/`bot.install`/`bot.message` span metadata extend. UI bot compositor + hints. Bundle: `npm run test:phase490-bots`. See [BOTS.md](../../docs/BOTS.md).
**v0.8.452 (Phase 489):** `slash.register` `customCount`; `slash.delete` `deleted`; `slash.invoke` `replyLen`/`commandFound`. UI slash compositor + hints. Bundle: `npm run test:phase489-slash`. See [SLASH_COMMANDS.md](../../docs/SLASH_COMMANDS.md).
+70 -1
View File
@@ -6093,6 +6093,9 @@ class PearcordPlatform extends EventEmitter {
}
async dryRunAutomationHook (hookId, eventType) {
const guildId = this.guild?.guild?.id || null
const span = this.log.time('automation.hook.dryRun', { guildId, hookId, eventType })
try {
if (!this.guild?.guild) throw new Error('no guild')
await this._initAutomationExport(this.guild.guild.id)
const hook = await this.automationExport.getHook(hookId)
@@ -6128,7 +6131,24 @@ class PearcordPlatform extends EventEmitter {
})
}
}
span.end({
guildId,
hookId,
eventType,
matches: !!result.matches,
actionType: hook.action?.type || null
})
return { sample, result }
} catch (err) {
this.log.error('automation.hook.dryRun error', {
guildId,
hookId,
eventType,
error: err?.message || String(err)
})
span.fail(err)
throw err
}
}
async dryRunAllAutomationHooks (eventType) {
@@ -8918,6 +8938,9 @@ class PearcordPlatform extends EventEmitter {
}
async upsertAutomationHook (hook = {}) {
const guildId = this.guild?.guild?.id || null
const span = this.log.time('automation.hook', { guildId })
try {
if (!this.guild?.guild) throw new Error('no guild')
const roles = await this._memberRoles()
if (!roleHasPermission(roles, PERMISSION.MANAGE_GUILD)) {
@@ -8926,10 +8949,28 @@ class PearcordPlatform extends EventEmitter {
await this._initAutomationExport(this.guild.guild.id)
const row = await this.automationExport.upsertHook(hook)
this.emit('automation-hook', row)
span.end({
guildId,
hookId: row.id,
hookName: String(row.name || '').slice(0, 48),
actionType: row.action?.type || null,
enabled: row.enabled !== false
})
return row
} catch (err) {
this.log.error('automation.hook error', {
guildId,
error: err?.message || String(err)
})
span.fail(err)
throw err
}
}
async deleteAutomationHook (hookId) {
const guildId = this.guild?.guild?.id || null
const span = this.log.time('automation.hook', { guildId, hookId, op: 'delete' })
try {
if (!this.guild?.guild) throw new Error('no guild')
const roles = await this._memberRoles()
if (!roleHasPermission(roles, PERMISSION.MANAGE_GUILD)) {
@@ -8938,10 +8979,23 @@ class PearcordPlatform extends EventEmitter {
await this._initAutomationExport(this.guild.guild.id)
const ok = await this.automationExport.deleteHook(hookId)
if (ok) this.emit('automation-hook-delete', { hookId, guildId: this.guild.guild.id })
span.end({ guildId, hookId, deleted: !!ok })
return ok
} catch (err) {
this.log.error('automation.hook error', {
guildId,
hookId,
error: err?.message || String(err)
})
span.fail(err)
throw err
}
}
async exportAutomationManifest () {
const guildId = this.guild?.guild?.id || null
const span = this.log.time('automation.manifest', { guildId })
try {
if (!this.guild?.guild) throw new Error('no guild')
const user = this.identity.user
if (!user) throw new Error('register first')
@@ -8956,7 +9010,7 @@ class PearcordPlatform extends EventEmitter {
const slashCommands = this.slashCommands ? await this.slashCommands.list() : []
const bots = await this.db.find(COLLECTIONS.GUILD_BOTS, { guildId: guild.id })
const hooks = await this.automationExport.listHooks()
return buildAutomationManifest({
const manifest = buildAutomationManifest({
guildId: guild.id,
guildName: guild.name,
exportedBy: user.id,
@@ -8966,6 +9020,21 @@ class PearcordPlatform extends EventEmitter {
hooks,
appEventCategories: APP_EVENT_CATEGORIES.ALL
})
span.end({
guildId,
hookCount: hooks.length,
botCount: bots.length,
slashCount: slashCommands.length
})
return manifest
} catch (err) {
this.log.error('automation.manifest error', {
guildId,
error: err?.message || String(err)
})
span.fail(err)
throw err
}
}
async importAutomationManifest (manifest, opts = {}) {