@spinabot/brigade 1.6.1 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/agent-loop.d.ts +13 -0
- package/dist/agents/agent-loop.d.ts.map +1 -1
- package/dist/agents/agent-loop.js +5 -0
- package/dist/agents/agent-loop.js.map +1 -1
- package/dist/agents/channels/access-control/group-tool-policy.d.ts +73 -0
- package/dist/agents/channels/access-control/group-tool-policy.d.ts.map +1 -0
- package/dist/agents/channels/access-control/group-tool-policy.js +193 -0
- package/dist/agents/channels/access-control/group-tool-policy.js.map +1 -0
- package/dist/agents/channels/access-control/index.d.ts +1 -0
- package/dist/agents/channels/access-control/index.d.ts.map +1 -1
- package/dist/agents/channels/access-control/index.js +1 -0
- package/dist/agents/channels/access-control/index.js.map +1 -1
- package/dist/agents/channels/bluebubbles/account-config.d.ts +253 -0
- package/dist/agents/channels/bluebubbles/account-config.d.ts.map +1 -0
- package/dist/agents/channels/bluebubbles/account-config.js +486 -0
- package/dist/agents/channels/bluebubbles/account-config.js.map +1 -0
- package/dist/agents/channels/bluebubbles/account-registry.d.ts +33 -0
- package/dist/agents/channels/bluebubbles/account-registry.d.ts.map +1 -0
- package/dist/agents/channels/bluebubbles/account-registry.js +46 -0
- package/dist/agents/channels/bluebubbles/account-registry.js.map +1 -0
- package/dist/agents/channels/bluebubbles/adapter.d.ts +45 -0
- package/dist/agents/channels/bluebubbles/adapter.d.ts.map +1 -0
- package/dist/agents/channels/bluebubbles/adapter.js +366 -0
- package/dist/agents/channels/bluebubbles/adapter.js.map +1 -0
- package/dist/agents/channels/bluebubbles/catchup-cursor.d.ts +52 -0
- package/dist/agents/channels/bluebubbles/catchup-cursor.d.ts.map +1 -0
- package/dist/agents/channels/bluebubbles/catchup-cursor.js +118 -0
- package/dist/agents/channels/bluebubbles/catchup-cursor.js.map +1 -0
- package/dist/agents/channels/bluebubbles/catchup.d.ts +114 -0
- package/dist/agents/channels/bluebubbles/catchup.d.ts.map +1 -0
- package/dist/agents/channels/bluebubbles/catchup.js +220 -0
- package/dist/agents/channels/bluebubbles/catchup.js.map +1 -0
- package/dist/agents/channels/bluebubbles/chat.d.ts +75 -0
- package/dist/agents/channels/bluebubbles/chat.d.ts.map +1 -0
- package/dist/agents/channels/bluebubbles/chat.js +182 -0
- package/dist/agents/channels/bluebubbles/chat.js.map +1 -0
- package/dist/agents/channels/bluebubbles/connection.d.ts +95 -0
- package/dist/agents/channels/bluebubbles/connection.d.ts.map +1 -0
- package/dist/agents/channels/bluebubbles/connection.js +413 -0
- package/dist/agents/channels/bluebubbles/connection.js.map +1 -0
- package/dist/agents/channels/bluebubbles/contact-names.d.ts +79 -0
- package/dist/agents/channels/bluebubbles/contact-names.d.ts.map +1 -0
- package/dist/agents/channels/bluebubbles/contact-names.js +188 -0
- package/dist/agents/channels/bluebubbles/contact-names.js.map +1 -0
- package/dist/agents/channels/bluebubbles/debounce.d.ts +78 -0
- package/dist/agents/channels/bluebubbles/debounce.d.ts.map +1 -0
- package/dist/agents/channels/bluebubbles/debounce.js +173 -0
- package/dist/agents/channels/bluebubbles/debounce.js.map +1 -0
- package/dist/agents/channels/bluebubbles/dedupe.d.ts +25 -0
- package/dist/agents/channels/bluebubbles/dedupe.d.ts.map +1 -0
- package/dist/agents/channels/bluebubbles/dedupe.js +35 -0
- package/dist/agents/channels/bluebubbles/dedupe.js.map +1 -0
- package/dist/agents/channels/bluebubbles/effects.d.ts +17 -0
- package/dist/agents/channels/bluebubbles/effects.d.ts.map +1 -0
- package/dist/agents/channels/bluebubbles/effects.js +57 -0
- package/dist/agents/channels/bluebubbles/effects.js.map +1 -0
- package/dist/agents/channels/bluebubbles/history.d.ts +56 -0
- package/dist/agents/channels/bluebubbles/history.d.ts.map +1 -0
- package/dist/agents/channels/bluebubbles/history.js +140 -0
- package/dist/agents/channels/bluebubbles/history.js.map +1 -0
- package/dist/agents/channels/bluebubbles/index.d.ts +18 -0
- package/dist/agents/channels/bluebubbles/index.d.ts.map +1 -0
- package/dist/agents/channels/bluebubbles/index.js +18 -0
- package/dist/agents/channels/bluebubbles/index.js.map +1 -0
- package/dist/agents/channels/bluebubbles/media.d.ts +104 -0
- package/dist/agents/channels/bluebubbles/media.d.ts.map +1 -0
- package/dist/agents/channels/bluebubbles/media.js +222 -0
- package/dist/agents/channels/bluebubbles/media.js.map +1 -0
- package/dist/agents/channels/bluebubbles/messaging.d.ts +13 -0
- package/dist/agents/channels/bluebubbles/messaging.d.ts.map +1 -0
- package/dist/agents/channels/bluebubbles/messaging.js +36 -0
- package/dist/agents/channels/bluebubbles/messaging.js.map +1 -0
- package/dist/agents/channels/bluebubbles/module.d.ts +24 -0
- package/dist/agents/channels/bluebubbles/module.d.ts.map +1 -0
- package/dist/agents/channels/bluebubbles/module.js +52 -0
- package/dist/agents/channels/bluebubbles/module.js.map +1 -0
- package/dist/agents/channels/bluebubbles/normalize.d.ts +111 -0
- package/dist/agents/channels/bluebubbles/normalize.d.ts.map +1 -0
- package/dist/agents/channels/bluebubbles/normalize.js +239 -0
- package/dist/agents/channels/bluebubbles/normalize.js.map +1 -0
- package/dist/agents/channels/bluebubbles/plugin.d.ts +46 -0
- package/dist/agents/channels/bluebubbles/plugin.d.ts.map +1 -0
- package/dist/agents/channels/bluebubbles/plugin.js +242 -0
- package/dist/agents/channels/bluebubbles/plugin.js.map +1 -0
- package/dist/agents/channels/bluebubbles/probe.d.ts +79 -0
- package/dist/agents/channels/bluebubbles/probe.d.ts.map +1 -0
- package/dist/agents/channels/bluebubbles/probe.js +138 -0
- package/dist/agents/channels/bluebubbles/probe.js.map +1 -0
- package/dist/agents/channels/bluebubbles/reactions.d.ts +46 -0
- package/dist/agents/channels/bluebubbles/reactions.d.ts.map +1 -0
- package/dist/agents/channels/bluebubbles/reactions.js +207 -0
- package/dist/agents/channels/bluebubbles/reactions.js.map +1 -0
- package/dist/agents/channels/bluebubbles/send.d.ts +132 -0
- package/dist/agents/channels/bluebubbles/send.d.ts.map +1 -0
- package/dist/agents/channels/bluebubbles/send.js +327 -0
- package/dist/agents/channels/bluebubbles/send.js.map +1 -0
- package/dist/agents/channels/bluebubbles/status-issues.d.ts +57 -0
- package/dist/agents/channels/bluebubbles/status-issues.d.ts.map +1 -0
- package/dist/agents/channels/bluebubbles/status-issues.js +93 -0
- package/dist/agents/channels/bluebubbles/status-issues.js.map +1 -0
- package/dist/agents/channels/bluebubbles/types.d.ts +69 -0
- package/dist/agents/channels/bluebubbles/types.d.ts.map +1 -0
- package/dist/agents/channels/bluebubbles/types.js +118 -0
- package/dist/agents/channels/bluebubbles/types.js.map +1 -0
- package/dist/agents/channels/bluebubbles/webhook.d.ts +97 -0
- package/dist/agents/channels/bluebubbles/webhook.d.ts.map +1 -0
- package/dist/agents/channels/bluebubbles/webhook.js +249 -0
- package/dist/agents/channels/bluebubbles/webhook.js.map +1 -0
- package/dist/agents/channels/bundled-channel-metas.d.ts +13 -0
- package/dist/agents/channels/bundled-channel-metas.d.ts.map +1 -1
- package/dist/agents/channels/bundled-channel-metas.js +33 -0
- package/dist/agents/channels/bundled-channel-metas.js.map +1 -1
- package/dist/agents/channels/discord/account-config.d.ts +60 -0
- package/dist/agents/channels/discord/account-config.d.ts.map +1 -1
- package/dist/agents/channels/discord/account-config.js +89 -0
- package/dist/agents/channels/discord/account-config.js.map +1 -1
- package/dist/agents/channels/discord/adapter.d.ts +24 -1
- package/dist/agents/channels/discord/adapter.d.ts.map +1 -1
- package/dist/agents/channels/discord/adapter.js +208 -41
- package/dist/agents/channels/discord/adapter.js.map +1 -1
- package/dist/agents/channels/discord/component-blocks.d.ts +108 -0
- package/dist/agents/channels/discord/component-blocks.d.ts.map +1 -0
- package/dist/agents/channels/discord/component-blocks.js +113 -0
- package/dist/agents/channels/discord/component-blocks.js.map +1 -0
- package/dist/agents/channels/discord/components.d.ts +78 -0
- package/dist/agents/channels/discord/components.d.ts.map +1 -1
- package/dist/agents/channels/discord/components.js +89 -0
- package/dist/agents/channels/discord/components.js.map +1 -1
- package/dist/agents/channels/discord/connection.d.ts +195 -12
- package/dist/agents/channels/discord/connection.d.ts.map +1 -1
- package/dist/agents/channels/discord/connection.js +852 -38
- package/dist/agents/channels/discord/connection.js.map +1 -1
- package/dist/agents/channels/discord/directory-cache.d.ts +47 -0
- package/dist/agents/channels/discord/directory-cache.d.ts.map +1 -0
- package/dist/agents/channels/discord/directory-cache.js +131 -0
- package/dist/agents/channels/discord/directory-cache.js.map +1 -0
- package/dist/agents/channels/discord/directory-live.d.ts +61 -0
- package/dist/agents/channels/discord/directory-live.d.ts.map +1 -0
- package/dist/agents/channels/discord/directory-live.js +140 -0
- package/dist/agents/channels/discord/directory-live.js.map +1 -0
- package/dist/agents/channels/discord/format.d.ts +15 -0
- package/dist/agents/channels/discord/format.d.ts.map +1 -1
- package/dist/agents/channels/discord/format.js +56 -0
- package/dist/agents/channels/discord/format.js.map +1 -1
- package/dist/agents/channels/discord/guilds.d.ts +25 -0
- package/dist/agents/channels/discord/guilds.d.ts.map +1 -0
- package/dist/agents/channels/discord/guilds.js +46 -0
- package/dist/agents/channels/discord/guilds.js.map +1 -0
- package/dist/agents/channels/discord/inbound-extras.d.ts +166 -9
- package/dist/agents/channels/discord/inbound-extras.d.ts.map +1 -1
- package/dist/agents/channels/discord/inbound-extras.js +246 -8
- package/dist/agents/channels/discord/inbound-extras.js.map +1 -1
- package/dist/agents/channels/discord/index.d.ts +10 -3
- package/dist/agents/channels/discord/index.d.ts.map +1 -1
- package/dist/agents/channels/discord/index.js +10 -3
- package/dist/agents/channels/discord/index.js.map +1 -1
- package/dist/agents/channels/discord/modal-registry.d.ts +89 -0
- package/dist/agents/channels/discord/modal-registry.d.ts.map +1 -0
- package/dist/agents/channels/discord/modal-registry.js +104 -0
- package/dist/agents/channels/discord/modal-registry.js.map +1 -0
- package/dist/agents/channels/discord/modals.d.ts +100 -0
- package/dist/agents/channels/discord/modals.d.ts.map +1 -0
- package/dist/agents/channels/discord/modals.js +124 -0
- package/dist/agents/channels/discord/modals.js.map +1 -0
- package/dist/agents/channels/discord/permission-audit.d.ts +43 -0
- package/dist/agents/channels/discord/permission-audit.d.ts.map +1 -0
- package/dist/agents/channels/discord/permission-audit.js +192 -0
- package/dist/agents/channels/discord/permission-audit.js.map +1 -0
- package/dist/agents/channels/discord/plugin.d.ts +18 -2
- package/dist/agents/channels/discord/plugin.d.ts.map +1 -1
- package/dist/agents/channels/discord/plugin.js +73 -4
- package/dist/agents/channels/discord/plugin.js.map +1 -1
- package/dist/agents/channels/discord/probe.d.ts +23 -1
- package/dist/agents/channels/discord/probe.d.ts.map +1 -1
- package/dist/agents/channels/discord/probe.js +40 -5
- package/dist/agents/channels/discord/probe.js.map +1 -1
- package/dist/agents/channels/discord/rest-actions.d.ts +346 -0
- package/dist/agents/channels/discord/rest-actions.d.ts.map +1 -0
- package/dist/agents/channels/discord/rest-actions.js +559 -0
- package/dist/agents/channels/discord/rest-actions.js.map +1 -0
- package/dist/agents/channels/discord/rest-components.d.ts +122 -0
- package/dist/agents/channels/discord/rest-components.d.ts.map +1 -0
- package/dist/agents/channels/discord/rest-components.js +243 -0
- package/dist/agents/channels/discord/rest-components.js.map +1 -0
- package/dist/agents/channels/discord/security-audit.d.ts +29 -0
- package/dist/agents/channels/discord/security-audit.d.ts.map +1 -0
- package/dist/agents/channels/discord/security-audit.js +94 -0
- package/dist/agents/channels/discord/security-audit.js.map +1 -0
- package/dist/agents/channels/discord/security-doctor.d.ts +43 -0
- package/dist/agents/channels/discord/security-doctor.d.ts.map +1 -0
- package/dist/agents/channels/discord/security-doctor.js +83 -0
- package/dist/agents/channels/discord/security-doctor.js.map +1 -0
- package/dist/agents/channels/discord/status-issues.d.ts +37 -0
- package/dist/agents/channels/discord/status-issues.d.ts.map +1 -0
- package/dist/agents/channels/discord/status-issues.js +66 -0
- package/dist/agents/channels/discord/status-issues.js.map +1 -0
- package/dist/agents/channels/discord/subagent-thread-binding-store.d.ts +57 -0
- package/dist/agents/channels/discord/subagent-thread-binding-store.d.ts.map +1 -0
- package/dist/agents/channels/discord/subagent-thread-binding-store.js +98 -0
- package/dist/agents/channels/discord/subagent-thread-binding-store.js.map +1 -0
- package/dist/agents/channels/discord/subagent-thread-binding.d.ts +95 -0
- package/dist/agents/channels/discord/subagent-thread-binding.d.ts.map +1 -0
- package/dist/agents/channels/discord/subagent-thread-binding.js +208 -0
- package/dist/agents/channels/discord/subagent-thread-binding.js.map +1 -0
- package/dist/agents/channels/discord/system-events.d.ts +31 -0
- package/dist/agents/channels/discord/system-events.d.ts.map +1 -0
- package/dist/agents/channels/discord/system-events.js +74 -0
- package/dist/agents/channels/discord/system-events.js.map +1 -0
- package/dist/agents/channels/general-callback.d.ts +12 -0
- package/dist/agents/channels/general-callback.d.ts.map +1 -1
- package/dist/agents/channels/general-callback.js +18 -0
- package/dist/agents/channels/general-callback.js.map +1 -1
- package/dist/agents/channels/imessage/account-config.d.ts +178 -0
- package/dist/agents/channels/imessage/account-config.d.ts.map +1 -0
- package/dist/agents/channels/imessage/account-config.js +371 -0
- package/dist/agents/channels/imessage/account-config.js.map +1 -0
- package/dist/agents/channels/imessage/adapter.d.ts +36 -0
- package/dist/agents/channels/imessage/adapter.d.ts.map +1 -0
- package/dist/agents/channels/imessage/adapter.js +286 -0
- package/dist/agents/channels/imessage/adapter.js.map +1 -0
- package/dist/agents/channels/imessage/client.d.ts +99 -0
- package/dist/agents/channels/imessage/client.d.ts.map +1 -0
- package/dist/agents/channels/imessage/client.js +231 -0
- package/dist/agents/channels/imessage/client.js.map +1 -0
- package/dist/agents/channels/imessage/connection.d.ts +101 -0
- package/dist/agents/channels/imessage/connection.d.ts.map +1 -0
- package/dist/agents/channels/imessage/connection.js +367 -0
- package/dist/agents/channels/imessage/connection.js.map +1 -0
- package/dist/agents/channels/imessage/format.d.ts +45 -0
- package/dist/agents/channels/imessage/format.d.ts.map +1 -0
- package/dist/agents/channels/imessage/format.js +170 -0
- package/dist/agents/channels/imessage/format.js.map +1 -0
- package/dist/agents/channels/imessage/history.d.ts +49 -0
- package/dist/agents/channels/imessage/history.d.ts.map +1 -0
- package/dist/agents/channels/imessage/history.js +74 -0
- package/dist/agents/channels/imessage/history.js.map +1 -0
- package/dist/agents/channels/imessage/index.d.ts +25 -0
- package/dist/agents/channels/imessage/index.d.ts.map +1 -0
- package/dist/agents/channels/imessage/index.js +25 -0
- package/dist/agents/channels/imessage/index.js.map +1 -0
- package/dist/agents/channels/imessage/media.d.ts +92 -0
- package/dist/agents/channels/imessage/media.d.ts.map +1 -0
- package/dist/agents/channels/imessage/media.js +196 -0
- package/dist/agents/channels/imessage/media.js.map +1 -0
- package/dist/agents/channels/imessage/messaging.d.ts +14 -0
- package/dist/agents/channels/imessage/messaging.d.ts.map +1 -0
- package/dist/agents/channels/imessage/messaging.js +37 -0
- package/dist/agents/channels/imessage/messaging.js.map +1 -0
- package/dist/agents/channels/imessage/module.d.ts +16 -0
- package/dist/agents/channels/imessage/module.d.ts.map +1 -0
- package/dist/agents/channels/imessage/module.js +23 -0
- package/dist/agents/channels/imessage/module.js.map +1 -0
- package/dist/agents/channels/imessage/monitor.d.ts +207 -0
- package/dist/agents/channels/imessage/monitor.d.ts.map +1 -0
- package/dist/agents/channels/imessage/monitor.js +504 -0
- package/dist/agents/channels/imessage/monitor.js.map +1 -0
- package/dist/agents/channels/imessage/plugin.d.ts +53 -0
- package/dist/agents/channels/imessage/plugin.d.ts.map +1 -0
- package/dist/agents/channels/imessage/plugin.js +215 -0
- package/dist/agents/channels/imessage/plugin.js.map +1 -0
- package/dist/agents/channels/imessage/probe.d.ts +68 -0
- package/dist/agents/channels/imessage/probe.d.ts.map +1 -0
- package/dist/agents/channels/imessage/probe.js +134 -0
- package/dist/agents/channels/imessage/probe.js.map +1 -0
- package/dist/agents/channels/imessage/remote-attachments.d.ts +61 -0
- package/dist/agents/channels/imessage/remote-attachments.d.ts.map +1 -0
- package/dist/agents/channels/imessage/remote-attachments.js +131 -0
- package/dist/agents/channels/imessage/remote-attachments.js.map +1 -0
- package/dist/agents/channels/imessage/send.d.ts +61 -0
- package/dist/agents/channels/imessage/send.d.ts.map +1 -0
- package/dist/agents/channels/imessage/send.js +108 -0
- package/dist/agents/channels/imessage/send.js.map +1 -0
- package/dist/agents/channels/imessage/targets.d.ts +91 -0
- package/dist/agents/channels/imessage/targets.d.ts.map +1 -0
- package/dist/agents/channels/imessage/targets.js +245 -0
- package/dist/agents/channels/imessage/targets.js.map +1 -0
- package/dist/agents/channels/imessage/watch-error.d.ts +23 -0
- package/dist/agents/channels/imessage/watch-error.d.ts.map +1 -0
- package/dist/agents/channels/imessage/watch-error.js +69 -0
- package/dist/agents/channels/imessage/watch-error.js.map +1 -0
- package/dist/agents/channels/inbound-pipeline.d.ts +11 -0
- package/dist/agents/channels/inbound-pipeline.d.ts.map +1 -1
- package/dist/agents/channels/inbound-pipeline.js +25 -4
- package/dist/agents/channels/inbound-pipeline.js.map +1 -1
- package/dist/agents/channels/manager.d.ts +9 -0
- package/dist/agents/channels/manager.d.ts.map +1 -1
- package/dist/agents/channels/manager.js.map +1 -1
- package/dist/agents/channels/sdk.d.ts +4 -0
- package/dist/agents/channels/sdk.d.ts.map +1 -1
- package/dist/agents/channels/sdk.js +4 -0
- package/dist/agents/channels/sdk.js.map +1 -1
- package/dist/agents/extensions/modules/index.d.ts.map +1 -1
- package/dist/agents/extensions/modules/index.js +12 -0
- package/dist/agents/extensions/modules/index.js.map +1 -1
- package/dist/agents/extensions/types.d.ts +7 -0
- package/dist/agents/extensions/types.d.ts.map +1 -1
- package/dist/agents/extensions/types.js.map +1 -1
- package/dist/agents/session-wiring.d.ts +31 -0
- package/dist/agents/session-wiring.d.ts.map +1 -1
- package/dist/agents/session-wiring.js +45 -2
- package/dist/agents/session-wiring.js.map +1 -1
- package/dist/agents/subagent-announce-delivery.d.ts +10 -0
- package/dist/agents/subagent-announce-delivery.d.ts.map +1 -1
- package/dist/agents/subagent-announce-delivery.js +1 -0
- package/dist/agents/subagent-announce-delivery.js.map +1 -1
- package/dist/agents/subagent-completion-bridge.d.ts.map +1 -1
- package/dist/agents/subagent-completion-bridge.js +81 -0
- package/dist/agents/subagent-completion-bridge.js.map +1 -1
- package/dist/agents/subagent-spawn.d.ts.map +1 -1
- package/dist/agents/subagent-spawn.js +57 -4
- package/dist/agents/subagent-spawn.js.map +1 -1
- package/dist/agents/tools/bluebubbles-action-tool.d.ts +66 -0
- package/dist/agents/tools/bluebubbles-action-tool.d.ts.map +1 -0
- package/dist/agents/tools/bluebubbles-action-tool.js +234 -0
- package/dist/agents/tools/bluebubbles-action-tool.js.map +1 -0
- package/dist/agents/tools/discord-action-tool.d.ts +224 -0
- package/dist/agents/tools/discord-action-tool.d.ts.map +1 -0
- package/dist/agents/tools/discord-action-tool.js +848 -0
- package/dist/agents/tools/discord-action-tool.js.map +1 -0
- package/dist/agents/tools/registry.d.ts.map +1 -1
- package/dist/agents/tools/registry.js +39 -0
- package/dist/agents/tools/registry.js.map +1 -1
- package/dist/agents/tools/sessions/index.d.ts +8 -0
- package/dist/agents/tools/sessions/index.d.ts.map +1 -1
- package/dist/agents/tools/sessions/index.js +15 -3
- package/dist/agents/tools/sessions/index.js.map +1 -1
- package/dist/buildstamp.json +1 -1
- package/dist/cli/commands/channels.d.ts +2 -0
- package/dist/cli/commands/channels.d.ts.map +1 -1
- package/dist/cli/commands/channels.js +58 -1
- package/dist/cli/commands/channels.js.map +1 -1
- package/dist/core/server.d.ts.map +1 -1
- package/dist/core/server.js +48 -2
- package/dist/core/server.js.map +1 -1
- package/dist/infra/net/fetch-guard.d.ts +24 -2
- package/dist/infra/net/fetch-guard.d.ts.map +1 -1
- package/dist/infra/net/fetch-guard.js +78 -31
- package/dist/infra/net/fetch-guard.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BlueBubbles transport connection — webhook-in + REST-out.
|
|
3
|
+
*
|
|
4
|
+
* Unlike a polling channel there is no socket to open: inbound arrives via the
|
|
5
|
+
* gateway webhook route, which calls `feedWebhookEvent(eventType, payload)` on
|
|
6
|
+
* the started adapter (bridged through `account-registry.ts` on the multi-account
|
|
7
|
+
* path). This module owns BOTH directions:
|
|
8
|
+
*
|
|
9
|
+
* INBOUND `feedWebhookEvent` → normalize (defensive) → dedupe (claim-once) →
|
|
10
|
+
* tapback-drop → defer media download → `onMessage`.
|
|
11
|
+
* OUTBOUND `sendText` (bubble-split into N text POSTs), `sendMedia` (multipart
|
|
12
|
+
* upload + caption as a SEPARATE bubble after), plus `react`/`edit`/
|
|
13
|
+
* `unsend` for `handleAction`.
|
|
14
|
+
*
|
|
15
|
+
* `fetch` is INJECTABLE end-to-end (threaded into every REST call), so the whole
|
|
16
|
+
* connection is exercised in tests with no live server. Private-API status is
|
|
17
|
+
* cached from the last probe and gates the rich actions.
|
|
18
|
+
*/
|
|
19
|
+
import { type InboundMediaAttachment, type OutboundMedia, type OutboundSendOptions } from "../sdk.js";
|
|
20
|
+
import type { ResolvedBlueBubblesAccount } from "./account-config.js";
|
|
21
|
+
import { type NormalizedBlueBubblesMessage } from "./normalize.js";
|
|
22
|
+
import { type BlueBubblesCatchupSummary } from "./catchup.js";
|
|
23
|
+
import type { BlueBubblesCursorStore } from "./catchup-cursor.js";
|
|
24
|
+
import type { FetchLike } from "./types.js";
|
|
25
|
+
/** The inbound message handed to the adapter (carries the deferred-media thunk). */
|
|
26
|
+
export interface BlueBubblesInboundMessage extends NormalizedBlueBubblesMessage {
|
|
27
|
+
resolveMedia?: () => Promise<InboundMediaAttachment[]>;
|
|
28
|
+
}
|
|
29
|
+
/** Optional note emitted when an inbound tapback is observed (surfaced as a system line). */
|
|
30
|
+
export interface BlueBubblesTapbackNote {
|
|
31
|
+
emoji: string;
|
|
32
|
+
action: "added" | "removed";
|
|
33
|
+
chatGuid: string;
|
|
34
|
+
conversationId: string;
|
|
35
|
+
from: string;
|
|
36
|
+
isGroup: boolean;
|
|
37
|
+
targetGuid?: string;
|
|
38
|
+
}
|
|
39
|
+
export interface ConnectBlueBubblesArgs {
|
|
40
|
+
account: ResolvedBlueBubblesAccount;
|
|
41
|
+
log: (msg: string, meta?: Record<string, unknown>) => void;
|
|
42
|
+
/** Dispatched inbound (post normalize + dedupe). */
|
|
43
|
+
onMessage: (msg: BlueBubblesInboundMessage) => void;
|
|
44
|
+
/** Optional inbound tapback observation (when `actions.reactions` is on). */
|
|
45
|
+
onTapback?: (note: BlueBubblesTapbackNote) => void;
|
|
46
|
+
/** Private-API status from the last probe (gates rich actions). null = unknown. */
|
|
47
|
+
privateApi?: boolean | null;
|
|
48
|
+
/** macOS MAJOR version of the BlueBubbles host from the last probe (gates `edit` on ≥ 26). null = unknown. */
|
|
49
|
+
macOSMajor?: number | null;
|
|
50
|
+
/** TEST SEAM — inject a mock fetch for every REST call. */
|
|
51
|
+
fetchImpl?: FetchLike;
|
|
52
|
+
/** TEST SEAM — override the late-index attachment re-fetch delay (ms). Default 2000. */
|
|
53
|
+
attachmentRefetchDelayMs?: number;
|
|
54
|
+
/** TEST SEAM — inject the catch-up cursor store (default: filesystem under the channel state dir). */
|
|
55
|
+
catchupCursorStore?: BlueBubblesCursorStore;
|
|
56
|
+
}
|
|
57
|
+
/** The live connection handle the adapter drives. */
|
|
58
|
+
export interface BlueBubblesConnection {
|
|
59
|
+
feedWebhookEvent(eventType: string | undefined, payload: unknown): void;
|
|
60
|
+
sendText(conversationId: string, text: string, opts?: OutboundSendOptions): Promise<{
|
|
61
|
+
messageId?: string;
|
|
62
|
+
}>;
|
|
63
|
+
sendMedia(conversationId: string, media: OutboundMedia): Promise<{
|
|
64
|
+
messageId?: string;
|
|
65
|
+
}>;
|
|
66
|
+
react(params: {
|
|
67
|
+
conversationId: string;
|
|
68
|
+
messageId: string;
|
|
69
|
+
reaction: string;
|
|
70
|
+
}): Promise<void>;
|
|
71
|
+
edit(params: {
|
|
72
|
+
messageId: string;
|
|
73
|
+
text: string;
|
|
74
|
+
}): Promise<void>;
|
|
75
|
+
unsend(params: {
|
|
76
|
+
messageId: string;
|
|
77
|
+
}): Promise<void>;
|
|
78
|
+
/** Signal "typing…" (or stop) in a conversation. Cosmetic; no-ops when the Private API is off. */
|
|
79
|
+
setTyping(conversationId: string, typing: boolean): Promise<void>;
|
|
80
|
+
/** Mark a conversation read (read receipt). Cosmetic; no-ops when the Private API is off. */
|
|
81
|
+
markRead(conversationId: string): Promise<void>;
|
|
82
|
+
/** Run a bounded backfill of recently-missed messages through the live inbound path. */
|
|
83
|
+
runCatchup(): Promise<BlueBubblesCatchupSummary>;
|
|
84
|
+
setPrivateApi(status: boolean | null): void;
|
|
85
|
+
/** Update the probed macOS major version (gates `edit` on ≥ 26). */
|
|
86
|
+
setMacOSMajor(major: number | null): void;
|
|
87
|
+
connectedAt(): number | null;
|
|
88
|
+
close(): void;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Build a BlueBubbles connection. Synchronous — there is nothing to connect to
|
|
92
|
+
* (inbound is push); the returned handle wires inbound + outbound.
|
|
93
|
+
*/
|
|
94
|
+
export declare function connectBlueBubbles(args: ConnectBlueBubblesArgs): BlueBubblesConnection;
|
|
95
|
+
//# sourceMappingURL=connection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../../../src/agents/channels/bluebubbles/connection.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BlueBubbles transport connection — webhook-in + REST-out.
|
|
3
|
+
*
|
|
4
|
+
* Unlike a polling channel there is no socket to open: inbound arrives via the
|
|
5
|
+
* gateway webhook route, which calls `feedWebhookEvent(eventType, payload)` on
|
|
6
|
+
* the started adapter (bridged through `account-registry.ts` on the multi-account
|
|
7
|
+
* path). This module owns BOTH directions:
|
|
8
|
+
*
|
|
9
|
+
* INBOUND `feedWebhookEvent` → normalize (defensive) → dedupe (claim-once) →
|
|
10
|
+
* tapback-drop → defer media download → `onMessage`.
|
|
11
|
+
* OUTBOUND `sendText` (bubble-split into N text POSTs), `sendMedia` (multipart
|
|
12
|
+
* upload + caption as a SEPARATE bubble after), plus `react`/`edit`/
|
|
13
|
+
* `unsend` for `handleAction`.
|
|
14
|
+
*
|
|
15
|
+
* `fetch` is INJECTABLE end-to-end (threaded into every REST call), so the whole
|
|
16
|
+
* connection is exercised in tests with no live server. Private-API status is
|
|
17
|
+
* cached from the last probe and gates the rich actions.
|
|
18
|
+
*/
|
|
19
|
+
import path from "node:path";
|
|
20
|
+
import { createDedupeCache, ensureDir, resolveOsCacheDir, } from "../sdk.js";
|
|
21
|
+
import { BLUEBUBBLES_DEDUPE_MAX_ENTRIES, BLUEBUBBLES_DEDUPE_TTL_MS, resolveBlueBubblesDedupeKey } from "./dedupe.js";
|
|
22
|
+
import { isUnderAllowedRoot, resolveOutboundAttachment } from "../imessage/media.js";
|
|
23
|
+
import { markdownToIMessageText, resolveDeliveredText, sanitizeOutboundIMessageText } from "../imessage/format.js";
|
|
24
|
+
import { downloadInboundAttachments, fetchBlueBubblesMessageAttachments, resolveOutboundVoiceAttachment } from "./media.js";
|
|
25
|
+
import { normalizeBlueBubblesWebhook } from "./normalize.js";
|
|
26
|
+
import { bubbleSplit, editBlueBubblesMessage, reactBlueBubbles, resolveChatGuid, sendBlueBubblesAttachment, sendBlueBubblesText, unsendBlueBubblesMessage, } from "./send.js";
|
|
27
|
+
import { markBlueBubblesChatRead, sendBlueBubblesTyping } from "./chat.js";
|
|
28
|
+
import { peekBlueBubblesContactName, warmBlueBubblesContactDirectory } from "./contact-names.js";
|
|
29
|
+
import { runBlueBubblesCatchup } from "./catchup.js";
|
|
30
|
+
import { createBlueBubblesInboundDebouncer } from "./debounce.js";
|
|
31
|
+
import { fetchBlueBubblesHistory, renderBlueBubblesHistoryBlock } from "./history.js";
|
|
32
|
+
import { isMacOSEditUnsupported } from "./probe.js";
|
|
33
|
+
/** Delay before the single late-index attachment re-fetch (BB indexes media ~2s after the webhook). */
|
|
34
|
+
const BLUEBUBBLES_ATTACHMENT_REFETCH_DELAY_MS = 2_000;
|
|
35
|
+
/**
|
|
36
|
+
* Build a BlueBubbles connection. Synchronous — there is nothing to connect to
|
|
37
|
+
* (inbound is push); the returned handle wires inbound + outbound.
|
|
38
|
+
*/
|
|
39
|
+
export function connectBlueBubbles(args) {
|
|
40
|
+
const { account } = args;
|
|
41
|
+
let privateApi = args.privateApi ?? null;
|
|
42
|
+
let macOSMajor = args.macOSMajor ?? null;
|
|
43
|
+
const connectedAtMs = Date.now();
|
|
44
|
+
const dedupe = createDedupeCache({
|
|
45
|
+
maxEntries: BLUEBUBBLES_DEDUPE_MAX_ENTRIES,
|
|
46
|
+
ttlMs: BLUEBUBBLES_DEDUPE_TTL_MS,
|
|
47
|
+
});
|
|
48
|
+
const cacheDir = path.join(resolveOsCacheDir(), "bluebubbles", account.accountId, "inbound-media");
|
|
49
|
+
// Optional inbound coalescing: when `inboundDebounceMs > 0`, a text + its
|
|
50
|
+
// link-preview balloon (or a text + a split-out attachment) arriving as TWO
|
|
51
|
+
// `new-message` webhooks are merged into ONE turn. Default 0 = disabled =
|
|
52
|
+
// the historical synchronous dispatch path (unchanged behaviour).
|
|
53
|
+
const debounceMs = account.inboundDebounceMs ?? 0;
|
|
54
|
+
const debouncer = debounceMs > 0
|
|
55
|
+
? createBlueBubblesInboundDebouncer({
|
|
56
|
+
accountId: account.accountId,
|
|
57
|
+
debounceMs,
|
|
58
|
+
dispatch: (msg) => args.onMessage(msg),
|
|
59
|
+
...(account.verbose ? { log: (m) => args.log(m) } : {}),
|
|
60
|
+
})
|
|
61
|
+
: null;
|
|
62
|
+
/** Dispatch an inbound — through the debouncer when enabled, else straight to `onMessage`. */
|
|
63
|
+
const dispatchInbound = (msg) => {
|
|
64
|
+
if (debouncer)
|
|
65
|
+
debouncer.enqueue(msg);
|
|
66
|
+
else
|
|
67
|
+
args.onMessage(msg);
|
|
68
|
+
};
|
|
69
|
+
/** Shared args for a rolling-history fetch. */
|
|
70
|
+
const historyArgs = () => ({
|
|
71
|
+
serverUrl: account.serverUrl,
|
|
72
|
+
password: account.password,
|
|
73
|
+
...(account.probeTimeoutMs !== undefined ? { timeoutMs: account.probeTimeoutMs } : {}),
|
|
74
|
+
...(args.fetchImpl ? { fetchImpl: args.fetchImpl } : {}),
|
|
75
|
+
...(account.allowPrivateNetwork === false ? { allowPrivateNetwork: false } : {}),
|
|
76
|
+
});
|
|
77
|
+
/**
|
|
78
|
+
* Resolve the rolling-history limit that applies to THIS inbound. Group
|
|
79
|
+
* messages use `historyLimit`; DMs use `dmHistoryLimit`. History is attached
|
|
80
|
+
* only for an UNTAGGED message (no self-mention, not itself a reply) — a tagged
|
|
81
|
+
* or reply message already carries its own context. Returns 0 (skip) otherwise.
|
|
82
|
+
*/
|
|
83
|
+
const historyLimitFor = (msg) => {
|
|
84
|
+
if (msg.mentions && msg.mentions.length > 0)
|
|
85
|
+
return 0;
|
|
86
|
+
if (msg.replyToGuid)
|
|
87
|
+
return 0;
|
|
88
|
+
if (!msg.text || !msg.text.trim())
|
|
89
|
+
return 0;
|
|
90
|
+
return msg.isGroup ? account.historyLimit : account.dmHistoryLimit;
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* Dispatch an inbound, first fetching + PREPENDING rolling-history context when
|
|
94
|
+
* it applies (untagged group/DM message, limit > 0). The history fetch is
|
|
95
|
+
* bounded + never throws; on any failure the message dispatches with no context.
|
|
96
|
+
* When history doesn't apply, dispatch is synchronous (unchanged behaviour).
|
|
97
|
+
*/
|
|
98
|
+
const dispatchWithHistory = (msg) => {
|
|
99
|
+
const limit = historyLimitFor(msg);
|
|
100
|
+
if (limit <= 0) {
|
|
101
|
+
dispatchInbound(msg);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
void (async () => {
|
|
105
|
+
try {
|
|
106
|
+
const { entries } = await fetchBlueBubblesHistory(msg.chatGuid, limit, historyArgs());
|
|
107
|
+
// Drop the current message from the tail if the listing already includes it.
|
|
108
|
+
const prior = entries.filter((e) => !msg.messageGuid || e.messageId !== msg.messageGuid);
|
|
109
|
+
const block = renderBlueBubblesHistoryBlock(prior);
|
|
110
|
+
if (block) {
|
|
111
|
+
msg.text = `${block}\n\n${msg.text}`;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
/* best-effort — dispatch without context */
|
|
116
|
+
}
|
|
117
|
+
dispatchInbound(msg);
|
|
118
|
+
})();
|
|
119
|
+
};
|
|
120
|
+
const restBase = () => ({
|
|
121
|
+
serverUrl: account.serverUrl,
|
|
122
|
+
password: account.password,
|
|
123
|
+
timeoutMs: account.probeTimeoutMs,
|
|
124
|
+
...(args.fetchImpl ? { fetchImpl: args.fetchImpl } : {}),
|
|
125
|
+
privateApiEnabled: privateApi === true,
|
|
126
|
+
...(account.allowPrivateNetwork === false ? { allowPrivateNetwork: false } : {}),
|
|
127
|
+
});
|
|
128
|
+
const contactArgs = () => ({
|
|
129
|
+
serverUrl: account.serverUrl,
|
|
130
|
+
password: account.password,
|
|
131
|
+
accountId: account.accountId,
|
|
132
|
+
...(account.probeTimeoutMs !== undefined ? { timeoutMs: account.probeTimeoutMs } : {}),
|
|
133
|
+
...(args.fetchImpl ? { fetchImpl: args.fetchImpl } : {}),
|
|
134
|
+
...(account.allowPrivateNetwork === false ? { allowPrivateNetwork: false } : {}),
|
|
135
|
+
});
|
|
136
|
+
/**
|
|
137
|
+
* Resolve an inbound sender handle → a human display name, SYNCHRONOUSLY, from
|
|
138
|
+
* the already-warm per-account contact directory (no network on the hot path,
|
|
139
|
+
* so dispatch stays synchronous). On a cache miss, kick a background warm so
|
|
140
|
+
* the NEXT message from this sender resolves. Mutates `fromName` in place when
|
|
141
|
+
* a cached name is found; the raw handle flows otherwise.
|
|
142
|
+
*/
|
|
143
|
+
const enrichFromNameSync = (inbound) => {
|
|
144
|
+
if (inbound.fromName && inbound.fromName.trim())
|
|
145
|
+
return;
|
|
146
|
+
if (!inbound.from || !inbound.from.trim())
|
|
147
|
+
return;
|
|
148
|
+
const cached = peekBlueBubblesContactName(inbound.from, account.accountId);
|
|
149
|
+
if (cached) {
|
|
150
|
+
inbound.fromName = cached;
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
// Cold cache — warm it in the background (best-effort, never throws).
|
|
154
|
+
void warmBlueBubblesContactDirectory(contactArgs()).catch(() => { });
|
|
155
|
+
};
|
|
156
|
+
const downloadArgs = () => ({
|
|
157
|
+
serverUrl: account.serverUrl,
|
|
158
|
+
password: account.password,
|
|
159
|
+
cacheDir,
|
|
160
|
+
maxBytes: account.mediaMaxBytes,
|
|
161
|
+
timeoutMs: account.probeTimeoutMs,
|
|
162
|
+
...(args.fetchImpl ? { fetchImpl: args.fetchImpl } : {}),
|
|
163
|
+
...(account.allowPrivateNetwork === false ? { allowPrivateNetwork: false } : {}),
|
|
164
|
+
});
|
|
165
|
+
const refetchArgs = () => ({
|
|
166
|
+
serverUrl: account.serverUrl,
|
|
167
|
+
password: account.password,
|
|
168
|
+
...(account.probeTimeoutMs !== undefined ? { timeoutMs: account.probeTimeoutMs } : {}),
|
|
169
|
+
...(args.fetchImpl ? { fetchImpl: args.fetchImpl } : {}),
|
|
170
|
+
...(account.allowPrivateNetwork === false ? { allowPrivateNetwork: false } : {}),
|
|
171
|
+
});
|
|
172
|
+
/** Wait the late-index delay (skippable in tests). */
|
|
173
|
+
const refetchDelay = async () => {
|
|
174
|
+
const delayMs = args.attachmentRefetchDelayMs ?? BLUEBUBBLES_ATTACHMENT_REFETCH_DELAY_MS;
|
|
175
|
+
if (delayMs <= 0)
|
|
176
|
+
return;
|
|
177
|
+
await new Promise((resolve) => {
|
|
178
|
+
const t = setTimeout(resolve, delayMs);
|
|
179
|
+
if (typeof t.unref === "function")
|
|
180
|
+
t.unref();
|
|
181
|
+
});
|
|
182
|
+
};
|
|
183
|
+
const feedWebhookEvent = (eventType, payload) => {
|
|
184
|
+
const allowed = new Set(["new-message", "updated-message", undefined]);
|
|
185
|
+
if (eventType && !allowed.has(eventType))
|
|
186
|
+
return;
|
|
187
|
+
const result = normalizeBlueBubblesWebhook(payload, eventType, account.selfHandle);
|
|
188
|
+
if (result.kind === "skip") {
|
|
189
|
+
// An "empty message" with a guid MIGHT be a media message whose
|
|
190
|
+
// attachments indexed late. Kick a SINGLE bounded background re-fetch; if
|
|
191
|
+
// media turns up, dispatch a synthetic inbound (deferred-media thunk).
|
|
192
|
+
// A genuinely empty message finds nothing and is never dispatched (no
|
|
193
|
+
// agent spam). Dedupe still guards against a follow-up `updated-message`.
|
|
194
|
+
if (result.mediaCandidate) {
|
|
195
|
+
const cand = result.mediaCandidate;
|
|
196
|
+
const key = resolveBlueBubblesDedupeKey(account.accountId, payload, eventType);
|
|
197
|
+
if (key && !dedupe.claim(key)) {
|
|
198
|
+
if (account.verbose)
|
|
199
|
+
args.log("inbound dropped: duplicate");
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
void (async () => {
|
|
203
|
+
try {
|
|
204
|
+
await refetchDelay();
|
|
205
|
+
const refetched = await fetchBlueBubblesMessageAttachments(cand.messageGuid, refetchArgs());
|
|
206
|
+
if (refetched.length === 0)
|
|
207
|
+
return;
|
|
208
|
+
if (account.verbose)
|
|
209
|
+
args.log(`attachment late-index re-fetch found ${refetched.length} for ${cand.messageGuid}`);
|
|
210
|
+
const inbound = {
|
|
211
|
+
conversationId: `chat_guid:${cand.chatGuid}`,
|
|
212
|
+
chatGuid: cand.chatGuid,
|
|
213
|
+
messageGuid: cand.messageGuid,
|
|
214
|
+
from: cand.from,
|
|
215
|
+
...(cand.fromName ? { fromName: cand.fromName } : {}),
|
|
216
|
+
text: "",
|
|
217
|
+
isGroup: cand.isGroup,
|
|
218
|
+
...(cand.timestampMs !== undefined ? { timestampMs: cand.timestampMs } : {}),
|
|
219
|
+
attachments: refetched,
|
|
220
|
+
raw: payload,
|
|
221
|
+
resolveMedia: async () => downloadInboundAttachments(refetched, downloadArgs()),
|
|
222
|
+
};
|
|
223
|
+
enrichFromNameSync(inbound);
|
|
224
|
+
dispatchInbound(inbound);
|
|
225
|
+
}
|
|
226
|
+
catch (err) {
|
|
227
|
+
if (account.verbose)
|
|
228
|
+
args.log(`attachment re-fetch failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
229
|
+
}
|
|
230
|
+
})();
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
if (account.verbose)
|
|
234
|
+
args.log(`inbound dropped: ${result.reason}`);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
if (result.kind === "tapback") {
|
|
238
|
+
// Tapbacks are dropped as normal messages; optionally surface a note.
|
|
239
|
+
if (account.actions.reactions && args.onTapback) {
|
|
240
|
+
args.onTapback({
|
|
241
|
+
emoji: result.tapback.emoji,
|
|
242
|
+
action: result.tapback.action,
|
|
243
|
+
chatGuid: result.chatGuid,
|
|
244
|
+
conversationId: `chat_guid:${result.chatGuid}`,
|
|
245
|
+
from: result.from,
|
|
246
|
+
isGroup: result.isGroup,
|
|
247
|
+
...(result.targetGuid ? { targetGuid: result.targetGuid } : {}),
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
// Dedupe (claim-once) — BlueBubbles replays its lookback window on restart.
|
|
253
|
+
const key = resolveBlueBubblesDedupeKey(account.accountId, payload, eventType);
|
|
254
|
+
if (key && !dedupe.claim(key)) {
|
|
255
|
+
if (account.verbose)
|
|
256
|
+
args.log("inbound dropped: duplicate");
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
const message = result.message;
|
|
260
|
+
const inbound = { ...message };
|
|
261
|
+
if (message.attachments.length > 0) {
|
|
262
|
+
inbound.resolveMedia = async () => downloadInboundAttachments(message.attachments, downloadArgs());
|
|
263
|
+
}
|
|
264
|
+
else if (message.messageGuid && eventType === "updated-message") {
|
|
265
|
+
// An `updated-message` follow-up with text but no attachments may carry
|
|
266
|
+
// media that indexed late. Defer a SINGLE bounded re-fetch (short delay →
|
|
267
|
+
// `message/{guid}` → download); resolves [] when there was truly none.
|
|
268
|
+
inbound.resolveMedia = async () => {
|
|
269
|
+
await refetchDelay();
|
|
270
|
+
const refetched = await fetchBlueBubblesMessageAttachments(message.messageGuid, refetchArgs());
|
|
271
|
+
if (refetched.length === 0)
|
|
272
|
+
return [];
|
|
273
|
+
if (account.verbose)
|
|
274
|
+
args.log(`attachment late-index re-fetch found ${refetched.length} for ${message.messageGuid}`);
|
|
275
|
+
return downloadInboundAttachments(refetched, downloadArgs());
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
// Resolve a human display name for the sender from the warm cache (sync) and
|
|
279
|
+
// dispatch — a cold cache warms in the background for next time. An untagged
|
|
280
|
+
// group/DM message first picks up rolling-history context (when configured).
|
|
281
|
+
enrichFromNameSync(inbound);
|
|
282
|
+
dispatchWithHistory(inbound);
|
|
283
|
+
};
|
|
284
|
+
const sendText = async (conversationId, text, opts) => {
|
|
285
|
+
const base = restBase();
|
|
286
|
+
const chatGuid = await resolveChatGuid(base, conversationId);
|
|
287
|
+
// Strip internal directive tags / role scaffolding / reasoning residue
|
|
288
|
+
// (the outbound twin of the reflection guard) BEFORE bubble-splitting.
|
|
289
|
+
const bubbles = bubbleSplit(sanitizeOutboundIMessageText(markdownToIMessageText(text)));
|
|
290
|
+
if (bubbles.length === 0)
|
|
291
|
+
return {};
|
|
292
|
+
let lastMessageId;
|
|
293
|
+
let first = true;
|
|
294
|
+
for (const bubble of bubbles) {
|
|
295
|
+
const sent = await sendBlueBubblesText(chatGuid, bubble, {
|
|
296
|
+
...base,
|
|
297
|
+
// Native reply target applies to the FIRST bubble only.
|
|
298
|
+
...(first && opts?.replyToId ? { replyToMessageGuid: opts.replyToId } : {}),
|
|
299
|
+
});
|
|
300
|
+
if (sent.messageId)
|
|
301
|
+
lastMessageId = sent.messageId;
|
|
302
|
+
first = false;
|
|
303
|
+
}
|
|
304
|
+
return lastMessageId ? { messageId: lastMessageId } : {};
|
|
305
|
+
};
|
|
306
|
+
const sendMedia = async (conversationId, media) => {
|
|
307
|
+
const base = restBase();
|
|
308
|
+
const chatGuid = await resolveChatGuid(base, conversationId);
|
|
309
|
+
// Validate + size-cap the local path (central exfil/denylist guard).
|
|
310
|
+
const resolved = resolveOutboundAttachment(media.path, account.mediaMaxBytes);
|
|
311
|
+
// Layer the per-account local-root ALLOW-LIST on top of the central denylist:
|
|
312
|
+
// when configured, an attachment must resolve UNDER one of the allowed roots.
|
|
313
|
+
if (account.mediaLocalRoots.length > 0 && !isUnderAllowedRoot(resolved.path, account.mediaLocalRoots)) {
|
|
314
|
+
throw new Error(`BlueBubbles refused to attach "${media.path}": path is outside the allowed media roots (channels.bluebubbles.mediaLocalRoots).`);
|
|
315
|
+
}
|
|
316
|
+
const asVoice = media.kind === "voice";
|
|
317
|
+
// Voice pre-flight: a voice memo must be mp3/caf — validate + coerce the
|
|
318
|
+
// extension / default the mime, failing fast with a clear error otherwise.
|
|
319
|
+
const voice = asVoice ? resolveOutboundVoiceAttachment(resolved.path, resolved.mimeType) : undefined;
|
|
320
|
+
const contentType = voice?.contentType ?? resolved.mimeType;
|
|
321
|
+
const sent = await sendBlueBubblesAttachment(chatGuid, {
|
|
322
|
+
...base,
|
|
323
|
+
filePath: resolved.path,
|
|
324
|
+
...(voice?.filename ? { fileName: voice.filename } : {}),
|
|
325
|
+
...(contentType ? { contentType } : {}),
|
|
326
|
+
...(asVoice ? { asVoice: true } : {}),
|
|
327
|
+
});
|
|
328
|
+
// iMessage has NO native caption — deliver any caption as a SEPARATE text
|
|
329
|
+
// bubble AFTER the media.
|
|
330
|
+
const caption = resolveDeliveredText(media.caption ?? "", media.kind);
|
|
331
|
+
if (caption && caption.trim() && !caption.startsWith("<media:")) {
|
|
332
|
+
await sendBlueBubblesText(chatGuid, sanitizeOutboundIMessageText(caption), base);
|
|
333
|
+
}
|
|
334
|
+
return sent.messageId ? { messageId: sent.messageId } : {};
|
|
335
|
+
};
|
|
336
|
+
const react = async (params) => {
|
|
337
|
+
const base = restBase();
|
|
338
|
+
const chatGuid = await resolveChatGuid(base, params.conversationId);
|
|
339
|
+
await reactBlueBubbles(base, { chatGuid, messageGuid: params.messageId, reaction: params.reaction });
|
|
340
|
+
};
|
|
341
|
+
const edit = async (params) => {
|
|
342
|
+
// macOS 26+ removed iMessage message EDIT — refuse cleanly rather than firing
|
|
343
|
+
// a request the server will reject.
|
|
344
|
+
if (isMacOSEditUnsupported(macOSMajor)) {
|
|
345
|
+
throw new Error("message edit isn't supported on macOS 26+");
|
|
346
|
+
}
|
|
347
|
+
await editBlueBubblesMessage(restBase(), { messageGuid: params.messageId, editedMessage: params.text });
|
|
348
|
+
};
|
|
349
|
+
const unsend = async (params) => {
|
|
350
|
+
await unsendBlueBubblesMessage(restBase(), { messageGuid: params.messageId });
|
|
351
|
+
};
|
|
352
|
+
const setTyping = async (conversationId, typing) => {
|
|
353
|
+
const base = restBase();
|
|
354
|
+
const chatGuid = await resolveChatGuid(base, conversationId);
|
|
355
|
+
await sendBlueBubblesTyping(base, { chatGuid, typing });
|
|
356
|
+
};
|
|
357
|
+
const markRead = async (conversationId) => {
|
|
358
|
+
const base = restBase();
|
|
359
|
+
const chatGuid = await resolveChatGuid(base, conversationId);
|
|
360
|
+
await markBlueBubblesChatRead(base, { chatGuid });
|
|
361
|
+
};
|
|
362
|
+
const runCatchup = async () => {
|
|
363
|
+
const catchupConfig = account.catchup;
|
|
364
|
+
return runBlueBubblesCatchup({
|
|
365
|
+
serverUrl: account.serverUrl,
|
|
366
|
+
password: account.password,
|
|
367
|
+
// Enable the PERSISTED cursor (lastSeenMs + give-up retries) per account.
|
|
368
|
+
accountId: account.accountId,
|
|
369
|
+
...(args.catchupCursorStore ? { cursorStore: args.catchupCursorStore } : {}),
|
|
370
|
+
...(catchupConfig ? { config: catchupConfig } : {}),
|
|
371
|
+
...(account.probeTimeoutMs !== undefined ? { timeoutMs: account.probeTimeoutMs } : {}),
|
|
372
|
+
...(args.fetchImpl ? { fetchImpl: args.fetchImpl } : {}),
|
|
373
|
+
...(account.allowPrivateNetwork === false ? { allowPrivateNetwork: false } : {}),
|
|
374
|
+
// Re-feed each fetched record through the SAME normalize+dedupe path a
|
|
375
|
+
// live webhook uses (as a `new-message` event), so dedupe drops anything
|
|
376
|
+
// already delivered — no double-delivery.
|
|
377
|
+
feedRecord: (record) => feedWebhookEvent("new-message", { type: "new-message", data: record }),
|
|
378
|
+
log: (msg) => args.log(msg),
|
|
379
|
+
});
|
|
380
|
+
};
|
|
381
|
+
// Ensure the cache dir exists lazily (best-effort; download also mkdir's).
|
|
382
|
+
try {
|
|
383
|
+
ensureDir(cacheDir);
|
|
384
|
+
}
|
|
385
|
+
catch {
|
|
386
|
+
/* best-effort */
|
|
387
|
+
}
|
|
388
|
+
return {
|
|
389
|
+
feedWebhookEvent,
|
|
390
|
+
sendText,
|
|
391
|
+
sendMedia,
|
|
392
|
+
react,
|
|
393
|
+
edit,
|
|
394
|
+
unsend,
|
|
395
|
+
setTyping,
|
|
396
|
+
markRead,
|
|
397
|
+
runCatchup,
|
|
398
|
+
setPrivateApi: (status) => {
|
|
399
|
+
privateApi = status;
|
|
400
|
+
},
|
|
401
|
+
setMacOSMajor: (major) => {
|
|
402
|
+
macOSMajor = major;
|
|
403
|
+
},
|
|
404
|
+
connectedAt: () => connectedAtMs,
|
|
405
|
+
close: () => {
|
|
406
|
+
// Flush any buffered (debounced) inbound so a pending coalesced turn isn't
|
|
407
|
+
// silently dropped on teardown, then clear timers.
|
|
408
|
+
debouncer?.flushAll();
|
|
409
|
+
debouncer?.clear();
|
|
410
|
+
},
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
//# sourceMappingURL=connection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection.js","sourceRoot":"","sources":["../../../../src/agents/channels/bluebubbles/connection.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BlueBubbles contact-name resolution.
|
|
3
|
+
*
|
|
4
|
+
* An inbound BlueBubbles message carries the sender as a raw handle — a phone
|
|
5
|
+
* number (`+15551234567`) or an email. For the agent's context it reads far
|
|
6
|
+
* better as a human display name ("Alex Rivera"). The BlueBubbles server exposes
|
|
7
|
+
* the host Mac's Contacts via `GET /api/v1/contact`, so this module asks the
|
|
8
|
+
* server for the address book ONCE per account, indexes it by a normalised phone
|
|
9
|
+
* key + lowercased email, and resolves a sender address → display name from that
|
|
10
|
+
* cache.
|
|
11
|
+
*
|
|
12
|
+
* The directory is cached per account in a small LRU with a TTL (like Discord's
|
|
13
|
+
* directory cache): the first inbound for an account warms it, subsequent
|
|
14
|
+
* inbound hits the in-memory index. A NEGATIVE result (no match) is cached for a
|
|
15
|
+
* shorter TTL so an unknown number isn't re-queried on every message. `fetch` is
|
|
16
|
+
* INJECTABLE (the test seam) so the whole path runs with no live server.
|
|
17
|
+
*/
|
|
18
|
+
import { type FetchLike } from "./types.js";
|
|
19
|
+
/** One BlueBubbles contact record (server `GET /api/v1/contact` shape, defensive). */
|
|
20
|
+
interface RawBlueBubblesContact {
|
|
21
|
+
displayName?: string;
|
|
22
|
+
firstName?: string;
|
|
23
|
+
lastName?: string;
|
|
24
|
+
nickname?: string;
|
|
25
|
+
phoneNumbers?: Array<{
|
|
26
|
+
address?: string;
|
|
27
|
+
} | string>;
|
|
28
|
+
emails?: Array<{
|
|
29
|
+
address?: string;
|
|
30
|
+
} | string>;
|
|
31
|
+
[key: string]: unknown;
|
|
32
|
+
}
|
|
33
|
+
/** Args every resolution call threads (the REST base + the account scope). */
|
|
34
|
+
export interface ResolveContactNameArgs {
|
|
35
|
+
serverUrl: string;
|
|
36
|
+
password: string;
|
|
37
|
+
accountId: string;
|
|
38
|
+
timeoutMs?: number;
|
|
39
|
+
/** TEST SEAM — inject a mock fetch. */
|
|
40
|
+
fetchImpl?: FetchLike;
|
|
41
|
+
/** Allow private/LAN/loopback hosts through the SSRF guard (default TRUE for BlueBubbles). */
|
|
42
|
+
allowPrivateNetwork?: boolean;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Normalise a phone-ish string to a comparison key: digits only, dropping a
|
|
46
|
+
* leading US country code so `+1 (555) 123-4567` and `5551234567` collide.
|
|
47
|
+
* Returns null for a too-short / non-numeric value (e.g. an email).
|
|
48
|
+
*/
|
|
49
|
+
export declare function normalizePhoneKey(value: string): string | null;
|
|
50
|
+
/** Build the lookup key for a sender address (email lowercased, phone digit-normalised). */
|
|
51
|
+
export declare function contactLookupKey(address: string): string | null;
|
|
52
|
+
/** Build the phone/email → name index from the raw contact list. */
|
|
53
|
+
export declare function buildContactIndex(contacts: RawBlueBubblesContact[]): Map<string, string>;
|
|
54
|
+
/**
|
|
55
|
+
* Resolve an inbound sender address → a human display name, or `undefined` when
|
|
56
|
+
* unknown. Warms (and caches) the account's contact directory on first use;
|
|
57
|
+
* subsequent calls hit the in-memory index. Negative results are cached briefly
|
|
58
|
+
* so an unknown number isn't re-queried every message. NEVER throws — a transport
|
|
59
|
+
* failure just yields `undefined` (the message still flows with the raw handle).
|
|
60
|
+
*/
|
|
61
|
+
export declare function resolveBlueBubblesContactName(address: string, args: ResolveContactNameArgs, now?: number): Promise<string | undefined>;
|
|
62
|
+
/**
|
|
63
|
+
* SYNCHRONOUS cache-only peek: resolve an address from an ALREADY-WARM account
|
|
64
|
+
* directory, or `undefined` when the directory isn't cached yet / it's a miss.
|
|
65
|
+
* Never fetches — use this on the hot inbound path so dispatch stays synchronous;
|
|
66
|
+
* pair it with `warmBlueBubblesContactDirectory` to populate the cache in the
|
|
67
|
+
* background.
|
|
68
|
+
*/
|
|
69
|
+
export declare function peekBlueBubblesContactName(address: string, accountId: string, now?: number): string | undefined;
|
|
70
|
+
/**
|
|
71
|
+
* Warm the account's contact directory cache in the background (best-effort).
|
|
72
|
+
* Fetches + indexes ONLY when the cache is cold/expired; a warm cache is a
|
|
73
|
+
* no-op. Never throws. Returns a promise the caller can ignore.
|
|
74
|
+
*/
|
|
75
|
+
export declare function warmBlueBubblesContactDirectory(args: ResolveContactNameArgs, now?: number): Promise<void>;
|
|
76
|
+
/** Drop every cached account directory (test isolation + reload teardown). */
|
|
77
|
+
export declare function clearBlueBubblesContactCache(): void;
|
|
78
|
+
export {};
|
|
79
|
+
//# sourceMappingURL=contact-names.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contact-names.d.ts","sourceRoot":"","sources":["../../../../src/agents/channels/bluebubbles/contact-names.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAuD,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AASjG,sFAAsF;AACtF,UAAU,qBAAqB;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,KAAK,CAAC;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,CAAC,CAAC;IACpD,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,CAAC,CAAC;IAC9C,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACvB;AAaD,8EAA8E;AAC9E,MAAM,WAAW,sBAAsB;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uCAAuC;IACvC,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,8FAA8F;IAC9F,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAK9D;AAED,4FAA4F;AAC5F,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAK/D;AAqBD,oEAAoE;AACpE,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,qBAAqB,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAkBxF;AAwCD;;;;;;GAMG;AACH,wBAAsB,6BAA6B,CAClD,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,sBAAsB,EAC5B,GAAG,GAAE,MAAmB,GACtB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAoB7B;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,GAAE,MAAmB,GAAG,MAAM,GAAG,SAAS,CAM3H;AAED;;;;GAIG;AACH,wBAAsB,+BAA+B,CAAC,IAAI,EAAE,sBAAsB,EAAE,GAAG,GAAE,MAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAK3H;AAED,8EAA8E;AAC9E,wBAAgB,4BAA4B,IAAI,IAAI,CAEnD"}
|