diff --git a/cmd/agent/main.go b/cmd/agent/main.go index 57ae4437..ec28dfca 100644 --- a/cmd/agent/main.go +++ b/cmd/agent/main.go @@ -51,6 +51,7 @@ import ( mcpmemory "github.com/memohai/memoh/internal/mcp/providers/memory" mcpmessage "github.com/memohai/memoh/internal/mcp/providers/message" mcpschedule "github.com/memohai/memoh/internal/mcp/providers/schedule" + mcpemail "github.com/memohai/memoh/internal/mcp/providers/email" mcpweb "github.com/memohai/memoh/internal/mcp/providers/web" mcpfederation "github.com/memohai/memoh/internal/mcp/sources/federation" "github.com/memohai/memoh/internal/media" @@ -63,6 +64,9 @@ import ( "github.com/memohai/memoh/internal/providers" "github.com/memohai/memoh/internal/heartbeat" "github.com/memohai/memoh/internal/schedule" + emailpkg "github.com/memohai/memoh/internal/email" + emailgeneric "github.com/memohai/memoh/internal/email/adapters/generic" + emailmailgun "github.com/memohai/memoh/internal/email/adapters/mailgun" "github.com/memohai/memoh/internal/searchproviders" "github.com/memohai/memoh/internal/server" "github.com/memohai/memoh/internal/settings" @@ -164,6 +168,14 @@ func runServe() { event.NewHub, inbox.NewService, + // email infrastructure + provideEmailRegistry, + emailpkg.NewService, + emailpkg.NewOutboxService, + provideEmailChatGateway, + provideEmailTrigger, + emailpkg.NewManager, + // services requiring provide functions provideRouteService, provideMessageService, @@ -207,6 +219,10 @@ func runServe() { provideServerHandler(handlers.NewChannelHandler), provideServerHandler(feishu.NewWebhookServerHandler), provideServerHandler(provideUsersHandler), + provideServerHandler(handlers.NewEmailProvidersHandler), + provideServerHandler(handlers.NewEmailBindingsHandler), + provideServerHandler(handlers.NewEmailOutboxHandler), + provideServerHandler(handlers.NewEmailWebhookHandler), provideServerHandler(handlers.NewMCPHandler), provideServerHandler(handlers.NewInboxHandler), provideServerHandler(provideCLIHandler), @@ -219,6 +235,7 @@ func runServe() { startScheduleService, startHeartbeatService, startChannelManager, + startEmailManager, startContainerReconciliation, startServer, ), @@ -460,7 +477,7 @@ func provideContainerdHandler(log *slog.Logger, service ctr.Service, manager *mc return handlers.NewContainerdHandler(log, service, manager, cfg.MCP, cfg.Containerd.Namespace, rc.ContainerBackend, botService, accountService, policyService, queries) } -func provideToolGatewayService(log *slog.Logger, cfg config.Config, channelManager *channel.Manager, registry *channel.Registry, routeService *route.DBService, scheduleService *schedule.Service, memoryService *memory.Service, chatService *conversation.Service, accountService *accounts.Service, settingsService *settings.Service, searchProviderService *searchproviders.Service, manager *mcp.Manager, containerdHandler *handlers.ContainerdHandler, mcpConnService *mcp.ConnectionService, mediaService *media.Service, inboxService *inbox.Service) *mcp.ToolGatewayService { +func provideToolGatewayService(log *slog.Logger, cfg config.Config, channelManager *channel.Manager, registry *channel.Registry, routeService *route.DBService, scheduleService *schedule.Service, memoryService *memory.Service, chatService *conversation.Service, accountService *accounts.Service, settingsService *settings.Service, searchProviderService *searchproviders.Service, manager *mcp.Manager, containerdHandler *handlers.ContainerdHandler, mcpConnService *mcp.ConnectionService, mediaService *media.Service, inboxService *inbox.Service, emailService *emailpkg.Service, emailManager *emailpkg.Manager) *mcp.ToolGatewayService { var assetResolver mcpmessage.AssetResolver if mediaService != nil { assetResolver = &mediaAssetResolverAdapter{media: mediaService} @@ -476,9 +493,11 @@ func provideToolGatewayService(log *slog.Logger, cfg config.Config, channelManag fedGateway := handlers.NewMCPFederationGateway(log, containerdHandler) fedSource := mcpfederation.NewSource(log, fedGateway, mcpConnService) + emailExec := mcpemail.NewExecutor(log, emailService, emailManager) + svc := mcp.NewToolGatewayService( log, - []mcp.ToolExecutor{messageExec, contactsExec, scheduleExec, memoryExec, webExec, fsExec, inboxExec}, + []mcp.ToolExecutor{messageExec, contactsExec, scheduleExec, memoryExec, webExec, fsExec, inboxExec, emailExec}, []mcp.ToolSource{fedSource}, ) containerdHandler.SetToolGatewayService(svc) @@ -532,6 +551,43 @@ func provideWebHandler(channelManager *channel.Manager, channelStore *channel.St return handlers.NewLocalChannelHandler(local.WebType, channelManager, channelStore, chatService, hub, botService, accountService) } +// --------------------------------------------------------------------------- +// email providers +// --------------------------------------------------------------------------- + +func provideEmailRegistry(log *slog.Logger) *emailpkg.Registry { + reg := emailpkg.NewRegistry() + reg.Register(emailgeneric.New(log)) + reg.Register(emailmailgun.New(log)) + return reg +} + +func provideEmailChatGateway(resolver *flow.Resolver, queries *dbsqlc.Queries, cfg config.Config, log *slog.Logger) emailpkg.ChatTriggerer { + return flow.NewEmailChatGateway(resolver, queries, cfg.Auth.JWTSecret, log) +} +func provideEmailTrigger(log *slog.Logger, service *emailpkg.Service, botInbox *inbox.Service, chatTriggerer emailpkg.ChatTriggerer) *emailpkg.Trigger { + return emailpkg.NewTrigger(log, service, botInbox, chatTriggerer) +} + +func startEmailManager(lc fx.Lifecycle, emailManager *emailpkg.Manager) { + ctx, cancel := context.WithCancel(context.Background()) + lc.Append(fx.Hook{ + OnStart: func(_ context.Context) error { + go func() { + if err := emailManager.Start(ctx); err != nil { + slog.Default().Error("email manager start failed", slog.Any("error", err)) + } + }() + return nil + }, + OnStop: func(_ context.Context) error { + cancel() + emailManager.Stop() + return nil + }, + }) +} + // --------------------------------------------------------------------------- // server // --------------------------------------------------------------------------- diff --git a/cmd/memoh/serve.go b/cmd/memoh/serve.go index 65dff89f..1b3d58c0 100644 --- a/cmd/memoh/serve.go +++ b/cmd/memoh/serve.go @@ -53,6 +53,7 @@ import ( mcpmemory "github.com/memohai/memoh/internal/mcp/providers/memory" mcpmessage "github.com/memohai/memoh/internal/mcp/providers/message" mcpschedule "github.com/memohai/memoh/internal/mcp/providers/schedule" + mcpemail "github.com/memohai/memoh/internal/mcp/providers/email" mcpweb "github.com/memohai/memoh/internal/mcp/providers/web" mcpfederation "github.com/memohai/memoh/internal/mcp/sources/federation" "github.com/memohai/memoh/internal/media" @@ -64,6 +65,9 @@ import ( "github.com/memohai/memoh/internal/preauth" "github.com/memohai/memoh/internal/providers" "github.com/memohai/memoh/internal/schedule" + emailpkg "github.com/memohai/memoh/internal/email" + emailgeneric "github.com/memohai/memoh/internal/email/adapters/generic" + emailmailgun "github.com/memohai/memoh/internal/email/adapters/mailgun" "github.com/memohai/memoh/internal/searchproviders" "github.com/memohai/memoh/internal/server" "github.com/memohai/memoh/internal/settings" @@ -105,6 +109,12 @@ func runServe() { bind.NewService, event.NewHub, inbox.NewService, + provideEmailRegistry, + emailpkg.NewService, + emailpkg.NewOutboxService, + provideEmailChatGateway, + provideEmailTrigger, + emailpkg.NewManager, provideRouteService, provideMessageService, provideMediaService, @@ -136,6 +146,10 @@ func runServe() { provideServerHandler(handlers.NewChannelHandler), provideServerHandler(feishu.NewWebhookServerHandler), provideServerHandler(provideUsersHandler), + provideServerHandler(handlers.NewEmailProvidersHandler), + provideServerHandler(handlers.NewEmailBindingsHandler), + provideServerHandler(handlers.NewEmailOutboxHandler), + provideServerHandler(handlers.NewEmailWebhookHandler), provideServerHandler(handlers.NewMCPHandler), provideServerHandler(handlers.NewInboxHandler), provideServerHandler(provideCLIHandler), @@ -147,6 +161,7 @@ func runServe() { startMemoryWarmup, startScheduleService, startChannelManager, + startEmailManager, startContainerReconciliation, startAgentRuntime, startServer, @@ -302,7 +317,7 @@ func provideChannelLifecycleService(channelStore *channel.Store, channelManager func provideContainerdHandler(log *slog.Logger, service ctr.Service, manager *mcp.Manager, cfg config.Config, rc *boot.RuntimeConfig, botService *bots.Service, accountService *accounts.Service, policyService *policy.Service, queries *dbsqlc.Queries) *handlers.ContainerdHandler { return handlers.NewContainerdHandler(log, service, manager, cfg.MCP, cfg.Containerd.Namespace, rc.ContainerBackend, botService, accountService, policyService, queries) } -func provideToolGatewayService(log *slog.Logger, cfg config.Config, channelManager *channel.Manager, registry *channel.Registry, routeService *route.DBService, scheduleService *schedule.Service, memoryService *memory.Service, chatService *conversation.Service, accountService *accounts.Service, settingsService *settings.Service, searchProviderService *searchproviders.Service, manager *mcp.Manager, containerdHandler *handlers.ContainerdHandler, mcpConnService *mcp.ConnectionService, mediaService *media.Service, inboxService *inbox.Service) *mcp.ToolGatewayService { +func provideToolGatewayService(log *slog.Logger, cfg config.Config, channelManager *channel.Manager, registry *channel.Registry, routeService *route.DBService, scheduleService *schedule.Service, memoryService *memory.Service, chatService *conversation.Service, accountService *accounts.Service, settingsService *settings.Service, searchProviderService *searchproviders.Service, manager *mcp.Manager, containerdHandler *handlers.ContainerdHandler, mcpConnService *mcp.ConnectionService, mediaService *media.Service, inboxService *inbox.Service, emailService *emailpkg.Service, emailManager *emailpkg.Manager) *mcp.ToolGatewayService { var assetResolver mcpmessage.AssetResolver if mediaService != nil { assetResolver = &mediaAssetResolverAdapter{media: mediaService} @@ -316,7 +331,8 @@ func provideToolGatewayService(log *slog.Logger, cfg config.Config, channelManag fsExec := mcpcontainer.NewExecutor(log, manager, config.DefaultDataMount) fedGateway := handlers.NewMCPFederationGateway(log, containerdHandler) fedSource := mcpfederation.NewSource(log, fedGateway, mcpConnService) - svc := mcp.NewToolGatewayService(log, []mcp.ToolExecutor{messageExec, contactsExec, scheduleExec, memoryExec, webExec, fsExec, inboxExec}, []mcp.ToolSource{fedSource}) + emailExec := mcpemail.NewExecutor(log, emailService, emailManager) + svc := mcp.NewToolGatewayService(log, []mcp.ToolExecutor{messageExec, contactsExec, scheduleExec, memoryExec, webExec, fsExec, inboxExec, emailExec}, []mcp.ToolSource{fedSource}) containerdHandler.SetToolGatewayService(svc) return svc } @@ -396,6 +412,7 @@ var ( "/assets/", "/api/docs", "/channels/feishu/webhook/", + "/email/mailgun/webhook/", } memohSPABackendPrefixes = []string{ "/api", @@ -408,6 +425,8 @@ var ( "/models", "/providers", "/search_providers", + "/email-providers", + "/email", "/settings", "/memory", "/message", @@ -584,6 +603,32 @@ func hasAnyPrefix(path string, prefixes []string) bool { } return false } +func provideEmailRegistry(log *slog.Logger) *emailpkg.Registry { + reg := emailpkg.NewRegistry() + reg.Register(emailgeneric.New(log)) + reg.Register(emailmailgun.New(log)) + return reg +} +func provideEmailChatGateway(resolver *flow.Resolver, queries *dbsqlc.Queries, cfg config.Config, log *slog.Logger) emailpkg.ChatTriggerer { + return flow.NewEmailChatGateway(resolver, queries, cfg.Auth.JWTSecret, log) +} +func provideEmailTrigger(log *slog.Logger, service *emailpkg.Service, botInbox *inbox.Service, chatTriggerer emailpkg.ChatTriggerer) *emailpkg.Trigger { + return emailpkg.NewTrigger(log, service, botInbox, chatTriggerer) +} +func startEmailManager(lc fx.Lifecycle, emailManager *emailpkg.Manager) { + ctx, cancel := context.WithCancel(context.Background()) + lc.Append(fx.Hook{ + OnStart: func(_ context.Context) error { + go func() { + if err := emailManager.Start(ctx); err != nil { + slog.Default().Error("email manager start failed", slog.Any("error", err)) + } + }() + return nil + }, + OnStop: func(_ context.Context) error { cancel(); emailManager.Stop(); return nil }, + }) +} func buildTextEmbedder(resolver *embeddings.Resolver, textModel models.GetResponse, hasModels bool, log *slog.Logger) embeddings.Embedder { if !hasModels { return nil diff --git a/db/migrations/0001_init.up.sql b/db/migrations/0001_init.up.sql index d28992b1..435137c2 100644 --- a/db/migrations/0001_init.up.sql +++ b/db/migrations/0001_init.up.sql @@ -428,3 +428,53 @@ CREATE TABLE IF NOT EXISTS bot_heartbeat_logs ( ); CREATE INDEX IF NOT EXISTS idx_heartbeat_logs_bot_started ON bot_heartbeat_logs(bot_id, started_at DESC); + +-- email_providers: pluggable email service backends +CREATE TABLE IF NOT EXISTS email_providers ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + name TEXT NOT NULL, + provider TEXT NOT NULL, + config JSONB NOT NULL DEFAULT '{}'::jsonb, + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), + CONSTRAINT email_providers_name_unique UNIQUE (name) +); + +-- bot_email_bindings: per-bot email provider binding with read/write/delete permissions +CREATE TABLE IF NOT EXISTS bot_email_bindings ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + bot_id UUID NOT NULL REFERENCES bots(id) ON DELETE CASCADE, + email_provider_id UUID NOT NULL REFERENCES email_providers(id) ON DELETE CASCADE, + email_address TEXT NOT NULL, + can_read BOOLEAN NOT NULL DEFAULT TRUE, + can_write BOOLEAN NOT NULL DEFAULT TRUE, + can_delete BOOLEAN NOT NULL DEFAULT FALSE, + config JSONB NOT NULL DEFAULT '{}'::jsonb, + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), + CONSTRAINT bot_email_bindings_unique UNIQUE (bot_id, email_provider_id) +); + +CREATE INDEX IF NOT EXISTS idx_bot_email_bindings_bot_id ON bot_email_bindings(bot_id); +CREATE INDEX IF NOT EXISTS idx_bot_email_bindings_provider_id ON bot_email_bindings(email_provider_id); + +-- email_outbox: outbound email audit log +CREATE TABLE IF NOT EXISTS email_outbox ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + provider_id UUID NOT NULL REFERENCES email_providers(id) ON DELETE CASCADE, + bot_id UUID NOT NULL REFERENCES bots(id) ON DELETE CASCADE, + message_id TEXT NOT NULL DEFAULT '', + from_address TEXT NOT NULL DEFAULT '', + to_addresses JSONB NOT NULL DEFAULT '[]'::jsonb, + subject TEXT NOT NULL DEFAULT '', + body_text TEXT NOT NULL DEFAULT '', + body_html TEXT NOT NULL DEFAULT '', + attachments JSONB NOT NULL DEFAULT '[]'::jsonb, + status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'sent', 'failed')), + error TEXT NOT NULL DEFAULT '', + sent_at TIMESTAMPTZ, + created_at TIMESTAMPTZ NOT NULL DEFAULT now() +); + +CREATE INDEX IF NOT EXISTS idx_email_outbox_provider_id ON email_outbox(provider_id); +CREATE INDEX IF NOT EXISTS idx_email_outbox_bot_id ON email_outbox(bot_id, created_at DESC); diff --git a/db/migrations/0019_add_email.down.sql b/db/migrations/0019_add_email.down.sql new file mode 100644 index 00000000..af6fdd28 --- /dev/null +++ b/db/migrations/0019_add_email.down.sql @@ -0,0 +1,6 @@ +-- 0019_add_email (rollback) +-- Drop email tables in reverse order of creation. + +DROP TABLE IF EXISTS email_outbox; +DROP TABLE IF EXISTS bot_email_bindings; +DROP TABLE IF EXISTS email_providers; diff --git a/db/migrations/0019_add_email.up.sql b/db/migrations/0019_add_email.up.sql new file mode 100644 index 00000000..6fa43ed2 --- /dev/null +++ b/db/migrations/0019_add_email.up.sql @@ -0,0 +1,49 @@ +-- 0019_add_email +-- Add email providers, outbox, and bot email bindings tables. + +CREATE TABLE IF NOT EXISTS email_providers ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + name TEXT NOT NULL, + provider TEXT NOT NULL, + config JSONB NOT NULL DEFAULT '{}'::jsonb, + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), + CONSTRAINT email_providers_name_unique UNIQUE (name) +); + +CREATE TABLE IF NOT EXISTS bot_email_bindings ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + bot_id UUID NOT NULL REFERENCES bots(id) ON DELETE CASCADE, + email_provider_id UUID NOT NULL REFERENCES email_providers(id) ON DELETE CASCADE, + email_address TEXT NOT NULL, + can_read BOOLEAN NOT NULL DEFAULT TRUE, + can_write BOOLEAN NOT NULL DEFAULT TRUE, + can_delete BOOLEAN NOT NULL DEFAULT FALSE, + config JSONB NOT NULL DEFAULT '{}'::jsonb, + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), + CONSTRAINT bot_email_bindings_unique UNIQUE (bot_id, email_provider_id) +); + +CREATE INDEX IF NOT EXISTS idx_bot_email_bindings_bot_id ON bot_email_bindings(bot_id); +CREATE INDEX IF NOT EXISTS idx_bot_email_bindings_provider_id ON bot_email_bindings(email_provider_id); + +CREATE TABLE IF NOT EXISTS email_outbox ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + provider_id UUID NOT NULL REFERENCES email_providers(id) ON DELETE CASCADE, + bot_id UUID NOT NULL REFERENCES bots(id) ON DELETE CASCADE, + message_id TEXT NOT NULL DEFAULT '', + from_address TEXT NOT NULL DEFAULT '', + to_addresses JSONB NOT NULL DEFAULT '[]'::jsonb, + subject TEXT NOT NULL DEFAULT '', + body_text TEXT NOT NULL DEFAULT '', + body_html TEXT NOT NULL DEFAULT '', + attachments JSONB NOT NULL DEFAULT '[]'::jsonb, + status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'sent', 'failed')), + error TEXT NOT NULL DEFAULT '', + sent_at TIMESTAMPTZ, + created_at TIMESTAMPTZ NOT NULL DEFAULT now() +); + +CREATE INDEX IF NOT EXISTS idx_email_outbox_provider_id ON email_outbox(provider_id); +CREATE INDEX IF NOT EXISTS idx_email_outbox_bot_id ON email_outbox(bot_id, created_at DESC); diff --git a/db/queries/email_bindings.sql b/db/queries/email_bindings.sql new file mode 100644 index 00000000..70876b30 --- /dev/null +++ b/db/queries/email_bindings.sql @@ -0,0 +1,49 @@ +-- name: CreateBotEmailBinding :one +INSERT INTO bot_email_bindings (bot_id, email_provider_id, email_address, can_read, can_write, can_delete, config) +VALUES ( + sqlc.arg(bot_id), + sqlc.arg(email_provider_id), + sqlc.arg(email_address), + sqlc.arg(can_read), + sqlc.arg(can_write), + sqlc.arg(can_delete), + sqlc.arg(config) +) +RETURNING *; + +-- name: GetBotEmailBindingByID :one +SELECT * FROM bot_email_bindings WHERE id = sqlc.arg(id); + +-- name: GetBotEmailBindingByBotAndProvider :one +SELECT * FROM bot_email_bindings +WHERE bot_id = sqlc.arg(bot_id) AND email_provider_id = sqlc.arg(email_provider_id); + +-- name: ListBotEmailBindings :many +SELECT * FROM bot_email_bindings +WHERE bot_id = sqlc.arg(bot_id) +ORDER BY created_at DESC; + +-- name: ListBotEmailBindingsByProvider :many +SELECT * FROM bot_email_bindings +WHERE email_provider_id = sqlc.arg(email_provider_id) +ORDER BY created_at DESC; + +-- name: ListReadableBindingsByProvider :many +SELECT * FROM bot_email_bindings +WHERE email_provider_id = sqlc.arg(email_provider_id) AND can_read = TRUE +ORDER BY created_at DESC; + +-- name: UpdateBotEmailBinding :one +UPDATE bot_email_bindings +SET + email_address = sqlc.arg(email_address), + can_read = sqlc.arg(can_read), + can_write = sqlc.arg(can_write), + can_delete = sqlc.arg(can_delete), + config = sqlc.arg(config), + updated_at = now() +WHERE id = sqlc.arg(id) +RETURNING *; + +-- name: DeleteBotEmailBinding :exec +DELETE FROM bot_email_bindings WHERE id = sqlc.arg(id); diff --git a/db/queries/email_outbox.sql b/db/queries/email_outbox.sql new file mode 100644 index 00000000..b1db6808 --- /dev/null +++ b/db/queries/email_outbox.sql @@ -0,0 +1,37 @@ +-- name: CreateEmailOutbox :one +INSERT INTO email_outbox (provider_id, bot_id, from_address, to_addresses, subject, body_text, body_html, attachments, status) +VALUES ( + sqlc.arg(provider_id), + sqlc.arg(bot_id), + sqlc.arg(from_address), + sqlc.arg(to_addresses), + sqlc.arg(subject), + sqlc.arg(body_text), + sqlc.arg(body_html), + sqlc.arg(attachments), + sqlc.arg(status) +) +RETURNING *; + +-- name: GetEmailOutboxByID :one +SELECT * FROM email_outbox WHERE id = sqlc.arg(id); + +-- name: ListEmailOutboxByBot :many +SELECT * FROM email_outbox +WHERE bot_id = sqlc.arg(bot_id) +ORDER BY created_at DESC +LIMIT sqlc.arg(lim) OFFSET sqlc.arg(off); + +-- name: CountEmailOutboxByBot :one +SELECT count(*) FROM email_outbox +WHERE bot_id = sqlc.arg(bot_id); + +-- name: UpdateEmailOutboxSent :exec +UPDATE email_outbox +SET message_id = sqlc.arg(message_id), status = 'sent', sent_at = now() +WHERE id = sqlc.arg(id); + +-- name: UpdateEmailOutboxFailed :exec +UPDATE email_outbox +SET status = 'failed', error = sqlc.arg(error) +WHERE id = sqlc.arg(id); diff --git a/db/queries/email_providers.sql b/db/queries/email_providers.sql new file mode 100644 index 00000000..adff3272 --- /dev/null +++ b/db/queries/email_providers.sql @@ -0,0 +1,36 @@ +-- name: CreateEmailProvider :one +INSERT INTO email_providers (name, provider, config) +VALUES ( + sqlc.arg(name), + sqlc.arg(provider), + sqlc.arg(config) +) +RETURNING *; + +-- name: GetEmailProviderByID :one +SELECT * FROM email_providers WHERE id = sqlc.arg(id); + +-- name: GetEmailProviderByName :one +SELECT * FROM email_providers WHERE name = sqlc.arg(name); + +-- name: ListEmailProviders :many +SELECT * FROM email_providers +ORDER BY created_at DESC; + +-- name: ListEmailProvidersByProvider :many +SELECT * FROM email_providers +WHERE provider = sqlc.arg(provider) +ORDER BY created_at DESC; + +-- name: UpdateEmailProvider :one +UPDATE email_providers +SET + name = sqlc.arg(name), + provider = sqlc.arg(provider), + config = sqlc.arg(config), + updated_at = now() +WHERE id = sqlc.arg(id) +RETURNING *; + +-- name: DeleteEmailProvider :exec +DELETE FROM email_providers WHERE id = sqlc.arg(id); diff --git a/go.mod b/go.mod index ac82d617..58bf6266 100644 --- a/go.mod +++ b/go.mod @@ -5,11 +5,12 @@ go 1.25.2 require ( github.com/BurntSushi/toml v1.6.0 github.com/blevesearch/bleve/v2 v2.5.7 + github.com/bwmarrin/discordgo v0.29.0 github.com/containerd/containerd/api v1.10.0 github.com/containerd/containerd/v2 v2.2.1 github.com/containerd/errdefs v1.0.0 github.com/containerd/go-cni v1.1.13 - github.com/containerd/platforms v1.0.0-rc.2 + github.com/emersion/go-imap/v2 v2.0.0-beta.8 github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 github.com/golang-jwt/jwt/v5 v5.3.1 github.com/golang-migrate/migrate/v4 v4.19.1 @@ -18,15 +19,17 @@ require ( github.com/labstack/echo-jwt/v4 v4.4.0 github.com/labstack/echo/v4 v4.15.0 github.com/larksuite/oapi-sdk-go/v3 v3.5.3 + github.com/mailgun/mailgun-go/v5 v5.14.0 github.com/memohai/acgo v0.0.0-20260221232113-babac0d6acd7 github.com/modelcontextprotocol/go-sdk v1.3.0 - github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.1.1 github.com/opencontainers/runtime-spec v1.3.0 github.com/qdrant/go-client v1.16.2 github.com/robfig/cron/v3 v3.0.1 + github.com/spf13/cobra v1.10.2 github.com/stretchr/testify v1.11.1 github.com/swaggo/swag v1.16.6 + github.com/wneessen/go-mail v0.7.2 go.uber.org/fx v1.24.0 golang.org/x/crypto v0.48.0 google.golang.org/grpc v1.78.0 @@ -46,13 +49,13 @@ require ( github.com/blevesearch/snowballstem v0.9.0 // indirect github.com/blevesearch/stempel v0.2.0 // indirect github.com/blevesearch/upsidedown_store_api v1.0.2 // indirect - github.com/bwmarrin/discordgo v0.29.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/containerd/cgroups/v3 v3.1.2 // indirect github.com/containerd/continuity v0.4.5 // indirect github.com/containerd/errdefs/pkg v0.3.0 // indirect github.com/containerd/fifo v1.1.0 // indirect github.com/containerd/log v0.1.0 // indirect + github.com/containerd/platforms v1.0.0-rc.2 // indirect github.com/containerd/plugin v1.0.0 // indirect github.com/containerd/ttrpc v1.2.7 // indirect github.com/containerd/typeurl/v2 v2.2.3 // indirect @@ -60,6 +63,8 @@ require ( github.com/cyphar/filepath-securejoin v0.6.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/distribution/reference v0.6.0 // indirect + github.com/emersion/go-message v0.18.2 // indirect + github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect @@ -96,13 +101,14 @@ require ( github.com/moby/sys/userns v0.1.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect + github.com/oapi-codegen/runtime v1.1.2 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/selinux v1.13.1 // indirect github.com/petermattis/goid v0.0.0-20260113132338-7c7de50cc741 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/sasha-s/go-deadlock v0.3.6 // indirect github.com/sirupsen/logrus v1.9.4 // indirect - github.com/spf13/cobra v1.10.2 // indirect github.com/spf13/pflag v1.0.9 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect diff --git a/go.sum b/go.sum index 39854861..36af2c44 100644 --- a/go.sum +++ b/go.sum @@ -84,12 +84,20 @@ github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pM github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/emersion/go-imap/v2 v2.0.0-beta.8 h1:5IXZK1E33DyeP526320J3RS7eFlCYGFgtbrfapqDPug= +github.com/emersion/go-imap/v2 v2.0.0-beta.8/go.mod h1:dhoFe2Q0PwLrMD7oZw8ODuaD0vLYPe5uj2wcOMnvh48= +github.com/emersion/go-message v0.18.2 h1:rl55SQdjd9oJcIoQNhubD2Acs1E6IzlZISRTK7x/Lpg= +github.com/emersion/go-message v0.18.2/go.mod h1:XpJyL70LwRvq2a8rVbHXikPgKj8+aI0kGdHlg16ibYA= +github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 h1:oP4q0fw+fOSWn3DfFi4EXdT+B+gTtzx8GC9xsc26Znk= +github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/go-chi/chi/v5 v5.2.5 h1:Eg4myHZBjyvJmAFjFvWgrqDTXFyOzjj7YIm3L3mu6Ug= +github.com/go-chi/chi/v5 v5.2.5/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -198,6 +206,8 @@ github.com/larksuite/oapi-sdk-go/v3 v3.5.3 h1:xvf8Dv29kBXC5/DNDCLhHkAFW8l/0LlQJi github.com/larksuite/oapi-sdk-go/v3 v3.5.3/go.mod h1:ZEplY+kwuIrj/nqw5uSCINNATcH3KdxSN7y+UxYY5fI= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/mailgun/mailgun-go/v5 v5.14.0 h1:s1S7MbO0G24zew1cCvTGUA7yvJNf9T/HEiVElNrwF1k= +github.com/mailgun/mailgun-go/v5 v5.14.0/go.mod h1:8jl24zvg8DPd5R3dUGIM77J76CWE+esAO+3w0/1c9AA= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= @@ -230,6 +240,8 @@ github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFd github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/oapi-codegen/runtime v1.1.2 h1:P2+CubHq8fO4Q6fV1tqDBZHCwpVpvPg7oKiYzQgXIyI= +github.com/oapi-codegen/runtime v1.1.2/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg= github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo/v2 v2.20.1 h1:YlVIbqct+ZmnEph770q9Q7NVAz4wwIiVNahee6JyUzo= github.com/onsi/ginkgo/v2 v2.20.1/go.mod h1:lG9ey2Z29hR41WMVthyJBGUBcBhGOtoPF2VFMvBXFCI= @@ -289,10 +301,13 @@ github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQ github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY= github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= +github.com/wneessen/go-mail v0.7.2 h1:xxPnhZ6IZLSgxShebmZ6DPKh1b6OJcoHfzy7UjOkzS8= +github.com/wneessen/go-mail v0.7.2/go.mod h1:+TkW6QP3EVkgTEqHtVmnAE/1MRhmzb8Y9/W3pweuS+k= github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4= github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= @@ -325,6 +340,7 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -335,6 +351,8 @@ golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvx golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -347,6 +365,8 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60= golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -357,6 +377,8 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -364,13 +386,22 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= @@ -383,6 +414,8 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/internal/conversation/flow/email_gateway.go b/internal/conversation/flow/email_gateway.go new file mode 100644 index 00000000..7bd91363 --- /dev/null +++ b/internal/conversation/flow/email_gateway.go @@ -0,0 +1,92 @@ +package flow + +import ( + "context" + "fmt" + "log/slog" + "strings" + "time" + + "github.com/memohai/memoh/internal/auth" + "github.com/memohai/memoh/internal/conversation" + "github.com/memohai/memoh/internal/db" + "github.com/memohai/memoh/internal/db/sqlc" +) + +const emailTriggerTokenTTL = 10 * time.Minute + +// EmailChatGateway implements email.ChatTriggerer by delegating to the Resolver. +type EmailChatGateway struct { + resolver *Resolver + queries *sqlc.Queries + jwtSecret string + logger *slog.Logger +} + +func NewEmailChatGateway(resolver *Resolver, queries *sqlc.Queries, jwtSecret string, logger *slog.Logger) *EmailChatGateway { + return &EmailChatGateway{ + resolver: resolver, + queries: queries, + jwtSecret: jwtSecret, + logger: logger, + } +} + +func (g *EmailChatGateway) TriggerBotChat(ctx context.Context, botID, content string) error { + if g == nil || g.resolver == nil { + return fmt.Errorf("chat resolver not configured") + } + + ownerUserID, err := g.resolveBotOwner(ctx, botID) + if err != nil { + return fmt.Errorf("resolve bot owner: %w", err) + } + + token, err := g.generateToken(ownerUserID) + if err != nil { + return fmt.Errorf("generate trigger token: %w", err) + } + + _, err = g.resolver.Chat(ctx, conversation.ChatRequest{ + BotID: botID, + ChatID: botID, + Query: content, + UserID: ownerUserID, + Token: token, + CurrentChannel: "email", + }) + if err != nil { + return fmt.Errorf("trigger chat: %w", err) + } + + g.logger.Info("email trigger chat completed", + slog.String("bot_id", botID)) + return nil +} + +func (g *EmailChatGateway) resolveBotOwner(ctx context.Context, botID string) (string, error) { + pgBotID, err := db.ParseUUID(botID) + if err != nil { + return "", err + } + bot, err := g.queries.GetBotByID(ctx, pgBotID) + if err != nil { + return "", fmt.Errorf("get bot: %w", err) + } + ownerID := bot.OwnerUserID.String() + if ownerID == "" { + return "", fmt.Errorf("bot owner not found") + } + return ownerID, nil +} + +func (g *EmailChatGateway) generateToken(userID string) (string, error) { + if strings.TrimSpace(g.jwtSecret) == "" { + return "", fmt.Errorf("jwt secret not configured") + } + signed, _, err := auth.GenerateToken(userID, g.jwtSecret, emailTriggerTokenTTL) + if err != nil { + return "", err + } + return "Bearer " + signed, nil +} diff --git a/internal/db/sqlc/email_bindings.sql.go b/internal/db/sqlc/email_bindings.sql.go new file mode 100644 index 00000000..c654fb32 --- /dev/null +++ b/internal/db/sqlc/email_bindings.sql.go @@ -0,0 +1,279 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 +// source: email_bindings.sql + +package sqlc + +import ( + "context" + + "github.com/jackc/pgx/v5/pgtype" +) + +const createBotEmailBinding = `-- name: CreateBotEmailBinding :one +INSERT INTO bot_email_bindings (bot_id, email_provider_id, email_address, can_read, can_write, can_delete, config) +VALUES ( + $1, + $2, + $3, + $4, + $5, + $6, + $7 +) +RETURNING id, bot_id, email_provider_id, email_address, can_read, can_write, can_delete, config, created_at, updated_at +` + +type CreateBotEmailBindingParams struct { + BotID pgtype.UUID `json:"bot_id"` + EmailProviderID pgtype.UUID `json:"email_provider_id"` + EmailAddress string `json:"email_address"` + CanRead bool `json:"can_read"` + CanWrite bool `json:"can_write"` + CanDelete bool `json:"can_delete"` + Config []byte `json:"config"` +} + +func (q *Queries) CreateBotEmailBinding(ctx context.Context, arg CreateBotEmailBindingParams) (BotEmailBinding, error) { + row := q.db.QueryRow(ctx, createBotEmailBinding, + arg.BotID, + arg.EmailProviderID, + arg.EmailAddress, + arg.CanRead, + arg.CanWrite, + arg.CanDelete, + arg.Config, + ) + var i BotEmailBinding + err := row.Scan( + &i.ID, + &i.BotID, + &i.EmailProviderID, + &i.EmailAddress, + &i.CanRead, + &i.CanWrite, + &i.CanDelete, + &i.Config, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const deleteBotEmailBinding = `-- name: DeleteBotEmailBinding :exec +DELETE FROM bot_email_bindings WHERE id = $1 +` + +func (q *Queries) DeleteBotEmailBinding(ctx context.Context, id pgtype.UUID) error { + _, err := q.db.Exec(ctx, deleteBotEmailBinding, id) + return err +} + +const getBotEmailBindingByBotAndProvider = `-- name: GetBotEmailBindingByBotAndProvider :one +SELECT id, bot_id, email_provider_id, email_address, can_read, can_write, can_delete, config, created_at, updated_at FROM bot_email_bindings +WHERE bot_id = $1 AND email_provider_id = $2 +` + +type GetBotEmailBindingByBotAndProviderParams struct { + BotID pgtype.UUID `json:"bot_id"` + EmailProviderID pgtype.UUID `json:"email_provider_id"` +} + +func (q *Queries) GetBotEmailBindingByBotAndProvider(ctx context.Context, arg GetBotEmailBindingByBotAndProviderParams) (BotEmailBinding, error) { + row := q.db.QueryRow(ctx, getBotEmailBindingByBotAndProvider, arg.BotID, arg.EmailProviderID) + var i BotEmailBinding + err := row.Scan( + &i.ID, + &i.BotID, + &i.EmailProviderID, + &i.EmailAddress, + &i.CanRead, + &i.CanWrite, + &i.CanDelete, + &i.Config, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const getBotEmailBindingByID = `-- name: GetBotEmailBindingByID :one +SELECT id, bot_id, email_provider_id, email_address, can_read, can_write, can_delete, config, created_at, updated_at FROM bot_email_bindings WHERE id = $1 +` + +func (q *Queries) GetBotEmailBindingByID(ctx context.Context, id pgtype.UUID) (BotEmailBinding, error) { + row := q.db.QueryRow(ctx, getBotEmailBindingByID, id) + var i BotEmailBinding + err := row.Scan( + &i.ID, + &i.BotID, + &i.EmailProviderID, + &i.EmailAddress, + &i.CanRead, + &i.CanWrite, + &i.CanDelete, + &i.Config, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const listBotEmailBindings = `-- name: ListBotEmailBindings :many +SELECT id, bot_id, email_provider_id, email_address, can_read, can_write, can_delete, config, created_at, updated_at FROM bot_email_bindings +WHERE bot_id = $1 +ORDER BY created_at DESC +` + +func (q *Queries) ListBotEmailBindings(ctx context.Context, botID pgtype.UUID) ([]BotEmailBinding, error) { + rows, err := q.db.Query(ctx, listBotEmailBindings, botID) + if err != nil { + return nil, err + } + defer rows.Close() + var items []BotEmailBinding + for rows.Next() { + var i BotEmailBinding + if err := rows.Scan( + &i.ID, + &i.BotID, + &i.EmailProviderID, + &i.EmailAddress, + &i.CanRead, + &i.CanWrite, + &i.CanDelete, + &i.Config, + &i.CreatedAt, + &i.UpdatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listBotEmailBindingsByProvider = `-- name: ListBotEmailBindingsByProvider :many +SELECT id, bot_id, email_provider_id, email_address, can_read, can_write, can_delete, config, created_at, updated_at FROM bot_email_bindings +WHERE email_provider_id = $1 +ORDER BY created_at DESC +` + +func (q *Queries) ListBotEmailBindingsByProvider(ctx context.Context, emailProviderID pgtype.UUID) ([]BotEmailBinding, error) { + rows, err := q.db.Query(ctx, listBotEmailBindingsByProvider, emailProviderID) + if err != nil { + return nil, err + } + defer rows.Close() + var items []BotEmailBinding + for rows.Next() { + var i BotEmailBinding + if err := rows.Scan( + &i.ID, + &i.BotID, + &i.EmailProviderID, + &i.EmailAddress, + &i.CanRead, + &i.CanWrite, + &i.CanDelete, + &i.Config, + &i.CreatedAt, + &i.UpdatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listReadableBindingsByProvider = `-- name: ListReadableBindingsByProvider :many +SELECT id, bot_id, email_provider_id, email_address, can_read, can_write, can_delete, config, created_at, updated_at FROM bot_email_bindings +WHERE email_provider_id = $1 AND can_read = TRUE +ORDER BY created_at DESC +` + +func (q *Queries) ListReadableBindingsByProvider(ctx context.Context, emailProviderID pgtype.UUID) ([]BotEmailBinding, error) { + rows, err := q.db.Query(ctx, listReadableBindingsByProvider, emailProviderID) + if err != nil { + return nil, err + } + defer rows.Close() + var items []BotEmailBinding + for rows.Next() { + var i BotEmailBinding + if err := rows.Scan( + &i.ID, + &i.BotID, + &i.EmailProviderID, + &i.EmailAddress, + &i.CanRead, + &i.CanWrite, + &i.CanDelete, + &i.Config, + &i.CreatedAt, + &i.UpdatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const updateBotEmailBinding = `-- name: UpdateBotEmailBinding :one +UPDATE bot_email_bindings +SET + email_address = $1, + can_read = $2, + can_write = $3, + can_delete = $4, + config = $5, + updated_at = now() +WHERE id = $6 +RETURNING id, bot_id, email_provider_id, email_address, can_read, can_write, can_delete, config, created_at, updated_at +` + +type UpdateBotEmailBindingParams struct { + EmailAddress string `json:"email_address"` + CanRead bool `json:"can_read"` + CanWrite bool `json:"can_write"` + CanDelete bool `json:"can_delete"` + Config []byte `json:"config"` + ID pgtype.UUID `json:"id"` +} + +func (q *Queries) UpdateBotEmailBinding(ctx context.Context, arg UpdateBotEmailBindingParams) (BotEmailBinding, error) { + row := q.db.QueryRow(ctx, updateBotEmailBinding, + arg.EmailAddress, + arg.CanRead, + arg.CanWrite, + arg.CanDelete, + arg.Config, + arg.ID, + ) + var i BotEmailBinding + err := row.Scan( + &i.ID, + &i.BotID, + &i.EmailProviderID, + &i.EmailAddress, + &i.CanRead, + &i.CanWrite, + &i.CanDelete, + &i.Config, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} diff --git a/internal/db/sqlc/email_outbox.sql.go b/internal/db/sqlc/email_outbox.sql.go new file mode 100644 index 00000000..42d27542 --- /dev/null +++ b/internal/db/sqlc/email_outbox.sql.go @@ -0,0 +1,190 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 +// source: email_outbox.sql + +package sqlc + +import ( + "context" + + "github.com/jackc/pgx/v5/pgtype" +) + +const countEmailOutboxByBot = `-- name: CountEmailOutboxByBot :one +SELECT count(*) FROM email_outbox +WHERE bot_id = $1 +` + +func (q *Queries) CountEmailOutboxByBot(ctx context.Context, botID pgtype.UUID) (int64, error) { + row := q.db.QueryRow(ctx, countEmailOutboxByBot, botID) + var count int64 + err := row.Scan(&count) + return count, err +} + +const createEmailOutbox = `-- name: CreateEmailOutbox :one +INSERT INTO email_outbox (provider_id, bot_id, from_address, to_addresses, subject, body_text, body_html, attachments, status) +VALUES ( + $1, + $2, + $3, + $4, + $5, + $6, + $7, + $8, + $9 +) +RETURNING id, provider_id, bot_id, message_id, from_address, to_addresses, subject, body_text, body_html, attachments, status, error, sent_at, created_at +` + +type CreateEmailOutboxParams struct { + ProviderID pgtype.UUID `json:"provider_id"` + BotID pgtype.UUID `json:"bot_id"` + FromAddress string `json:"from_address"` + ToAddresses []byte `json:"to_addresses"` + Subject string `json:"subject"` + BodyText string `json:"body_text"` + BodyHtml string `json:"body_html"` + Attachments []byte `json:"attachments"` + Status string `json:"status"` +} + +func (q *Queries) CreateEmailOutbox(ctx context.Context, arg CreateEmailOutboxParams) (EmailOutbox, error) { + row := q.db.QueryRow(ctx, createEmailOutbox, + arg.ProviderID, + arg.BotID, + arg.FromAddress, + arg.ToAddresses, + arg.Subject, + arg.BodyText, + arg.BodyHtml, + arg.Attachments, + arg.Status, + ) + var i EmailOutbox + err := row.Scan( + &i.ID, + &i.ProviderID, + &i.BotID, + &i.MessageID, + &i.FromAddress, + &i.ToAddresses, + &i.Subject, + &i.BodyText, + &i.BodyHtml, + &i.Attachments, + &i.Status, + &i.Error, + &i.SentAt, + &i.CreatedAt, + ) + return i, err +} + +const getEmailOutboxByID = `-- name: GetEmailOutboxByID :one +SELECT id, provider_id, bot_id, message_id, from_address, to_addresses, subject, body_text, body_html, attachments, status, error, sent_at, created_at FROM email_outbox WHERE id = $1 +` + +func (q *Queries) GetEmailOutboxByID(ctx context.Context, id pgtype.UUID) (EmailOutbox, error) { + row := q.db.QueryRow(ctx, getEmailOutboxByID, id) + var i EmailOutbox + err := row.Scan( + &i.ID, + &i.ProviderID, + &i.BotID, + &i.MessageID, + &i.FromAddress, + &i.ToAddresses, + &i.Subject, + &i.BodyText, + &i.BodyHtml, + &i.Attachments, + &i.Status, + &i.Error, + &i.SentAt, + &i.CreatedAt, + ) + return i, err +} + +const listEmailOutboxByBot = `-- name: ListEmailOutboxByBot :many +SELECT id, provider_id, bot_id, message_id, from_address, to_addresses, subject, body_text, body_html, attachments, status, error, sent_at, created_at FROM email_outbox +WHERE bot_id = $1 +ORDER BY created_at DESC +LIMIT $3 OFFSET $2 +` + +type ListEmailOutboxByBotParams struct { + BotID pgtype.UUID `json:"bot_id"` + Off int32 `json:"off"` + Lim int32 `json:"lim"` +} + +func (q *Queries) ListEmailOutboxByBot(ctx context.Context, arg ListEmailOutboxByBotParams) ([]EmailOutbox, error) { + rows, err := q.db.Query(ctx, listEmailOutboxByBot, arg.BotID, arg.Off, arg.Lim) + if err != nil { + return nil, err + } + defer rows.Close() + var items []EmailOutbox + for rows.Next() { + var i EmailOutbox + if err := rows.Scan( + &i.ID, + &i.ProviderID, + &i.BotID, + &i.MessageID, + &i.FromAddress, + &i.ToAddresses, + &i.Subject, + &i.BodyText, + &i.BodyHtml, + &i.Attachments, + &i.Status, + &i.Error, + &i.SentAt, + &i.CreatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const updateEmailOutboxFailed = `-- name: UpdateEmailOutboxFailed :exec +UPDATE email_outbox +SET status = 'failed', error = $1 +WHERE id = $2 +` + +type UpdateEmailOutboxFailedParams struct { + Error string `json:"error"` + ID pgtype.UUID `json:"id"` +} + +func (q *Queries) UpdateEmailOutboxFailed(ctx context.Context, arg UpdateEmailOutboxFailedParams) error { + _, err := q.db.Exec(ctx, updateEmailOutboxFailed, arg.Error, arg.ID) + return err +} + +const updateEmailOutboxSent = `-- name: UpdateEmailOutboxSent :exec +UPDATE email_outbox +SET message_id = $1, status = 'sent', sent_at = now() +WHERE id = $2 +` + +type UpdateEmailOutboxSentParams struct { + MessageID string `json:"message_id"` + ID pgtype.UUID `json:"id"` +} + +func (q *Queries) UpdateEmailOutboxSent(ctx context.Context, arg UpdateEmailOutboxSentParams) error { + _, err := q.db.Exec(ctx, updateEmailOutboxSent, arg.MessageID, arg.ID) + return err +} diff --git a/internal/db/sqlc/email_providers.sql.go b/internal/db/sqlc/email_providers.sql.go new file mode 100644 index 00000000..ecea39ec --- /dev/null +++ b/internal/db/sqlc/email_providers.sql.go @@ -0,0 +1,189 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 +// source: email_providers.sql + +package sqlc + +import ( + "context" + + "github.com/jackc/pgx/v5/pgtype" +) + +const createEmailProvider = `-- name: CreateEmailProvider :one +INSERT INTO email_providers (name, provider, config) +VALUES ( + $1, + $2, + $3 +) +RETURNING id, name, provider, config, created_at, updated_at +` + +type CreateEmailProviderParams struct { + Name string `json:"name"` + Provider string `json:"provider"` + Config []byte `json:"config"` +} + +func (q *Queries) CreateEmailProvider(ctx context.Context, arg CreateEmailProviderParams) (EmailProvider, error) { + row := q.db.QueryRow(ctx, createEmailProvider, arg.Name, arg.Provider, arg.Config) + var i EmailProvider + err := row.Scan( + &i.ID, + &i.Name, + &i.Provider, + &i.Config, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const deleteEmailProvider = `-- name: DeleteEmailProvider :exec +DELETE FROM email_providers WHERE id = $1 +` + +func (q *Queries) DeleteEmailProvider(ctx context.Context, id pgtype.UUID) error { + _, err := q.db.Exec(ctx, deleteEmailProvider, id) + return err +} + +const getEmailProviderByID = `-- name: GetEmailProviderByID :one +SELECT id, name, provider, config, created_at, updated_at FROM email_providers WHERE id = $1 +` + +func (q *Queries) GetEmailProviderByID(ctx context.Context, id pgtype.UUID) (EmailProvider, error) { + row := q.db.QueryRow(ctx, getEmailProviderByID, id) + var i EmailProvider + err := row.Scan( + &i.ID, + &i.Name, + &i.Provider, + &i.Config, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const getEmailProviderByName = `-- name: GetEmailProviderByName :one +SELECT id, name, provider, config, created_at, updated_at FROM email_providers WHERE name = $1 +` + +func (q *Queries) GetEmailProviderByName(ctx context.Context, name string) (EmailProvider, error) { + row := q.db.QueryRow(ctx, getEmailProviderByName, name) + var i EmailProvider + err := row.Scan( + &i.ID, + &i.Name, + &i.Provider, + &i.Config, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const listEmailProviders = `-- name: ListEmailProviders :many +SELECT id, name, provider, config, created_at, updated_at FROM email_providers +ORDER BY created_at DESC +` + +func (q *Queries) ListEmailProviders(ctx context.Context) ([]EmailProvider, error) { + rows, err := q.db.Query(ctx, listEmailProviders) + if err != nil { + return nil, err + } + defer rows.Close() + var items []EmailProvider + for rows.Next() { + var i EmailProvider + if err := rows.Scan( + &i.ID, + &i.Name, + &i.Provider, + &i.Config, + &i.CreatedAt, + &i.UpdatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listEmailProvidersByProvider = `-- name: ListEmailProvidersByProvider :many +SELECT id, name, provider, config, created_at, updated_at FROM email_providers +WHERE provider = $1 +ORDER BY created_at DESC +` + +func (q *Queries) ListEmailProvidersByProvider(ctx context.Context, provider string) ([]EmailProvider, error) { + rows, err := q.db.Query(ctx, listEmailProvidersByProvider, provider) + if err != nil { + return nil, err + } + defer rows.Close() + var items []EmailProvider + for rows.Next() { + var i EmailProvider + if err := rows.Scan( + &i.ID, + &i.Name, + &i.Provider, + &i.Config, + &i.CreatedAt, + &i.UpdatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const updateEmailProvider = `-- name: UpdateEmailProvider :one +UPDATE email_providers +SET + name = $1, + provider = $2, + config = $3, + updated_at = now() +WHERE id = $4 +RETURNING id, name, provider, config, created_at, updated_at +` + +type UpdateEmailProviderParams struct { + Name string `json:"name"` + Provider string `json:"provider"` + Config []byte `json:"config"` + ID pgtype.UUID `json:"id"` +} + +func (q *Queries) UpdateEmailProvider(ctx context.Context, arg UpdateEmailProviderParams) (EmailProvider, error) { + row := q.db.QueryRow(ctx, updateEmailProvider, + arg.Name, + arg.Provider, + arg.Config, + arg.ID, + ) + var i EmailProvider + err := row.Scan( + &i.ID, + &i.Name, + &i.Provider, + &i.Config, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} diff --git a/internal/db/sqlc/models.go b/internal/db/sqlc/models.go index 9ef18de1..f1d20346 100644 --- a/internal/db/sqlc/models.go +++ b/internal/db/sqlc/models.go @@ -65,6 +65,19 @@ type BotChannelRoute struct { UpdatedAt pgtype.Timestamptz `json:"updated_at"` } +type BotEmailBinding struct { + ID pgtype.UUID `json:"id"` + BotID pgtype.UUID `json:"bot_id"` + EmailProviderID pgtype.UUID `json:"email_provider_id"` + EmailAddress string `json:"email_address"` + CanRead bool `json:"can_read"` + CanWrite bool `json:"can_write"` + CanDelete bool `json:"can_delete"` + Config []byte `json:"config"` + CreatedAt pgtype.Timestamptz `json:"created_at"` + UpdatedAt pgtype.Timestamptz `json:"updated_at"` +} + type BotHeartbeatLog struct { ID pgtype.UUID `json:"id"` BotID pgtype.UUID `json:"bot_id"` @@ -187,6 +200,32 @@ type ContainerVersion struct { CreatedAt pgtype.Timestamptz `json:"created_at"` } +type EmailOutbox struct { + ID pgtype.UUID `json:"id"` + ProviderID pgtype.UUID `json:"provider_id"` + BotID pgtype.UUID `json:"bot_id"` + MessageID string `json:"message_id"` + FromAddress string `json:"from_address"` + ToAddresses []byte `json:"to_addresses"` + Subject string `json:"subject"` + BodyText string `json:"body_text"` + BodyHtml string `json:"body_html"` + Attachments []byte `json:"attachments"` + Status string `json:"status"` + Error string `json:"error"` + SentAt pgtype.Timestamptz `json:"sent_at"` + CreatedAt pgtype.Timestamptz `json:"created_at"` +} + +type EmailProvider struct { + ID pgtype.UUID `json:"id"` + Name string `json:"name"` + Provider string `json:"provider"` + Config []byte `json:"config"` + CreatedAt pgtype.Timestamptz `json:"created_at"` + UpdatedAt pgtype.Timestamptz `json:"updated_at"` +} + type LifecycleEvent struct { ID string `json:"id"` ContainerID string `json:"container_id"` diff --git a/internal/email/adapters/generic/adapter.go b/internal/email/adapters/generic/adapter.go new file mode 100644 index 00000000..886ba764 --- /dev/null +++ b/internal/email/adapters/generic/adapter.go @@ -0,0 +1,542 @@ +package generic + +import ( + "context" + "crypto/tls" + "fmt" + "log/slog" + "strings" + "sync" + "time" + + mail "github.com/wneessen/go-mail" + + "github.com/emersion/go-imap/v2" + "github.com/emersion/go-imap/v2/imapclient" + + "github.com/memohai/memoh/internal/email" +) + +const ProviderName email.ProviderName = "generic" + +type Adapter struct { + logger *slog.Logger +} + +func New(log *slog.Logger) *Adapter { + return &Adapter{logger: log.With(slog.String("adapter", "generic"))} +} + +func (a *Adapter) Type() email.ProviderName { return ProviderName } + +func (a *Adapter) Meta() email.ProviderMeta { + return email.ProviderMeta{ + Provider: string(ProviderName), + DisplayName: "Generic (SMTP/IMAP)", + ConfigSchema: email.ConfigSchema{ + Fields: []email.FieldSchema{ + {Key: "username", Type: "string", Title: "Username", Required: true, Example: "user@gmail.com", Order: 1}, + {Key: "password", Type: "secret", Title: "Password", Required: true, Order: 2}, + {Key: "smtp_host", Type: "string", Title: "SMTP Host", Required: true, Example: "smtp.gmail.com", Order: 3}, + {Key: "smtp_port", Type: "number", Title: "SMTP Port", Required: true, Example: 587, Order: 4}, + {Key: "smtp_security", Type: "enum", Title: "SMTP Security", Enum: []string{"tls", "starttls", "none"}, Example: "starttls", Order: 5}, + {Key: "imap_host", Type: "string", Title: "IMAP Host", Required: true, Example: "imap.gmail.com", Order: 6}, + {Key: "imap_port", Type: "number", Title: "IMAP Port", Required: true, Example: 993, Order: 7}, + {Key: "imap_security", Type: "enum", Title: "IMAP Security", Enum: []string{"tls", "starttls", "none"}, Example: "tls", Order: 8}, + {Key: "poll_interval_seconds", Type: "number", Title: "Poll Interval (seconds)", Description: "Fallback poll interval when IDLE is not supported", Example: 300, Order: 9}, + }, + }, + } +} + +func (a *Adapter) NormalizeConfig(raw map[string]any) (map[string]any, error) { + for _, key := range []string{"smtp_host", "imap_host", "username", "password"} { + if v, _ := raw[key].(string); strings.TrimSpace(v) == "" { + return nil, fmt.Errorf("%s is required", key) + } + } + if _, ok := raw["smtp_port"]; !ok { + raw["smtp_port"] = float64(587) + } + if _, ok := raw["imap_port"]; !ok { + raw["imap_port"] = float64(993) + } + if _, ok := raw["smtp_security"]; !ok { + raw["smtp_security"] = "starttls" + } + if _, ok := raw["imap_security"]; !ok { + raw["imap_security"] = "tls" + } + if _, ok := raw["poll_interval_seconds"]; !ok { + raw["poll_interval_seconds"] = float64(300) + } + return raw, nil +} + +// ---- Sender ---- + +func (a *Adapter) Send(ctx context.Context, config map[string]any, msg email.OutboundEmail) (string, error) { + host, _ := config["smtp_host"].(string) + port := intVal(config["smtp_port"], 587) + username, _ := config["username"].(string) + password, _ := config["password"].(string) + smtpSecurity, _ := config["smtp_security"].(string) + + m := mail.NewMsg() + if err := m.From(username); err != nil { + return "", fmt.Errorf("set from: %w", err) + } + if err := m.To(msg.To...); err != nil { + return "", fmt.Errorf("set to: %w", err) + } + m.Subject(msg.Subject) + if msg.HTML { + m.SetBodyString(mail.TypeTextHTML, msg.Body) + } else { + m.SetBodyString(mail.TypeTextPlain, msg.Body) + } + m.SetMessageID() + + opts := []mail.Option{ + mail.WithPort(port), + mail.WithSMTPAuth(mail.SMTPAuthPlain), + mail.WithUsername(username), + mail.WithPassword(password), + } + switch smtpSecurity { + case "tls": + opts = append(opts, mail.WithSSLPort(false), mail.WithTLSPolicy(mail.TLSMandatory)) + case "starttls": + opts = append(opts, mail.WithTLSPolicy(mail.TLSMandatory)) + default: + opts = append(opts, mail.WithTLSPolicy(mail.NoTLS)) + } + + client, err := mail.NewClient(host, opts...) + if err != nil { + return "", fmt.Errorf("create smtp client: %w", err) + } + if err := client.DialAndSendWithContext(ctx, m); err != nil { + return "", fmt.Errorf("send email: %w", err) + } + + return m.GetMessageID(), nil +} + +// ---- Receiver (IMAP IDLE + poll fallback) ---- + +func (a *Adapter) StartReceiving(ctx context.Context, config map[string]any, handler email.InboundHandler) (email.Stopper, error) { + host, _ := config["imap_host"].(string) + port := intVal(config["imap_port"], 993) + username, _ := config["username"].(string) + password, _ := config["password"].(string) + imapSecurity, _ := config["imap_security"].(string) + pollInterval := time.Duration(intVal(config["poll_interval_seconds"], 300)) * time.Second + + providerID, _ := config["_provider_id"].(string) + + rctx, cancel := context.WithCancel(ctx) + conn := &imapConn{ + logger: a.logger, + host: host, + port: port, + username: username, + password: password, + security: imapSecurity, + pollInterval: pollInterval, + providerID: providerID, + handler: handler, + cancel: cancel, + } + go conn.run(rctx) + return conn, nil +} + +type imapConn struct { + logger *slog.Logger + host string + port int + username string + password string + security string + pollInterval time.Duration + providerID string + handler email.InboundHandler + cancel context.CancelFunc + once sync.Once + lastUID imap.UID +} + +func (c *imapConn) Stop(_ context.Context) error { + c.once.Do(func() { c.cancel() }) + return nil +} + +func (c *imapConn) run(ctx context.Context) { + for { + if err := c.connectAndReceive(ctx); err != nil { + if ctx.Err() != nil { + return + } + c.logger.Error("imap connection error, retrying in 30s", slog.Any("error", err)) + select { + case <-ctx.Done(): + return + case <-time.After(30 * time.Second): + } + } + } +} + +func (c *imapConn) connectAndReceive(ctx context.Context) error { + addr := fmt.Sprintf("%s:%d", c.host, c.port) + + // Channel to receive "new mail" signals from IMAP unilateral notifications + newMailCh := make(chan struct{}, 1) + notifyNewMail := func() { + select { + case newMailCh <- struct{}{}: + default: + } + } + + opts := &imapclient.Options{ + TLSConfig: &tls.Config{ServerName: c.host}, + UnilateralDataHandler: &imapclient.UnilateralDataHandler{ + Mailbox: func(data *imapclient.UnilateralDataMailbox) { + if data.NumMessages != nil { + notifyNewMail() + } + }, + }, + } + var client *imapclient.Client + var err error + switch c.security { + case "starttls": + client, err = imapclient.DialStartTLS(addr, opts) + case "none": + client, err = imapclient.DialInsecure(addr, opts) + default: + client, err = imapclient.DialTLS(addr, opts) + } + if err != nil { + return fmt.Errorf("dial imap (%s): %w", c.security, err) + } + defer client.Close() + + if err := client.Login(c.username, c.password).Wait(); err != nil { + return fmt.Errorf("imap login: %w", err) + } + defer client.Logout() + + if _, err := client.Select("INBOX", nil).Wait(); err != nil { + return fmt.Errorf("select inbox: %w", err) + } + + c.logger.Info("imap connected, fetching initial messages", slog.String("host", c.host), slog.Int("port", c.port)) + c.fetchNewMessages(ctx, client) + + idleCmd, idleErr := client.Idle() + if idleErr != nil { + c.logger.Warn("IDLE not supported, falling back to polling", slog.Any("error", idleErr)) + return c.pollLoop(ctx, client) + } + c.logger.Info("IDLE mode active") + + // Even with IDLE, periodically check for new mail as a safety net + // (some servers accept IDLE but don't push EXISTS notifications) + checkInterval := c.pollInterval + if checkInterval > 2*time.Minute { + checkInterval = 2 * time.Minute + } + + for { + select { + case <-ctx.Done(): + _ = idleCmd.Close() + return nil + case <-newMailCh: + c.logger.Info("IDLE: new mail notification received") + _ = idleCmd.Close() + c.fetchNewMessages(ctx, client) + idleCmd, idleErr = client.Idle() + if idleErr != nil { + return c.pollLoop(ctx, client) + } + case <-time.After(checkInterval): + _ = idleCmd.Close() + c.fetchNewMessages(ctx, client) + idleCmd, idleErr = client.Idle() + if idleErr != nil { + return c.pollLoop(ctx, client) + } + } + } +} + +func (c *imapConn) pollLoop(ctx context.Context, client *imapclient.Client) error { + for { + c.fetchNewMessages(ctx, client) + select { + case <-ctx.Done(): + return nil + case <-time.After(c.pollInterval): + } + } +} + +func (c *imapConn) fetchNewMessages(ctx context.Context, client *imapclient.Client) { + // Use UID range to find messages newer than the last processed one. + // This works regardless of \Seen flag, so other clients reading mail won't interfere. + var uidSet imap.UIDSet + if c.lastUID > 0 { + uidSet.AddRange(c.lastUID+1, 0) + } else { + uidSet.AddRange(1, 0) + } + + fetchOpts := &imap.FetchOptions{ + Envelope: true, + UID: true, + BodySection: []*imap.FetchItemBodySection{{}}, + } + fetchCmd := client.Fetch(uidSet, fetchOpts) + defer fetchCmd.Close() + + isFirstRun := c.lastUID == 0 + processed := 0 + + for { + msgData := fetchCmd.Next() + if msgData == nil { + break + } + + buf, err := msgData.Collect() + if err != nil || buf.Envelope == nil { + continue + } + + if buf.UID > c.lastUID { + c.lastUID = buf.UID + } + + // On first run, just record the highest UID without processing old messages + if isFirstRun { + continue + } + + inbound := c.bufToInbound(buf) + if inbound == nil { + continue + } + processed++ + + if err := c.handler(ctx, c.providerID, *inbound); err != nil { + c.logger.Error("inbound handler failed", slog.Any("error", err)) + } + } + + c.logger.Info("imap fetch completed", slog.Int("processed", processed), slog.Uint64("last_uid", uint64(c.lastUID))) +} + +func (c *imapConn) bufToInbound(buf *imapclient.FetchMessageBuffer) *email.InboundEmail { + env := buf.Envelope + if env == nil { + return nil + } + + var bodyText string + if len(buf.BodySection) > 0 { + bodyText = string(buf.BodySection[0].Bytes) + } + + from := "" + if len(env.From) > 0 { + from = env.From[0].Addr() + } + var to []string + for _, addr := range env.To { + to = append(to, addr.Addr()) + } + + return &email.InboundEmail{ + MessageID: env.MessageID, + From: from, + To: to, + Subject: env.Subject, + BodyText: bodyText, + ReceivedAt: env.Date, + } +} + +// ---- MailboxReader (on-demand IMAP queries) ---- + +func (a *Adapter) dialIMAP(config map[string]any) (*imapclient.Client, error) { + host, _ := config["imap_host"].(string) + port := intVal(config["imap_port"], 993) + username, _ := config["username"].(string) + password, _ := config["password"].(string) + security, _ := config["imap_security"].(string) + + addr := fmt.Sprintf("%s:%d", host, port) + opts := &imapclient.Options{TLSConfig: &tls.Config{ServerName: host}} + + var client *imapclient.Client + var err error + switch security { + case "starttls": + client, err = imapclient.DialStartTLS(addr, opts) + case "none": + client, err = imapclient.DialInsecure(addr, opts) + default: + client, err = imapclient.DialTLS(addr, opts) + } + if err != nil { + return nil, err + } + if err := client.Login(username, password).Wait(); err != nil { + client.Close() + return nil, err + } + if _, err := client.Select("INBOX", nil).Wait(); err != nil { + client.Close() + return nil, err + } + return client, nil +} + +func (a *Adapter) ListMailbox(ctx context.Context, config map[string]any, page, pageSize int) ([]email.InboundEmail, int, error) { + client, err := a.dialIMAP(config) + if err != nil { + return nil, 0, fmt.Errorf("imap connect: %w", err) + } + defer client.Close() + + // Get total message count via STATUS + statusData, err := client.Status("INBOX", &imap.StatusOptions{NumMessages: true}).Wait() + if err != nil { + return nil, 0, fmt.Errorf("imap status: %w", err) + } + var total int + if statusData.NumMessages != nil { + total = int(*statusData.NumMessages) + } + if total == 0 { + return nil, 0, nil + } + + // Calculate sequence range for the requested page (newest first) + end := total - (page * pageSize) + start := end - pageSize + 1 + if start < 1 { + start = 1 + } + if end < 1 { + return nil, total, nil + } + + seqSet := imap.SeqSet{} + seqSet.AddRange(uint32(start), uint32(end)) + + fetchOpts := &imap.FetchOptions{ + Envelope: true, + UID: true, + } + fetchCmd := client.Fetch(seqSet, fetchOpts) + defer fetchCmd.Close() + + var results []email.InboundEmail + for { + msgData := fetchCmd.Next() + if msgData == nil { + break + } + buf, err := msgData.Collect() + if err != nil || buf.Envelope == nil { + continue + } + env := buf.Envelope + from := "" + if len(env.From) > 0 { + from = env.From[0].Addr() + } + results = append(results, email.InboundEmail{ + MessageID: fmt.Sprintf("%d", buf.UID), + From: from, + Subject: env.Subject, + ReceivedAt: env.Date, + }) + } + + // Reverse to show newest first + for i, j := 0, len(results)-1; i < j; i, j = i+1, j-1 { + results[i], results[j] = results[j], results[i] + } + + return results, total, nil +} + +func (a *Adapter) ReadMailbox(ctx context.Context, config map[string]any, uid uint32) (*email.InboundEmail, error) { + client, err := a.dialIMAP(config) + if err != nil { + return nil, fmt.Errorf("imap connect: %w", err) + } + defer client.Close() + + uidSet := imap.UIDSet{} + uidSet.AddNum(imap.UID(uid)) + + fetchOpts := &imap.FetchOptions{ + Envelope: true, + UID: true, + BodySection: []*imap.FetchItemBodySection{{}}, + } + fetchCmd := client.Fetch(uidSet, fetchOpts) + defer fetchCmd.Close() + + msgData := fetchCmd.Next() + if msgData == nil { + return nil, fmt.Errorf("email not found: UID %d", uid) + } + + buf, err := msgData.Collect() + if err != nil || buf.Envelope == nil { + return nil, fmt.Errorf("failed to parse email UID %d", uid) + } + + env := buf.Envelope + from := "" + if len(env.From) > 0 { + from = env.From[0].Addr() + } + var to []string + for _, addr := range env.To { + to = append(to, addr.Addr()) + } + var bodyText string + if len(buf.BodySection) > 0 { + bodyText = string(buf.BodySection[0].Bytes) + } + + return &email.InboundEmail{ + MessageID: fmt.Sprintf("%d", buf.UID), + From: from, + To: to, + Subject: env.Subject, + BodyText: bodyText, + ReceivedAt: env.Date, + }, nil +} + +func intVal(v any, fallback int) int { + switch n := v.(type) { + case float64: + return int(n) + case int: + return n + default: + return fallback + } +} diff --git a/internal/email/adapters/mailgun/adapter.go b/internal/email/adapters/mailgun/adapter.go new file mode 100644 index 00000000..0c1990eb --- /dev/null +++ b/internal/email/adapters/mailgun/adapter.go @@ -0,0 +1,270 @@ +package mailgun + +import ( + "context" + "crypto/hmac" + "crypto/sha256" + "encoding/hex" + "fmt" + "log/slog" + "net/http" + "strings" + "sync" + "time" + + mg "github.com/mailgun/mailgun-go/v5" + "github.com/mailgun/mailgun-go/v5/events" + + "github.com/memohai/memoh/internal/email" +) + +const ( + InboundModeWebhook = "webhook" + InboundModePoll = "poll" +) + +const ProviderName email.ProviderName = "mailgun" + +type Adapter struct { + logger *slog.Logger +} + +func New(log *slog.Logger) *Adapter { + return &Adapter{logger: log.With(slog.String("adapter", "mailgun"))} +} + +func (a *Adapter) Type() email.ProviderName { return ProviderName } + +func (a *Adapter) Meta() email.ProviderMeta { + return email.ProviderMeta{ + Provider: string(ProviderName), + DisplayName: "Mailgun", + ConfigSchema: email.ConfigSchema{ + Fields: []email.FieldSchema{ + {Key: "domain", Type: "string", Title: "Domain", Required: true, Example: "mg.example.com", Order: 1}, + {Key: "api_key", Type: "secret", Title: "API Key", Required: true, Order: 2}, + {Key: "region", Type: "enum", Title: "Region", Enum: []string{"us", "eu"}, Example: "us", Order: 3}, + {Key: "inbound_mode", Type: "enum", Title: "Inbound Mode", Description: "webhook requires public IP; poll does not", Enum: []string{InboundModeWebhook, InboundModePoll}, Example: InboundModePoll, Order: 4}, + {Key: "webhook_signing_key", Type: "secret", Title: "Webhook Signing Key", Description: "Required for webhook mode", Order: 5}, + {Key: "poll_interval_seconds", Type: "number", Title: "Poll Interval (seconds)", Description: "For poll mode (minimum 15)", Example: 30, Order: 6}, + }, + }, + } +} + +func (a *Adapter) NormalizeConfig(raw map[string]any) (map[string]any, error) { + for _, key := range []string{"domain", "api_key"} { + if v, _ := raw[key].(string); strings.TrimSpace(v) == "" { + return nil, fmt.Errorf("%s is required", key) + } + } + mode, _ := raw["inbound_mode"].(string) + if mode == "" { + raw["inbound_mode"] = InboundModePoll + } + if mode == InboundModeWebhook { + if v, _ := raw["webhook_signing_key"].(string); strings.TrimSpace(v) == "" { + return nil, fmt.Errorf("webhook_signing_key is required for webhook mode") + } + } + if _, ok := raw["region"]; !ok { + raw["region"] = "us" + } + if _, ok := raw["poll_interval_seconds"]; !ok { + raw["poll_interval_seconds"] = float64(30) + } + return raw, nil +} + +func newClient(config map[string]any) *mg.Client { + apiKey, _ := config["api_key"].(string) + client := mg.NewMailgun(apiKey) + region, _ := config["region"].(string) + if region == "eu" { + client.SetAPIBase(mg.APIBaseEU) + } + return client +} + +// ---- Sender ---- + +func (a *Adapter) Send(ctx context.Context, config map[string]any, msg email.OutboundEmail) (string, error) { + client := newClient(config) + domain, _ := config["domain"].(string) + + from := fmt.Sprintf("noreply@%s", domain) + + m := mg.NewMessage(domain, from, msg.Subject, msg.Body, msg.To...) + if msg.HTML { + m.SetHTML(msg.Body) + } + + resp, err := client.Send(ctx, m) + if err != nil { + return "", fmt.Errorf("mailgun send: %w", err) + } + return resp.ID, nil +} + +// ---- Receiver (poll mode) ---- + +func (a *Adapter) StartReceiving(ctx context.Context, config map[string]any, handler email.InboundHandler) (email.Stopper, error) { + mode, _ := config["inbound_mode"].(string) + if mode == InboundModeWebhook { + return &noopStopper{}, nil + } + + pollInterval := intVal(config["poll_interval_seconds"], 30) + if pollInterval < 15 { + pollInterval = 15 + } + providerID, _ := config["_provider_id"].(string) + domain, _ := config["domain"].(string) + + rctx, cancel := context.WithCancel(ctx) + conn := &pollConn{ + logger: a.logger, + client: newClient(config), + domain: domain, + pollInterval: time.Duration(pollInterval) * time.Second, + providerID: providerID, + handler: handler, + cancel: cancel, + } + go conn.run(rctx) + return conn, nil +} + +// ---- WebhookReceiver ---- + +func (a *Adapter) HandleWebhook(_ context.Context, config map[string]any, r *http.Request) (*email.InboundEmail, error) { + signingKey, _ := config["webhook_signing_key"].(string) + + if err := r.ParseMultipartForm(10 << 20); err != nil { + if err2 := r.ParseForm(); err2 != nil { + return nil, fmt.Errorf("parse form: %w", err2) + } + } + + timestamp := r.FormValue("timestamp") + token := r.FormValue("token") + signature := r.FormValue("signature") + if signingKey != "" { + mac := hmac.New(sha256.New, []byte(signingKey)) + mac.Write([]byte(timestamp + token)) + expected := hex.EncodeToString(mac.Sum(nil)) + if !hmac.Equal([]byte(expected), []byte(signature)) { + return nil, fmt.Errorf("webhook signature verification failed") + } + } + + toAddrs := strings.Split(r.FormValue("recipient"), ",") + for i := range toAddrs { + toAddrs[i] = strings.TrimSpace(toAddrs[i]) + } + + return &email.InboundEmail{ + MessageID: r.FormValue("Message-Id"), + From: r.FormValue("sender"), + To: toAddrs, + Subject: r.FormValue("subject"), + BodyText: r.FormValue("body-plain"), + BodyHTML: r.FormValue("body-html"), + ReceivedAt: time.Now(), + }, nil +} + +// ---- Poll connection ---- + +type pollConn struct { + logger *slog.Logger + client *mg.Client + domain string + pollInterval time.Duration + providerID string + handler email.InboundHandler + cancel context.CancelFunc + once sync.Once + lastTime time.Time +} + +func (c *pollConn) Stop(_ context.Context) error { + c.once.Do(func() { c.cancel() }) + return nil +} + +func (c *pollConn) run(ctx context.Context) { + c.lastTime = time.Now().Add(-1 * time.Hour) + for { + c.pollEvents(ctx) + select { + case <-ctx.Done(): + return + case <-time.After(c.pollInterval): + } + } +} + +func (c *pollConn) pollEvents(ctx context.Context) { + opts := &mg.ListEventOptions{ + Begin: c.lastTime, + End: time.Now(), + Limit: 100, + Filter: map[string]string{"event": "stored"}, + } + + iter := c.client.ListEvents(c.domain, opts) + var evts []events.Event + if !iter.Next(ctx, &evts) { + if err := iter.Err(); err != nil { + c.logger.Error("mailgun events poll failed", slog.Any("error", err)) + } + return + } + + for _, evt := range evts { + stored, ok := evt.(*events.Stored) + if !ok { + continue + } + + ts := stored.GetTimestamp() + if ts.After(c.lastTime) { + c.lastTime = ts.Add(time.Millisecond) + } + + inbound := email.InboundEmail{ + MessageID: stored.Message.Headers.MessageID, + From: stored.Message.Headers.From, + To: []string{stored.Message.Headers.To}, + Subject: stored.Message.Headers.Subject, + ReceivedAt: ts, + } + + if err := c.handler(ctx, c.providerID, inbound); err != nil { + c.logger.Error("inbound handler failed", slog.Any("error", err)) + } + } +} + +type noopStopper struct{} + +func (n *noopStopper) Stop(_ context.Context) error { return nil } + +func intVal(v any, fallback int) int { + switch n := v.(type) { + case float64: + return int(n) + case int: + return n + default: + return fallback + } +} + +var ( + _ email.Adapter = (*Adapter)(nil) + _ email.Sender = (*Adapter)(nil) + _ email.Receiver = (*Adapter)(nil) + _ email.WebhookReceiver = (*Adapter)(nil) +) diff --git a/internal/email/manager.go b/internal/email/manager.go new file mode 100644 index 00000000..f860b015 --- /dev/null +++ b/internal/email/manager.go @@ -0,0 +1,179 @@ +package email + +import ( + "context" + "fmt" + "log/slog" + "sync" +) + +// Manager manages the lifecycle of all email receiving connections. +type Manager struct { + logger *slog.Logger + service *Service + trigger *Trigger + outbox *OutboxService + + mu sync.Mutex + conns map[string]Stopper // provider_id -> stopper + stopped bool +} + +func NewManager(log *slog.Logger, service *Service, trigger *Trigger, outbox *OutboxService) *Manager { + return &Manager{ + logger: log.With(slog.String("component", "email_manager")), + service: service, + trigger: trigger, + outbox: outbox, + conns: make(map[string]Stopper), + } +} + +// Start initializes receiving for all providers that have readable bindings. +func (m *Manager) Start(ctx context.Context) error { + providers, err := m.service.ListProviders(ctx, "") + if err != nil { + return fmt.Errorf("list email providers: %w", err) + } + + for _, p := range providers { + bindings, err := m.service.ListReadableBindingsByProvider(ctx, p.ID) + if err != nil { + m.logger.Error("failed to list bindings", slog.String("provider", p.ID), slog.Any("error", err)) + continue + } + if len(bindings) == 0 { + continue + } + if err := m.startProvider(ctx, p); err != nil { + m.logger.Error("failed to start provider", slog.String("provider", p.ID), slog.Any("error", err)) + } + } + return nil +} + +func (m *Manager) startProvider(ctx context.Context, p ProviderResponse) error { + m.mu.Lock() + defer m.mu.Unlock() + + if m.stopped { + return fmt.Errorf("manager is stopped") + } + if _, exists := m.conns[p.ID]; exists { + return nil + } + + receiver, err := m.service.registry.GetReceiver(ProviderName(p.Provider)) + if err != nil { + return err + } + + config := p.Config + if config == nil { + config = make(map[string]any) + } + config["_provider_id"] = p.ID + + stopper, err := receiver.StartReceiving(ctx, config, m.trigger.HandleInbound) + if err != nil { + return fmt.Errorf("start receiving for %s: %w", p.ID, err) + } + + m.conns[p.ID] = stopper + m.logger.Info("started email receiving", slog.String("provider_id", p.ID), slog.String("type", p.Provider)) + return nil +} + +// RefreshProvider restarts receiving for a specific provider. +func (m *Manager) RefreshProvider(ctx context.Context, providerID string) error { + m.stopProvider(providerID) + + p, err := m.service.GetProvider(ctx, providerID) + if err != nil { + return err + } + + bindings, err := m.service.ListReadableBindingsByProvider(ctx, providerID) + if err != nil { + return err + } + if len(bindings) == 0 { + return nil + } + + return m.startProvider(ctx, p) +} + +func (m *Manager) stopProvider(providerID string) { + m.mu.Lock() + stopper, exists := m.conns[providerID] + if exists { + delete(m.conns, providerID) + } + m.mu.Unlock() + + if exists && stopper != nil { + if err := stopper.Stop(context.Background()); err != nil { + m.logger.Error("failed to stop provider", slog.String("provider_id", providerID), slog.Any("error", err)) + } + } +} + +// Stop gracefully shuts down all receiving connections. +func (m *Manager) Stop() { + m.mu.Lock() + m.stopped = true + conns := make(map[string]Stopper, len(m.conns)) + for k, v := range m.conns { + conns[k] = v + } + m.conns = make(map[string]Stopper) + m.mu.Unlock() + + for id, stopper := range conns { + if err := stopper.Stop(context.Background()); err != nil { + m.logger.Error("failed to stop provider", slog.String("provider_id", id), slog.Any("error", err)) + } + } +} + +// SendEmail sends an email through the specified provider, recording to outbox. +// If providerID is empty, it falls back to the first writable binding for the bot. +func (m *Manager) SendEmail(ctx context.Context, botID string, providerID string, msg OutboundEmail) (string, error) { + if providerID == "" { + binding, err := m.service.GetBotBinding(ctx, botID) + if err != nil { + return "", err + } + if !binding.CanWrite { + return "", fmt.Errorf("email write permission denied for bot %s", botID) + } + providerID = binding.EmailProviderID + } + + providerName, config, err := m.service.ProviderConfig(ctx, providerID) + if err != nil { + return "", err + } + + sender, err := m.service.registry.GetSender(providerName) + if err != nil { + return "", err + } + + fromAddr, _ := config["username"].(string) + + outboxID, err := m.outbox.Create(ctx, providerID, botID, msg, fromAddr) + if err != nil { + return "", fmt.Errorf("record outbox: %w", err) + } + + messageID, err := sender.Send(ctx, config, msg) + if err != nil { + _ = m.outbox.MarkFailed(ctx, outboxID, err.Error()) + return "", fmt.Errorf("send email: %w", err) + } + + _ = m.outbox.MarkSent(ctx, outboxID, messageID) + return messageID, nil +} diff --git a/internal/email/outbox.go b/internal/email/outbox.go new file mode 100644 index 00000000..48109e35 --- /dev/null +++ b/internal/email/outbox.go @@ -0,0 +1,147 @@ +package email + +import ( + "context" + "encoding/json" + "fmt" + "log/slog" + + "github.com/memohai/memoh/internal/db" + "github.com/memohai/memoh/internal/db/sqlc" +) + +// OutboxService manages the email outbox audit log. +type OutboxService struct { + queries *sqlc.Queries + logger *slog.Logger +} + +func NewOutboxService(log *slog.Logger, queries *sqlc.Queries) *OutboxService { + return &OutboxService{ + queries: queries, + logger: log.With(slog.String("service", "email_outbox")), + } +} + +// Create records a pending outbound email. +func (s *OutboxService) Create(ctx context.Context, providerID, botID string, msg OutboundEmail, fromAddr string) (string, error) { + pgProviderID, err := db.ParseUUID(providerID) + if err != nil { + return "", fmt.Errorf("invalid provider_id: %w", err) + } + pgBotID, err := db.ParseUUID(botID) + if err != nil { + return "", fmt.Errorf("invalid bot_id: %w", err) + } + toJSON, _ := json.Marshal(msg.To) + bodyText, bodyHTML := msg.Body, "" + if msg.HTML { + bodyHTML = msg.Body + bodyText = "" + } + + row, err := s.queries.CreateEmailOutbox(ctx, sqlc.CreateEmailOutboxParams{ + ProviderID: pgProviderID, + BotID: pgBotID, + FromAddress: fromAddr, + ToAddresses: toJSON, + Subject: msg.Subject, + BodyText: bodyText, + BodyHtml: bodyHTML, + Attachments: []byte("[]"), + Status: "pending", + }) + if err != nil { + return "", fmt.Errorf("create outbox: %w", err) + } + return row.ID.String(), nil +} + +// MarkSent updates the outbox record with a successful send. +func (s *OutboxService) MarkSent(ctx context.Context, id, messageID string) error { + pgID, err := db.ParseUUID(id) + if err != nil { + return err + } + return s.queries.UpdateEmailOutboxSent(ctx, sqlc.UpdateEmailOutboxSentParams{ + ID: pgID, + MessageID: messageID, + }) +} + +// MarkFailed updates the outbox record with an error. +func (s *OutboxService) MarkFailed(ctx context.Context, id, errMsg string) error { + pgID, err := db.ParseUUID(id) + if err != nil { + return err + } + return s.queries.UpdateEmailOutboxFailed(ctx, sqlc.UpdateEmailOutboxFailedParams{ + ID: pgID, + Error: errMsg, + }) +} + +func (s *OutboxService) Get(ctx context.Context, id string) (OutboxItemResponse, error) { + pgID, err := db.ParseUUID(id) + if err != nil { + return OutboxItemResponse{}, err + } + row, err := s.queries.GetEmailOutboxByID(ctx, pgID) + if err != nil { + return OutboxItemResponse{}, fmt.Errorf("get outbox: %w", err) + } + return s.toOutboxResponse(row), nil +} + +func (s *OutboxService) ListByBot(ctx context.Context, botID string, limit, offset int32) ([]OutboxItemResponse, int64, error) { + pgBotID, err := db.ParseUUID(botID) + if err != nil { + return nil, 0, err + } + rows, err := s.queries.ListEmailOutboxByBot(ctx, sqlc.ListEmailOutboxByBotParams{ + BotID: pgBotID, + Lim: limit, + Off: offset, + }) + if err != nil { + return nil, 0, fmt.Errorf("list outbox: %w", err) + } + count, err := s.queries.CountEmailOutboxByBot(ctx, pgBotID) + if err != nil { + return nil, 0, fmt.Errorf("count outbox: %w", err) + } + items := make([]OutboxItemResponse, 0, len(rows)) + for _, row := range rows { + items = append(items, s.toOutboxResponse(row)) + } + return items, count, nil +} + +func (s *OutboxService) toOutboxResponse(row sqlc.EmailOutbox) OutboxItemResponse { + var to []string + _ = json.Unmarshal(row.ToAddresses, &to) + var attachments []any + _ = json.Unmarshal(row.Attachments, &attachments) + + var sentAt interface{ IsZero() bool } + resp := OutboxItemResponse{ + ID: row.ID.String(), + ProviderID: row.ProviderID.String(), + BotID: row.BotID.String(), + MessageID: row.MessageID, + From: row.FromAddress, + To: to, + Subject: row.Subject, + BodyText: row.BodyText, + BodyHTML: row.BodyHtml, + Attachments: attachments, + Status: row.Status, + Error: row.Error, + CreatedAt: row.CreatedAt.Time, + } + _ = sentAt + if row.SentAt.Valid { + resp.SentAt = row.SentAt.Time + } + return resp +} diff --git a/internal/email/provider.go b/internal/email/provider.go new file mode 100644 index 00000000..825598ba --- /dev/null +++ b/internal/email/provider.go @@ -0,0 +1,121 @@ +package email + +import ( + "context" + "fmt" + "net/http" + "sync" +) + +// Adapter is the base interface every email adapter must implement. +type Adapter interface { + Type() ProviderName + Meta() ProviderMeta + NormalizeConfig(raw map[string]any) (map[string]any, error) +} + +// Sender sends outbound emails. +type Sender interface { + Send(ctx context.Context, config map[string]any, msg OutboundEmail) (messageID string, err error) +} + +// Receiver establishes a long-lived connection (IMAP IDLE / polling) to receive emails. +type Receiver interface { + StartReceiving(ctx context.Context, config map[string]any, handler InboundHandler) (Stopper, error) +} + +// WebhookReceiver handles inbound emails via HTTP webhook callbacks. +type WebhookReceiver interface { + HandleWebhook(ctx context.Context, config map[string]any, r *http.Request) (*InboundEmail, error) +} + +// MailboxReader lists and reads emails directly from the remote mailbox. +type MailboxReader interface { + ListMailbox(ctx context.Context, config map[string]any, page, pageSize int) ([]InboundEmail, int, error) + ReadMailbox(ctx context.Context, config map[string]any, uid uint32) (*InboundEmail, error) +} + +// Deleter removes an email from the remote mailbox. +type Deleter interface { + DeleteRemote(ctx context.Context, config map[string]any, messageID string) error +} + +// InboundHandler is invoked when a new email arrives. +type InboundHandler func(ctx context.Context, providerID string, email InboundEmail) error + +// Stopper represents a stoppable background process. +type Stopper interface { + Stop(ctx context.Context) error +} + +// Registry holds all registered email adapters. +type Registry struct { + mu sync.RWMutex + adapters map[ProviderName]Adapter +} + +func NewRegistry() *Registry { + return &Registry{adapters: make(map[ProviderName]Adapter)} +} + +func (r *Registry) Register(a Adapter) { + r.mu.Lock() + defer r.mu.Unlock() + r.adapters[a.Type()] = a +} + +func (r *Registry) Get(name ProviderName) (Adapter, error) { + r.mu.RLock() + defer r.mu.RUnlock() + a, ok := r.adapters[name] + if !ok { + return nil, fmt.Errorf("email adapter not found: %s", name) + } + return a, nil +} + +func (r *Registry) GetSender(name ProviderName) (Sender, error) { + a, err := r.Get(name) + if err != nil { + return nil, err + } + s, ok := a.(Sender) + if !ok { + return nil, fmt.Errorf("email adapter %s does not support sending", name) + } + return s, nil +} + +func (r *Registry) GetReceiver(name ProviderName) (Receiver, error) { + a, err := r.Get(name) + if err != nil { + return nil, err + } + recv, ok := a.(Receiver) + if !ok { + return nil, fmt.Errorf("email adapter %s does not support receiving", name) + } + return recv, nil +} + +func (r *Registry) GetMailboxReader(name ProviderName) (MailboxReader, error) { + a, err := r.Get(name) + if err != nil { + return nil, err + } + reader, ok := a.(MailboxReader) + if !ok { + return nil, fmt.Errorf("email adapter %s does not support mailbox reading", name) + } + return reader, nil +} + +func (r *Registry) ListMeta() []ProviderMeta { + r.mu.RLock() + defer r.mu.RUnlock() + metas := make([]ProviderMeta, 0, len(r.adapters)) + for _, a := range r.adapters { + metas = append(metas, a.Meta()) + } + return metas +} diff --git a/internal/email/service.go b/internal/email/service.go new file mode 100644 index 00000000..62cb6244 --- /dev/null +++ b/internal/email/service.go @@ -0,0 +1,359 @@ +package email + +import ( + "context" + "encoding/json" + "fmt" + "log/slog" + "strings" + + "github.com/memohai/memoh/internal/db" + "github.com/memohai/memoh/internal/db/sqlc" +) + +// Service manages email provider CRUD and bindings. +type Service struct { + queries *sqlc.Queries + logger *slog.Logger + registry *Registry +} + +func NewService(log *slog.Logger, queries *sqlc.Queries, registry *Registry) *Service { + return &Service{ + queries: queries, + logger: log.With(slog.String("service", "email")), + registry: registry, + } +} + +func (s *Service) Registry() *Registry { return s.registry } + +// ---- Provider CRUD ---- + +func (s *Service) ListMeta(_ context.Context) []ProviderMeta { + return s.registry.ListMeta() +} + +func (s *Service) CreateProvider(ctx context.Context, req CreateProviderRequest) (ProviderResponse, error) { + if _, err := s.registry.Get(req.Provider); err != nil { + return ProviderResponse{}, fmt.Errorf("unsupported provider: %s", req.Provider) + } + if len(req.Config) > 0 { + if a, err := s.registry.Get(req.Provider); err == nil { + normalized, normErr := a.NormalizeConfig(req.Config) + if normErr != nil { + return ProviderResponse{}, fmt.Errorf("invalid config: %w", normErr) + } + req.Config = normalized + } + } + if req.Config == nil { + req.Config = make(map[string]any) + } + configJSON, err := json.Marshal(req.Config) + if err != nil { + return ProviderResponse{}, fmt.Errorf("marshal config: %w", err) + } + row, err := s.queries.CreateEmailProvider(ctx, sqlc.CreateEmailProviderParams{ + Name: strings.TrimSpace(req.Name), + Provider: string(req.Provider), + Config: configJSON, + }) + if err != nil { + return ProviderResponse{}, fmt.Errorf("create email provider: %w", err) + } + return s.toProviderResponse(row), nil +} + +func (s *Service) GetProvider(ctx context.Context, id string) (ProviderResponse, error) { + pgID, err := db.ParseUUID(id) + if err != nil { + return ProviderResponse{}, err + } + row, err := s.queries.GetEmailProviderByID(ctx, pgID) + if err != nil { + return ProviderResponse{}, fmt.Errorf("get email provider: %w", err) + } + return s.toProviderResponse(row), nil +} + +func (s *Service) GetRawProvider(ctx context.Context, id string) (sqlc.EmailProvider, error) { + pgID, err := db.ParseUUID(id) + if err != nil { + return sqlc.EmailProvider{}, err + } + return s.queries.GetEmailProviderByID(ctx, pgID) +} + +func (s *Service) ListProviders(ctx context.Context, provider string) ([]ProviderResponse, error) { + provider = strings.TrimSpace(provider) + var ( + rows []sqlc.EmailProvider + err error + ) + if provider == "" { + rows, err = s.queries.ListEmailProviders(ctx) + } else { + rows, err = s.queries.ListEmailProvidersByProvider(ctx, provider) + } + if err != nil { + return nil, fmt.Errorf("list email providers: %w", err) + } + items := make([]ProviderResponse, 0, len(rows)) + for _, row := range rows { + items = append(items, s.toProviderResponse(row)) + } + return items, nil +} + +func (s *Service) UpdateProvider(ctx context.Context, id string, req UpdateProviderRequest) (ProviderResponse, error) { + pgID, err := db.ParseUUID(id) + if err != nil { + return ProviderResponse{}, err + } + current, err := s.queries.GetEmailProviderByID(ctx, pgID) + if err != nil { + return ProviderResponse{}, fmt.Errorf("get email provider: %w", err) + } + name := current.Name + if req.Name != nil { + name = strings.TrimSpace(*req.Name) + } + provider := current.Provider + if req.Provider != nil { + if _, err := s.registry.Get(*req.Provider); err != nil { + return ProviderResponse{}, fmt.Errorf("unsupported provider: %s", *req.Provider) + } + provider = string(*req.Provider) + } + config := current.Config + if req.Config != nil { + if a, aErr := s.registry.Get(ProviderName(provider)); aErr == nil { + normalized, normErr := a.NormalizeConfig(req.Config) + if normErr != nil { + return ProviderResponse{}, fmt.Errorf("invalid config: %w", normErr) + } + req.Config = normalized + } + configJSON, marshalErr := json.Marshal(req.Config) + if marshalErr != nil { + return ProviderResponse{}, fmt.Errorf("marshal config: %w", marshalErr) + } + config = configJSON + } + updated, err := s.queries.UpdateEmailProvider(ctx, sqlc.UpdateEmailProviderParams{ + ID: pgID, + Name: name, + Provider: provider, + Config: config, + }) + if err != nil { + return ProviderResponse{}, fmt.Errorf("update email provider: %w", err) + } + return s.toProviderResponse(updated), nil +} + +func (s *Service) DeleteProvider(ctx context.Context, id string) error { + pgID, err := db.ParseUUID(id) + if err != nil { + return err + } + return s.queries.DeleteEmailProvider(ctx, pgID) +} + +func (s *Service) toProviderResponse(row sqlc.EmailProvider) ProviderResponse { + var cfg map[string]any + if len(row.Config) > 0 { + if err := json.Unmarshal(row.Config, &cfg); err != nil { + s.logger.Warn("email provider config unmarshal failed", slog.String("id", row.ID.String()), slog.Any("error", err)) + } + } + return ProviderResponse{ + ID: row.ID.String(), + Name: row.Name, + Provider: row.Provider, + Config: cfg, + CreatedAt: row.CreatedAt.Time, + UpdatedAt: row.UpdatedAt.Time, + } +} + +// ---- Binding CRUD ---- + +func (s *Service) CreateBinding(ctx context.Context, botID string, req CreateBindingRequest) (BindingResponse, error) { + pgBotID, err := db.ParseUUID(botID) + if err != nil { + return BindingResponse{}, fmt.Errorf("invalid bot_id: %w", err) + } + pgProviderID, err := db.ParseUUID(req.EmailProviderID) + if err != nil { + return BindingResponse{}, fmt.Errorf("invalid email_provider_id: %w", err) + } + canRead, canWrite, canDelete := true, true, false + if req.CanRead != nil { + canRead = *req.CanRead + } + if req.CanWrite != nil { + canWrite = *req.CanWrite + } + if req.CanDelete != nil { + canDelete = *req.CanDelete + } + configJSON, err := json.Marshal(req.Config) + if err != nil { + return BindingResponse{}, fmt.Errorf("marshal config: %w", err) + } + row, err := s.queries.CreateBotEmailBinding(ctx, sqlc.CreateBotEmailBindingParams{ + BotID: pgBotID, + EmailProviderID: pgProviderID, + EmailAddress: strings.TrimSpace(req.EmailAddress), + CanRead: canRead, + CanWrite: canWrite, + CanDelete: canDelete, + Config: configJSON, + }) + if err != nil { + return BindingResponse{}, fmt.Errorf("create email binding: %w", err) + } + return s.toBindingResponse(row), nil +} + +func (s *Service) GetBinding(ctx context.Context, id string) (BindingResponse, error) { + pgID, err := db.ParseUUID(id) + if err != nil { + return BindingResponse{}, err + } + row, err := s.queries.GetBotEmailBindingByID(ctx, pgID) + if err != nil { + return BindingResponse{}, fmt.Errorf("get email binding: %w", err) + } + return s.toBindingResponse(row), nil +} + +func (s *Service) ListBindings(ctx context.Context, botID string) ([]BindingResponse, error) { + pgBotID, err := db.ParseUUID(botID) + if err != nil { + return nil, err + } + rows, err := s.queries.ListBotEmailBindings(ctx, pgBotID) + if err != nil { + return nil, fmt.Errorf("list email bindings: %w", err) + } + items := make([]BindingResponse, 0, len(rows)) + for _, row := range rows { + items = append(items, s.toBindingResponse(row)) + } + return items, nil +} + +func (s *Service) ListReadableBindingsByProvider(ctx context.Context, providerID string) ([]BindingResponse, error) { + pgID, err := db.ParseUUID(providerID) + if err != nil { + return nil, err + } + rows, err := s.queries.ListReadableBindingsByProvider(ctx, pgID) + if err != nil { + return nil, fmt.Errorf("list readable bindings: %w", err) + } + items := make([]BindingResponse, 0, len(rows)) + for _, row := range rows { + items = append(items, s.toBindingResponse(row)) + } + return items, nil +} + +func (s *Service) GetBotBinding(ctx context.Context, botID string) (BindingResponse, error) { + bindings, err := s.ListBindings(ctx, botID) + if err != nil { + return BindingResponse{}, err + } + if len(bindings) == 0 { + return BindingResponse{}, fmt.Errorf("no email binding for bot %s", botID) + } + return bindings[0], nil +} + +func (s *Service) UpdateBinding(ctx context.Context, id string, req UpdateBindingRequest) (BindingResponse, error) { + pgID, err := db.ParseUUID(id) + if err != nil { + return BindingResponse{}, err + } + current, err := s.queries.GetBotEmailBindingByID(ctx, pgID) + if err != nil { + return BindingResponse{}, fmt.Errorf("get email binding: %w", err) + } + emailAddr := current.EmailAddress + if req.EmailAddress != nil { + emailAddr = strings.TrimSpace(*req.EmailAddress) + } + canRead := current.CanRead + if req.CanRead != nil { + canRead = *req.CanRead + } + canWrite := current.CanWrite + if req.CanWrite != nil { + canWrite = *req.CanWrite + } + canDelete := current.CanDelete + if req.CanDelete != nil { + canDelete = *req.CanDelete + } + config := current.Config + if req.Config != nil { + configJSON, marshalErr := json.Marshal(req.Config) + if marshalErr != nil { + return BindingResponse{}, fmt.Errorf("marshal config: %w", marshalErr) + } + config = configJSON + } + updated, err := s.queries.UpdateBotEmailBinding(ctx, sqlc.UpdateBotEmailBindingParams{ + ID: pgID, + EmailAddress: emailAddr, + CanRead: canRead, + CanWrite: canWrite, + CanDelete: canDelete, + Config: config, + }) + if err != nil { + return BindingResponse{}, fmt.Errorf("update email binding: %w", err) + } + return s.toBindingResponse(updated), nil +} + +func (s *Service) DeleteBinding(ctx context.Context, id string) error { + pgID, err := db.ParseUUID(id) + if err != nil { + return err + } + return s.queries.DeleteBotEmailBinding(ctx, pgID) +} + +func (s *Service) toBindingResponse(row sqlc.BotEmailBinding) BindingResponse { + var cfg map[string]any + if len(row.Config) > 0 { + if err := json.Unmarshal(row.Config, &cfg); err != nil { + s.logger.Warn("email binding config unmarshal failed", slog.String("id", row.ID.String()), slog.Any("error", err)) + } + } + return BindingResponse{ + ID: row.ID.String(), + BotID: row.BotID.String(), + EmailProviderID: row.EmailProviderID.String(), + EmailAddress: row.EmailAddress, + CanRead: row.CanRead, + CanWrite: row.CanWrite, + CanDelete: row.CanDelete, + Config: cfg, + CreatedAt: row.CreatedAt.Time, + UpdatedAt: row.UpdatedAt.Time, + } +} + +// ProviderConfig returns the deserialized config for a given provider ID. +func (s *Service) ProviderConfig(ctx context.Context, providerID string) (ProviderName, map[string]any, error) { + resp, err := s.GetProvider(ctx, providerID) + if err != nil { + return "", nil, err + } + return ProviderName(resp.Provider), resp.Config, nil +} diff --git a/internal/email/trigger.go b/internal/email/trigger.go new file mode 100644 index 00000000..ea632c14 --- /dev/null +++ b/internal/email/trigger.go @@ -0,0 +1,86 @@ +package email + +import ( + "context" + "fmt" + "log/slog" + + "github.com/memohai/memoh/internal/inbox" +) + +// ChatTriggerer triggers a proactive bot conversation (e.g. when a new email arrives). +type ChatTriggerer interface { + TriggerBotChat(ctx context.Context, botID, content string) error +} + +// Trigger pushes a notification to bot_inbox when a new email arrives +// and immediately triggers the bot's LLM to process it. +type Trigger struct { + logger *slog.Logger + emailService *Service + botInbox *inbox.Service + chatTriggerer ChatTriggerer +} + +func NewTrigger(log *slog.Logger, emailService *Service, botInbox *inbox.Service, chatTriggerer ChatTriggerer) *Trigger { + return &Trigger{ + logger: log.With(slog.String("component", "email_trigger")), + emailService: emailService, + botInbox: botInbox, + chatTriggerer: chatTriggerer, + } +} + +// HandleInbound pushes a notification into each bound bot's inbox +// and triggers a conversation so the bot processes it immediately. +func (t *Trigger) HandleInbound(ctx context.Context, providerID string, mail InboundEmail) error { + t.logger.Info("new email arrived", + slog.String("provider_id", providerID), + slog.String("from", mail.From), + slog.String("subject", mail.Subject)) + + bindings, err := t.emailService.ListReadableBindingsByProvider(ctx, providerID) + if err != nil { + t.logger.Error("failed to list readable bindings", slog.Any("error", err)) + return err + } + + for _, binding := range bindings { + content := fmt.Sprintf("New email received at %s from %s — %s", binding.EmailAddress, mail.From, mail.Subject) + + _, err := t.botInbox.Create(ctx, inbox.CreateRequest{ + BotID: binding.BotID, + Source: "email", + Header: map[string]any{ + "provider_id": providerID, + "email_address": binding.EmailAddress, + "from": mail.From, + "subject": mail.Subject, + "message_id": mail.MessageID, + }, + Content: content, + Action: inbox.ActionTrigger, + }) + if err != nil { + t.logger.Error("failed to create bot inbox notification", + slog.String("bot_id", binding.BotID), + slog.Any("error", err)) + continue + } + t.logger.Info("bot notified of new email", + slog.String("bot_id", binding.BotID), + slog.String("from", mail.From)) + + if t.chatTriggerer != nil { + go func(botID, text string) { + if err := t.chatTriggerer.TriggerBotChat(ctx, botID, text); err != nil { + t.logger.Error("failed to trigger bot chat for email", + slog.String("bot_id", botID), + slog.Any("error", err)) + } + }(binding.BotID, content) + } + } + + return nil +} diff --git a/internal/email/types.go b/internal/email/types.go new file mode 100644 index 00000000..9246d462 --- /dev/null +++ b/internal/email/types.go @@ -0,0 +1,140 @@ +package email + +import "time" + +type ProviderName string + + +// FieldSchema describes a single configuration field for dynamic form generation. +type FieldSchema struct { + Key string `json:"key"` + Type string `json:"type"` + Title string `json:"title,omitempty"` + Description string `json:"description,omitempty"` + Required bool `json:"required,omitempty"` + Enum []string `json:"enum,omitempty"` + Example any `json:"example,omitempty"` + Order int `json:"order"` +} + +type ConfigSchema struct { + Fields []FieldSchema `json:"fields"` +} + +type ProviderMeta struct { + Provider string `json:"provider"` + DisplayName string `json:"display_name"` + ConfigSchema ConfigSchema `json:"config_schema"` +} + +// ---- Provider CRUD DTOs ---- + +type CreateProviderRequest struct { + Name string `json:"name"` + Provider ProviderName `json:"provider"` + Config map[string]any `json:"config,omitempty"` +} + +type UpdateProviderRequest struct { + Name *string `json:"name,omitempty"` + Provider *ProviderName `json:"provider,omitempty"` + Config map[string]any `json:"config,omitempty"` +} + +type ProviderResponse struct { + ID string `json:"id"` + Name string `json:"name"` + Provider string `json:"provider"` + Config map[string]any `json:"config,omitempty"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +// ---- Binding DTOs ---- + +type CreateBindingRequest struct { + EmailProviderID string `json:"email_provider_id"` + EmailAddress string `json:"email_address"` + CanRead *bool `json:"can_read,omitempty"` + CanWrite *bool `json:"can_write,omitempty"` + CanDelete *bool `json:"can_delete,omitempty"` + Config map[string]any `json:"config,omitempty"` +} + +type UpdateBindingRequest struct { + EmailAddress *string `json:"email_address,omitempty"` + CanRead *bool `json:"can_read,omitempty"` + CanWrite *bool `json:"can_write,omitempty"` + CanDelete *bool `json:"can_delete,omitempty"` + Config map[string]any `json:"config,omitempty"` +} + +type BindingResponse struct { + ID string `json:"id"` + BotID string `json:"bot_id"` + EmailProviderID string `json:"email_provider_id"` + EmailAddress string `json:"email_address"` + CanRead bool `json:"can_read"` + CanWrite bool `json:"can_write"` + CanDelete bool `json:"can_delete"` + Config map[string]any `json:"config,omitempty"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +// ---- Email message types ---- + +type OutboundEmail struct { + To []string `json:"to"` + Subject string `json:"subject"` + Body string `json:"body"` + HTML bool `json:"html,omitempty"` +} + +type InboundEmail struct { + MessageID string `json:"message_id"` + From string `json:"from"` + To []string `json:"to"` + Subject string `json:"subject"` + BodyText string `json:"body_text"` + BodyHTML string `json:"body_html"` + Attachments []any `json:"attachments,omitempty"` + Headers map[string]any `json:"headers,omitempty"` + ReceivedAt time.Time `json:"received_at"` +} + +// ---- Inbox / Outbox response DTOs ---- + +type InboxItemResponse struct { + ID string `json:"id"` + ProviderID string `json:"provider_id"` + BotID string `json:"bot_id,omitempty"` + MessageID string `json:"message_id"` + From string `json:"from"` + To []string `json:"to"` + Subject string `json:"subject"` + BodyText string `json:"body_text,omitempty"` + BodyHTML string `json:"body_html,omitempty"` + Attachments []any `json:"attachments,omitempty"` + Headers any `json:"headers,omitempty"` + ReceivedAt time.Time `json:"received_at"` + Status string `json:"status"` + CreatedAt time.Time `json:"created_at"` +} + +type OutboxItemResponse struct { + ID string `json:"id"` + ProviderID string `json:"provider_id"` + BotID string `json:"bot_id"` + MessageID string `json:"message_id,omitempty"` + From string `json:"from"` + To []string `json:"to"` + Subject string `json:"subject"` + BodyText string `json:"body_text,omitempty"` + BodyHTML string `json:"body_html,omitempty"` + Attachments []any `json:"attachments,omitempty"` + Status string `json:"status"` + Error string `json:"error,omitempty"` + SentAt time.Time `json:"sent_at,omitempty"` + CreatedAt time.Time `json:"created_at"` +} diff --git a/internal/handlers/email_bindings.go b/internal/handlers/email_bindings.go new file mode 100644 index 00000000..6c6ac73f --- /dev/null +++ b/internal/handlers/email_bindings.go @@ -0,0 +1,141 @@ +package handlers + +import ( + "log/slog" + "net/http" + "strings" + + "github.com/labstack/echo/v4" + + "github.com/memohai/memoh/internal/email" +) + +type EmailBindingsHandler struct { + service *email.Service + manager *email.Manager + logger *slog.Logger +} + +func NewEmailBindingsHandler(log *slog.Logger, service *email.Service, manager *email.Manager) *EmailBindingsHandler { + return &EmailBindingsHandler{ + service: service, + manager: manager, + logger: log.With(slog.String("handler", "email_bindings")), + } +} + +func (h *EmailBindingsHandler) Register(e *echo.Echo) { + g := e.Group("/bots/:bot_id/email-bindings") + g.POST("", h.Create) + g.GET("", h.List) + g.PUT("/:id", h.Update) + g.DELETE("/:id", h.Delete) +} + +// Create godoc +// @Summary Bind an email provider to a bot +// @Tags email-bindings +// @Accept json +// @Produce json +// @Param bot_id path string true "Bot ID" +// @Param request body email.CreateBindingRequest true "Binding configuration" +// @Success 201 {object} email.BindingResponse +// @Failure 400 {object} ErrorResponse +// @Failure 500 {object} ErrorResponse +// @Router /bots/{bot_id}/email-bindings [post] +func (h *EmailBindingsHandler) Create(c echo.Context) error { + botID := strings.TrimSpace(c.Param("bot_id")) + if botID == "" { + return echo.NewHTTPError(http.StatusBadRequest, "bot_id is required") + } + var req email.CreateBindingRequest + if err := c.Bind(&req); err != nil { + return echo.NewHTTPError(http.StatusBadRequest, err.Error()) + } + if strings.TrimSpace(req.EmailProviderID) == "" { + return echo.NewHTTPError(http.StatusBadRequest, "email_provider_id is required") + } + if strings.TrimSpace(req.EmailAddress) == "" { + return echo.NewHTTPError(http.StatusBadRequest, "email_address is required") + } + resp, err := h.service.CreateBinding(c.Request().Context(), botID, req) + if err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) + } + // Refresh provider connections after binding change + _ = h.manager.RefreshProvider(c.Request().Context(), req.EmailProviderID) + return c.JSON(http.StatusCreated, resp) +} + +// List godoc +// @Summary List email bindings for a bot +// @Tags email-bindings +// @Produce json +// @Param bot_id path string true "Bot ID" +// @Success 200 {array} email.BindingResponse +// @Failure 500 {object} ErrorResponse +// @Router /bots/{bot_id}/email-bindings [get] +func (h *EmailBindingsHandler) List(c echo.Context) error { + botID := strings.TrimSpace(c.Param("bot_id")) + if botID == "" { + return echo.NewHTTPError(http.StatusBadRequest, "bot_id is required") + } + items, err := h.service.ListBindings(c.Request().Context(), botID) + if err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) + } + return c.JSON(http.StatusOK, items) +} + +// Update godoc +// @Summary Update an email binding +// @Tags email-bindings +// @Accept json +// @Produce json +// @Param bot_id path string true "Bot ID" +// @Param id path string true "Binding ID" +// @Param request body email.UpdateBindingRequest true "Updated binding" +// @Success 200 {object} email.BindingResponse +// @Failure 400 {object} ErrorResponse +// @Failure 500 {object} ErrorResponse +// @Router /bots/{bot_id}/email-bindings/{id} [put] +func (h *EmailBindingsHandler) Update(c echo.Context) error { + id := strings.TrimSpace(c.Param("id")) + if id == "" { + return echo.NewHTTPError(http.StatusBadRequest, "id is required") + } + var req email.UpdateBindingRequest + if err := c.Bind(&req); err != nil { + return echo.NewHTTPError(http.StatusBadRequest, err.Error()) + } + resp, err := h.service.UpdateBinding(c.Request().Context(), id, req) + if err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) + } + _ = h.manager.RefreshProvider(c.Request().Context(), resp.EmailProviderID) + return c.JSON(http.StatusOK, resp) +} + +// Delete godoc +// @Summary Remove an email binding +// @Tags email-bindings +// @Param bot_id path string true "Bot ID" +// @Param id path string true "Binding ID" +// @Success 204 "No Content" +// @Failure 500 {object} ErrorResponse +// @Router /bots/{bot_id}/email-bindings/{id} [delete] +func (h *EmailBindingsHandler) Delete(c echo.Context) error { + id := strings.TrimSpace(c.Param("id")) + if id == "" { + return echo.NewHTTPError(http.StatusBadRequest, "id is required") + } + // Get binding info before delete for refresh + binding, _ := h.service.GetBinding(c.Request().Context(), id) + if err := h.service.DeleteBinding(c.Request().Context(), id); err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) + } + if binding.EmailProviderID != "" { + _ = h.manager.RefreshProvider(c.Request().Context(), binding.EmailProviderID) + } + return c.NoContent(http.StatusNoContent) +} diff --git a/internal/handlers/email_outbox.go b/internal/handlers/email_outbox.go new file mode 100644 index 00000000..71f58ff5 --- /dev/null +++ b/internal/handlers/email_outbox.go @@ -0,0 +1,82 @@ +package handlers + +import ( + "log/slog" + "net/http" + "strconv" + "strings" + + "github.com/labstack/echo/v4" + + "github.com/memohai/memoh/internal/email" +) + +type EmailOutboxHandler struct { + outbox *email.OutboxService + logger *slog.Logger +} + +func NewEmailOutboxHandler(log *slog.Logger, outbox *email.OutboxService) *EmailOutboxHandler { + return &EmailOutboxHandler{ + outbox: outbox, + logger: log.With(slog.String("handler", "email_outbox")), + } +} + +func (h *EmailOutboxHandler) Register(e *echo.Echo) { + g := e.Group("/bots/:bot_id/email-outbox") + g.GET("", h.List) + g.GET("/:id", h.Get) +} + +// List godoc +// @Summary List outbox emails for a bot (audit) +// @Tags email-outbox +// @Produce json +// @Param bot_id path string true "Bot ID" +// @Param limit query int false "Limit" default(20) +// @Param offset query int false "Offset" default(0) +// @Success 200 {object} map[string]any +// @Failure 500 {object} ErrorResponse +// @Router /bots/{bot_id}/email-outbox [get] +func (h *EmailOutboxHandler) List(c echo.Context) error { + botID := strings.TrimSpace(c.Param("bot_id")) + if botID == "" { + return echo.NewHTTPError(http.StatusBadRequest, "bot_id is required") + } + limit, _ := strconv.Atoi(c.QueryParam("limit")) + if limit <= 0 { + limit = 20 + } + offset, _ := strconv.Atoi(c.QueryParam("offset")) + + items, total, err := h.outbox.ListByBot(c.Request().Context(), botID, int32(limit), int32(offset)) + if err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) + } + return c.JSON(http.StatusOK, map[string]any{ + "items": items, + "total": total, + }) +} + +// Get godoc +// @Summary Get outbox email detail +// @Tags email-outbox +// @Produce json +// @Param bot_id path string true "Bot ID" +// @Param id path string true "Email ID" +// @Success 200 {object} email.OutboxItemResponse +// @Failure 404 {object} ErrorResponse +// @Router /bots/{bot_id}/email-outbox/{id} [get] +func (h *EmailOutboxHandler) Get(c echo.Context) error { + id := strings.TrimSpace(c.Param("id")) + if id == "" { + return echo.NewHTTPError(http.StatusBadRequest, "id is required") + } + resp, err := h.outbox.Get(c.Request().Context(), id) + if err != nil { + return echo.NewHTTPError(http.StatusNotFound, err.Error()) + } + return c.JSON(http.StatusOK, resp) +} diff --git a/internal/handlers/email_providers.go b/internal/handlers/email_providers.go new file mode 100644 index 00000000..fa1f0a22 --- /dev/null +++ b/internal/handlers/email_providers.go @@ -0,0 +1,152 @@ +package handlers + +import ( + "log/slog" + "net/http" + "strings" + + "github.com/labstack/echo/v4" + + "github.com/memohai/memoh/internal/email" +) + +type EmailProvidersHandler struct { + service *email.Service + logger *slog.Logger +} + +func NewEmailProvidersHandler(log *slog.Logger, service *email.Service) *EmailProvidersHandler { + return &EmailProvidersHandler{ + service: service, + logger: log.With(slog.String("handler", "email_providers")), + } +} + +func (h *EmailProvidersHandler) Register(e *echo.Echo) { + g := e.Group("/email-providers") + g.GET("/meta", h.ListMeta) + g.POST("", h.Create) + g.GET("", h.List) + g.GET("/:id", h.Get) + g.PUT("/:id", h.Update) + g.DELETE("/:id", h.Delete) +} + +// ListMeta godoc +// @Summary List email provider metadata +// @Description List available email provider types and config schemas +// @Tags email-providers +// @Success 200 {array} email.ProviderMeta +// @Router /email-providers/meta [get] +func (h *EmailProvidersHandler) ListMeta(c echo.Context) error { + return c.JSON(http.StatusOK, h.service.ListMeta(c.Request().Context())) +} + +// Create godoc +// @Summary Create an email provider +// @Tags email-providers +// @Accept json +// @Produce json +// @Param request body email.CreateProviderRequest true "Email provider configuration" +// @Success 201 {object} email.ProviderResponse +// @Failure 400 {object} ErrorResponse +// @Failure 500 {object} ErrorResponse +// @Router /email-providers [post] +func (h *EmailProvidersHandler) Create(c echo.Context) error { + var req email.CreateProviderRequest + if err := c.Bind(&req); err != nil { + return echo.NewHTTPError(http.StatusBadRequest, err.Error()) + } + if strings.TrimSpace(req.Name) == "" { + return echo.NewHTTPError(http.StatusBadRequest, "name is required") + } + if strings.TrimSpace(string(req.Provider)) == "" { + return echo.NewHTTPError(http.StatusBadRequest, "provider is required") + } + resp, err := h.service.CreateProvider(c.Request().Context(), req) + if err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) + } + return c.JSON(http.StatusCreated, resp) +} + +// List godoc +// @Summary List email providers +// @Tags email-providers +// @Produce json +// @Param provider query string false "Provider type filter" +// @Success 200 {array} email.ProviderResponse +// @Failure 500 {object} ErrorResponse +// @Router /email-providers [get] +func (h *EmailProvidersHandler) List(c echo.Context) error { + items, err := h.service.ListProviders(c.Request().Context(), c.QueryParam("provider")) + if err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) + } + return c.JSON(http.StatusOK, items) +} + +// Get godoc +// @Summary Get an email provider +// @Tags email-providers +// @Produce json +// @Param id path string true "Provider ID" +// @Success 200 {object} email.ProviderResponse +// @Failure 404 {object} ErrorResponse +// @Router /email-providers/{id} [get] +func (h *EmailProvidersHandler) Get(c echo.Context) error { + id := strings.TrimSpace(c.Param("id")) + if id == "" { + return echo.NewHTTPError(http.StatusBadRequest, "id is required") + } + resp, err := h.service.GetProvider(c.Request().Context(), id) + if err != nil { + return echo.NewHTTPError(http.StatusNotFound, err.Error()) + } + return c.JSON(http.StatusOK, resp) +} + +// Update godoc +// @Summary Update an email provider +// @Tags email-providers +// @Accept json +// @Produce json +// @Param id path string true "Provider ID" +// @Param request body email.UpdateProviderRequest true "Updated configuration" +// @Success 200 {object} email.ProviderResponse +// @Failure 400 {object} ErrorResponse +// @Failure 500 {object} ErrorResponse +// @Router /email-providers/{id} [put] +func (h *EmailProvidersHandler) Update(c echo.Context) error { + id := strings.TrimSpace(c.Param("id")) + if id == "" { + return echo.NewHTTPError(http.StatusBadRequest, "id is required") + } + var req email.UpdateProviderRequest + if err := c.Bind(&req); err != nil { + return echo.NewHTTPError(http.StatusBadRequest, err.Error()) + } + resp, err := h.service.UpdateProvider(c.Request().Context(), id, req) + if err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) + } + return c.JSON(http.StatusOK, resp) +} + +// Delete godoc +// @Summary Delete an email provider +// @Tags email-providers +// @Param id path string true "Provider ID" +// @Success 204 "No Content" +// @Failure 500 {object} ErrorResponse +// @Router /email-providers/{id} [delete] +func (h *EmailProvidersHandler) Delete(c echo.Context) error { + id := strings.TrimSpace(c.Param("id")) + if id == "" { + return echo.NewHTTPError(http.StatusBadRequest, "id is required") + } + if err := h.service.DeleteProvider(c.Request().Context(), id); err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) + } + return c.NoContent(http.StatusNoContent) +} diff --git a/internal/handlers/email_webhook.go b/internal/handlers/email_webhook.go new file mode 100644 index 00000000..2063a2b0 --- /dev/null +++ b/internal/handlers/email_webhook.go @@ -0,0 +1,92 @@ +package handlers + +import ( + "encoding/json" + "log/slog" + "net/http" + "strings" + + "github.com/labstack/echo/v4" + + "github.com/memohai/memoh/internal/email" + emailmailgun "github.com/memohai/memoh/internal/email/adapters/mailgun" +) + +// EmailWebhookHandler handles inbound email webhooks (Mailgun). +// Modeled after the Feishu WebhookHandler pattern. +type EmailWebhookHandler struct { + service *email.Service + manager *email.Manager + trigger *email.Trigger + logger *slog.Logger +} + +func NewEmailWebhookHandler(log *slog.Logger, service *email.Service, manager *email.Manager, trigger *email.Trigger) *EmailWebhookHandler { + return &EmailWebhookHandler{ + service: service, + manager: manager, + trigger: trigger, + logger: log.With(slog.String("handler", "email_webhook")), + } +} + +func (h *EmailWebhookHandler) Register(e *echo.Echo) { + e.POST("/email/mailgun/webhook/:config_id", h.HandleMailgun) +} + +// HandleMailgun godoc +// @Summary Mailgun inbound email webhook +// @Description Receives inbound emails from Mailgun +// @Tags email-webhook +// @Param config_id path string true "Email provider config ID" +// @Success 200 {object} map[string]string +// @Failure 400 {object} ErrorResponse +// @Failure 403 {object} ErrorResponse +// @Failure 500 {object} ErrorResponse +// @Router /email/mailgun/webhook/{config_id} [post] +func (h *EmailWebhookHandler) HandleMailgun(c echo.Context) error { + configID := strings.TrimSpace(c.Param("config_id")) + if configID == "" { + return echo.NewHTTPError(http.StatusBadRequest, "config_id is required") + } + + provider, err := h.service.GetProvider(c.Request().Context(), configID) + if err != nil { + return echo.NewHTTPError(http.StatusNotFound, "provider not found") + } + + if provider.Provider != string(emailmailgun.ProviderName) { + return echo.NewHTTPError(http.StatusBadRequest, "provider is not mailgun") + } + + mode, _ := provider.Config["inbound_mode"].(string) + if mode != emailmailgun.InboundModeWebhook { + return echo.NewHTTPError(http.StatusBadRequest, "provider is not in webhook mode") + } + + adapter, err := h.service.Registry().Get(emailmailgun.ProviderName) + if err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, "mailgun adapter not available") + } + webhookReceiver, ok := adapter.(email.WebhookReceiver) + if !ok { + return echo.NewHTTPError(http.StatusInternalServerError, "mailgun adapter does not support webhooks") + } + + var configMap map[string]any + configBytes, _ := json.Marshal(provider.Config) + _ = json.Unmarshal(configBytes, &configMap) + + inbound, err := webhookReceiver.HandleWebhook(c.Request().Context(), configMap, c.Request()) + if err != nil { + h.logger.Error("webhook handling failed", slog.Any("error", err)) + return echo.NewHTTPError(http.StatusForbidden, err.Error()) + } + + if err := h.trigger.HandleInbound(c.Request().Context(), configID, *inbound); err != nil { + h.logger.Error("inbound processing failed", slog.Any("error", err)) + return echo.NewHTTPError(http.StatusInternalServerError, "processing failed") + } + + return c.JSON(http.StatusOK, map[string]string{"status": "ok"}) +} diff --git a/internal/mcp/providers/email/provider.go b/internal/mcp/providers/email/provider.go new file mode 100644 index 00000000..71973f72 --- /dev/null +++ b/internal/mcp/providers/email/provider.go @@ -0,0 +1,277 @@ +package email + +import ( + "context" + "fmt" + "log/slog" + "strconv" + "strings" + + "github.com/memohai/memoh/internal/email" + mcpgw "github.com/memohai/memoh/internal/mcp" +) + +const ( + toolEmailAccounts = "email_accounts" + toolEmailSend = "email_send" + toolEmailList = "email_list" + toolEmailRead = "email_read" +) + +type Executor struct { + logger *slog.Logger + service *email.Service + manager *email.Manager +} + +func NewExecutor(log *slog.Logger, service *email.Service, manager *email.Manager) *Executor { + return &Executor{ + logger: log.With(slog.String("provider", "email_tool")), + service: service, + manager: manager, + } +} + +func (e *Executor) ListTools(_ context.Context, _ mcpgw.ToolSessionContext) ([]mcpgw.ToolDescriptor, error) { + return []mcpgw.ToolDescriptor{ + { + Name: toolEmailAccounts, + Description: "List the email accounts (provider bindings) configured for this bot, including provider IDs, email addresses, and permissions.", + InputSchema: map[string]any{ + "type": "object", + "properties": map[string]any{}, + }, + }, + { + Name: toolEmailSend, + Description: "Send an email via the bot's configured email provider. Requires write permission.", + InputSchema: map[string]any{ + "type": "object", + "properties": map[string]any{ + "to": map[string]any{"type": "string", "description": "Recipient email address(es), comma-separated"}, + "subject": map[string]any{"type": "string", "description": "Email subject"}, + "body": map[string]any{"type": "string", "description": "Email body content"}, + "html": map[string]any{"type": "boolean", "description": "Whether body is HTML (default false)"}, + "provider_id": map[string]any{"type": "string", "description": "Email provider ID to send from (optional, uses default if omitted)"}, + }, + "required": []string{"to", "subject", "body"}, + }, + }, + { + Name: toolEmailList, + Description: "List emails from the mailbox (newest first). Supports pagination. Requires read permission.", + InputSchema: map[string]any{ + "type": "object", + "properties": map[string]any{ + "page": map[string]any{"type": "integer", "description": "Page number, 0-based (default 0 = newest)"}, + "page_size": map[string]any{"type": "integer", "description": "Emails per page (default 20)"}, + "provider_id": map[string]any{"type": "string", "description": "Email provider ID (optional, uses first readable binding)"}, + }, + }, + }, + { + Name: toolEmailRead, + Description: "Read the full content of an email by its UID. Requires read permission.", + InputSchema: map[string]any{ + "type": "object", + "properties": map[string]any{ + "uid": map[string]any{"type": "integer", "description": "The email UID from email_list results"}, + "provider_id": map[string]any{"type": "string", "description": "Email provider ID (optional, uses first readable binding)"}, + }, + "required": []string{"uid"}, + }, + }, + }, nil +} + +func (e *Executor) CallTool(ctx context.Context, session mcpgw.ToolSessionContext, toolName string, arguments map[string]any) (map[string]any, error) { + botID := strings.TrimSpace(session.BotID) + if botID == "" { + return mcpgw.BuildToolErrorResult("bot_id is required"), nil + } + + bindings, err := e.service.ListBindings(ctx, botID) + if err != nil || len(bindings) == 0 { + return mcpgw.BuildToolErrorResult("no email binding configured for this bot"), nil + } + + resolveReadBinding := func() *email.BindingResponse { + providerID := mcpgw.StringArg(arguments, "provider_id") + for i := range bindings { + if !bindings[i].CanRead { + continue + } + if providerID == "" || bindings[i].EmailProviderID == providerID { + return &bindings[i] + } + } + return nil + } + + resolveWriteBinding := func() *email.BindingResponse { + providerID := mcpgw.StringArg(arguments, "provider_id") + for i := range bindings { + if !bindings[i].CanWrite { + continue + } + if providerID == "" || bindings[i].EmailProviderID == providerID { + return &bindings[i] + } + } + return nil + } + + switch toolName { + case toolEmailAccounts: + return e.callAccounts(ctx, bindings) + case toolEmailSend: + binding := resolveWriteBinding() + if binding == nil { + return mcpgw.BuildToolErrorResult("email write permission denied or provider not found"), nil + } + return e.callSend(ctx, botID, binding.EmailProviderID, arguments) + case toolEmailList: + binding := resolveReadBinding() + if binding == nil { + return mcpgw.BuildToolErrorResult("email read permission denied or provider not found"), nil + } + return e.callList(ctx, binding.EmailProviderID, arguments) + case toolEmailRead: + binding := resolveReadBinding() + if binding == nil { + return mcpgw.BuildToolErrorResult("email read permission denied or provider not found"), nil + } + return e.callRead(ctx, binding.EmailProviderID, arguments) + default: + return nil, fmt.Errorf("unknown tool: %s", toolName) + } +} + +func (e *Executor) callAccounts(_ context.Context, bindings []email.BindingResponse) (map[string]any, error) { + accounts := make([]map[string]any, 0, len(bindings)) + for _, b := range bindings { + accounts = append(accounts, map[string]any{ + "provider_id": b.EmailProviderID, + "email_address": b.EmailAddress, + "can_read": b.CanRead, + "can_write": b.CanWrite, + "can_delete": b.CanDelete, + }) + } + return mcpgw.BuildToolSuccessResult(map[string]any{ + "accounts": accounts, + }), nil +} + +func (e *Executor) callSend(ctx context.Context, botID string, providerID string, args map[string]any) (map[string]any, error) { + toRaw := mcpgw.StringArg(args, "to") + subject := mcpgw.StringArg(args, "subject") + body := mcpgw.StringArg(args, "body") + isHTML, _, _ := mcpgw.BoolArg(args, "html") + + if toRaw == "" || subject == "" || body == "" { + return mcpgw.BuildToolErrorResult("to, subject, and body are required"), nil + } + + var toList []string + for _, addr := range strings.Split(toRaw, ",") { + addr = strings.TrimSpace(addr) + if addr != "" { + toList = append(toList, addr) + } + } + + msg := email.OutboundEmail{ + To: toList, + Subject: subject, + Body: body, + HTML: isHTML, + } + + messageID, err := e.manager.SendEmail(ctx, botID, providerID, msg) + if err != nil { + return mcpgw.BuildToolErrorResult(err.Error()), nil + } + + return mcpgw.BuildToolSuccessResult(map[string]any{ + "message_id": messageID, + "status": "sent", + }), nil +} + +func (e *Executor) callList(ctx context.Context, providerID string, args map[string]any) (map[string]any, error) { + providerName, config, err := e.service.ProviderConfig(ctx, providerID) + if err != nil { + return mcpgw.BuildToolErrorResult(err.Error()), nil + } + + reader, err := e.service.Registry().GetMailboxReader(providerName) + if err != nil { + return mcpgw.BuildToolErrorResult("mailbox listing not supported for this provider"), nil + } + + page, _, _ := mcpgw.IntArg(args, "page") + pageSize, _, _ := mcpgw.IntArg(args, "page_size") + if pageSize <= 0 { + pageSize = 20 + } + + emails, total, err := reader.ListMailbox(ctx, config, page, pageSize) + if err != nil { + return mcpgw.BuildToolErrorResult(err.Error()), nil + } + + summaries := make([]map[string]any, 0, len(emails)) + for _, item := range emails { + summaries = append(summaries, map[string]any{ + "uid": item.MessageID, + "from": item.From, + "subject": item.Subject, + "received_at": item.ReceivedAt, + }) + } + + return mcpgw.BuildToolSuccessResult(map[string]any{ + "emails": summaries, + "total": total, + "page": page, + }), nil +} + +func (e *Executor) callRead(ctx context.Context, providerID string, args map[string]any) (map[string]any, error) { + uidRaw, ok, _ := mcpgw.IntArg(args, "uid") + if !ok || uidRaw <= 0 { + uidStr := mcpgw.StringArg(args, "uid") + if uidStr != "" { + parsed, _ := strconv.Atoi(uidStr) + uidRaw = parsed + } + } + if uidRaw <= 0 { + return mcpgw.BuildToolErrorResult("uid is required"), nil + } + + providerName, config, err := e.service.ProviderConfig(ctx, providerID) + if err != nil { + return mcpgw.BuildToolErrorResult(err.Error()), nil + } + + reader, err := e.service.Registry().GetMailboxReader(providerName) + if err != nil { + return mcpgw.BuildToolErrorResult("mailbox reading not supported for this provider"), nil + } + + item, err := reader.ReadMailbox(ctx, config, uint32(uidRaw)) + if err != nil { + return mcpgw.BuildToolErrorResult(err.Error()), nil + } + + return mcpgw.BuildToolSuccessResult(map[string]any{ + "uid": item.MessageID, + "from": item.From, + "to": item.To, + "subject": item.Subject, + "body": item.BodyText, + "received_at": item.ReceivedAt, + }), nil +} diff --git a/internal/searchproviders/types.go b/internal/searchproviders/types.go index 7056cfa2..a17c0bff 100644 --- a/internal/searchproviders/types.go +++ b/internal/searchproviders/types.go @@ -5,10 +5,10 @@ import "time" type ProviderName string const ( - ProviderBrave ProviderName = "brave" - ProviderBing ProviderName = "bing" - ProviderGoogle ProviderName = "google" - ProviderTavily ProviderName = "tavily" + ProviderBrave ProviderName = "brave" + ProviderBing ProviderName = "bing" + ProviderGoogle ProviderName = "google" + ProviderTavily ProviderName = "tavily" ProviderSogou ProviderName = "sogou" ProviderSerper ProviderName = "serper" ProviderSearXNG ProviderName = "searxng" diff --git a/internal/server/server.go b/internal/server/server.go index a6452b4f..041aa22b 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -84,5 +84,8 @@ func shouldSkipJWT(path string) bool { if strings.HasPrefix(path, "/channels/feishu/webhook/") { return true } + if strings.HasPrefix(path, "/email/mailgun/webhook/") { + return true + } return false } diff --git a/packages/sdk/src/@pinia/colada.gen.ts b/packages/sdk/src/@pinia/colada.gen.ts index be24bf99..147a9552 100644 --- a/packages/sdk/src/@pinia/colada.gen.ts +++ b/packages/sdk/src/@pinia/colada.gen.ts @@ -4,8 +4,8 @@ import { type _JSONValue, defineQueryOptions, type UseMutationOptions } from '@p import { serializeQueryKeyValue } from '../client'; import { client } from '../client.gen'; -import { deleteBotsByBotIdContainer, deleteBotsByBotIdContainerSkills, deleteBotsByBotIdHeartbeatLogs, deleteBotsByBotIdInboxById, deleteBotsByBotIdMcpById, deleteBotsByBotIdMemory, deleteBotsByBotIdMemoryById, deleteBotsByBotIdMessages, deleteBotsByBotIdScheduleById, deleteBotsByBotIdSettings, deleteBotsByBotIdSubagentsById, deleteBotsById, deleteBotsByIdChannelByPlatform, deleteBotsByIdMembersByUserId, deleteModelsById, deleteModelsModelByModelId, deleteProvidersById, deleteSearchProvidersById, getBots, getBotsByBotIdContainer, getBotsByBotIdContainerFs, getBotsByBotIdContainerFsDownload, getBotsByBotIdContainerFsList, getBotsByBotIdContainerFsRead, getBotsByBotIdContainerSkills, getBotsByBotIdContainerSnapshots, getBotsByBotIdHeartbeatLogs, getBotsByBotIdInbox, getBotsByBotIdInboxById, getBotsByBotIdInboxCount, getBotsByBotIdMcp, getBotsByBotIdMcpById, getBotsByBotIdMcpExport, getBotsByBotIdMemory, getBotsByBotIdMemoryUsage, getBotsByBotIdMessages, getBotsByBotIdSchedule, getBotsByBotIdScheduleById, getBotsByBotIdSettings, getBotsByBotIdSubagents, getBotsByBotIdSubagentsById, getBotsByBotIdSubagentsByIdContext, getBotsByBotIdSubagentsByIdSkills, getBotsById, getBotsByIdChannelByPlatform, getBotsByIdChecks, getBotsByIdMembers, getChannels, getChannelsByPlatform, getModels, getModelsById, getModelsCount, getModelsModelByModelId, getPing, getProviders, getProvidersById, getProvidersByIdModels, getProvidersCount, getProvidersNameByName, getSearchProviders, getSearchProvidersById, getSearchProvidersMeta, getUsers, getUsersById, getUsersMe, getUsersMeChannelsByPlatform, getUsersMeIdentities, type Options, patchBotsByIdChannelByPlatformStatus, postAuthLogin, postAuthRefresh, postBots, postBotsByBotIdCliMessages, postBotsByBotIdContainer, postBotsByBotIdContainerFsDelete, postBotsByBotIdContainerFsMkdir, postBotsByBotIdContainerFsRename, postBotsByBotIdContainerFsUpload, postBotsByBotIdContainerFsWrite, postBotsByBotIdContainerSkills, postBotsByBotIdContainerSnapshots, postBotsByBotIdContainerStart, postBotsByBotIdContainerStop, postBotsByBotIdInbox, postBotsByBotIdInboxMarkRead, postBotsByBotIdMcp, postBotsByBotIdMcpOpsBatchDelete, postBotsByBotIdMcpStdio, postBotsByBotIdMcpStdioByConnectionId, postBotsByBotIdMemory, postBotsByBotIdMemoryCompact, postBotsByBotIdMemoryRebuild, postBotsByBotIdMemorySearch, postBotsByBotIdSchedule, postBotsByBotIdSettings, postBotsByBotIdSubagents, postBotsByBotIdSubagentsByIdSkills, postBotsByBotIdTools, postBotsByBotIdWebMessages, postBotsByIdChannelByPlatformSend, postBotsByIdChannelByPlatformSendChat, postEmbeddings, postModels, postProviders, postProvidersByIdTest, postSearchProviders, postUsers, putBotsByBotIdMcpById, putBotsByBotIdMcpImport, putBotsByBotIdScheduleById, putBotsByBotIdSettings, putBotsByBotIdSubagentsById, putBotsByBotIdSubagentsByIdContext, putBotsByBotIdSubagentsByIdSkills, putBotsById, putBotsByIdChannelByPlatform, putBotsByIdMembers, putBotsByIdOwner, putModelsById, putModelsModelByModelId, putProvidersById, putSearchProvidersById, putUsersById, putUsersByIdPassword, putUsersMe, putUsersMeChannelsByPlatform, putUsersMePassword } from '../sdk.gen'; -import type { DeleteBotsByBotIdContainerData, DeleteBotsByBotIdContainerError, DeleteBotsByBotIdContainerSkillsData, DeleteBotsByBotIdContainerSkillsError, DeleteBotsByBotIdContainerSkillsResponse, DeleteBotsByBotIdHeartbeatLogsData, DeleteBotsByBotIdHeartbeatLogsError, DeleteBotsByBotIdInboxByIdData, DeleteBotsByBotIdInboxByIdError, DeleteBotsByBotIdMcpByIdData, DeleteBotsByBotIdMcpByIdError, DeleteBotsByBotIdMemoryByIdData, DeleteBotsByBotIdMemoryByIdError, DeleteBotsByBotIdMemoryByIdResponse, DeleteBotsByBotIdMemoryData, DeleteBotsByBotIdMemoryError, DeleteBotsByBotIdMemoryResponse, DeleteBotsByBotIdMessagesData, DeleteBotsByBotIdMessagesError, DeleteBotsByBotIdScheduleByIdData, DeleteBotsByBotIdScheduleByIdError, DeleteBotsByBotIdSettingsData, DeleteBotsByBotIdSettingsError, DeleteBotsByBotIdSubagentsByIdData, DeleteBotsByBotIdSubagentsByIdError, DeleteBotsByIdChannelByPlatformData, DeleteBotsByIdChannelByPlatformError, DeleteBotsByIdData, DeleteBotsByIdError, DeleteBotsByIdMembersByUserIdData, DeleteBotsByIdMembersByUserIdError, DeleteBotsByIdResponse, DeleteModelsByIdData, DeleteModelsByIdError, DeleteModelsModelByModelIdData, DeleteModelsModelByModelIdError, DeleteProvidersByIdData, DeleteProvidersByIdError, DeleteSearchProvidersByIdData, DeleteSearchProvidersByIdError, GetBotsByBotIdContainerData, GetBotsByBotIdContainerFsData, GetBotsByBotIdContainerFsDownloadData, GetBotsByBotIdContainerFsListData, GetBotsByBotIdContainerFsReadData, GetBotsByBotIdContainerSkillsData, GetBotsByBotIdContainerSnapshotsData, GetBotsByBotIdHeartbeatLogsData, GetBotsByBotIdInboxByIdData, GetBotsByBotIdInboxCountData, GetBotsByBotIdInboxData, GetBotsByBotIdMcpByIdData, GetBotsByBotIdMcpData, GetBotsByBotIdMcpExportData, GetBotsByBotIdMemoryData, GetBotsByBotIdMemoryUsageData, GetBotsByBotIdMessagesData, GetBotsByBotIdScheduleByIdData, GetBotsByBotIdScheduleData, GetBotsByBotIdSettingsData, GetBotsByBotIdSubagentsByIdContextData, GetBotsByBotIdSubagentsByIdData, GetBotsByBotIdSubagentsByIdSkillsData, GetBotsByBotIdSubagentsData, GetBotsByIdChannelByPlatformData, GetBotsByIdChecksData, GetBotsByIdData, GetBotsByIdMembersData, GetBotsData, GetChannelsByPlatformData, GetChannelsData, GetModelsByIdData, GetModelsCountData, GetModelsData, GetModelsModelByModelIdData, GetPingData, GetProvidersByIdData, GetProvidersByIdModelsData, GetProvidersCountData, GetProvidersData, GetProvidersNameByNameData, GetSearchProvidersByIdData, GetSearchProvidersData, GetSearchProvidersMetaData, GetUsersByIdData, GetUsersData, GetUsersMeChannelsByPlatformData, GetUsersMeData, GetUsersMeIdentitiesData, PatchBotsByIdChannelByPlatformStatusData, PatchBotsByIdChannelByPlatformStatusError, PatchBotsByIdChannelByPlatformStatusResponse, PostAuthLoginData, PostAuthLoginError, PostAuthLoginResponse, PostAuthRefreshData, PostAuthRefreshError, PostAuthRefreshResponse, PostBotsByBotIdCliMessagesData, PostBotsByBotIdCliMessagesError, PostBotsByBotIdCliMessagesResponse, PostBotsByBotIdContainerData, PostBotsByBotIdContainerError, PostBotsByBotIdContainerFsDeleteData, PostBotsByBotIdContainerFsDeleteError, PostBotsByBotIdContainerFsDeleteResponse, PostBotsByBotIdContainerFsMkdirData, PostBotsByBotIdContainerFsMkdirError, PostBotsByBotIdContainerFsMkdirResponse, PostBotsByBotIdContainerFsRenameData, PostBotsByBotIdContainerFsRenameError, PostBotsByBotIdContainerFsRenameResponse, PostBotsByBotIdContainerFsUploadData, PostBotsByBotIdContainerFsUploadError, PostBotsByBotIdContainerFsUploadResponse, PostBotsByBotIdContainerFsWriteData, PostBotsByBotIdContainerFsWriteError, PostBotsByBotIdContainerFsWriteResponse, PostBotsByBotIdContainerResponse, PostBotsByBotIdContainerSkillsData, PostBotsByBotIdContainerSkillsError, PostBotsByBotIdContainerSkillsResponse, PostBotsByBotIdContainerSnapshotsData, PostBotsByBotIdContainerSnapshotsError, PostBotsByBotIdContainerSnapshotsResponse, PostBotsByBotIdContainerStartData, PostBotsByBotIdContainerStartError, PostBotsByBotIdContainerStartResponse, PostBotsByBotIdContainerStopData, PostBotsByBotIdContainerStopError, PostBotsByBotIdContainerStopResponse, PostBotsByBotIdInboxData, PostBotsByBotIdInboxError, PostBotsByBotIdInboxMarkReadData, PostBotsByBotIdInboxMarkReadError, PostBotsByBotIdInboxResponse, PostBotsByBotIdMcpData, PostBotsByBotIdMcpError, PostBotsByBotIdMcpOpsBatchDeleteData, PostBotsByBotIdMcpOpsBatchDeleteError, PostBotsByBotIdMcpResponse, PostBotsByBotIdMcpStdioByConnectionIdData, PostBotsByBotIdMcpStdioByConnectionIdError, PostBotsByBotIdMcpStdioByConnectionIdResponse, PostBotsByBotIdMcpStdioData, PostBotsByBotIdMcpStdioError, PostBotsByBotIdMcpStdioResponse, PostBotsByBotIdMemoryCompactData, PostBotsByBotIdMemoryCompactError, PostBotsByBotIdMemoryCompactResponse, PostBotsByBotIdMemoryData, PostBotsByBotIdMemoryError, PostBotsByBotIdMemoryRebuildData, PostBotsByBotIdMemoryRebuildError, PostBotsByBotIdMemoryRebuildResponse, PostBotsByBotIdMemoryResponse, PostBotsByBotIdMemorySearchData, PostBotsByBotIdMemorySearchError, PostBotsByBotIdMemorySearchResponse, PostBotsByBotIdScheduleData, PostBotsByBotIdScheduleError, PostBotsByBotIdScheduleResponse, PostBotsByBotIdSettingsData, PostBotsByBotIdSettingsError, PostBotsByBotIdSettingsResponse, PostBotsByBotIdSubagentsByIdSkillsData, PostBotsByBotIdSubagentsByIdSkillsError, PostBotsByBotIdSubagentsByIdSkillsResponse, PostBotsByBotIdSubagentsData, PostBotsByBotIdSubagentsError, PostBotsByBotIdSubagentsResponse, PostBotsByBotIdToolsData, PostBotsByBotIdToolsError, PostBotsByBotIdToolsResponse, PostBotsByBotIdWebMessagesData, PostBotsByBotIdWebMessagesError, PostBotsByBotIdWebMessagesResponse, PostBotsByIdChannelByPlatformSendChatData, PostBotsByIdChannelByPlatformSendChatError, PostBotsByIdChannelByPlatformSendChatResponse, PostBotsByIdChannelByPlatformSendData, PostBotsByIdChannelByPlatformSendError, PostBotsByIdChannelByPlatformSendResponse, PostBotsData, PostBotsError, PostBotsResponse, PostEmbeddingsData, PostEmbeddingsError, PostEmbeddingsResponse, PostModelsData, PostModelsError, PostModelsResponse, PostProvidersByIdTestData, PostProvidersByIdTestError, PostProvidersByIdTestResponse, PostProvidersData, PostProvidersError, PostProvidersResponse, PostSearchProvidersData, PostSearchProvidersError, PostSearchProvidersResponse, PostUsersData, PostUsersError, PostUsersResponse, PutBotsByBotIdMcpByIdData, PutBotsByBotIdMcpByIdError, PutBotsByBotIdMcpByIdResponse, PutBotsByBotIdMcpImportData, PutBotsByBotIdMcpImportError, PutBotsByBotIdMcpImportResponse, PutBotsByBotIdScheduleByIdData, PutBotsByBotIdScheduleByIdError, PutBotsByBotIdScheduleByIdResponse, PutBotsByBotIdSettingsData, PutBotsByBotIdSettingsError, PutBotsByBotIdSettingsResponse, PutBotsByBotIdSubagentsByIdContextData, PutBotsByBotIdSubagentsByIdContextError, PutBotsByBotIdSubagentsByIdContextResponse, PutBotsByBotIdSubagentsByIdData, PutBotsByBotIdSubagentsByIdError, PutBotsByBotIdSubagentsByIdResponse, PutBotsByBotIdSubagentsByIdSkillsData, PutBotsByBotIdSubagentsByIdSkillsError, PutBotsByBotIdSubagentsByIdSkillsResponse, PutBotsByIdChannelByPlatformData, PutBotsByIdChannelByPlatformError, PutBotsByIdChannelByPlatformResponse, PutBotsByIdData, PutBotsByIdError, PutBotsByIdMembersData, PutBotsByIdMembersError, PutBotsByIdMembersResponse, PutBotsByIdOwnerData, PutBotsByIdOwnerError, PutBotsByIdOwnerResponse, PutBotsByIdResponse, PutModelsByIdData, PutModelsByIdError, PutModelsByIdResponse, PutModelsModelByModelIdData, PutModelsModelByModelIdError, PutModelsModelByModelIdResponse, PutProvidersByIdData, PutProvidersByIdError, PutProvidersByIdResponse, PutSearchProvidersByIdData, PutSearchProvidersByIdError, PutSearchProvidersByIdResponse, PutUsersByIdData, PutUsersByIdError, PutUsersByIdPasswordData, PutUsersByIdPasswordError, PutUsersByIdResponse, PutUsersMeChannelsByPlatformData, PutUsersMeChannelsByPlatformError, PutUsersMeChannelsByPlatformResponse, PutUsersMeData, PutUsersMeError, PutUsersMePasswordData, PutUsersMePasswordError, PutUsersMeResponse } from '../types.gen'; +import { deleteBotsByBotIdContainer, deleteBotsByBotIdContainerSkills, deleteBotsByBotIdEmailBindingsById, deleteBotsByBotIdHeartbeatLogs, deleteBotsByBotIdInboxById, deleteBotsByBotIdMcpById, deleteBotsByBotIdMemory, deleteBotsByBotIdMemoryById, deleteBotsByBotIdMessages, deleteBotsByBotIdScheduleById, deleteBotsByBotIdSettings, deleteBotsByBotIdSubagentsById, deleteBotsById, deleteBotsByIdChannelByPlatform, deleteBotsByIdMembersByUserId, deleteEmailProvidersById, deleteModelsById, deleteModelsModelByModelId, deleteProvidersById, deleteSearchProvidersById, getBots, getBotsByBotIdContainer, getBotsByBotIdContainerFs, getBotsByBotIdContainerFsDownload, getBotsByBotIdContainerFsList, getBotsByBotIdContainerFsRead, getBotsByBotIdContainerSkills, getBotsByBotIdContainerSnapshots, getBotsByBotIdEmailBindings, getBotsByBotIdEmailOutbox, getBotsByBotIdEmailOutboxById, getBotsByBotIdHeartbeatLogs, getBotsByBotIdInbox, getBotsByBotIdInboxById, getBotsByBotIdInboxCount, getBotsByBotIdMcp, getBotsByBotIdMcpById, getBotsByBotIdMcpExport, getBotsByBotIdMemory, getBotsByBotIdMemoryUsage, getBotsByBotIdMessages, getBotsByBotIdSchedule, getBotsByBotIdScheduleById, getBotsByBotIdSettings, getBotsByBotIdSubagents, getBotsByBotIdSubagentsById, getBotsByBotIdSubagentsByIdContext, getBotsByBotIdSubagentsByIdSkills, getBotsById, getBotsByIdChannelByPlatform, getBotsByIdChecks, getBotsByIdMembers, getChannels, getChannelsByPlatform, getEmailProviders, getEmailProvidersById, getEmailProvidersMeta, getModels, getModelsById, getModelsCount, getModelsModelByModelId, getPing, getProviders, getProvidersById, getProvidersByIdModels, getProvidersCount, getProvidersNameByName, getSearchProviders, getSearchProvidersById, getSearchProvidersMeta, getUsers, getUsersById, getUsersMe, getUsersMeChannelsByPlatform, getUsersMeIdentities, type Options, patchBotsByIdChannelByPlatformStatus, postAuthLogin, postAuthRefresh, postBots, postBotsByBotIdCliMessages, postBotsByBotIdContainer, postBotsByBotIdContainerFsDelete, postBotsByBotIdContainerFsMkdir, postBotsByBotIdContainerFsRename, postBotsByBotIdContainerFsUpload, postBotsByBotIdContainerFsWrite, postBotsByBotIdContainerSkills, postBotsByBotIdContainerSnapshots, postBotsByBotIdContainerStart, postBotsByBotIdContainerStop, postBotsByBotIdEmailBindings, postBotsByBotIdInbox, postBotsByBotIdInboxMarkRead, postBotsByBotIdMcp, postBotsByBotIdMcpOpsBatchDelete, postBotsByBotIdMcpStdio, postBotsByBotIdMcpStdioByConnectionId, postBotsByBotIdMemory, postBotsByBotIdMemoryCompact, postBotsByBotIdMemoryRebuild, postBotsByBotIdMemorySearch, postBotsByBotIdSchedule, postBotsByBotIdSettings, postBotsByBotIdSubagents, postBotsByBotIdSubagentsByIdSkills, postBotsByBotIdTools, postBotsByBotIdWebMessages, postBotsByIdChannelByPlatformSend, postBotsByIdChannelByPlatformSendChat, postEmailMailgunWebhookByConfigId, postEmailProviders, postEmbeddings, postModels, postProviders, postProvidersByIdTest, postSearchProviders, postUsers, putBotsByBotIdEmailBindingsById, putBotsByBotIdMcpById, putBotsByBotIdMcpImport, putBotsByBotIdScheduleById, putBotsByBotIdSettings, putBotsByBotIdSubagentsById, putBotsByBotIdSubagentsByIdContext, putBotsByBotIdSubagentsByIdSkills, putBotsById, putBotsByIdChannelByPlatform, putBotsByIdMembers, putBotsByIdOwner, putEmailProvidersById, putModelsById, putModelsModelByModelId, putProvidersById, putSearchProvidersById, putUsersById, putUsersByIdPassword, putUsersMe, putUsersMeChannelsByPlatform, putUsersMePassword } from '../sdk.gen'; +import type { DeleteBotsByBotIdContainerData, DeleteBotsByBotIdContainerError, DeleteBotsByBotIdContainerSkillsData, DeleteBotsByBotIdContainerSkillsError, DeleteBotsByBotIdContainerSkillsResponse, DeleteBotsByBotIdEmailBindingsByIdData, DeleteBotsByBotIdEmailBindingsByIdError, DeleteBotsByBotIdHeartbeatLogsData, DeleteBotsByBotIdHeartbeatLogsError, DeleteBotsByBotIdInboxByIdData, DeleteBotsByBotIdInboxByIdError, DeleteBotsByBotIdMcpByIdData, DeleteBotsByBotIdMcpByIdError, DeleteBotsByBotIdMemoryByIdData, DeleteBotsByBotIdMemoryByIdError, DeleteBotsByBotIdMemoryByIdResponse, DeleteBotsByBotIdMemoryData, DeleteBotsByBotIdMemoryError, DeleteBotsByBotIdMemoryResponse, DeleteBotsByBotIdMessagesData, DeleteBotsByBotIdMessagesError, DeleteBotsByBotIdScheduleByIdData, DeleteBotsByBotIdScheduleByIdError, DeleteBotsByBotIdSettingsData, DeleteBotsByBotIdSettingsError, DeleteBotsByBotIdSubagentsByIdData, DeleteBotsByBotIdSubagentsByIdError, DeleteBotsByIdChannelByPlatformData, DeleteBotsByIdChannelByPlatformError, DeleteBotsByIdData, DeleteBotsByIdError, DeleteBotsByIdMembersByUserIdData, DeleteBotsByIdMembersByUserIdError, DeleteBotsByIdResponse, DeleteEmailProvidersByIdData, DeleteEmailProvidersByIdError, DeleteModelsByIdData, DeleteModelsByIdError, DeleteModelsModelByModelIdData, DeleteModelsModelByModelIdError, DeleteProvidersByIdData, DeleteProvidersByIdError, DeleteSearchProvidersByIdData, DeleteSearchProvidersByIdError, GetBotsByBotIdContainerData, GetBotsByBotIdContainerFsData, GetBotsByBotIdContainerFsDownloadData, GetBotsByBotIdContainerFsListData, GetBotsByBotIdContainerFsReadData, GetBotsByBotIdContainerSkillsData, GetBotsByBotIdContainerSnapshotsData, GetBotsByBotIdEmailBindingsData, GetBotsByBotIdEmailOutboxByIdData, GetBotsByBotIdEmailOutboxData, GetBotsByBotIdHeartbeatLogsData, GetBotsByBotIdInboxByIdData, GetBotsByBotIdInboxCountData, GetBotsByBotIdInboxData, GetBotsByBotIdMcpByIdData, GetBotsByBotIdMcpData, GetBotsByBotIdMcpExportData, GetBotsByBotIdMemoryData, GetBotsByBotIdMemoryUsageData, GetBotsByBotIdMessagesData, GetBotsByBotIdScheduleByIdData, GetBotsByBotIdScheduleData, GetBotsByBotIdSettingsData, GetBotsByBotIdSubagentsByIdContextData, GetBotsByBotIdSubagentsByIdData, GetBotsByBotIdSubagentsByIdSkillsData, GetBotsByBotIdSubagentsData, GetBotsByIdChannelByPlatformData, GetBotsByIdChecksData, GetBotsByIdData, GetBotsByIdMembersData, GetBotsData, GetChannelsByPlatformData, GetChannelsData, GetEmailProvidersByIdData, GetEmailProvidersData, GetEmailProvidersMetaData, GetModelsByIdData, GetModelsCountData, GetModelsData, GetModelsModelByModelIdData, GetPingData, GetProvidersByIdData, GetProvidersByIdModelsData, GetProvidersCountData, GetProvidersData, GetProvidersNameByNameData, GetSearchProvidersByIdData, GetSearchProvidersData, GetSearchProvidersMetaData, GetUsersByIdData, GetUsersData, GetUsersMeChannelsByPlatformData, GetUsersMeData, GetUsersMeIdentitiesData, PatchBotsByIdChannelByPlatformStatusData, PatchBotsByIdChannelByPlatformStatusError, PatchBotsByIdChannelByPlatformStatusResponse, PostAuthLoginData, PostAuthLoginError, PostAuthLoginResponse, PostAuthRefreshData, PostAuthRefreshError, PostAuthRefreshResponse, PostBotsByBotIdCliMessagesData, PostBotsByBotIdCliMessagesError, PostBotsByBotIdCliMessagesResponse, PostBotsByBotIdContainerData, PostBotsByBotIdContainerError, PostBotsByBotIdContainerFsDeleteData, PostBotsByBotIdContainerFsDeleteError, PostBotsByBotIdContainerFsDeleteResponse, PostBotsByBotIdContainerFsMkdirData, PostBotsByBotIdContainerFsMkdirError, PostBotsByBotIdContainerFsMkdirResponse, PostBotsByBotIdContainerFsRenameData, PostBotsByBotIdContainerFsRenameError, PostBotsByBotIdContainerFsRenameResponse, PostBotsByBotIdContainerFsUploadData, PostBotsByBotIdContainerFsUploadError, PostBotsByBotIdContainerFsUploadResponse, PostBotsByBotIdContainerFsWriteData, PostBotsByBotIdContainerFsWriteError, PostBotsByBotIdContainerFsWriteResponse, PostBotsByBotIdContainerResponse, PostBotsByBotIdContainerSkillsData, PostBotsByBotIdContainerSkillsError, PostBotsByBotIdContainerSkillsResponse, PostBotsByBotIdContainerSnapshotsData, PostBotsByBotIdContainerSnapshotsError, PostBotsByBotIdContainerSnapshotsResponse, PostBotsByBotIdContainerStartData, PostBotsByBotIdContainerStartError, PostBotsByBotIdContainerStartResponse, PostBotsByBotIdContainerStopData, PostBotsByBotIdContainerStopError, PostBotsByBotIdContainerStopResponse, PostBotsByBotIdEmailBindingsData, PostBotsByBotIdEmailBindingsError, PostBotsByBotIdEmailBindingsResponse, PostBotsByBotIdInboxData, PostBotsByBotIdInboxError, PostBotsByBotIdInboxMarkReadData, PostBotsByBotIdInboxMarkReadError, PostBotsByBotIdInboxResponse, PostBotsByBotIdMcpData, PostBotsByBotIdMcpError, PostBotsByBotIdMcpOpsBatchDeleteData, PostBotsByBotIdMcpOpsBatchDeleteError, PostBotsByBotIdMcpResponse, PostBotsByBotIdMcpStdioByConnectionIdData, PostBotsByBotIdMcpStdioByConnectionIdError, PostBotsByBotIdMcpStdioByConnectionIdResponse, PostBotsByBotIdMcpStdioData, PostBotsByBotIdMcpStdioError, PostBotsByBotIdMcpStdioResponse, PostBotsByBotIdMemoryCompactData, PostBotsByBotIdMemoryCompactError, PostBotsByBotIdMemoryCompactResponse, PostBotsByBotIdMemoryData, PostBotsByBotIdMemoryError, PostBotsByBotIdMemoryRebuildData, PostBotsByBotIdMemoryRebuildError, PostBotsByBotIdMemoryRebuildResponse, PostBotsByBotIdMemoryResponse, PostBotsByBotIdMemorySearchData, PostBotsByBotIdMemorySearchError, PostBotsByBotIdMemorySearchResponse, PostBotsByBotIdScheduleData, PostBotsByBotIdScheduleError, PostBotsByBotIdScheduleResponse, PostBotsByBotIdSettingsData, PostBotsByBotIdSettingsError, PostBotsByBotIdSettingsResponse, PostBotsByBotIdSubagentsByIdSkillsData, PostBotsByBotIdSubagentsByIdSkillsError, PostBotsByBotIdSubagentsByIdSkillsResponse, PostBotsByBotIdSubagentsData, PostBotsByBotIdSubagentsError, PostBotsByBotIdSubagentsResponse, PostBotsByBotIdToolsData, PostBotsByBotIdToolsError, PostBotsByBotIdToolsResponse, PostBotsByBotIdWebMessagesData, PostBotsByBotIdWebMessagesError, PostBotsByBotIdWebMessagesResponse, PostBotsByIdChannelByPlatformSendChatData, PostBotsByIdChannelByPlatformSendChatError, PostBotsByIdChannelByPlatformSendChatResponse, PostBotsByIdChannelByPlatformSendData, PostBotsByIdChannelByPlatformSendError, PostBotsByIdChannelByPlatformSendResponse, PostBotsData, PostBotsError, PostBotsResponse, PostEmailMailgunWebhookByConfigIdData, PostEmailMailgunWebhookByConfigIdError, PostEmailMailgunWebhookByConfigIdResponse, PostEmailProvidersData, PostEmailProvidersError, PostEmailProvidersResponse, PostEmbeddingsData, PostEmbeddingsError, PostEmbeddingsResponse, PostModelsData, PostModelsError, PostModelsResponse, PostProvidersByIdTestData, PostProvidersByIdTestError, PostProvidersByIdTestResponse, PostProvidersData, PostProvidersError, PostProvidersResponse, PostSearchProvidersData, PostSearchProvidersError, PostSearchProvidersResponse, PostUsersData, PostUsersError, PostUsersResponse, PutBotsByBotIdEmailBindingsByIdData, PutBotsByBotIdEmailBindingsByIdError, PutBotsByBotIdEmailBindingsByIdResponse, PutBotsByBotIdMcpByIdData, PutBotsByBotIdMcpByIdError, PutBotsByBotIdMcpByIdResponse, PutBotsByBotIdMcpImportData, PutBotsByBotIdMcpImportError, PutBotsByBotIdMcpImportResponse, PutBotsByBotIdScheduleByIdData, PutBotsByBotIdScheduleByIdError, PutBotsByBotIdScheduleByIdResponse, PutBotsByBotIdSettingsData, PutBotsByBotIdSettingsError, PutBotsByBotIdSettingsResponse, PutBotsByBotIdSubagentsByIdContextData, PutBotsByBotIdSubagentsByIdContextError, PutBotsByBotIdSubagentsByIdContextResponse, PutBotsByBotIdSubagentsByIdData, PutBotsByBotIdSubagentsByIdError, PutBotsByBotIdSubagentsByIdResponse, PutBotsByBotIdSubagentsByIdSkillsData, PutBotsByBotIdSubagentsByIdSkillsError, PutBotsByBotIdSubagentsByIdSkillsResponse, PutBotsByIdChannelByPlatformData, PutBotsByIdChannelByPlatformError, PutBotsByIdChannelByPlatformResponse, PutBotsByIdData, PutBotsByIdError, PutBotsByIdMembersData, PutBotsByIdMembersError, PutBotsByIdMembersResponse, PutBotsByIdOwnerData, PutBotsByIdOwnerError, PutBotsByIdOwnerResponse, PutBotsByIdResponse, PutEmailProvidersByIdData, PutEmailProvidersByIdError, PutEmailProvidersByIdResponse, PutModelsByIdData, PutModelsByIdError, PutModelsByIdResponse, PutModelsModelByModelIdData, PutModelsModelByModelIdError, PutModelsModelByModelIdResponse, PutProvidersByIdData, PutProvidersByIdError, PutProvidersByIdResponse, PutSearchProvidersByIdData, PutSearchProvidersByIdError, PutSearchProvidersByIdResponse, PutUsersByIdData, PutUsersByIdError, PutUsersByIdPasswordData, PutUsersByIdPasswordError, PutUsersByIdResponse, PutUsersMeChannelsByPlatformData, PutUsersMeChannelsByPlatformError, PutUsersMeChannelsByPlatformResponse, PutUsersMeData, PutUsersMeError, PutUsersMePasswordData, PutUsersMePasswordError, PutUsersMeResponse } from '../types.gen'; /** * Login @@ -430,6 +430,99 @@ export const postBotsByBotIdContainerStopMutation = (options?: Partial) => createQueryKey('getBotsByBotIdEmailBindings', options); + +/** + * List email bindings for a bot + */ +export const getBotsByBotIdEmailBindingsQuery = defineQueryOptions((options: Options) => ({ + key: getBotsByBotIdEmailBindingsQueryKey(options), + query: async (context) => { + const { data } = await getBotsByBotIdEmailBindings({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); + +/** + * Bind an email provider to a bot + */ +export const postBotsByBotIdEmailBindingsMutation = (options?: Partial>): UseMutationOptions, PostBotsByBotIdEmailBindingsError> => ({ + mutation: async (vars) => { + const { data } = await postBotsByBotIdEmailBindings({ + ...options, + ...vars, + throwOnError: true + }); + return data; + } +}); + +/** + * Remove an email binding + */ +export const deleteBotsByBotIdEmailBindingsByIdMutation = (options?: Partial>): UseMutationOptions, DeleteBotsByBotIdEmailBindingsByIdError> => ({ + mutation: async (vars) => { + const { data } = await deleteBotsByBotIdEmailBindingsById({ + ...options, + ...vars, + throwOnError: true + }); + return data; + } +}); + +/** + * Update an email binding + */ +export const putBotsByBotIdEmailBindingsByIdMutation = (options?: Partial>): UseMutationOptions, PutBotsByBotIdEmailBindingsByIdError> => ({ + mutation: async (vars) => { + const { data } = await putBotsByBotIdEmailBindingsById({ + ...options, + ...vars, + throwOnError: true + }); + return data; + } +}); + +export const getBotsByBotIdEmailOutboxQueryKey = (options: Options) => createQueryKey('getBotsByBotIdEmailOutbox', options); + +/** + * List outbox emails for a bot (audit) + */ +export const getBotsByBotIdEmailOutboxQuery = defineQueryOptions((options: Options) => ({ + key: getBotsByBotIdEmailOutboxQueryKey(options), + query: async (context) => { + const { data } = await getBotsByBotIdEmailOutbox({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); + +export const getBotsByBotIdEmailOutboxByIdQueryKey = (options: Options) => createQueryKey('getBotsByBotIdEmailOutboxById', options); + +/** + * Get outbox email detail + */ +export const getBotsByBotIdEmailOutboxByIdQuery = defineQueryOptions((options: Options) => ({ + key: getBotsByBotIdEmailOutboxByIdQueryKey(options), + query: async (context) => { + const { data } = await getBotsByBotIdEmailOutboxById({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); + /** * Delete heartbeat logs * @@ -1546,6 +1639,117 @@ export const getChannelsByPlatformQuery = defineQueryOptions((options: Options) => createQueryKey('getEmailProviders', options); + +/** + * List email providers + */ +export const getEmailProvidersQuery = defineQueryOptions((options?: Options) => ({ + key: getEmailProvidersQueryKey(options), + query: async (context) => { + const { data } = await getEmailProviders({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); + +/** + * Create an email provider + */ +export const postEmailProvidersMutation = (options?: Partial>): UseMutationOptions, PostEmailProvidersError> => ({ + mutation: async (vars) => { + const { data } = await postEmailProviders({ + ...options, + ...vars, + throwOnError: true + }); + return data; + } +}); + +export const getEmailProvidersMetaQueryKey = (options?: Options) => createQueryKey('getEmailProvidersMeta', options); + +/** + * List email provider metadata + * + * List available email provider types and config schemas + */ +export const getEmailProvidersMetaQuery = defineQueryOptions((options?: Options) => ({ + key: getEmailProvidersMetaQueryKey(options), + query: async (context) => { + const { data } = await getEmailProvidersMeta({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); + +/** + * Delete an email provider + */ +export const deleteEmailProvidersByIdMutation = (options?: Partial>): UseMutationOptions, DeleteEmailProvidersByIdError> => ({ + mutation: async (vars) => { + const { data } = await deleteEmailProvidersById({ + ...options, + ...vars, + throwOnError: true + }); + return data; + } +}); + +export const getEmailProvidersByIdQueryKey = (options: Options) => createQueryKey('getEmailProvidersById', options); + +/** + * Get an email provider + */ +export const getEmailProvidersByIdQuery = defineQueryOptions((options: Options) => ({ + key: getEmailProvidersByIdQueryKey(options), + query: async (context) => { + const { data } = await getEmailProvidersById({ + ...options, + ...context, + throwOnError: true + }); + return data; + } +})); + +/** + * Update an email provider + */ +export const putEmailProvidersByIdMutation = (options?: Partial>): UseMutationOptions, PutEmailProvidersByIdError> => ({ + mutation: async (vars) => { + const { data } = await putEmailProvidersById({ + ...options, + ...vars, + throwOnError: true + }); + return data; + } +}); + +/** + * Mailgun inbound email webhook + * + * Receives inbound emails from Mailgun + */ +export const postEmailMailgunWebhookByConfigIdMutation = (options?: Partial>): UseMutationOptions, PostEmailMailgunWebhookByConfigIdError> => ({ + mutation: async (vars) => { + const { data } = await postEmailMailgunWebhookByConfigId({ + ...options, + ...vars, + throwOnError: true + }); + return data; + } +}); + /** * Create embeddings * diff --git a/packages/sdk/src/index.ts b/packages/sdk/src/index.ts index fb50fded..765297c1 100644 --- a/packages/sdk/src/index.ts +++ b/packages/sdk/src/index.ts @@ -1,4 +1,4 @@ // This file is auto-generated by @hey-api/openapi-ts -export { deleteBotsByBotIdContainer, deleteBotsByBotIdContainerSkills, deleteBotsByBotIdHeartbeatLogs, deleteBotsByBotIdInboxById, deleteBotsByBotIdMcpById, deleteBotsByBotIdMemory, deleteBotsByBotIdMemoryById, deleteBotsByBotIdMessages, deleteBotsByBotIdScheduleById, deleteBotsByBotIdSettings, deleteBotsByBotIdSubagentsById, deleteBotsById, deleteBotsByIdChannelByPlatform, deleteBotsByIdMembersByUserId, deleteModelsById, deleteModelsModelByModelId, deleteProvidersById, deleteSearchProvidersById, getBots, getBotsByBotIdCliStream, getBotsByBotIdContainer, getBotsByBotIdContainerFs, getBotsByBotIdContainerFsDownload, getBotsByBotIdContainerFsList, getBotsByBotIdContainerFsRead, getBotsByBotIdContainerSkills, getBotsByBotIdContainerSnapshots, getBotsByBotIdHeartbeatLogs, getBotsByBotIdInbox, getBotsByBotIdInboxById, getBotsByBotIdInboxCount, getBotsByBotIdMcp, getBotsByBotIdMcpById, getBotsByBotIdMcpExport, getBotsByBotIdMemory, getBotsByBotIdMemoryUsage, getBotsByBotIdMessages, getBotsByBotIdSchedule, getBotsByBotIdScheduleById, getBotsByBotIdSettings, getBotsByBotIdSubagents, getBotsByBotIdSubagentsById, getBotsByBotIdSubagentsByIdContext, getBotsByBotIdSubagentsByIdSkills, getBotsByBotIdWebStream, getBotsById, getBotsByIdChannelByPlatform, getBotsByIdChecks, getBotsByIdMembers, getChannels, getChannelsByPlatform, getModels, getModelsById, getModelsCount, getModelsModelByModelId, getPing, getProviders, getProvidersById, getProvidersByIdModels, getProvidersCount, getProvidersNameByName, getSearchProviders, getSearchProvidersById, getSearchProvidersMeta, getUsers, getUsersById, getUsersMe, getUsersMeChannelsByPlatform, getUsersMeIdentities, type Options, patchBotsByIdChannelByPlatformStatus, postAuthLogin, postAuthRefresh, postBots, postBotsByBotIdCliMessages, postBotsByBotIdContainer, postBotsByBotIdContainerFsDelete, postBotsByBotIdContainerFsMkdir, postBotsByBotIdContainerFsRename, postBotsByBotIdContainerFsUpload, postBotsByBotIdContainerFsWrite, postBotsByBotIdContainerSkills, postBotsByBotIdContainerSnapshots, postBotsByBotIdContainerStart, postBotsByBotIdContainerStop, postBotsByBotIdInbox, postBotsByBotIdInboxMarkRead, postBotsByBotIdMcp, postBotsByBotIdMcpOpsBatchDelete, postBotsByBotIdMcpStdio, postBotsByBotIdMcpStdioByConnectionId, postBotsByBotIdMemory, postBotsByBotIdMemoryCompact, postBotsByBotIdMemoryRebuild, postBotsByBotIdMemorySearch, postBotsByBotIdSchedule, postBotsByBotIdSettings, postBotsByBotIdSubagents, postBotsByBotIdSubagentsByIdSkills, postBotsByBotIdTools, postBotsByBotIdWebMessages, postBotsByIdChannelByPlatformSend, postBotsByIdChannelByPlatformSendChat, postEmbeddings, postModels, postProviders, postProvidersByIdTest, postSearchProviders, postUsers, putBotsByBotIdMcpById, putBotsByBotIdMcpImport, putBotsByBotIdScheduleById, putBotsByBotIdSettings, putBotsByBotIdSubagentsById, putBotsByBotIdSubagentsByIdContext, putBotsByBotIdSubagentsByIdSkills, putBotsById, putBotsByIdChannelByPlatform, putBotsByIdMembers, putBotsByIdOwner, putModelsById, putModelsModelByModelId, putProvidersById, putSearchProvidersById, putUsersById, putUsersByIdPassword, putUsersMe, putUsersMeChannelsByPlatform, putUsersMePassword } from './sdk.gen'; -export type { AccountsAccount, AccountsCreateAccountRequest, AccountsListAccountsResponse, AccountsResetPasswordRequest, AccountsUpdateAccountRequest, AccountsUpdatePasswordRequest, AccountsUpdateProfileRequest, BotsBot, BotsBotCheck, BotsBotMember, BotsCreateBotRequest, BotsListBotsResponse, BotsListChecksResponse, BotsListMembersResponse, BotsTransferBotRequest, BotsUpdateBotRequest, BotsUpsertMemberRequest, ChannelAction, ChannelAttachment, ChannelAttachmentType, ChannelChannelCapabilities, ChannelChannelConfig, ChannelChannelIdentityBinding, ChannelConfigSchema, ChannelFieldSchema, ChannelFieldType, ChannelMessage, ChannelMessageFormat, ChannelMessagePart, ChannelMessagePartType, ChannelMessageTextStyle, ChannelReplyRef, ChannelSendRequest, ChannelTargetHint, ChannelTargetSpec, ChannelThreadRef, ChannelUpdateChannelStatusRequest, ChannelUpsertChannelIdentityConfigRequest, ChannelUpsertConfigRequest, ClientOptions, DeleteBotsByBotIdContainerData, DeleteBotsByBotIdContainerError, DeleteBotsByBotIdContainerErrors, DeleteBotsByBotIdContainerResponses, DeleteBotsByBotIdContainerSkillsData, DeleteBotsByBotIdContainerSkillsError, DeleteBotsByBotIdContainerSkillsErrors, DeleteBotsByBotIdContainerSkillsResponse, DeleteBotsByBotIdContainerSkillsResponses, DeleteBotsByBotIdHeartbeatLogsData, DeleteBotsByBotIdHeartbeatLogsError, DeleteBotsByBotIdHeartbeatLogsErrors, DeleteBotsByBotIdHeartbeatLogsResponses, DeleteBotsByBotIdInboxByIdData, DeleteBotsByBotIdInboxByIdError, DeleteBotsByBotIdInboxByIdErrors, DeleteBotsByBotIdInboxByIdResponses, DeleteBotsByBotIdMcpByIdData, DeleteBotsByBotIdMcpByIdError, DeleteBotsByBotIdMcpByIdErrors, DeleteBotsByBotIdMcpByIdResponses, DeleteBotsByBotIdMemoryByIdData, DeleteBotsByBotIdMemoryByIdError, DeleteBotsByBotIdMemoryByIdErrors, DeleteBotsByBotIdMemoryByIdResponse, DeleteBotsByBotIdMemoryByIdResponses, DeleteBotsByBotIdMemoryData, DeleteBotsByBotIdMemoryError, DeleteBotsByBotIdMemoryErrors, DeleteBotsByBotIdMemoryResponse, DeleteBotsByBotIdMemoryResponses, DeleteBotsByBotIdMessagesData, DeleteBotsByBotIdMessagesError, DeleteBotsByBotIdMessagesErrors, DeleteBotsByBotIdMessagesResponses, DeleteBotsByBotIdScheduleByIdData, DeleteBotsByBotIdScheduleByIdError, DeleteBotsByBotIdScheduleByIdErrors, DeleteBotsByBotIdScheduleByIdResponses, DeleteBotsByBotIdSettingsData, DeleteBotsByBotIdSettingsError, DeleteBotsByBotIdSettingsErrors, DeleteBotsByBotIdSettingsResponses, DeleteBotsByBotIdSubagentsByIdData, DeleteBotsByBotIdSubagentsByIdError, DeleteBotsByBotIdSubagentsByIdErrors, DeleteBotsByBotIdSubagentsByIdResponses, DeleteBotsByIdChannelByPlatformData, DeleteBotsByIdChannelByPlatformError, DeleteBotsByIdChannelByPlatformErrors, DeleteBotsByIdChannelByPlatformResponses, DeleteBotsByIdData, DeleteBotsByIdError, DeleteBotsByIdErrors, DeleteBotsByIdMembersByUserIdData, DeleteBotsByIdMembersByUserIdError, DeleteBotsByIdMembersByUserIdErrors, DeleteBotsByIdMembersByUserIdResponses, DeleteBotsByIdResponse, DeleteBotsByIdResponses, DeleteModelsByIdData, DeleteModelsByIdError, DeleteModelsByIdErrors, DeleteModelsByIdResponses, DeleteModelsModelByModelIdData, DeleteModelsModelByModelIdError, DeleteModelsModelByModelIdErrors, DeleteModelsModelByModelIdResponses, DeleteProvidersByIdData, DeleteProvidersByIdError, DeleteProvidersByIdErrors, DeleteProvidersByIdResponses, DeleteSearchProvidersByIdData, DeleteSearchProvidersByIdError, DeleteSearchProvidersByIdErrors, DeleteSearchProvidersByIdResponses, GetBotsByBotIdCliStreamData, GetBotsByBotIdCliStreamError, GetBotsByBotIdCliStreamErrors, GetBotsByBotIdCliStreamResponse, GetBotsByBotIdCliStreamResponses, GetBotsByBotIdContainerData, GetBotsByBotIdContainerError, GetBotsByBotIdContainerErrors, GetBotsByBotIdContainerFsData, GetBotsByBotIdContainerFsDownloadData, GetBotsByBotIdContainerFsDownloadError, GetBotsByBotIdContainerFsDownloadErrors, GetBotsByBotIdContainerFsDownloadResponses, GetBotsByBotIdContainerFsError, GetBotsByBotIdContainerFsErrors, GetBotsByBotIdContainerFsListData, GetBotsByBotIdContainerFsListError, GetBotsByBotIdContainerFsListErrors, GetBotsByBotIdContainerFsListResponse, GetBotsByBotIdContainerFsListResponses, GetBotsByBotIdContainerFsReadData, GetBotsByBotIdContainerFsReadError, GetBotsByBotIdContainerFsReadErrors, GetBotsByBotIdContainerFsReadResponse, GetBotsByBotIdContainerFsReadResponses, GetBotsByBotIdContainerFsResponse, GetBotsByBotIdContainerFsResponses, GetBotsByBotIdContainerResponse, GetBotsByBotIdContainerResponses, GetBotsByBotIdContainerSkillsData, GetBotsByBotIdContainerSkillsError, GetBotsByBotIdContainerSkillsErrors, GetBotsByBotIdContainerSkillsResponse, GetBotsByBotIdContainerSkillsResponses, GetBotsByBotIdContainerSnapshotsData, GetBotsByBotIdContainerSnapshotsError, GetBotsByBotIdContainerSnapshotsErrors, GetBotsByBotIdContainerSnapshotsResponse, GetBotsByBotIdContainerSnapshotsResponses, GetBotsByBotIdHeartbeatLogsData, GetBotsByBotIdHeartbeatLogsError, GetBotsByBotIdHeartbeatLogsErrors, GetBotsByBotIdHeartbeatLogsResponse, GetBotsByBotIdHeartbeatLogsResponses, GetBotsByBotIdInboxByIdData, GetBotsByBotIdInboxByIdError, GetBotsByBotIdInboxByIdErrors, GetBotsByBotIdInboxByIdResponse, GetBotsByBotIdInboxByIdResponses, GetBotsByBotIdInboxCountData, GetBotsByBotIdInboxCountError, GetBotsByBotIdInboxCountErrors, GetBotsByBotIdInboxCountResponse, GetBotsByBotIdInboxCountResponses, GetBotsByBotIdInboxData, GetBotsByBotIdInboxError, GetBotsByBotIdInboxErrors, GetBotsByBotIdInboxResponse, GetBotsByBotIdInboxResponses, GetBotsByBotIdMcpByIdData, GetBotsByBotIdMcpByIdError, GetBotsByBotIdMcpByIdErrors, GetBotsByBotIdMcpByIdResponse, GetBotsByBotIdMcpByIdResponses, GetBotsByBotIdMcpData, GetBotsByBotIdMcpError, GetBotsByBotIdMcpErrors, GetBotsByBotIdMcpExportData, GetBotsByBotIdMcpExportError, GetBotsByBotIdMcpExportErrors, GetBotsByBotIdMcpExportResponse, GetBotsByBotIdMcpExportResponses, GetBotsByBotIdMcpResponse, GetBotsByBotIdMcpResponses, GetBotsByBotIdMemoryData, GetBotsByBotIdMemoryError, GetBotsByBotIdMemoryErrors, GetBotsByBotIdMemoryResponse, GetBotsByBotIdMemoryResponses, GetBotsByBotIdMemoryUsageData, GetBotsByBotIdMemoryUsageError, GetBotsByBotIdMemoryUsageErrors, GetBotsByBotIdMemoryUsageResponse, GetBotsByBotIdMemoryUsageResponses, GetBotsByBotIdMessagesData, GetBotsByBotIdMessagesError, GetBotsByBotIdMessagesErrors, GetBotsByBotIdMessagesResponse, GetBotsByBotIdMessagesResponses, GetBotsByBotIdScheduleByIdData, GetBotsByBotIdScheduleByIdError, GetBotsByBotIdScheduleByIdErrors, GetBotsByBotIdScheduleByIdResponse, GetBotsByBotIdScheduleByIdResponses, GetBotsByBotIdScheduleData, GetBotsByBotIdScheduleError, GetBotsByBotIdScheduleErrors, GetBotsByBotIdScheduleResponse, GetBotsByBotIdScheduleResponses, GetBotsByBotIdSettingsData, GetBotsByBotIdSettingsError, GetBotsByBotIdSettingsErrors, GetBotsByBotIdSettingsResponse, GetBotsByBotIdSettingsResponses, GetBotsByBotIdSubagentsByIdContextData, GetBotsByBotIdSubagentsByIdContextError, GetBotsByBotIdSubagentsByIdContextErrors, GetBotsByBotIdSubagentsByIdContextResponse, GetBotsByBotIdSubagentsByIdContextResponses, GetBotsByBotIdSubagentsByIdData, GetBotsByBotIdSubagentsByIdError, GetBotsByBotIdSubagentsByIdErrors, GetBotsByBotIdSubagentsByIdResponse, GetBotsByBotIdSubagentsByIdResponses, GetBotsByBotIdSubagentsByIdSkillsData, GetBotsByBotIdSubagentsByIdSkillsError, GetBotsByBotIdSubagentsByIdSkillsErrors, GetBotsByBotIdSubagentsByIdSkillsResponse, GetBotsByBotIdSubagentsByIdSkillsResponses, GetBotsByBotIdSubagentsData, GetBotsByBotIdSubagentsError, GetBotsByBotIdSubagentsErrors, GetBotsByBotIdSubagentsResponse, GetBotsByBotIdSubagentsResponses, GetBotsByBotIdWebStreamData, GetBotsByBotIdWebStreamError, GetBotsByBotIdWebStreamErrors, GetBotsByBotIdWebStreamResponse, GetBotsByBotIdWebStreamResponses, GetBotsByIdChannelByPlatformData, GetBotsByIdChannelByPlatformError, GetBotsByIdChannelByPlatformErrors, GetBotsByIdChannelByPlatformResponse, GetBotsByIdChannelByPlatformResponses, GetBotsByIdChecksData, GetBotsByIdChecksError, GetBotsByIdChecksErrors, GetBotsByIdChecksResponse, GetBotsByIdChecksResponses, GetBotsByIdData, GetBotsByIdError, GetBotsByIdErrors, GetBotsByIdMembersData, GetBotsByIdMembersError, GetBotsByIdMembersErrors, GetBotsByIdMembersResponse, GetBotsByIdMembersResponses, GetBotsByIdResponse, GetBotsByIdResponses, GetBotsData, GetBotsError, GetBotsErrors, GetBotsResponse, GetBotsResponses, GetChannelsByPlatformData, GetChannelsByPlatformError, GetChannelsByPlatformErrors, GetChannelsByPlatformResponse, GetChannelsByPlatformResponses, GetChannelsData, GetChannelsError, GetChannelsErrors, GetChannelsResponse, GetChannelsResponses, GetModelsByIdData, GetModelsByIdError, GetModelsByIdErrors, GetModelsByIdResponse, GetModelsByIdResponses, GetModelsCountData, GetModelsCountError, GetModelsCountErrors, GetModelsCountResponse, GetModelsCountResponses, GetModelsData, GetModelsError, GetModelsErrors, GetModelsModelByModelIdData, GetModelsModelByModelIdError, GetModelsModelByModelIdErrors, GetModelsModelByModelIdResponse, GetModelsModelByModelIdResponses, GetModelsResponse, GetModelsResponses, GetPingData, GetPingResponse, GetPingResponses, GetProvidersByIdData, GetProvidersByIdError, GetProvidersByIdErrors, GetProvidersByIdModelsData, GetProvidersByIdModelsError, GetProvidersByIdModelsErrors, GetProvidersByIdModelsResponse, GetProvidersByIdModelsResponses, GetProvidersByIdResponse, GetProvidersByIdResponses, GetProvidersCountData, GetProvidersCountError, GetProvidersCountErrors, GetProvidersCountResponse, GetProvidersCountResponses, GetProvidersData, GetProvidersError, GetProvidersErrors, GetProvidersNameByNameData, GetProvidersNameByNameError, GetProvidersNameByNameErrors, GetProvidersNameByNameResponse, GetProvidersNameByNameResponses, GetProvidersResponse, GetProvidersResponses, GetSearchProvidersByIdData, GetSearchProvidersByIdError, GetSearchProvidersByIdErrors, GetSearchProvidersByIdResponse, GetSearchProvidersByIdResponses, GetSearchProvidersData, GetSearchProvidersError, GetSearchProvidersErrors, GetSearchProvidersMetaData, GetSearchProvidersMetaResponse, GetSearchProvidersMetaResponses, GetSearchProvidersResponse, GetSearchProvidersResponses, GetUsersByIdData, GetUsersByIdError, GetUsersByIdErrors, GetUsersByIdResponse, GetUsersByIdResponses, GetUsersData, GetUsersError, GetUsersErrors, GetUsersMeChannelsByPlatformData, GetUsersMeChannelsByPlatformError, GetUsersMeChannelsByPlatformErrors, GetUsersMeChannelsByPlatformResponse, GetUsersMeChannelsByPlatformResponses, GetUsersMeData, GetUsersMeError, GetUsersMeErrors, GetUsersMeIdentitiesData, GetUsersMeIdentitiesError, GetUsersMeIdentitiesErrors, GetUsersMeIdentitiesResponse, GetUsersMeIdentitiesResponses, GetUsersMeResponse, GetUsersMeResponses, GetUsersResponse, GetUsersResponses, GithubComMemohaiMemohInternalMcpConnection, HandlersBatchDeleteRequest, HandlersChannelMeta, HandlersCreateContainerRequest, HandlersCreateContainerResponse, HandlersCreateSnapshotRequest, HandlersCreateSnapshotResponse, HandlersEmbeddingsInput, HandlersEmbeddingsRequest, HandlersEmbeddingsResponse, HandlersEmbeddingsUsage, HandlersErrorResponse, HandlersFsDeleteRequest, HandlersFsFileInfo, HandlersFsListResponse, HandlersFsMkdirRequest, HandlersFsOpResponse, HandlersFsReadResponse, HandlersFsRenameRequest, HandlersFsUploadResponse, HandlersFsWriteRequest, HandlersGetContainerResponse, HandlersListMyIdentitiesResponse, HandlersListSnapshotsResponse, HandlersLocalChannelMessageRequest, HandlersLoginRequest, HandlersLoginResponse, HandlersMarkReadRequest, HandlersMcpStdioRequest, HandlersMcpStdioResponse, HandlersMemoryAddPayload, HandlersMemoryCompactPayload, HandlersMemoryDeletePayload, HandlersMemorySearchPayload, HandlersPingResponse, HandlersRefreshResponse, HandlersSkillItem, HandlersSkillsDeleteRequest, HandlersSkillsOpResponse, HandlersSkillsResponse, HandlersSkillsUpsertRequest, HandlersSnapshotInfo, HeartbeatListLogsResponse, HeartbeatLog, IdentitiesChannelIdentity, InboxCountResult, InboxCreateRequest, InboxItem, McpExportResponse, McpImportRequest, McpListResponse, McpMcpServerEntry, McpUpsertRequest, MemoryCdfPoint, MemoryCompactResult, MemoryDeleteResponse, MemoryMemoryItem, MemoryMessage, MemoryRebuildResult, MemorySearchResponse, MemoryTopKBucket, MemoryUsageResponse, MessageMessage, MessageMessageAsset, ModelsAddRequest, ModelsAddResponse, ModelsClientType, ModelsCountResponse, ModelsGetResponse, ModelsModelType, ModelsUpdateRequest, PatchBotsByIdChannelByPlatformStatusData, PatchBotsByIdChannelByPlatformStatusError, PatchBotsByIdChannelByPlatformStatusErrors, PatchBotsByIdChannelByPlatformStatusResponse, PatchBotsByIdChannelByPlatformStatusResponses, PostAuthLoginData, PostAuthLoginError, PostAuthLoginErrors, PostAuthLoginResponse, PostAuthLoginResponses, PostAuthRefreshData, PostAuthRefreshError, PostAuthRefreshErrors, PostAuthRefreshResponse, PostAuthRefreshResponses, PostBotsByBotIdCliMessagesData, PostBotsByBotIdCliMessagesError, PostBotsByBotIdCliMessagesErrors, PostBotsByBotIdCliMessagesResponse, PostBotsByBotIdCliMessagesResponses, PostBotsByBotIdContainerData, PostBotsByBotIdContainerError, PostBotsByBotIdContainerErrors, PostBotsByBotIdContainerFsDeleteData, PostBotsByBotIdContainerFsDeleteError, PostBotsByBotIdContainerFsDeleteErrors, PostBotsByBotIdContainerFsDeleteResponse, PostBotsByBotIdContainerFsDeleteResponses, PostBotsByBotIdContainerFsMkdirData, PostBotsByBotIdContainerFsMkdirError, PostBotsByBotIdContainerFsMkdirErrors, PostBotsByBotIdContainerFsMkdirResponse, PostBotsByBotIdContainerFsMkdirResponses, PostBotsByBotIdContainerFsRenameData, PostBotsByBotIdContainerFsRenameError, PostBotsByBotIdContainerFsRenameErrors, PostBotsByBotIdContainerFsRenameResponse, PostBotsByBotIdContainerFsRenameResponses, PostBotsByBotIdContainerFsUploadData, PostBotsByBotIdContainerFsUploadError, PostBotsByBotIdContainerFsUploadErrors, PostBotsByBotIdContainerFsUploadResponse, PostBotsByBotIdContainerFsUploadResponses, PostBotsByBotIdContainerFsWriteData, PostBotsByBotIdContainerFsWriteError, PostBotsByBotIdContainerFsWriteErrors, PostBotsByBotIdContainerFsWriteResponse, PostBotsByBotIdContainerFsWriteResponses, PostBotsByBotIdContainerResponse, PostBotsByBotIdContainerResponses, PostBotsByBotIdContainerSkillsData, PostBotsByBotIdContainerSkillsError, PostBotsByBotIdContainerSkillsErrors, PostBotsByBotIdContainerSkillsResponse, PostBotsByBotIdContainerSkillsResponses, PostBotsByBotIdContainerSnapshotsData, PostBotsByBotIdContainerSnapshotsError, PostBotsByBotIdContainerSnapshotsErrors, PostBotsByBotIdContainerSnapshotsResponse, PostBotsByBotIdContainerSnapshotsResponses, PostBotsByBotIdContainerStartData, PostBotsByBotIdContainerStartError, PostBotsByBotIdContainerStartErrors, PostBotsByBotIdContainerStartResponse, PostBotsByBotIdContainerStartResponses, PostBotsByBotIdContainerStopData, PostBotsByBotIdContainerStopError, PostBotsByBotIdContainerStopErrors, PostBotsByBotIdContainerStopResponse, PostBotsByBotIdContainerStopResponses, PostBotsByBotIdInboxData, PostBotsByBotIdInboxError, PostBotsByBotIdInboxErrors, PostBotsByBotIdInboxMarkReadData, PostBotsByBotIdInboxMarkReadError, PostBotsByBotIdInboxMarkReadErrors, PostBotsByBotIdInboxMarkReadResponses, PostBotsByBotIdInboxResponse, PostBotsByBotIdInboxResponses, PostBotsByBotIdMcpData, PostBotsByBotIdMcpError, PostBotsByBotIdMcpErrors, PostBotsByBotIdMcpOpsBatchDeleteData, PostBotsByBotIdMcpOpsBatchDeleteError, PostBotsByBotIdMcpOpsBatchDeleteErrors, PostBotsByBotIdMcpOpsBatchDeleteResponses, PostBotsByBotIdMcpResponse, PostBotsByBotIdMcpResponses, PostBotsByBotIdMcpStdioByConnectionIdData, PostBotsByBotIdMcpStdioByConnectionIdError, PostBotsByBotIdMcpStdioByConnectionIdErrors, PostBotsByBotIdMcpStdioByConnectionIdResponse, PostBotsByBotIdMcpStdioByConnectionIdResponses, PostBotsByBotIdMcpStdioData, PostBotsByBotIdMcpStdioError, PostBotsByBotIdMcpStdioErrors, PostBotsByBotIdMcpStdioResponse, PostBotsByBotIdMcpStdioResponses, PostBotsByBotIdMemoryCompactData, PostBotsByBotIdMemoryCompactError, PostBotsByBotIdMemoryCompactErrors, PostBotsByBotIdMemoryCompactResponse, PostBotsByBotIdMemoryCompactResponses, PostBotsByBotIdMemoryData, PostBotsByBotIdMemoryError, PostBotsByBotIdMemoryErrors, PostBotsByBotIdMemoryRebuildData, PostBotsByBotIdMemoryRebuildError, PostBotsByBotIdMemoryRebuildErrors, PostBotsByBotIdMemoryRebuildResponse, PostBotsByBotIdMemoryRebuildResponses, PostBotsByBotIdMemoryResponse, PostBotsByBotIdMemoryResponses, PostBotsByBotIdMemorySearchData, PostBotsByBotIdMemorySearchError, PostBotsByBotIdMemorySearchErrors, PostBotsByBotIdMemorySearchResponse, PostBotsByBotIdMemorySearchResponses, PostBotsByBotIdScheduleData, PostBotsByBotIdScheduleError, PostBotsByBotIdScheduleErrors, PostBotsByBotIdScheduleResponse, PostBotsByBotIdScheduleResponses, PostBotsByBotIdSettingsData, PostBotsByBotIdSettingsError, PostBotsByBotIdSettingsErrors, PostBotsByBotIdSettingsResponse, PostBotsByBotIdSettingsResponses, PostBotsByBotIdSubagentsByIdSkillsData, PostBotsByBotIdSubagentsByIdSkillsError, PostBotsByBotIdSubagentsByIdSkillsErrors, PostBotsByBotIdSubagentsByIdSkillsResponse, PostBotsByBotIdSubagentsByIdSkillsResponses, PostBotsByBotIdSubagentsData, PostBotsByBotIdSubagentsError, PostBotsByBotIdSubagentsErrors, PostBotsByBotIdSubagentsResponse, PostBotsByBotIdSubagentsResponses, PostBotsByBotIdToolsData, PostBotsByBotIdToolsError, PostBotsByBotIdToolsErrors, PostBotsByBotIdToolsResponse, PostBotsByBotIdToolsResponses, PostBotsByBotIdWebMessagesData, PostBotsByBotIdWebMessagesError, PostBotsByBotIdWebMessagesErrors, PostBotsByBotIdWebMessagesResponse, PostBotsByBotIdWebMessagesResponses, PostBotsByIdChannelByPlatformSendChatData, PostBotsByIdChannelByPlatformSendChatError, PostBotsByIdChannelByPlatformSendChatErrors, PostBotsByIdChannelByPlatformSendChatResponse, PostBotsByIdChannelByPlatformSendChatResponses, PostBotsByIdChannelByPlatformSendData, PostBotsByIdChannelByPlatformSendError, PostBotsByIdChannelByPlatformSendErrors, PostBotsByIdChannelByPlatformSendResponse, PostBotsByIdChannelByPlatformSendResponses, PostBotsData, PostBotsError, PostBotsErrors, PostBotsResponse, PostBotsResponses, PostEmbeddingsData, PostEmbeddingsError, PostEmbeddingsErrors, PostEmbeddingsResponse, PostEmbeddingsResponses, PostModelsData, PostModelsError, PostModelsErrors, PostModelsResponse, PostModelsResponses, PostProvidersByIdTestData, PostProvidersByIdTestError, PostProvidersByIdTestErrors, PostProvidersByIdTestResponse, PostProvidersByIdTestResponses, PostProvidersData, PostProvidersError, PostProvidersErrors, PostProvidersResponse, PostProvidersResponses, PostSearchProvidersData, PostSearchProvidersError, PostSearchProvidersErrors, PostSearchProvidersResponse, PostSearchProvidersResponses, PostUsersData, PostUsersError, PostUsersErrors, PostUsersResponse, PostUsersResponses, ProvidersCheckResult, ProvidersCheckStatus, ProvidersCountResponse, ProvidersCreateRequest, ProvidersGetResponse, ProvidersTestResponse, ProvidersUpdateRequest, PutBotsByBotIdMcpByIdData, PutBotsByBotIdMcpByIdError, PutBotsByBotIdMcpByIdErrors, PutBotsByBotIdMcpByIdResponse, PutBotsByBotIdMcpByIdResponses, PutBotsByBotIdMcpImportData, PutBotsByBotIdMcpImportError, PutBotsByBotIdMcpImportErrors, PutBotsByBotIdMcpImportResponse, PutBotsByBotIdMcpImportResponses, PutBotsByBotIdScheduleByIdData, PutBotsByBotIdScheduleByIdError, PutBotsByBotIdScheduleByIdErrors, PutBotsByBotIdScheduleByIdResponse, PutBotsByBotIdScheduleByIdResponses, PutBotsByBotIdSettingsData, PutBotsByBotIdSettingsError, PutBotsByBotIdSettingsErrors, PutBotsByBotIdSettingsResponse, PutBotsByBotIdSettingsResponses, PutBotsByBotIdSubagentsByIdContextData, PutBotsByBotIdSubagentsByIdContextError, PutBotsByBotIdSubagentsByIdContextErrors, PutBotsByBotIdSubagentsByIdContextResponse, PutBotsByBotIdSubagentsByIdContextResponses, PutBotsByBotIdSubagentsByIdData, PutBotsByBotIdSubagentsByIdError, PutBotsByBotIdSubagentsByIdErrors, PutBotsByBotIdSubagentsByIdResponse, PutBotsByBotIdSubagentsByIdResponses, PutBotsByBotIdSubagentsByIdSkillsData, PutBotsByBotIdSubagentsByIdSkillsError, PutBotsByBotIdSubagentsByIdSkillsErrors, PutBotsByBotIdSubagentsByIdSkillsResponse, PutBotsByBotIdSubagentsByIdSkillsResponses, PutBotsByIdChannelByPlatformData, PutBotsByIdChannelByPlatformError, PutBotsByIdChannelByPlatformErrors, PutBotsByIdChannelByPlatformResponse, PutBotsByIdChannelByPlatformResponses, PutBotsByIdData, PutBotsByIdError, PutBotsByIdErrors, PutBotsByIdMembersData, PutBotsByIdMembersError, PutBotsByIdMembersErrors, PutBotsByIdMembersResponse, PutBotsByIdMembersResponses, PutBotsByIdOwnerData, PutBotsByIdOwnerError, PutBotsByIdOwnerErrors, PutBotsByIdOwnerResponse, PutBotsByIdOwnerResponses, PutBotsByIdResponse, PutBotsByIdResponses, PutModelsByIdData, PutModelsByIdError, PutModelsByIdErrors, PutModelsByIdResponse, PutModelsByIdResponses, PutModelsModelByModelIdData, PutModelsModelByModelIdError, PutModelsModelByModelIdErrors, PutModelsModelByModelIdResponse, PutModelsModelByModelIdResponses, PutProvidersByIdData, PutProvidersByIdError, PutProvidersByIdErrors, PutProvidersByIdResponse, PutProvidersByIdResponses, PutSearchProvidersByIdData, PutSearchProvidersByIdError, PutSearchProvidersByIdErrors, PutSearchProvidersByIdResponse, PutSearchProvidersByIdResponses, PutUsersByIdData, PutUsersByIdError, PutUsersByIdErrors, PutUsersByIdPasswordData, PutUsersByIdPasswordError, PutUsersByIdPasswordErrors, PutUsersByIdPasswordResponses, PutUsersByIdResponse, PutUsersByIdResponses, PutUsersMeChannelsByPlatformData, PutUsersMeChannelsByPlatformError, PutUsersMeChannelsByPlatformErrors, PutUsersMeChannelsByPlatformResponse, PutUsersMeChannelsByPlatformResponses, PutUsersMeData, PutUsersMeError, PutUsersMeErrors, PutUsersMePasswordData, PutUsersMePasswordError, PutUsersMePasswordErrors, PutUsersMePasswordResponses, PutUsersMeResponse, PutUsersMeResponses, ScheduleCreateRequest, ScheduleListResponse, ScheduleNullableInt, ScheduleSchedule, ScheduleUpdateRequest, SearchprovidersCreateRequest, SearchprovidersGetResponse, SearchprovidersProviderConfigSchema, SearchprovidersProviderFieldSchema, SearchprovidersProviderMeta, SearchprovidersProviderName, SearchprovidersUpdateRequest, SettingsSettings, SettingsUpsertRequest, SubagentAddSkillsRequest, SubagentContextResponse, SubagentCreateRequest, SubagentListResponse, SubagentSkillsResponse, SubagentSubagent, SubagentUpdateContextRequest, SubagentUpdateRequest, SubagentUpdateSkillsRequest } from './types.gen'; +export { deleteBotsByBotIdContainer, deleteBotsByBotIdContainerSkills, deleteBotsByBotIdEmailBindingsById, deleteBotsByBotIdHeartbeatLogs, deleteBotsByBotIdInboxById, deleteBotsByBotIdMcpById, deleteBotsByBotIdMemory, deleteBotsByBotIdMemoryById, deleteBotsByBotIdMessages, deleteBotsByBotIdScheduleById, deleteBotsByBotIdSettings, deleteBotsByBotIdSubagentsById, deleteBotsById, deleteBotsByIdChannelByPlatform, deleteBotsByIdMembersByUserId, deleteEmailProvidersById, deleteModelsById, deleteModelsModelByModelId, deleteProvidersById, deleteSearchProvidersById, getBots, getBotsByBotIdCliStream, getBotsByBotIdContainer, getBotsByBotIdContainerFs, getBotsByBotIdContainerFsDownload, getBotsByBotIdContainerFsList, getBotsByBotIdContainerFsRead, getBotsByBotIdContainerSkills, getBotsByBotIdContainerSnapshots, getBotsByBotIdEmailBindings, getBotsByBotIdEmailOutbox, getBotsByBotIdEmailOutboxById, getBotsByBotIdHeartbeatLogs, getBotsByBotIdInbox, getBotsByBotIdInboxById, getBotsByBotIdInboxCount, getBotsByBotIdMcp, getBotsByBotIdMcpById, getBotsByBotIdMcpExport, getBotsByBotIdMemory, getBotsByBotIdMemoryUsage, getBotsByBotIdMessages, getBotsByBotIdSchedule, getBotsByBotIdScheduleById, getBotsByBotIdSettings, getBotsByBotIdSubagents, getBotsByBotIdSubagentsById, getBotsByBotIdSubagentsByIdContext, getBotsByBotIdSubagentsByIdSkills, getBotsByBotIdWebStream, getBotsById, getBotsByIdChannelByPlatform, getBotsByIdChecks, getBotsByIdMembers, getChannels, getChannelsByPlatform, getEmailProviders, getEmailProvidersById, getEmailProvidersMeta, getModels, getModelsById, getModelsCount, getModelsModelByModelId, getPing, getProviders, getProvidersById, getProvidersByIdModels, getProvidersCount, getProvidersNameByName, getSearchProviders, getSearchProvidersById, getSearchProvidersMeta, getUsers, getUsersById, getUsersMe, getUsersMeChannelsByPlatform, getUsersMeIdentities, type Options, patchBotsByIdChannelByPlatformStatus, postAuthLogin, postAuthRefresh, postBots, postBotsByBotIdCliMessages, postBotsByBotIdContainer, postBotsByBotIdContainerFsDelete, postBotsByBotIdContainerFsMkdir, postBotsByBotIdContainerFsRename, postBotsByBotIdContainerFsUpload, postBotsByBotIdContainerFsWrite, postBotsByBotIdContainerSkills, postBotsByBotIdContainerSnapshots, postBotsByBotIdContainerStart, postBotsByBotIdContainerStop, postBotsByBotIdEmailBindings, postBotsByBotIdInbox, postBotsByBotIdInboxMarkRead, postBotsByBotIdMcp, postBotsByBotIdMcpOpsBatchDelete, postBotsByBotIdMcpStdio, postBotsByBotIdMcpStdioByConnectionId, postBotsByBotIdMemory, postBotsByBotIdMemoryCompact, postBotsByBotIdMemoryRebuild, postBotsByBotIdMemorySearch, postBotsByBotIdSchedule, postBotsByBotIdSettings, postBotsByBotIdSubagents, postBotsByBotIdSubagentsByIdSkills, postBotsByBotIdTools, postBotsByBotIdWebMessages, postBotsByIdChannelByPlatformSend, postBotsByIdChannelByPlatformSendChat, postEmailMailgunWebhookByConfigId, postEmailProviders, postEmbeddings, postModels, postProviders, postProvidersByIdTest, postSearchProviders, postUsers, putBotsByBotIdEmailBindingsById, putBotsByBotIdMcpById, putBotsByBotIdMcpImport, putBotsByBotIdScheduleById, putBotsByBotIdSettings, putBotsByBotIdSubagentsById, putBotsByBotIdSubagentsByIdContext, putBotsByBotIdSubagentsByIdSkills, putBotsById, putBotsByIdChannelByPlatform, putBotsByIdMembers, putBotsByIdOwner, putEmailProvidersById, putModelsById, putModelsModelByModelId, putProvidersById, putSearchProvidersById, putUsersById, putUsersByIdPassword, putUsersMe, putUsersMeChannelsByPlatform, putUsersMePassword } from './sdk.gen'; +export type { AccountsAccount, AccountsCreateAccountRequest, AccountsListAccountsResponse, AccountsResetPasswordRequest, AccountsUpdateAccountRequest, AccountsUpdatePasswordRequest, AccountsUpdateProfileRequest, BotsBot, BotsBotCheck, BotsBotMember, BotsCreateBotRequest, BotsListBotsResponse, BotsListChecksResponse, BotsListMembersResponse, BotsTransferBotRequest, BotsUpdateBotRequest, BotsUpsertMemberRequest, ChannelAction, ChannelAttachment, ChannelAttachmentType, ChannelChannelCapabilities, ChannelChannelConfig, ChannelChannelIdentityBinding, ChannelConfigSchema, ChannelFieldSchema, ChannelFieldType, ChannelMessage, ChannelMessageFormat, ChannelMessagePart, ChannelMessagePartType, ChannelMessageTextStyle, ChannelReplyRef, ChannelSendRequest, ChannelTargetHint, ChannelTargetSpec, ChannelThreadRef, ChannelUpdateChannelStatusRequest, ChannelUpsertChannelIdentityConfigRequest, ChannelUpsertConfigRequest, ClientOptions, DeleteBotsByBotIdContainerData, DeleteBotsByBotIdContainerError, DeleteBotsByBotIdContainerErrors, DeleteBotsByBotIdContainerResponses, DeleteBotsByBotIdContainerSkillsData, DeleteBotsByBotIdContainerSkillsError, DeleteBotsByBotIdContainerSkillsErrors, DeleteBotsByBotIdContainerSkillsResponse, DeleteBotsByBotIdContainerSkillsResponses, DeleteBotsByBotIdEmailBindingsByIdData, DeleteBotsByBotIdEmailBindingsByIdError, DeleteBotsByBotIdEmailBindingsByIdErrors, DeleteBotsByBotIdEmailBindingsByIdResponses, DeleteBotsByBotIdHeartbeatLogsData, DeleteBotsByBotIdHeartbeatLogsError, DeleteBotsByBotIdHeartbeatLogsErrors, DeleteBotsByBotIdHeartbeatLogsResponses, DeleteBotsByBotIdInboxByIdData, DeleteBotsByBotIdInboxByIdError, DeleteBotsByBotIdInboxByIdErrors, DeleteBotsByBotIdInboxByIdResponses, DeleteBotsByBotIdMcpByIdData, DeleteBotsByBotIdMcpByIdError, DeleteBotsByBotIdMcpByIdErrors, DeleteBotsByBotIdMcpByIdResponses, DeleteBotsByBotIdMemoryByIdData, DeleteBotsByBotIdMemoryByIdError, DeleteBotsByBotIdMemoryByIdErrors, DeleteBotsByBotIdMemoryByIdResponse, DeleteBotsByBotIdMemoryByIdResponses, DeleteBotsByBotIdMemoryData, DeleteBotsByBotIdMemoryError, DeleteBotsByBotIdMemoryErrors, DeleteBotsByBotIdMemoryResponse, DeleteBotsByBotIdMemoryResponses, DeleteBotsByBotIdMessagesData, DeleteBotsByBotIdMessagesError, DeleteBotsByBotIdMessagesErrors, DeleteBotsByBotIdMessagesResponses, DeleteBotsByBotIdScheduleByIdData, DeleteBotsByBotIdScheduleByIdError, DeleteBotsByBotIdScheduleByIdErrors, DeleteBotsByBotIdScheduleByIdResponses, DeleteBotsByBotIdSettingsData, DeleteBotsByBotIdSettingsError, DeleteBotsByBotIdSettingsErrors, DeleteBotsByBotIdSettingsResponses, DeleteBotsByBotIdSubagentsByIdData, DeleteBotsByBotIdSubagentsByIdError, DeleteBotsByBotIdSubagentsByIdErrors, DeleteBotsByBotIdSubagentsByIdResponses, DeleteBotsByIdChannelByPlatformData, DeleteBotsByIdChannelByPlatformError, DeleteBotsByIdChannelByPlatformErrors, DeleteBotsByIdChannelByPlatformResponses, DeleteBotsByIdData, DeleteBotsByIdError, DeleteBotsByIdErrors, DeleteBotsByIdMembersByUserIdData, DeleteBotsByIdMembersByUserIdError, DeleteBotsByIdMembersByUserIdErrors, DeleteBotsByIdMembersByUserIdResponses, DeleteBotsByIdResponse, DeleteBotsByIdResponses, DeleteEmailProvidersByIdData, DeleteEmailProvidersByIdError, DeleteEmailProvidersByIdErrors, DeleteEmailProvidersByIdResponses, DeleteModelsByIdData, DeleteModelsByIdError, DeleteModelsByIdErrors, DeleteModelsByIdResponses, DeleteModelsModelByModelIdData, DeleteModelsModelByModelIdError, DeleteModelsModelByModelIdErrors, DeleteModelsModelByModelIdResponses, DeleteProvidersByIdData, DeleteProvidersByIdError, DeleteProvidersByIdErrors, DeleteProvidersByIdResponses, DeleteSearchProvidersByIdData, DeleteSearchProvidersByIdError, DeleteSearchProvidersByIdErrors, DeleteSearchProvidersByIdResponses, EmailBindingResponse, EmailConfigSchema, EmailCreateBindingRequest, EmailCreateProviderRequest, EmailFieldSchema, EmailOutboxItemResponse, EmailProviderMeta, EmailProviderResponse, EmailUpdateBindingRequest, EmailUpdateProviderRequest, GetBotsByBotIdCliStreamData, GetBotsByBotIdCliStreamError, GetBotsByBotIdCliStreamErrors, GetBotsByBotIdCliStreamResponse, GetBotsByBotIdCliStreamResponses, GetBotsByBotIdContainerData, GetBotsByBotIdContainerError, GetBotsByBotIdContainerErrors, GetBotsByBotIdContainerFsData, GetBotsByBotIdContainerFsDownloadData, GetBotsByBotIdContainerFsDownloadError, GetBotsByBotIdContainerFsDownloadErrors, GetBotsByBotIdContainerFsDownloadResponses, GetBotsByBotIdContainerFsError, GetBotsByBotIdContainerFsErrors, GetBotsByBotIdContainerFsListData, GetBotsByBotIdContainerFsListError, GetBotsByBotIdContainerFsListErrors, GetBotsByBotIdContainerFsListResponse, GetBotsByBotIdContainerFsListResponses, GetBotsByBotIdContainerFsReadData, GetBotsByBotIdContainerFsReadError, GetBotsByBotIdContainerFsReadErrors, GetBotsByBotIdContainerFsReadResponse, GetBotsByBotIdContainerFsReadResponses, GetBotsByBotIdContainerFsResponse, GetBotsByBotIdContainerFsResponses, GetBotsByBotIdContainerResponse, GetBotsByBotIdContainerResponses, GetBotsByBotIdContainerSkillsData, GetBotsByBotIdContainerSkillsError, GetBotsByBotIdContainerSkillsErrors, GetBotsByBotIdContainerSkillsResponse, GetBotsByBotIdContainerSkillsResponses, GetBotsByBotIdContainerSnapshotsData, GetBotsByBotIdContainerSnapshotsError, GetBotsByBotIdContainerSnapshotsErrors, GetBotsByBotIdContainerSnapshotsResponse, GetBotsByBotIdContainerSnapshotsResponses, GetBotsByBotIdEmailBindingsData, GetBotsByBotIdEmailBindingsError, GetBotsByBotIdEmailBindingsErrors, GetBotsByBotIdEmailBindingsResponse, GetBotsByBotIdEmailBindingsResponses, GetBotsByBotIdEmailOutboxByIdData, GetBotsByBotIdEmailOutboxByIdError, GetBotsByBotIdEmailOutboxByIdErrors, GetBotsByBotIdEmailOutboxByIdResponse, GetBotsByBotIdEmailOutboxByIdResponses, GetBotsByBotIdEmailOutboxData, GetBotsByBotIdEmailOutboxError, GetBotsByBotIdEmailOutboxErrors, GetBotsByBotIdEmailOutboxResponse, GetBotsByBotIdEmailOutboxResponses, GetBotsByBotIdHeartbeatLogsData, GetBotsByBotIdHeartbeatLogsError, GetBotsByBotIdHeartbeatLogsErrors, GetBotsByBotIdHeartbeatLogsResponse, GetBotsByBotIdHeartbeatLogsResponses, GetBotsByBotIdInboxByIdData, GetBotsByBotIdInboxByIdError, GetBotsByBotIdInboxByIdErrors, GetBotsByBotIdInboxByIdResponse, GetBotsByBotIdInboxByIdResponses, GetBotsByBotIdInboxCountData, GetBotsByBotIdInboxCountError, GetBotsByBotIdInboxCountErrors, GetBotsByBotIdInboxCountResponse, GetBotsByBotIdInboxCountResponses, GetBotsByBotIdInboxData, GetBotsByBotIdInboxError, GetBotsByBotIdInboxErrors, GetBotsByBotIdInboxResponse, GetBotsByBotIdInboxResponses, GetBotsByBotIdMcpByIdData, GetBotsByBotIdMcpByIdError, GetBotsByBotIdMcpByIdErrors, GetBotsByBotIdMcpByIdResponse, GetBotsByBotIdMcpByIdResponses, GetBotsByBotIdMcpData, GetBotsByBotIdMcpError, GetBotsByBotIdMcpErrors, GetBotsByBotIdMcpExportData, GetBotsByBotIdMcpExportError, GetBotsByBotIdMcpExportErrors, GetBotsByBotIdMcpExportResponse, GetBotsByBotIdMcpExportResponses, GetBotsByBotIdMcpResponse, GetBotsByBotIdMcpResponses, GetBotsByBotIdMemoryData, GetBotsByBotIdMemoryError, GetBotsByBotIdMemoryErrors, GetBotsByBotIdMemoryResponse, GetBotsByBotIdMemoryResponses, GetBotsByBotIdMemoryUsageData, GetBotsByBotIdMemoryUsageError, GetBotsByBotIdMemoryUsageErrors, GetBotsByBotIdMemoryUsageResponse, GetBotsByBotIdMemoryUsageResponses, GetBotsByBotIdMessagesData, GetBotsByBotIdMessagesError, GetBotsByBotIdMessagesErrors, GetBotsByBotIdMessagesResponse, GetBotsByBotIdMessagesResponses, GetBotsByBotIdScheduleByIdData, GetBotsByBotIdScheduleByIdError, GetBotsByBotIdScheduleByIdErrors, GetBotsByBotIdScheduleByIdResponse, GetBotsByBotIdScheduleByIdResponses, GetBotsByBotIdScheduleData, GetBotsByBotIdScheduleError, GetBotsByBotIdScheduleErrors, GetBotsByBotIdScheduleResponse, GetBotsByBotIdScheduleResponses, GetBotsByBotIdSettingsData, GetBotsByBotIdSettingsError, GetBotsByBotIdSettingsErrors, GetBotsByBotIdSettingsResponse, GetBotsByBotIdSettingsResponses, GetBotsByBotIdSubagentsByIdContextData, GetBotsByBotIdSubagentsByIdContextError, GetBotsByBotIdSubagentsByIdContextErrors, GetBotsByBotIdSubagentsByIdContextResponse, GetBotsByBotIdSubagentsByIdContextResponses, GetBotsByBotIdSubagentsByIdData, GetBotsByBotIdSubagentsByIdError, GetBotsByBotIdSubagentsByIdErrors, GetBotsByBotIdSubagentsByIdResponse, GetBotsByBotIdSubagentsByIdResponses, GetBotsByBotIdSubagentsByIdSkillsData, GetBotsByBotIdSubagentsByIdSkillsError, GetBotsByBotIdSubagentsByIdSkillsErrors, GetBotsByBotIdSubagentsByIdSkillsResponse, GetBotsByBotIdSubagentsByIdSkillsResponses, GetBotsByBotIdSubagentsData, GetBotsByBotIdSubagentsError, GetBotsByBotIdSubagentsErrors, GetBotsByBotIdSubagentsResponse, GetBotsByBotIdSubagentsResponses, GetBotsByBotIdWebStreamData, GetBotsByBotIdWebStreamError, GetBotsByBotIdWebStreamErrors, GetBotsByBotIdWebStreamResponse, GetBotsByBotIdWebStreamResponses, GetBotsByIdChannelByPlatformData, GetBotsByIdChannelByPlatformError, GetBotsByIdChannelByPlatformErrors, GetBotsByIdChannelByPlatformResponse, GetBotsByIdChannelByPlatformResponses, GetBotsByIdChecksData, GetBotsByIdChecksError, GetBotsByIdChecksErrors, GetBotsByIdChecksResponse, GetBotsByIdChecksResponses, GetBotsByIdData, GetBotsByIdError, GetBotsByIdErrors, GetBotsByIdMembersData, GetBotsByIdMembersError, GetBotsByIdMembersErrors, GetBotsByIdMembersResponse, GetBotsByIdMembersResponses, GetBotsByIdResponse, GetBotsByIdResponses, GetBotsData, GetBotsError, GetBotsErrors, GetBotsResponse, GetBotsResponses, GetChannelsByPlatformData, GetChannelsByPlatformError, GetChannelsByPlatformErrors, GetChannelsByPlatformResponse, GetChannelsByPlatformResponses, GetChannelsData, GetChannelsError, GetChannelsErrors, GetChannelsResponse, GetChannelsResponses, GetEmailProvidersByIdData, GetEmailProvidersByIdError, GetEmailProvidersByIdErrors, GetEmailProvidersByIdResponse, GetEmailProvidersByIdResponses, GetEmailProvidersData, GetEmailProvidersError, GetEmailProvidersErrors, GetEmailProvidersMetaData, GetEmailProvidersMetaResponse, GetEmailProvidersMetaResponses, GetEmailProvidersResponse, GetEmailProvidersResponses, GetModelsByIdData, GetModelsByIdError, GetModelsByIdErrors, GetModelsByIdResponse, GetModelsByIdResponses, GetModelsCountData, GetModelsCountError, GetModelsCountErrors, GetModelsCountResponse, GetModelsCountResponses, GetModelsData, GetModelsError, GetModelsErrors, GetModelsModelByModelIdData, GetModelsModelByModelIdError, GetModelsModelByModelIdErrors, GetModelsModelByModelIdResponse, GetModelsModelByModelIdResponses, GetModelsResponse, GetModelsResponses, GetPingData, GetPingResponse, GetPingResponses, GetProvidersByIdData, GetProvidersByIdError, GetProvidersByIdErrors, GetProvidersByIdModelsData, GetProvidersByIdModelsError, GetProvidersByIdModelsErrors, GetProvidersByIdModelsResponse, GetProvidersByIdModelsResponses, GetProvidersByIdResponse, GetProvidersByIdResponses, GetProvidersCountData, GetProvidersCountError, GetProvidersCountErrors, GetProvidersCountResponse, GetProvidersCountResponses, GetProvidersData, GetProvidersError, GetProvidersErrors, GetProvidersNameByNameData, GetProvidersNameByNameError, GetProvidersNameByNameErrors, GetProvidersNameByNameResponse, GetProvidersNameByNameResponses, GetProvidersResponse, GetProvidersResponses, GetSearchProvidersByIdData, GetSearchProvidersByIdError, GetSearchProvidersByIdErrors, GetSearchProvidersByIdResponse, GetSearchProvidersByIdResponses, GetSearchProvidersData, GetSearchProvidersError, GetSearchProvidersErrors, GetSearchProvidersMetaData, GetSearchProvidersMetaResponse, GetSearchProvidersMetaResponses, GetSearchProvidersResponse, GetSearchProvidersResponses, GetUsersByIdData, GetUsersByIdError, GetUsersByIdErrors, GetUsersByIdResponse, GetUsersByIdResponses, GetUsersData, GetUsersError, GetUsersErrors, GetUsersMeChannelsByPlatformData, GetUsersMeChannelsByPlatformError, GetUsersMeChannelsByPlatformErrors, GetUsersMeChannelsByPlatformResponse, GetUsersMeChannelsByPlatformResponses, GetUsersMeData, GetUsersMeError, GetUsersMeErrors, GetUsersMeIdentitiesData, GetUsersMeIdentitiesError, GetUsersMeIdentitiesErrors, GetUsersMeIdentitiesResponse, GetUsersMeIdentitiesResponses, GetUsersMeResponse, GetUsersMeResponses, GetUsersResponse, GetUsersResponses, GithubComMemohaiMemohInternalMcpConnection, HandlersBatchDeleteRequest, HandlersChannelMeta, HandlersCreateContainerRequest, HandlersCreateContainerResponse, HandlersCreateSnapshotRequest, HandlersCreateSnapshotResponse, HandlersEmbeddingsInput, HandlersEmbeddingsRequest, HandlersEmbeddingsResponse, HandlersEmbeddingsUsage, HandlersErrorResponse, HandlersFsDeleteRequest, HandlersFsFileInfo, HandlersFsListResponse, HandlersFsMkdirRequest, HandlersFsOpResponse, HandlersFsReadResponse, HandlersFsRenameRequest, HandlersFsUploadResponse, HandlersFsWriteRequest, HandlersGetContainerResponse, HandlersListMyIdentitiesResponse, HandlersListSnapshotsResponse, HandlersLocalChannelMessageRequest, HandlersLoginRequest, HandlersLoginResponse, HandlersMarkReadRequest, HandlersMcpStdioRequest, HandlersMcpStdioResponse, HandlersMemoryAddPayload, HandlersMemoryCompactPayload, HandlersMemoryDeletePayload, HandlersMemorySearchPayload, HandlersPingResponse, HandlersRefreshResponse, HandlersSkillItem, HandlersSkillsDeleteRequest, HandlersSkillsOpResponse, HandlersSkillsResponse, HandlersSkillsUpsertRequest, HandlersSnapshotInfo, HeartbeatListLogsResponse, HeartbeatLog, IdentitiesChannelIdentity, InboxCountResult, InboxCreateRequest, InboxItem, McpExportResponse, McpImportRequest, McpListResponse, McpMcpServerEntry, McpUpsertRequest, MemoryCdfPoint, MemoryCompactResult, MemoryDeleteResponse, MemoryMemoryItem, MemoryMessage, MemoryRebuildResult, MemorySearchResponse, MemoryTopKBucket, MemoryUsageResponse, MessageMessage, MessageMessageAsset, ModelsAddRequest, ModelsAddResponse, ModelsClientType, ModelsCountResponse, ModelsGetResponse, ModelsModelType, ModelsUpdateRequest, PatchBotsByIdChannelByPlatformStatusData, PatchBotsByIdChannelByPlatformStatusError, PatchBotsByIdChannelByPlatformStatusErrors, PatchBotsByIdChannelByPlatformStatusResponse, PatchBotsByIdChannelByPlatformStatusResponses, PostAuthLoginData, PostAuthLoginError, PostAuthLoginErrors, PostAuthLoginResponse, PostAuthLoginResponses, PostAuthRefreshData, PostAuthRefreshError, PostAuthRefreshErrors, PostAuthRefreshResponse, PostAuthRefreshResponses, PostBotsByBotIdCliMessagesData, PostBotsByBotIdCliMessagesError, PostBotsByBotIdCliMessagesErrors, PostBotsByBotIdCliMessagesResponse, PostBotsByBotIdCliMessagesResponses, PostBotsByBotIdContainerData, PostBotsByBotIdContainerError, PostBotsByBotIdContainerErrors, PostBotsByBotIdContainerFsDeleteData, PostBotsByBotIdContainerFsDeleteError, PostBotsByBotIdContainerFsDeleteErrors, PostBotsByBotIdContainerFsDeleteResponse, PostBotsByBotIdContainerFsDeleteResponses, PostBotsByBotIdContainerFsMkdirData, PostBotsByBotIdContainerFsMkdirError, PostBotsByBotIdContainerFsMkdirErrors, PostBotsByBotIdContainerFsMkdirResponse, PostBotsByBotIdContainerFsMkdirResponses, PostBotsByBotIdContainerFsRenameData, PostBotsByBotIdContainerFsRenameError, PostBotsByBotIdContainerFsRenameErrors, PostBotsByBotIdContainerFsRenameResponse, PostBotsByBotIdContainerFsRenameResponses, PostBotsByBotIdContainerFsUploadData, PostBotsByBotIdContainerFsUploadError, PostBotsByBotIdContainerFsUploadErrors, PostBotsByBotIdContainerFsUploadResponse, PostBotsByBotIdContainerFsUploadResponses, PostBotsByBotIdContainerFsWriteData, PostBotsByBotIdContainerFsWriteError, PostBotsByBotIdContainerFsWriteErrors, PostBotsByBotIdContainerFsWriteResponse, PostBotsByBotIdContainerFsWriteResponses, PostBotsByBotIdContainerResponse, PostBotsByBotIdContainerResponses, PostBotsByBotIdContainerSkillsData, PostBotsByBotIdContainerSkillsError, PostBotsByBotIdContainerSkillsErrors, PostBotsByBotIdContainerSkillsResponse, PostBotsByBotIdContainerSkillsResponses, PostBotsByBotIdContainerSnapshotsData, PostBotsByBotIdContainerSnapshotsError, PostBotsByBotIdContainerSnapshotsErrors, PostBotsByBotIdContainerSnapshotsResponse, PostBotsByBotIdContainerSnapshotsResponses, PostBotsByBotIdContainerStartData, PostBotsByBotIdContainerStartError, PostBotsByBotIdContainerStartErrors, PostBotsByBotIdContainerStartResponse, PostBotsByBotIdContainerStartResponses, PostBotsByBotIdContainerStopData, PostBotsByBotIdContainerStopError, PostBotsByBotIdContainerStopErrors, PostBotsByBotIdContainerStopResponse, PostBotsByBotIdContainerStopResponses, PostBotsByBotIdEmailBindingsData, PostBotsByBotIdEmailBindingsError, PostBotsByBotIdEmailBindingsErrors, PostBotsByBotIdEmailBindingsResponse, PostBotsByBotIdEmailBindingsResponses, PostBotsByBotIdInboxData, PostBotsByBotIdInboxError, PostBotsByBotIdInboxErrors, PostBotsByBotIdInboxMarkReadData, PostBotsByBotIdInboxMarkReadError, PostBotsByBotIdInboxMarkReadErrors, PostBotsByBotIdInboxMarkReadResponses, PostBotsByBotIdInboxResponse, PostBotsByBotIdInboxResponses, PostBotsByBotIdMcpData, PostBotsByBotIdMcpError, PostBotsByBotIdMcpErrors, PostBotsByBotIdMcpOpsBatchDeleteData, PostBotsByBotIdMcpOpsBatchDeleteError, PostBotsByBotIdMcpOpsBatchDeleteErrors, PostBotsByBotIdMcpOpsBatchDeleteResponses, PostBotsByBotIdMcpResponse, PostBotsByBotIdMcpResponses, PostBotsByBotIdMcpStdioByConnectionIdData, PostBotsByBotIdMcpStdioByConnectionIdError, PostBotsByBotIdMcpStdioByConnectionIdErrors, PostBotsByBotIdMcpStdioByConnectionIdResponse, PostBotsByBotIdMcpStdioByConnectionIdResponses, PostBotsByBotIdMcpStdioData, PostBotsByBotIdMcpStdioError, PostBotsByBotIdMcpStdioErrors, PostBotsByBotIdMcpStdioResponse, PostBotsByBotIdMcpStdioResponses, PostBotsByBotIdMemoryCompactData, PostBotsByBotIdMemoryCompactError, PostBotsByBotIdMemoryCompactErrors, PostBotsByBotIdMemoryCompactResponse, PostBotsByBotIdMemoryCompactResponses, PostBotsByBotIdMemoryData, PostBotsByBotIdMemoryError, PostBotsByBotIdMemoryErrors, PostBotsByBotIdMemoryRebuildData, PostBotsByBotIdMemoryRebuildError, PostBotsByBotIdMemoryRebuildErrors, PostBotsByBotIdMemoryRebuildResponse, PostBotsByBotIdMemoryRebuildResponses, PostBotsByBotIdMemoryResponse, PostBotsByBotIdMemoryResponses, PostBotsByBotIdMemorySearchData, PostBotsByBotIdMemorySearchError, PostBotsByBotIdMemorySearchErrors, PostBotsByBotIdMemorySearchResponse, PostBotsByBotIdMemorySearchResponses, PostBotsByBotIdScheduleData, PostBotsByBotIdScheduleError, PostBotsByBotIdScheduleErrors, PostBotsByBotIdScheduleResponse, PostBotsByBotIdScheduleResponses, PostBotsByBotIdSettingsData, PostBotsByBotIdSettingsError, PostBotsByBotIdSettingsErrors, PostBotsByBotIdSettingsResponse, PostBotsByBotIdSettingsResponses, PostBotsByBotIdSubagentsByIdSkillsData, PostBotsByBotIdSubagentsByIdSkillsError, PostBotsByBotIdSubagentsByIdSkillsErrors, PostBotsByBotIdSubagentsByIdSkillsResponse, PostBotsByBotIdSubagentsByIdSkillsResponses, PostBotsByBotIdSubagentsData, PostBotsByBotIdSubagentsError, PostBotsByBotIdSubagentsErrors, PostBotsByBotIdSubagentsResponse, PostBotsByBotIdSubagentsResponses, PostBotsByBotIdToolsData, PostBotsByBotIdToolsError, PostBotsByBotIdToolsErrors, PostBotsByBotIdToolsResponse, PostBotsByBotIdToolsResponses, PostBotsByBotIdWebMessagesData, PostBotsByBotIdWebMessagesError, PostBotsByBotIdWebMessagesErrors, PostBotsByBotIdWebMessagesResponse, PostBotsByBotIdWebMessagesResponses, PostBotsByIdChannelByPlatformSendChatData, PostBotsByIdChannelByPlatformSendChatError, PostBotsByIdChannelByPlatformSendChatErrors, PostBotsByIdChannelByPlatformSendChatResponse, PostBotsByIdChannelByPlatformSendChatResponses, PostBotsByIdChannelByPlatformSendData, PostBotsByIdChannelByPlatformSendError, PostBotsByIdChannelByPlatformSendErrors, PostBotsByIdChannelByPlatformSendResponse, PostBotsByIdChannelByPlatformSendResponses, PostBotsData, PostBotsError, PostBotsErrors, PostBotsResponse, PostBotsResponses, PostEmailMailgunWebhookByConfigIdData, PostEmailMailgunWebhookByConfigIdError, PostEmailMailgunWebhookByConfigIdErrors, PostEmailMailgunWebhookByConfigIdResponse, PostEmailMailgunWebhookByConfigIdResponses, PostEmailProvidersData, PostEmailProvidersError, PostEmailProvidersErrors, PostEmailProvidersResponse, PostEmailProvidersResponses, PostEmbeddingsData, PostEmbeddingsError, PostEmbeddingsErrors, PostEmbeddingsResponse, PostEmbeddingsResponses, PostModelsData, PostModelsError, PostModelsErrors, PostModelsResponse, PostModelsResponses, PostProvidersByIdTestData, PostProvidersByIdTestError, PostProvidersByIdTestErrors, PostProvidersByIdTestResponse, PostProvidersByIdTestResponses, PostProvidersData, PostProvidersError, PostProvidersErrors, PostProvidersResponse, PostProvidersResponses, PostSearchProvidersData, PostSearchProvidersError, PostSearchProvidersErrors, PostSearchProvidersResponse, PostSearchProvidersResponses, PostUsersData, PostUsersError, PostUsersErrors, PostUsersResponse, PostUsersResponses, ProvidersCheckResult, ProvidersCheckStatus, ProvidersCountResponse, ProvidersCreateRequest, ProvidersGetResponse, ProvidersTestResponse, ProvidersUpdateRequest, PutBotsByBotIdEmailBindingsByIdData, PutBotsByBotIdEmailBindingsByIdError, PutBotsByBotIdEmailBindingsByIdErrors, PutBotsByBotIdEmailBindingsByIdResponse, PutBotsByBotIdEmailBindingsByIdResponses, PutBotsByBotIdMcpByIdData, PutBotsByBotIdMcpByIdError, PutBotsByBotIdMcpByIdErrors, PutBotsByBotIdMcpByIdResponse, PutBotsByBotIdMcpByIdResponses, PutBotsByBotIdMcpImportData, PutBotsByBotIdMcpImportError, PutBotsByBotIdMcpImportErrors, PutBotsByBotIdMcpImportResponse, PutBotsByBotIdMcpImportResponses, PutBotsByBotIdScheduleByIdData, PutBotsByBotIdScheduleByIdError, PutBotsByBotIdScheduleByIdErrors, PutBotsByBotIdScheduleByIdResponse, PutBotsByBotIdScheduleByIdResponses, PutBotsByBotIdSettingsData, PutBotsByBotIdSettingsError, PutBotsByBotIdSettingsErrors, PutBotsByBotIdSettingsResponse, PutBotsByBotIdSettingsResponses, PutBotsByBotIdSubagentsByIdContextData, PutBotsByBotIdSubagentsByIdContextError, PutBotsByBotIdSubagentsByIdContextErrors, PutBotsByBotIdSubagentsByIdContextResponse, PutBotsByBotIdSubagentsByIdContextResponses, PutBotsByBotIdSubagentsByIdData, PutBotsByBotIdSubagentsByIdError, PutBotsByBotIdSubagentsByIdErrors, PutBotsByBotIdSubagentsByIdResponse, PutBotsByBotIdSubagentsByIdResponses, PutBotsByBotIdSubagentsByIdSkillsData, PutBotsByBotIdSubagentsByIdSkillsError, PutBotsByBotIdSubagentsByIdSkillsErrors, PutBotsByBotIdSubagentsByIdSkillsResponse, PutBotsByBotIdSubagentsByIdSkillsResponses, PutBotsByIdChannelByPlatformData, PutBotsByIdChannelByPlatformError, PutBotsByIdChannelByPlatformErrors, PutBotsByIdChannelByPlatformResponse, PutBotsByIdChannelByPlatformResponses, PutBotsByIdData, PutBotsByIdError, PutBotsByIdErrors, PutBotsByIdMembersData, PutBotsByIdMembersError, PutBotsByIdMembersErrors, PutBotsByIdMembersResponse, PutBotsByIdMembersResponses, PutBotsByIdOwnerData, PutBotsByIdOwnerError, PutBotsByIdOwnerErrors, PutBotsByIdOwnerResponse, PutBotsByIdOwnerResponses, PutBotsByIdResponse, PutBotsByIdResponses, PutEmailProvidersByIdData, PutEmailProvidersByIdError, PutEmailProvidersByIdErrors, PutEmailProvidersByIdResponse, PutEmailProvidersByIdResponses, PutModelsByIdData, PutModelsByIdError, PutModelsByIdErrors, PutModelsByIdResponse, PutModelsByIdResponses, PutModelsModelByModelIdData, PutModelsModelByModelIdError, PutModelsModelByModelIdErrors, PutModelsModelByModelIdResponse, PutModelsModelByModelIdResponses, PutProvidersByIdData, PutProvidersByIdError, PutProvidersByIdErrors, PutProvidersByIdResponse, PutProvidersByIdResponses, PutSearchProvidersByIdData, PutSearchProvidersByIdError, PutSearchProvidersByIdErrors, PutSearchProvidersByIdResponse, PutSearchProvidersByIdResponses, PutUsersByIdData, PutUsersByIdError, PutUsersByIdErrors, PutUsersByIdPasswordData, PutUsersByIdPasswordError, PutUsersByIdPasswordErrors, PutUsersByIdPasswordResponses, PutUsersByIdResponse, PutUsersByIdResponses, PutUsersMeChannelsByPlatformData, PutUsersMeChannelsByPlatformError, PutUsersMeChannelsByPlatformErrors, PutUsersMeChannelsByPlatformResponse, PutUsersMeChannelsByPlatformResponses, PutUsersMeData, PutUsersMeError, PutUsersMeErrors, PutUsersMePasswordData, PutUsersMePasswordError, PutUsersMePasswordErrors, PutUsersMePasswordResponses, PutUsersMeResponse, PutUsersMeResponses, ScheduleCreateRequest, ScheduleListResponse, ScheduleNullableInt, ScheduleSchedule, ScheduleUpdateRequest, SearchprovidersCreateRequest, SearchprovidersGetResponse, SearchprovidersProviderConfigSchema, SearchprovidersProviderFieldSchema, SearchprovidersProviderMeta, SearchprovidersProviderName, SearchprovidersUpdateRequest, SettingsSettings, SettingsUpsertRequest, SubagentAddSkillsRequest, SubagentContextResponse, SubagentCreateRequest, SubagentListResponse, SubagentSkillsResponse, SubagentSubagent, SubagentUpdateContextRequest, SubagentUpdateRequest, SubagentUpdateSkillsRequest } from './types.gen'; diff --git a/packages/sdk/src/sdk.gen.ts b/packages/sdk/src/sdk.gen.ts index 3d6aac6e..6693d9cf 100644 --- a/packages/sdk/src/sdk.gen.ts +++ b/packages/sdk/src/sdk.gen.ts @@ -2,7 +2,7 @@ import { type Client, formDataBodySerializer, type Options as Options2, type TDataShape } from './client'; import { client } from './client.gen'; -import type { DeleteBotsByBotIdContainerData, DeleteBotsByBotIdContainerErrors, DeleteBotsByBotIdContainerResponses, DeleteBotsByBotIdContainerSkillsData, DeleteBotsByBotIdContainerSkillsErrors, DeleteBotsByBotIdContainerSkillsResponses, DeleteBotsByBotIdHeartbeatLogsData, DeleteBotsByBotIdHeartbeatLogsErrors, DeleteBotsByBotIdHeartbeatLogsResponses, DeleteBotsByBotIdInboxByIdData, DeleteBotsByBotIdInboxByIdErrors, DeleteBotsByBotIdInboxByIdResponses, DeleteBotsByBotIdMcpByIdData, DeleteBotsByBotIdMcpByIdErrors, DeleteBotsByBotIdMcpByIdResponses, DeleteBotsByBotIdMemoryByIdData, DeleteBotsByBotIdMemoryByIdErrors, DeleteBotsByBotIdMemoryByIdResponses, DeleteBotsByBotIdMemoryData, DeleteBotsByBotIdMemoryErrors, DeleteBotsByBotIdMemoryResponses, DeleteBotsByBotIdMessagesData, DeleteBotsByBotIdMessagesErrors, DeleteBotsByBotIdMessagesResponses, DeleteBotsByBotIdScheduleByIdData, DeleteBotsByBotIdScheduleByIdErrors, DeleteBotsByBotIdScheduleByIdResponses, DeleteBotsByBotIdSettingsData, DeleteBotsByBotIdSettingsErrors, DeleteBotsByBotIdSettingsResponses, DeleteBotsByBotIdSubagentsByIdData, DeleteBotsByBotIdSubagentsByIdErrors, DeleteBotsByBotIdSubagentsByIdResponses, DeleteBotsByIdChannelByPlatformData, DeleteBotsByIdChannelByPlatformErrors, DeleteBotsByIdChannelByPlatformResponses, DeleteBotsByIdData, DeleteBotsByIdErrors, DeleteBotsByIdMembersByUserIdData, DeleteBotsByIdMembersByUserIdErrors, DeleteBotsByIdMembersByUserIdResponses, DeleteBotsByIdResponses, DeleteModelsByIdData, DeleteModelsByIdErrors, DeleteModelsByIdResponses, DeleteModelsModelByModelIdData, DeleteModelsModelByModelIdErrors, DeleteModelsModelByModelIdResponses, DeleteProvidersByIdData, DeleteProvidersByIdErrors, DeleteProvidersByIdResponses, DeleteSearchProvidersByIdData, DeleteSearchProvidersByIdErrors, DeleteSearchProvidersByIdResponses, GetBotsByBotIdCliStreamData, GetBotsByBotIdCliStreamErrors, GetBotsByBotIdCliStreamResponses, GetBotsByBotIdContainerData, GetBotsByBotIdContainerErrors, GetBotsByBotIdContainerFsData, GetBotsByBotIdContainerFsDownloadData, GetBotsByBotIdContainerFsDownloadErrors, GetBotsByBotIdContainerFsDownloadResponses, GetBotsByBotIdContainerFsErrors, GetBotsByBotIdContainerFsListData, GetBotsByBotIdContainerFsListErrors, GetBotsByBotIdContainerFsListResponses, GetBotsByBotIdContainerFsReadData, GetBotsByBotIdContainerFsReadErrors, GetBotsByBotIdContainerFsReadResponses, GetBotsByBotIdContainerFsResponses, GetBotsByBotIdContainerResponses, GetBotsByBotIdContainerSkillsData, GetBotsByBotIdContainerSkillsErrors, GetBotsByBotIdContainerSkillsResponses, GetBotsByBotIdContainerSnapshotsData, GetBotsByBotIdContainerSnapshotsErrors, GetBotsByBotIdContainerSnapshotsResponses, GetBotsByBotIdHeartbeatLogsData, GetBotsByBotIdHeartbeatLogsErrors, GetBotsByBotIdHeartbeatLogsResponses, GetBotsByBotIdInboxByIdData, GetBotsByBotIdInboxByIdErrors, GetBotsByBotIdInboxByIdResponses, GetBotsByBotIdInboxCountData, GetBotsByBotIdInboxCountErrors, GetBotsByBotIdInboxCountResponses, GetBotsByBotIdInboxData, GetBotsByBotIdInboxErrors, GetBotsByBotIdInboxResponses, GetBotsByBotIdMcpByIdData, GetBotsByBotIdMcpByIdErrors, GetBotsByBotIdMcpByIdResponses, GetBotsByBotIdMcpData, GetBotsByBotIdMcpErrors, GetBotsByBotIdMcpExportData, GetBotsByBotIdMcpExportErrors, GetBotsByBotIdMcpExportResponses, GetBotsByBotIdMcpResponses, GetBotsByBotIdMemoryData, GetBotsByBotIdMemoryErrors, GetBotsByBotIdMemoryResponses, GetBotsByBotIdMemoryUsageData, GetBotsByBotIdMemoryUsageErrors, GetBotsByBotIdMemoryUsageResponses, GetBotsByBotIdMessagesData, GetBotsByBotIdMessagesErrors, GetBotsByBotIdMessagesResponses, GetBotsByBotIdScheduleByIdData, GetBotsByBotIdScheduleByIdErrors, GetBotsByBotIdScheduleByIdResponses, GetBotsByBotIdScheduleData, GetBotsByBotIdScheduleErrors, GetBotsByBotIdScheduleResponses, GetBotsByBotIdSettingsData, GetBotsByBotIdSettingsErrors, GetBotsByBotIdSettingsResponses, GetBotsByBotIdSubagentsByIdContextData, GetBotsByBotIdSubagentsByIdContextErrors, GetBotsByBotIdSubagentsByIdContextResponses, GetBotsByBotIdSubagentsByIdData, GetBotsByBotIdSubagentsByIdErrors, GetBotsByBotIdSubagentsByIdResponses, GetBotsByBotIdSubagentsByIdSkillsData, GetBotsByBotIdSubagentsByIdSkillsErrors, GetBotsByBotIdSubagentsByIdSkillsResponses, GetBotsByBotIdSubagentsData, GetBotsByBotIdSubagentsErrors, GetBotsByBotIdSubagentsResponses, GetBotsByBotIdWebStreamData, GetBotsByBotIdWebStreamErrors, GetBotsByBotIdWebStreamResponses, GetBotsByIdChannelByPlatformData, GetBotsByIdChannelByPlatformErrors, GetBotsByIdChannelByPlatformResponses, GetBotsByIdChecksData, GetBotsByIdChecksErrors, GetBotsByIdChecksResponses, GetBotsByIdData, GetBotsByIdErrors, GetBotsByIdMembersData, GetBotsByIdMembersErrors, GetBotsByIdMembersResponses, GetBotsByIdResponses, GetBotsData, GetBotsErrors, GetBotsResponses, GetChannelsByPlatformData, GetChannelsByPlatformErrors, GetChannelsByPlatformResponses, GetChannelsData, GetChannelsErrors, GetChannelsResponses, GetModelsByIdData, GetModelsByIdErrors, GetModelsByIdResponses, GetModelsCountData, GetModelsCountErrors, GetModelsCountResponses, GetModelsData, GetModelsErrors, GetModelsModelByModelIdData, GetModelsModelByModelIdErrors, GetModelsModelByModelIdResponses, GetModelsResponses, GetPingData, GetPingResponses, GetProvidersByIdData, GetProvidersByIdErrors, GetProvidersByIdModelsData, GetProvidersByIdModelsErrors, GetProvidersByIdModelsResponses, GetProvidersByIdResponses, GetProvidersCountData, GetProvidersCountErrors, GetProvidersCountResponses, GetProvidersData, GetProvidersErrors, GetProvidersNameByNameData, GetProvidersNameByNameErrors, GetProvidersNameByNameResponses, GetProvidersResponses, GetSearchProvidersByIdData, GetSearchProvidersByIdErrors, GetSearchProvidersByIdResponses, GetSearchProvidersData, GetSearchProvidersErrors, GetSearchProvidersMetaData, GetSearchProvidersMetaResponses, GetSearchProvidersResponses, GetUsersByIdData, GetUsersByIdErrors, GetUsersByIdResponses, GetUsersData, GetUsersErrors, GetUsersMeChannelsByPlatformData, GetUsersMeChannelsByPlatformErrors, GetUsersMeChannelsByPlatformResponses, GetUsersMeData, GetUsersMeErrors, GetUsersMeIdentitiesData, GetUsersMeIdentitiesErrors, GetUsersMeIdentitiesResponses, GetUsersMeResponses, GetUsersResponses, PatchBotsByIdChannelByPlatformStatusData, PatchBotsByIdChannelByPlatformStatusErrors, PatchBotsByIdChannelByPlatformStatusResponses, PostAuthLoginData, PostAuthLoginErrors, PostAuthLoginResponses, PostAuthRefreshData, PostAuthRefreshErrors, PostAuthRefreshResponses, PostBotsByBotIdCliMessagesData, PostBotsByBotIdCliMessagesErrors, PostBotsByBotIdCliMessagesResponses, PostBotsByBotIdContainerData, PostBotsByBotIdContainerErrors, PostBotsByBotIdContainerFsDeleteData, PostBotsByBotIdContainerFsDeleteErrors, PostBotsByBotIdContainerFsDeleteResponses, PostBotsByBotIdContainerFsMkdirData, PostBotsByBotIdContainerFsMkdirErrors, PostBotsByBotIdContainerFsMkdirResponses, PostBotsByBotIdContainerFsRenameData, PostBotsByBotIdContainerFsRenameErrors, PostBotsByBotIdContainerFsRenameResponses, PostBotsByBotIdContainerFsUploadData, PostBotsByBotIdContainerFsUploadErrors, PostBotsByBotIdContainerFsUploadResponses, PostBotsByBotIdContainerFsWriteData, PostBotsByBotIdContainerFsWriteErrors, PostBotsByBotIdContainerFsWriteResponses, PostBotsByBotIdContainerResponses, PostBotsByBotIdContainerSkillsData, PostBotsByBotIdContainerSkillsErrors, PostBotsByBotIdContainerSkillsResponses, PostBotsByBotIdContainerSnapshotsData, PostBotsByBotIdContainerSnapshotsErrors, PostBotsByBotIdContainerSnapshotsResponses, PostBotsByBotIdContainerStartData, PostBotsByBotIdContainerStartErrors, PostBotsByBotIdContainerStartResponses, PostBotsByBotIdContainerStopData, PostBotsByBotIdContainerStopErrors, PostBotsByBotIdContainerStopResponses, PostBotsByBotIdInboxData, PostBotsByBotIdInboxErrors, PostBotsByBotIdInboxMarkReadData, PostBotsByBotIdInboxMarkReadErrors, PostBotsByBotIdInboxMarkReadResponses, PostBotsByBotIdInboxResponses, PostBotsByBotIdMcpData, PostBotsByBotIdMcpErrors, PostBotsByBotIdMcpOpsBatchDeleteData, PostBotsByBotIdMcpOpsBatchDeleteErrors, PostBotsByBotIdMcpOpsBatchDeleteResponses, PostBotsByBotIdMcpResponses, PostBotsByBotIdMcpStdioByConnectionIdData, PostBotsByBotIdMcpStdioByConnectionIdErrors, PostBotsByBotIdMcpStdioByConnectionIdResponses, PostBotsByBotIdMcpStdioData, PostBotsByBotIdMcpStdioErrors, PostBotsByBotIdMcpStdioResponses, PostBotsByBotIdMemoryCompactData, PostBotsByBotIdMemoryCompactErrors, PostBotsByBotIdMemoryCompactResponses, PostBotsByBotIdMemoryData, PostBotsByBotIdMemoryErrors, PostBotsByBotIdMemoryRebuildData, PostBotsByBotIdMemoryRebuildErrors, PostBotsByBotIdMemoryRebuildResponses, PostBotsByBotIdMemoryResponses, PostBotsByBotIdMemorySearchData, PostBotsByBotIdMemorySearchErrors, PostBotsByBotIdMemorySearchResponses, PostBotsByBotIdScheduleData, PostBotsByBotIdScheduleErrors, PostBotsByBotIdScheduleResponses, PostBotsByBotIdSettingsData, PostBotsByBotIdSettingsErrors, PostBotsByBotIdSettingsResponses, PostBotsByBotIdSubagentsByIdSkillsData, PostBotsByBotIdSubagentsByIdSkillsErrors, PostBotsByBotIdSubagentsByIdSkillsResponses, PostBotsByBotIdSubagentsData, PostBotsByBotIdSubagentsErrors, PostBotsByBotIdSubagentsResponses, PostBotsByBotIdToolsData, PostBotsByBotIdToolsErrors, PostBotsByBotIdToolsResponses, PostBotsByBotIdWebMessagesData, PostBotsByBotIdWebMessagesErrors, PostBotsByBotIdWebMessagesResponses, PostBotsByIdChannelByPlatformSendChatData, PostBotsByIdChannelByPlatformSendChatErrors, PostBotsByIdChannelByPlatformSendChatResponses, PostBotsByIdChannelByPlatformSendData, PostBotsByIdChannelByPlatformSendErrors, PostBotsByIdChannelByPlatformSendResponses, PostBotsData, PostBotsErrors, PostBotsResponses, PostEmbeddingsData, PostEmbeddingsErrors, PostEmbeddingsResponses, PostModelsData, PostModelsErrors, PostModelsResponses, PostProvidersByIdTestData, PostProvidersByIdTestErrors, PostProvidersByIdTestResponses, PostProvidersData, PostProvidersErrors, PostProvidersResponses, PostSearchProvidersData, PostSearchProvidersErrors, PostSearchProvidersResponses, PostUsersData, PostUsersErrors, PostUsersResponses, PutBotsByBotIdMcpByIdData, PutBotsByBotIdMcpByIdErrors, PutBotsByBotIdMcpByIdResponses, PutBotsByBotIdMcpImportData, PutBotsByBotIdMcpImportErrors, PutBotsByBotIdMcpImportResponses, PutBotsByBotIdScheduleByIdData, PutBotsByBotIdScheduleByIdErrors, PutBotsByBotIdScheduleByIdResponses, PutBotsByBotIdSettingsData, PutBotsByBotIdSettingsErrors, PutBotsByBotIdSettingsResponses, PutBotsByBotIdSubagentsByIdContextData, PutBotsByBotIdSubagentsByIdContextErrors, PutBotsByBotIdSubagentsByIdContextResponses, PutBotsByBotIdSubagentsByIdData, PutBotsByBotIdSubagentsByIdErrors, PutBotsByBotIdSubagentsByIdResponses, PutBotsByBotIdSubagentsByIdSkillsData, PutBotsByBotIdSubagentsByIdSkillsErrors, PutBotsByBotIdSubagentsByIdSkillsResponses, PutBotsByIdChannelByPlatformData, PutBotsByIdChannelByPlatformErrors, PutBotsByIdChannelByPlatformResponses, PutBotsByIdData, PutBotsByIdErrors, PutBotsByIdMembersData, PutBotsByIdMembersErrors, PutBotsByIdMembersResponses, PutBotsByIdOwnerData, PutBotsByIdOwnerErrors, PutBotsByIdOwnerResponses, PutBotsByIdResponses, PutModelsByIdData, PutModelsByIdErrors, PutModelsByIdResponses, PutModelsModelByModelIdData, PutModelsModelByModelIdErrors, PutModelsModelByModelIdResponses, PutProvidersByIdData, PutProvidersByIdErrors, PutProvidersByIdResponses, PutSearchProvidersByIdData, PutSearchProvidersByIdErrors, PutSearchProvidersByIdResponses, PutUsersByIdData, PutUsersByIdErrors, PutUsersByIdPasswordData, PutUsersByIdPasswordErrors, PutUsersByIdPasswordResponses, PutUsersByIdResponses, PutUsersMeChannelsByPlatformData, PutUsersMeChannelsByPlatformErrors, PutUsersMeChannelsByPlatformResponses, PutUsersMeData, PutUsersMeErrors, PutUsersMePasswordData, PutUsersMePasswordErrors, PutUsersMePasswordResponses, PutUsersMeResponses } from './types.gen'; +import type { DeleteBotsByBotIdContainerData, DeleteBotsByBotIdContainerErrors, DeleteBotsByBotIdContainerResponses, DeleteBotsByBotIdContainerSkillsData, DeleteBotsByBotIdContainerSkillsErrors, DeleteBotsByBotIdContainerSkillsResponses, DeleteBotsByBotIdEmailBindingsByIdData, DeleteBotsByBotIdEmailBindingsByIdErrors, DeleteBotsByBotIdEmailBindingsByIdResponses, DeleteBotsByBotIdHeartbeatLogsData, DeleteBotsByBotIdHeartbeatLogsErrors, DeleteBotsByBotIdHeartbeatLogsResponses, DeleteBotsByBotIdInboxByIdData, DeleteBotsByBotIdInboxByIdErrors, DeleteBotsByBotIdInboxByIdResponses, DeleteBotsByBotIdMcpByIdData, DeleteBotsByBotIdMcpByIdErrors, DeleteBotsByBotIdMcpByIdResponses, DeleteBotsByBotIdMemoryByIdData, DeleteBotsByBotIdMemoryByIdErrors, DeleteBotsByBotIdMemoryByIdResponses, DeleteBotsByBotIdMemoryData, DeleteBotsByBotIdMemoryErrors, DeleteBotsByBotIdMemoryResponses, DeleteBotsByBotIdMessagesData, DeleteBotsByBotIdMessagesErrors, DeleteBotsByBotIdMessagesResponses, DeleteBotsByBotIdScheduleByIdData, DeleteBotsByBotIdScheduleByIdErrors, DeleteBotsByBotIdScheduleByIdResponses, DeleteBotsByBotIdSettingsData, DeleteBotsByBotIdSettingsErrors, DeleteBotsByBotIdSettingsResponses, DeleteBotsByBotIdSubagentsByIdData, DeleteBotsByBotIdSubagentsByIdErrors, DeleteBotsByBotIdSubagentsByIdResponses, DeleteBotsByIdChannelByPlatformData, DeleteBotsByIdChannelByPlatformErrors, DeleteBotsByIdChannelByPlatformResponses, DeleteBotsByIdData, DeleteBotsByIdErrors, DeleteBotsByIdMembersByUserIdData, DeleteBotsByIdMembersByUserIdErrors, DeleteBotsByIdMembersByUserIdResponses, DeleteBotsByIdResponses, DeleteEmailProvidersByIdData, DeleteEmailProvidersByIdErrors, DeleteEmailProvidersByIdResponses, DeleteModelsByIdData, DeleteModelsByIdErrors, DeleteModelsByIdResponses, DeleteModelsModelByModelIdData, DeleteModelsModelByModelIdErrors, DeleteModelsModelByModelIdResponses, DeleteProvidersByIdData, DeleteProvidersByIdErrors, DeleteProvidersByIdResponses, DeleteSearchProvidersByIdData, DeleteSearchProvidersByIdErrors, DeleteSearchProvidersByIdResponses, GetBotsByBotIdCliStreamData, GetBotsByBotIdCliStreamErrors, GetBotsByBotIdCliStreamResponses, GetBotsByBotIdContainerData, GetBotsByBotIdContainerErrors, GetBotsByBotIdContainerFsData, GetBotsByBotIdContainerFsDownloadData, GetBotsByBotIdContainerFsDownloadErrors, GetBotsByBotIdContainerFsDownloadResponses, GetBotsByBotIdContainerFsErrors, GetBotsByBotIdContainerFsListData, GetBotsByBotIdContainerFsListErrors, GetBotsByBotIdContainerFsListResponses, GetBotsByBotIdContainerFsReadData, GetBotsByBotIdContainerFsReadErrors, GetBotsByBotIdContainerFsReadResponses, GetBotsByBotIdContainerFsResponses, GetBotsByBotIdContainerResponses, GetBotsByBotIdContainerSkillsData, GetBotsByBotIdContainerSkillsErrors, GetBotsByBotIdContainerSkillsResponses, GetBotsByBotIdContainerSnapshotsData, GetBotsByBotIdContainerSnapshotsErrors, GetBotsByBotIdContainerSnapshotsResponses, GetBotsByBotIdEmailBindingsData, GetBotsByBotIdEmailBindingsErrors, GetBotsByBotIdEmailBindingsResponses, GetBotsByBotIdEmailOutboxByIdData, GetBotsByBotIdEmailOutboxByIdErrors, GetBotsByBotIdEmailOutboxByIdResponses, GetBotsByBotIdEmailOutboxData, GetBotsByBotIdEmailOutboxErrors, GetBotsByBotIdEmailOutboxResponses, GetBotsByBotIdHeartbeatLogsData, GetBotsByBotIdHeartbeatLogsErrors, GetBotsByBotIdHeartbeatLogsResponses, GetBotsByBotIdInboxByIdData, GetBotsByBotIdInboxByIdErrors, GetBotsByBotIdInboxByIdResponses, GetBotsByBotIdInboxCountData, GetBotsByBotIdInboxCountErrors, GetBotsByBotIdInboxCountResponses, GetBotsByBotIdInboxData, GetBotsByBotIdInboxErrors, GetBotsByBotIdInboxResponses, GetBotsByBotIdMcpByIdData, GetBotsByBotIdMcpByIdErrors, GetBotsByBotIdMcpByIdResponses, GetBotsByBotIdMcpData, GetBotsByBotIdMcpErrors, GetBotsByBotIdMcpExportData, GetBotsByBotIdMcpExportErrors, GetBotsByBotIdMcpExportResponses, GetBotsByBotIdMcpResponses, GetBotsByBotIdMemoryData, GetBotsByBotIdMemoryErrors, GetBotsByBotIdMemoryResponses, GetBotsByBotIdMemoryUsageData, GetBotsByBotIdMemoryUsageErrors, GetBotsByBotIdMemoryUsageResponses, GetBotsByBotIdMessagesData, GetBotsByBotIdMessagesErrors, GetBotsByBotIdMessagesResponses, GetBotsByBotIdScheduleByIdData, GetBotsByBotIdScheduleByIdErrors, GetBotsByBotIdScheduleByIdResponses, GetBotsByBotIdScheduleData, GetBotsByBotIdScheduleErrors, GetBotsByBotIdScheduleResponses, GetBotsByBotIdSettingsData, GetBotsByBotIdSettingsErrors, GetBotsByBotIdSettingsResponses, GetBotsByBotIdSubagentsByIdContextData, GetBotsByBotIdSubagentsByIdContextErrors, GetBotsByBotIdSubagentsByIdContextResponses, GetBotsByBotIdSubagentsByIdData, GetBotsByBotIdSubagentsByIdErrors, GetBotsByBotIdSubagentsByIdResponses, GetBotsByBotIdSubagentsByIdSkillsData, GetBotsByBotIdSubagentsByIdSkillsErrors, GetBotsByBotIdSubagentsByIdSkillsResponses, GetBotsByBotIdSubagentsData, GetBotsByBotIdSubagentsErrors, GetBotsByBotIdSubagentsResponses, GetBotsByBotIdWebStreamData, GetBotsByBotIdWebStreamErrors, GetBotsByBotIdWebStreamResponses, GetBotsByIdChannelByPlatformData, GetBotsByIdChannelByPlatformErrors, GetBotsByIdChannelByPlatformResponses, GetBotsByIdChecksData, GetBotsByIdChecksErrors, GetBotsByIdChecksResponses, GetBotsByIdData, GetBotsByIdErrors, GetBotsByIdMembersData, GetBotsByIdMembersErrors, GetBotsByIdMembersResponses, GetBotsByIdResponses, GetBotsData, GetBotsErrors, GetBotsResponses, GetChannelsByPlatformData, GetChannelsByPlatformErrors, GetChannelsByPlatformResponses, GetChannelsData, GetChannelsErrors, GetChannelsResponses, GetEmailProvidersByIdData, GetEmailProvidersByIdErrors, GetEmailProvidersByIdResponses, GetEmailProvidersData, GetEmailProvidersErrors, GetEmailProvidersMetaData, GetEmailProvidersMetaResponses, GetEmailProvidersResponses, GetModelsByIdData, GetModelsByIdErrors, GetModelsByIdResponses, GetModelsCountData, GetModelsCountErrors, GetModelsCountResponses, GetModelsData, GetModelsErrors, GetModelsModelByModelIdData, GetModelsModelByModelIdErrors, GetModelsModelByModelIdResponses, GetModelsResponses, GetPingData, GetPingResponses, GetProvidersByIdData, GetProvidersByIdErrors, GetProvidersByIdModelsData, GetProvidersByIdModelsErrors, GetProvidersByIdModelsResponses, GetProvidersByIdResponses, GetProvidersCountData, GetProvidersCountErrors, GetProvidersCountResponses, GetProvidersData, GetProvidersErrors, GetProvidersNameByNameData, GetProvidersNameByNameErrors, GetProvidersNameByNameResponses, GetProvidersResponses, GetSearchProvidersByIdData, GetSearchProvidersByIdErrors, GetSearchProvidersByIdResponses, GetSearchProvidersData, GetSearchProvidersErrors, GetSearchProvidersMetaData, GetSearchProvidersMetaResponses, GetSearchProvidersResponses, GetUsersByIdData, GetUsersByIdErrors, GetUsersByIdResponses, GetUsersData, GetUsersErrors, GetUsersMeChannelsByPlatformData, GetUsersMeChannelsByPlatformErrors, GetUsersMeChannelsByPlatformResponses, GetUsersMeData, GetUsersMeErrors, GetUsersMeIdentitiesData, GetUsersMeIdentitiesErrors, GetUsersMeIdentitiesResponses, GetUsersMeResponses, GetUsersResponses, PatchBotsByIdChannelByPlatformStatusData, PatchBotsByIdChannelByPlatformStatusErrors, PatchBotsByIdChannelByPlatformStatusResponses, PostAuthLoginData, PostAuthLoginErrors, PostAuthLoginResponses, PostAuthRefreshData, PostAuthRefreshErrors, PostAuthRefreshResponses, PostBotsByBotIdCliMessagesData, PostBotsByBotIdCliMessagesErrors, PostBotsByBotIdCliMessagesResponses, PostBotsByBotIdContainerData, PostBotsByBotIdContainerErrors, PostBotsByBotIdContainerFsDeleteData, PostBotsByBotIdContainerFsDeleteErrors, PostBotsByBotIdContainerFsDeleteResponses, PostBotsByBotIdContainerFsMkdirData, PostBotsByBotIdContainerFsMkdirErrors, PostBotsByBotIdContainerFsMkdirResponses, PostBotsByBotIdContainerFsRenameData, PostBotsByBotIdContainerFsRenameErrors, PostBotsByBotIdContainerFsRenameResponses, PostBotsByBotIdContainerFsUploadData, PostBotsByBotIdContainerFsUploadErrors, PostBotsByBotIdContainerFsUploadResponses, PostBotsByBotIdContainerFsWriteData, PostBotsByBotIdContainerFsWriteErrors, PostBotsByBotIdContainerFsWriteResponses, PostBotsByBotIdContainerResponses, PostBotsByBotIdContainerSkillsData, PostBotsByBotIdContainerSkillsErrors, PostBotsByBotIdContainerSkillsResponses, PostBotsByBotIdContainerSnapshotsData, PostBotsByBotIdContainerSnapshotsErrors, PostBotsByBotIdContainerSnapshotsResponses, PostBotsByBotIdContainerStartData, PostBotsByBotIdContainerStartErrors, PostBotsByBotIdContainerStartResponses, PostBotsByBotIdContainerStopData, PostBotsByBotIdContainerStopErrors, PostBotsByBotIdContainerStopResponses, PostBotsByBotIdEmailBindingsData, PostBotsByBotIdEmailBindingsErrors, PostBotsByBotIdEmailBindingsResponses, PostBotsByBotIdInboxData, PostBotsByBotIdInboxErrors, PostBotsByBotIdInboxMarkReadData, PostBotsByBotIdInboxMarkReadErrors, PostBotsByBotIdInboxMarkReadResponses, PostBotsByBotIdInboxResponses, PostBotsByBotIdMcpData, PostBotsByBotIdMcpErrors, PostBotsByBotIdMcpOpsBatchDeleteData, PostBotsByBotIdMcpOpsBatchDeleteErrors, PostBotsByBotIdMcpOpsBatchDeleteResponses, PostBotsByBotIdMcpResponses, PostBotsByBotIdMcpStdioByConnectionIdData, PostBotsByBotIdMcpStdioByConnectionIdErrors, PostBotsByBotIdMcpStdioByConnectionIdResponses, PostBotsByBotIdMcpStdioData, PostBotsByBotIdMcpStdioErrors, PostBotsByBotIdMcpStdioResponses, PostBotsByBotIdMemoryCompactData, PostBotsByBotIdMemoryCompactErrors, PostBotsByBotIdMemoryCompactResponses, PostBotsByBotIdMemoryData, PostBotsByBotIdMemoryErrors, PostBotsByBotIdMemoryRebuildData, PostBotsByBotIdMemoryRebuildErrors, PostBotsByBotIdMemoryRebuildResponses, PostBotsByBotIdMemoryResponses, PostBotsByBotIdMemorySearchData, PostBotsByBotIdMemorySearchErrors, PostBotsByBotIdMemorySearchResponses, PostBotsByBotIdScheduleData, PostBotsByBotIdScheduleErrors, PostBotsByBotIdScheduleResponses, PostBotsByBotIdSettingsData, PostBotsByBotIdSettingsErrors, PostBotsByBotIdSettingsResponses, PostBotsByBotIdSubagentsByIdSkillsData, PostBotsByBotIdSubagentsByIdSkillsErrors, PostBotsByBotIdSubagentsByIdSkillsResponses, PostBotsByBotIdSubagentsData, PostBotsByBotIdSubagentsErrors, PostBotsByBotIdSubagentsResponses, PostBotsByBotIdToolsData, PostBotsByBotIdToolsErrors, PostBotsByBotIdToolsResponses, PostBotsByBotIdWebMessagesData, PostBotsByBotIdWebMessagesErrors, PostBotsByBotIdWebMessagesResponses, PostBotsByIdChannelByPlatformSendChatData, PostBotsByIdChannelByPlatformSendChatErrors, PostBotsByIdChannelByPlatformSendChatResponses, PostBotsByIdChannelByPlatformSendData, PostBotsByIdChannelByPlatformSendErrors, PostBotsByIdChannelByPlatformSendResponses, PostBotsData, PostBotsErrors, PostBotsResponses, PostEmailMailgunWebhookByConfigIdData, PostEmailMailgunWebhookByConfigIdErrors, PostEmailMailgunWebhookByConfigIdResponses, PostEmailProvidersData, PostEmailProvidersErrors, PostEmailProvidersResponses, PostEmbeddingsData, PostEmbeddingsErrors, PostEmbeddingsResponses, PostModelsData, PostModelsErrors, PostModelsResponses, PostProvidersByIdTestData, PostProvidersByIdTestErrors, PostProvidersByIdTestResponses, PostProvidersData, PostProvidersErrors, PostProvidersResponses, PostSearchProvidersData, PostSearchProvidersErrors, PostSearchProvidersResponses, PostUsersData, PostUsersErrors, PostUsersResponses, PutBotsByBotIdEmailBindingsByIdData, PutBotsByBotIdEmailBindingsByIdErrors, PutBotsByBotIdEmailBindingsByIdResponses, PutBotsByBotIdMcpByIdData, PutBotsByBotIdMcpByIdErrors, PutBotsByBotIdMcpByIdResponses, PutBotsByBotIdMcpImportData, PutBotsByBotIdMcpImportErrors, PutBotsByBotIdMcpImportResponses, PutBotsByBotIdScheduleByIdData, PutBotsByBotIdScheduleByIdErrors, PutBotsByBotIdScheduleByIdResponses, PutBotsByBotIdSettingsData, PutBotsByBotIdSettingsErrors, PutBotsByBotIdSettingsResponses, PutBotsByBotIdSubagentsByIdContextData, PutBotsByBotIdSubagentsByIdContextErrors, PutBotsByBotIdSubagentsByIdContextResponses, PutBotsByBotIdSubagentsByIdData, PutBotsByBotIdSubagentsByIdErrors, PutBotsByBotIdSubagentsByIdResponses, PutBotsByBotIdSubagentsByIdSkillsData, PutBotsByBotIdSubagentsByIdSkillsErrors, PutBotsByBotIdSubagentsByIdSkillsResponses, PutBotsByIdChannelByPlatformData, PutBotsByIdChannelByPlatformErrors, PutBotsByIdChannelByPlatformResponses, PutBotsByIdData, PutBotsByIdErrors, PutBotsByIdMembersData, PutBotsByIdMembersErrors, PutBotsByIdMembersResponses, PutBotsByIdOwnerData, PutBotsByIdOwnerErrors, PutBotsByIdOwnerResponses, PutBotsByIdResponses, PutEmailProvidersByIdData, PutEmailProvidersByIdErrors, PutEmailProvidersByIdResponses, PutModelsByIdData, PutModelsByIdErrors, PutModelsByIdResponses, PutModelsModelByModelIdData, PutModelsModelByModelIdErrors, PutModelsModelByModelIdResponses, PutProvidersByIdData, PutProvidersByIdErrors, PutProvidersByIdResponses, PutSearchProvidersByIdData, PutSearchProvidersByIdErrors, PutSearchProvidersByIdResponses, PutUsersByIdData, PutUsersByIdErrors, PutUsersByIdPasswordData, PutUsersByIdPasswordErrors, PutUsersByIdPasswordResponses, PutUsersByIdResponses, PutUsersMeChannelsByPlatformData, PutUsersMeChannelsByPlatformErrors, PutUsersMeChannelsByPlatformResponses, PutUsersMeData, PutUsersMeErrors, PutUsersMePasswordData, PutUsersMePasswordErrors, PutUsersMePasswordResponses, PutUsersMeResponses } from './types.gen'; export type Options = Options2 & { /** @@ -258,6 +258,50 @@ export const postBotsByBotIdContainerStart = (options: Options) => (options.client ?? client).post({ url: '/bots/{bot_id}/container/stop', ...options }); +/** + * List email bindings for a bot + */ +export const getBotsByBotIdEmailBindings = (options: Options) => (options.client ?? client).get({ url: '/bots/{bot_id}/email-bindings', ...options }); + +/** + * Bind an email provider to a bot + */ +export const postBotsByBotIdEmailBindings = (options: Options) => (options.client ?? client).post({ + url: '/bots/{bot_id}/email-bindings', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Remove an email binding + */ +export const deleteBotsByBotIdEmailBindingsById = (options: Options) => (options.client ?? client).delete({ url: '/bots/{bot_id}/email-bindings/{id}', ...options }); + +/** + * Update an email binding + */ +export const putBotsByBotIdEmailBindingsById = (options: Options) => (options.client ?? client).put({ + url: '/bots/{bot_id}/email-bindings/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * List outbox emails for a bot (audit) + */ +export const getBotsByBotIdEmailOutbox = (options: Options) => (options.client ?? client).get({ url: '/bots/{bot_id}/email-outbox', ...options }); + +/** + * Get outbox email detail + */ +export const getBotsByBotIdEmailOutboxById = (options: Options) => (options.client ?? client).get({ url: '/bots/{bot_id}/email-outbox/{id}', ...options }); + /** * Delete heartbeat logs * @@ -937,6 +981,59 @@ export const getChannels = (options?: Opti */ export const getChannelsByPlatform = (options: Options) => (options.client ?? client).get({ url: '/channels/{platform}', ...options }); +/** + * List email providers + */ +export const getEmailProviders = (options?: Options) => (options?.client ?? client).get({ url: '/email-providers', ...options }); + +/** + * Create an email provider + */ +export const postEmailProviders = (options: Options) => (options.client ?? client).post({ + url: '/email-providers', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * List email provider metadata + * + * List available email provider types and config schemas + */ +export const getEmailProvidersMeta = (options?: Options) => (options?.client ?? client).get({ url: '/email-providers/meta', ...options }); + +/** + * Delete an email provider + */ +export const deleteEmailProvidersById = (options: Options) => (options.client ?? client).delete({ url: '/email-providers/{id}', ...options }); + +/** + * Get an email provider + */ +export const getEmailProvidersById = (options: Options) => (options.client ?? client).get({ url: '/email-providers/{id}', ...options }); + +/** + * Update an email provider + */ +export const putEmailProvidersById = (options: Options) => (options.client ?? client).put({ + url: '/email-providers/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } +}); + +/** + * Mailgun inbound email webhook + * + * Receives inbound emails from Mailgun + */ +export const postEmailMailgunWebhookByConfigId = (options: Options) => (options.client ?? client).post({ url: '/email/mailgun/webhook/{config_id}', ...options }); + /** * Create embeddings * diff --git a/packages/sdk/src/types.gen.ts b/packages/sdk/src/types.gen.ts index 91225d5b..b42c7054 100644 --- a/packages/sdk/src/types.gen.ts +++ b/packages/sdk/src/types.gen.ts @@ -313,6 +313,107 @@ export type ChannelUpsertConfigRequest = { verified_at?: string; }; +export type EmailBindingResponse = { + bot_id?: string; + can_delete?: boolean; + can_read?: boolean; + can_write?: boolean; + config?: { + [key: string]: unknown; + }; + created_at?: string; + email_address?: string; + email_provider_id?: string; + id?: string; + updated_at?: string; +}; + +export type EmailConfigSchema = { + fields?: Array; +}; + +export type EmailCreateBindingRequest = { + can_delete?: boolean; + can_read?: boolean; + can_write?: boolean; + config?: { + [key: string]: unknown; + }; + email_address?: string; + email_provider_id?: string; +}; + +export type EmailCreateProviderRequest = { + config?: { + [key: string]: unknown; + }; + name?: string; + provider?: string; +}; + +export type EmailFieldSchema = { + description?: string; + enum?: Array; + example?: unknown; + key?: string; + order?: number; + required?: boolean; + title?: string; + type?: string; +}; + +export type EmailOutboxItemResponse = { + attachments?: Array; + body_html?: string; + body_text?: string; + bot_id?: string; + created_at?: string; + error?: string; + from?: string; + id?: string; + message_id?: string; + provider_id?: string; + sent_at?: string; + status?: string; + subject?: string; + to?: Array; +}; + +export type EmailProviderMeta = { + config_schema?: EmailConfigSchema; + display_name?: string; + provider?: string; +}; + +export type EmailProviderResponse = { + config?: { + [key: string]: unknown; + }; + created_at?: string; + id?: string; + name?: string; + provider?: string; + updated_at?: string; +}; + +export type EmailUpdateBindingRequest = { + can_delete?: boolean; + can_read?: boolean; + can_write?: boolean; + config?: { + [key: string]: unknown; + }; + email_address?: string; +}; + +export type EmailUpdateProviderRequest = { + config?: { + [key: string]: unknown; + }; + name?: string; + provider?: string; +}; + export type GithubComMemohaiMemohInternalMcpConnection = { bot_id?: string; config?: { @@ -985,7 +1086,7 @@ export type SearchprovidersProviderMeta = { provider?: string; }; -export type SearchprovidersProviderName = 'brave' | 'bing' | 'google' | 'tavily'; +export type SearchprovidersProviderName = 'brave' | 'bing' | 'google' | 'tavily' | 'sogou' | 'serper' | 'searxng' | 'jina' | 'exa' | 'bocha' | 'duckduckgo' | 'yandex'; export type SearchprovidersUpdateRequest = { config?: { @@ -2088,6 +2189,221 @@ export type PostBotsByBotIdContainerStopResponses = { export type PostBotsByBotIdContainerStopResponse = PostBotsByBotIdContainerStopResponses[keyof PostBotsByBotIdContainerStopResponses]; +export type GetBotsByBotIdEmailBindingsData = { + body?: never; + path: { + /** + * Bot ID + */ + bot_id: string; + }; + query?: never; + url: '/bots/{bot_id}/email-bindings'; +}; + +export type GetBotsByBotIdEmailBindingsErrors = { + /** + * Internal Server Error + */ + 500: HandlersErrorResponse; +}; + +export type GetBotsByBotIdEmailBindingsError = GetBotsByBotIdEmailBindingsErrors[keyof GetBotsByBotIdEmailBindingsErrors]; + +export type GetBotsByBotIdEmailBindingsResponses = { + /** + * OK + */ + 200: Array; +}; + +export type GetBotsByBotIdEmailBindingsResponse = GetBotsByBotIdEmailBindingsResponses[keyof GetBotsByBotIdEmailBindingsResponses]; + +export type PostBotsByBotIdEmailBindingsData = { + /** + * Binding configuration + */ + body: EmailCreateBindingRequest; + path: { + /** + * Bot ID + */ + bot_id: string; + }; + query?: never; + url: '/bots/{bot_id}/email-bindings'; +}; + +export type PostBotsByBotIdEmailBindingsErrors = { + /** + * Bad Request + */ + 400: HandlersErrorResponse; + /** + * Internal Server Error + */ + 500: HandlersErrorResponse; +}; + +export type PostBotsByBotIdEmailBindingsError = PostBotsByBotIdEmailBindingsErrors[keyof PostBotsByBotIdEmailBindingsErrors]; + +export type PostBotsByBotIdEmailBindingsResponses = { + /** + * Created + */ + 201: EmailBindingResponse; +}; + +export type PostBotsByBotIdEmailBindingsResponse = PostBotsByBotIdEmailBindingsResponses[keyof PostBotsByBotIdEmailBindingsResponses]; + +export type DeleteBotsByBotIdEmailBindingsByIdData = { + body?: never; + path: { + /** + * Bot ID + */ + bot_id: string; + /** + * Binding ID + */ + id: string; + }; + query?: never; + url: '/bots/{bot_id}/email-bindings/{id}'; +}; + +export type DeleteBotsByBotIdEmailBindingsByIdErrors = { + /** + * Internal Server Error + */ + 500: HandlersErrorResponse; +}; + +export type DeleteBotsByBotIdEmailBindingsByIdError = DeleteBotsByBotIdEmailBindingsByIdErrors[keyof DeleteBotsByBotIdEmailBindingsByIdErrors]; + +export type DeleteBotsByBotIdEmailBindingsByIdResponses = { + /** + * No Content + */ + 204: unknown; +}; + +export type PutBotsByBotIdEmailBindingsByIdData = { + /** + * Updated binding + */ + body: EmailUpdateBindingRequest; + path: { + /** + * Bot ID + */ + bot_id: string; + /** + * Binding ID + */ + id: string; + }; + query?: never; + url: '/bots/{bot_id}/email-bindings/{id}'; +}; + +export type PutBotsByBotIdEmailBindingsByIdErrors = { + /** + * Bad Request + */ + 400: HandlersErrorResponse; + /** + * Internal Server Error + */ + 500: HandlersErrorResponse; +}; + +export type PutBotsByBotIdEmailBindingsByIdError = PutBotsByBotIdEmailBindingsByIdErrors[keyof PutBotsByBotIdEmailBindingsByIdErrors]; + +export type PutBotsByBotIdEmailBindingsByIdResponses = { + /** + * OK + */ + 200: EmailBindingResponse; +}; + +export type PutBotsByBotIdEmailBindingsByIdResponse = PutBotsByBotIdEmailBindingsByIdResponses[keyof PutBotsByBotIdEmailBindingsByIdResponses]; + +export type GetBotsByBotIdEmailOutboxData = { + body?: never; + path: { + /** + * Bot ID + */ + bot_id: string; + }; + query?: { + /** + * Limit + */ + limit?: number; + /** + * Offset + */ + offset?: number; + }; + url: '/bots/{bot_id}/email-outbox'; +}; + +export type GetBotsByBotIdEmailOutboxErrors = { + /** + * Internal Server Error + */ + 500: HandlersErrorResponse; +}; + +export type GetBotsByBotIdEmailOutboxError = GetBotsByBotIdEmailOutboxErrors[keyof GetBotsByBotIdEmailOutboxErrors]; + +export type GetBotsByBotIdEmailOutboxResponses = { + /** + * OK + */ + 200: { + [key: string]: unknown; + }; +}; + +export type GetBotsByBotIdEmailOutboxResponse = GetBotsByBotIdEmailOutboxResponses[keyof GetBotsByBotIdEmailOutboxResponses]; + +export type GetBotsByBotIdEmailOutboxByIdData = { + body?: never; + path: { + /** + * Bot ID + */ + bot_id: string; + /** + * Email ID + */ + id: string; + }; + query?: never; + url: '/bots/{bot_id}/email-outbox/{id}'; +}; + +export type GetBotsByBotIdEmailOutboxByIdErrors = { + /** + * Not Found + */ + 404: HandlersErrorResponse; +}; + +export type GetBotsByBotIdEmailOutboxByIdError = GetBotsByBotIdEmailOutboxByIdErrors[keyof GetBotsByBotIdEmailOutboxByIdErrors]; + +export type GetBotsByBotIdEmailOutboxByIdResponses = { + /** + * OK + */ + 200: EmailOutboxItemResponse; +}; + +export type GetBotsByBotIdEmailOutboxByIdResponse = GetBotsByBotIdEmailOutboxByIdResponses[keyof GetBotsByBotIdEmailOutboxByIdResponses]; + export type DeleteBotsByBotIdHeartbeatLogsData = { body?: never; path: { @@ -4724,6 +5040,219 @@ export type GetChannelsByPlatformResponses = { export type GetChannelsByPlatformResponse = GetChannelsByPlatformResponses[keyof GetChannelsByPlatformResponses]; +export type GetEmailProvidersData = { + body?: never; + path?: never; + query?: { + /** + * Provider type filter + */ + provider?: string; + }; + url: '/email-providers'; +}; + +export type GetEmailProvidersErrors = { + /** + * Internal Server Error + */ + 500: HandlersErrorResponse; +}; + +export type GetEmailProvidersError = GetEmailProvidersErrors[keyof GetEmailProvidersErrors]; + +export type GetEmailProvidersResponses = { + /** + * OK + */ + 200: Array; +}; + +export type GetEmailProvidersResponse = GetEmailProvidersResponses[keyof GetEmailProvidersResponses]; + +export type PostEmailProvidersData = { + /** + * Email provider configuration + */ + body: EmailCreateProviderRequest; + path?: never; + query?: never; + url: '/email-providers'; +}; + +export type PostEmailProvidersErrors = { + /** + * Bad Request + */ + 400: HandlersErrorResponse; + /** + * Internal Server Error + */ + 500: HandlersErrorResponse; +}; + +export type PostEmailProvidersError = PostEmailProvidersErrors[keyof PostEmailProvidersErrors]; + +export type PostEmailProvidersResponses = { + /** + * Created + */ + 201: EmailProviderResponse; +}; + +export type PostEmailProvidersResponse = PostEmailProvidersResponses[keyof PostEmailProvidersResponses]; + +export type GetEmailProvidersMetaData = { + body?: never; + path?: never; + query?: never; + url: '/email-providers/meta'; +}; + +export type GetEmailProvidersMetaResponses = { + /** + * OK + */ + 200: Array; +}; + +export type GetEmailProvidersMetaResponse = GetEmailProvidersMetaResponses[keyof GetEmailProvidersMetaResponses]; + +export type DeleteEmailProvidersByIdData = { + body?: never; + path: { + /** + * Provider ID + */ + id: string; + }; + query?: never; + url: '/email-providers/{id}'; +}; + +export type DeleteEmailProvidersByIdErrors = { + /** + * Internal Server Error + */ + 500: HandlersErrorResponse; +}; + +export type DeleteEmailProvidersByIdError = DeleteEmailProvidersByIdErrors[keyof DeleteEmailProvidersByIdErrors]; + +export type DeleteEmailProvidersByIdResponses = { + /** + * No Content + */ + 204: unknown; +}; + +export type GetEmailProvidersByIdData = { + body?: never; + path: { + /** + * Provider ID + */ + id: string; + }; + query?: never; + url: '/email-providers/{id}'; +}; + +export type GetEmailProvidersByIdErrors = { + /** + * Not Found + */ + 404: HandlersErrorResponse; +}; + +export type GetEmailProvidersByIdError = GetEmailProvidersByIdErrors[keyof GetEmailProvidersByIdErrors]; + +export type GetEmailProvidersByIdResponses = { + /** + * OK + */ + 200: EmailProviderResponse; +}; + +export type GetEmailProvidersByIdResponse = GetEmailProvidersByIdResponses[keyof GetEmailProvidersByIdResponses]; + +export type PutEmailProvidersByIdData = { + /** + * Updated configuration + */ + body: EmailUpdateProviderRequest; + path: { + /** + * Provider ID + */ + id: string; + }; + query?: never; + url: '/email-providers/{id}'; +}; + +export type PutEmailProvidersByIdErrors = { + /** + * Bad Request + */ + 400: HandlersErrorResponse; + /** + * Internal Server Error + */ + 500: HandlersErrorResponse; +}; + +export type PutEmailProvidersByIdError = PutEmailProvidersByIdErrors[keyof PutEmailProvidersByIdErrors]; + +export type PutEmailProvidersByIdResponses = { + /** + * OK + */ + 200: EmailProviderResponse; +}; + +export type PutEmailProvidersByIdResponse = PutEmailProvidersByIdResponses[keyof PutEmailProvidersByIdResponses]; + +export type PostEmailMailgunWebhookByConfigIdData = { + body?: never; + path: { + /** + * Email provider config ID + */ + config_id: string; + }; + query?: never; + url: '/email/mailgun/webhook/{config_id}'; +}; + +export type PostEmailMailgunWebhookByConfigIdErrors = { + /** + * Bad Request + */ + 400: HandlersErrorResponse; + /** + * Forbidden + */ + 403: HandlersErrorResponse; + /** + * Internal Server Error + */ + 500: HandlersErrorResponse; +}; + +export type PostEmailMailgunWebhookByConfigIdError = PostEmailMailgunWebhookByConfigIdErrors[keyof PostEmailMailgunWebhookByConfigIdErrors]; + +export type PostEmailMailgunWebhookByConfigIdResponses = { + /** + * OK + */ + 200: { + [key: string]: string; + }; +}; + +export type PostEmailMailgunWebhookByConfigIdResponse = PostEmailMailgunWebhookByConfigIdResponses[keyof PostEmailMailgunWebhookByConfigIdResponses]; + export type PostEmbeddingsData = { /** * Embeddings request diff --git a/packages/web/src/components/sidebar/index.vue b/packages/web/src/components/sidebar/index.vue index 98b7ab5d..e042d9ba 100644 --- a/packages/web/src/components/sidebar/index.vue +++ b/packages/web/src/components/sidebar/index.vue @@ -139,6 +139,11 @@ const sidebarInfo = computed(() => [ name: 'search-providers', icon: ['fas', 'globe'], }, + { + title: t('sidebar.emailProvider'), + name: 'email-providers', + icon: ['fas', 'envelope'], + }, { title: t('sidebar.settings'), name: 'settings', diff --git a/packages/web/src/i18n/locales/en.json b/packages/web/src/i18n/locales/en.json index 3bfbc11c..03050bd7 100644 --- a/packages/web/src/i18n/locales/en.json +++ b/packages/web/src/i18n/locales/en.json @@ -54,6 +54,7 @@ "bots": "Bots", "models": "Models", "searchProvider": "Search Providers", + "emailProvider": "Email Providers", "settings": "Settings", "home": "Home", "mcp": "MCP", @@ -212,6 +213,31 @@ "yandex": "Yandex" } }, + "emailProvider": { + "title": "Email Providers", + "add": "Add Email Provider", + "providerType": "Provider Type", + "searchPlaceholder": "Search email providers...", + "emptyTitle": "No Email Providers", + "emptyDescription": "Add an email provider to enable email for your bots", + "deleteConfirm": "Are you sure you want to delete this email provider? This action cannot be undone.", + "fields": { + "username": "Username", + "password": "Password", + "smtp_host": "SMTP Host", + "smtp_port": "SMTP Port", + "smtp_security": "SMTP Security", + "imap_host": "IMAP Host", + "imap_port": "IMAP Port", + "imap_security": "IMAP Security", + "poll_interval_seconds": "Poll Interval (seconds)", + "domain": "Domain", + "api_key": "API Key", + "region": "Region", + "inbound_mode": "Inbound Mode", + "webhook_signing_key": "Webhook Signing Key" + } + }, "mcp": { "addTitle": "Add MCP", "addDescription": "Configure MCP server connection", @@ -324,8 +350,34 @@ "heartbeat": "Heartbeat", "history": "History", "skills": "Skills", + "email": "Email", "settings": "Settings" }, + "email": { + "title": "Email", + "subtitle": "Manage email provider bindings and view inbox / outbox.", + "bindings": "Bindings", + "addBinding": "Add Binding", + "inbox": "Inbox", + "outbox": "Outbox", + "emailAddress": "Email Address", + "emailPlaceholder": "user@example.com", + "noBindings": "No email bindings configured", + "noEmails": "No emails", + "canRead": "Read", + "canWrite": "Write", + "canDelete": "Delete", + "unbind": "Unbind", + "unbindConfirm": "Are you sure you want to remove this email binding?", + "bindSuccess": "Email binding added", + "unbindSuccess": "Email binding removed", + "from": "From", + "to": "To", + "subject": "Subject", + "status": "Status", + "receivedAt": "Received", + "sentAt": "Sent" + }, "container": { "title": "Container Management", "subtitle": "Manage the runtime container attached to this bot.", diff --git a/packages/web/src/i18n/locales/zh.json b/packages/web/src/i18n/locales/zh.json index bc271c6d..53585040 100644 --- a/packages/web/src/i18n/locales/zh.json +++ b/packages/web/src/i18n/locales/zh.json @@ -54,6 +54,7 @@ "bots": "Bots", "models": "模型管理", "searchProvider": "搜索提供方", + "emailProvider": "邮件提供方", "settings": "设置", "home": "首页", "mcp": "MCP", @@ -208,6 +209,31 @@ "yandex": "Yandex" } }, + "emailProvider": { + "title": "邮件提供方", + "add": "添加邮件提供方", + "providerType": "提供方类型", + "searchPlaceholder": "搜索邮件提供方...", + "emptyTitle": "暂无邮件提供方", + "emptyDescription": "添加邮件提供方以为 Bot 启用邮件功能", + "deleteConfirm": "确定要删除此邮件提供方吗?此操作不可撤销。", + "fields": { + "username": "用户名", + "password": "密码", + "smtp_host": "SMTP 主机", + "smtp_port": "SMTP 端口", + "smtp_security": "SMTP 安全模式", + "imap_host": "IMAP 主机", + "imap_port": "IMAP 端口", + "imap_security": "IMAP 安全模式", + "poll_interval_seconds": "轮询间隔(秒)", + "domain": "域名", + "api_key": "API 密钥", + "region": "区域", + "inbound_mode": "入站模式", + "webhook_signing_key": "Webhook 签名密钥" + } + }, "mcp": { "addTitle": "添加 MCP", "addDescription": "配置 MCP 服务器连接", @@ -320,8 +346,34 @@ "heartbeat": "心跳", "history": "对话历史", "skills": "技能", + "email": "邮件", "settings": "设置" }, + "email": { + "title": "邮件", + "subtitle": "管理邮件提供方绑定,查看收件箱和发件箱。", + "bindings": "绑定", + "addBinding": "添加绑定", + "inbox": "收件箱", + "outbox": "发件箱", + "emailAddress": "邮箱地址", + "emailPlaceholder": "user@example.com", + "noBindings": "尚未配置邮箱绑定", + "noEmails": "暂无邮件", + "canRead": "读取", + "canWrite": "发送", + "canDelete": "删除", + "unbind": "解绑", + "unbindConfirm": "确定要解除此邮箱绑定吗?", + "bindSuccess": "邮箱绑定成功", + "unbindSuccess": "邮箱已解绑", + "from": "发件人", + "to": "收件人", + "subject": "主题", + "status": "状态", + "receivedAt": "接收时间", + "sentAt": "发送时间" + }, "container": { "title": "容器管理", "subtitle": "管理当前 Bot 对应的运行容器。", diff --git a/packages/web/src/main.ts b/packages/web/src/main.ts index e6de838d..454bec69 100644 --- a/packages/web/src/main.ts +++ b/packages/web/src/main.ts @@ -52,6 +52,7 @@ import { faFile, faMusic, faVideo, + faEnvelope, } from '@fortawesome/free-solid-svg-icons' import { faRectangleList, @@ -106,6 +107,7 @@ library.add( faBrave, faGoogle, faMicrosoft, + faEnvelope, faYandex, ...customSearchIcons, ) diff --git a/packages/web/src/pages/bots/components/bot-email.vue b/packages/web/src/pages/bots/components/bot-email.vue new file mode 100644 index 00000000..6c3bf3cd --- /dev/null +++ b/packages/web/src/pages/bots/components/bot-email.vue @@ -0,0 +1,346 @@ + + + diff --git a/packages/web/src/pages/bots/detail.vue b/packages/web/src/pages/bots/detail.vue index 96dfef4c..a1487512 100644 --- a/packages/web/src/pages/bots/detail.vue +++ b/packages/web/src/pages/bots/detail.vue @@ -96,7 +96,7 @@ -
+
- +