mirror of
https://github.com/memohai/Memoh.git
synced 2026-04-25 07:00:48 +09:00
refactor: inbox (#137)
* refactor: inbox * fix: migrations * fix: migrations
This commit is contained in:
+2
-1
@@ -90,6 +90,7 @@ export const MCPConnectionModel = z.union([HTTPMCPConnectionModel, SSEMCPConnect
|
||||
export const InboxItemModel = z.object({
|
||||
id: z.string(),
|
||||
source: z.string(),
|
||||
content: z.record(z.string(), z.unknown()).default({}),
|
||||
header: z.record(z.string(), z.unknown()).default({}),
|
||||
content: z.string().default(''),
|
||||
createdAt: z.string(),
|
||||
})
|
||||
@@ -399,12 +399,14 @@ CREATE TABLE IF NOT EXISTS bot_history_message_assets (
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_message_assets_message_id ON bot_history_message_assets(message_id);
|
||||
|
||||
-- bot_inbox: per-bot message inbox for non-mentioned group messages, emails, etc.
|
||||
-- bot_inbox: per-bot message inbox for channel messages, notifications, etc.
|
||||
CREATE TABLE IF NOT EXISTS bot_inbox (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
bot_id UUID NOT NULL REFERENCES bots(id) ON DELETE CASCADE,
|
||||
source TEXT NOT NULL DEFAULT '',
|
||||
content JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
header JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
content TEXT NOT NULL DEFAULT '',
|
||||
action TEXT NOT NULL DEFAULT 'notify',
|
||||
is_read BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
read_at TIMESTAMPTZ
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
-- 0018_inbox_refactor (down)
|
||||
-- Revert bot_inbox to original schema: merge header+content back into content JSONB.
|
||||
|
||||
-- 1. Convert content back to JSONB, merging header and text.
|
||||
ALTER TABLE bot_inbox ALTER COLUMN content DROP DEFAULT;
|
||||
ALTER TABLE bot_inbox ALTER COLUMN content TYPE JSONB USING (COALESCE(header, '{}'::jsonb) || jsonb_build_object('text', content));
|
||||
ALTER TABLE bot_inbox ALTER COLUMN content SET DEFAULT '{}'::jsonb;
|
||||
|
||||
-- 2. Drop added columns.
|
||||
ALTER TABLE bot_inbox DROP COLUMN IF EXISTS action;
|
||||
ALTER TABLE bot_inbox DROP COLUMN IF EXISTS header;
|
||||
@@ -0,0 +1,27 @@
|
||||
-- 0018_inbox_refactor
|
||||
-- Refactor bot_inbox: split content JSONB into content TEXT + header JSONB, add action column.
|
||||
|
||||
-- 1. Add new columns (idempotent).
|
||||
ALTER TABLE bot_inbox ADD COLUMN IF NOT EXISTS header JSONB NOT NULL DEFAULT '{}'::jsonb;
|
||||
ALTER TABLE bot_inbox ADD COLUMN IF NOT EXISTS action TEXT NOT NULL DEFAULT 'notify';
|
||||
|
||||
-- 2. Migrate data and convert column type.
|
||||
-- Only needed when content is still JSONB (upgrade path).
|
||||
-- On a fresh DB (0001_init already defines content as TEXT), this is a no-op.
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (
|
||||
SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'bot_inbox' AND column_name = 'content' AND data_type = 'jsonb'
|
||||
) THEN
|
||||
UPDATE bot_inbox
|
||||
SET header = content - 'text',
|
||||
action = 'notify'
|
||||
WHERE content IS NOT NULL AND content::text <> '{}';
|
||||
|
||||
ALTER TABLE bot_inbox ALTER COLUMN content DROP DEFAULT;
|
||||
ALTER TABLE bot_inbox ALTER COLUMN content TYPE TEXT USING COALESCE(content ->> 'text', '');
|
||||
ALTER TABLE bot_inbox ALTER COLUMN content SET DEFAULT '';
|
||||
END IF;
|
||||
END
|
||||
$$;
|
||||
@@ -1,6 +1,6 @@
|
||||
-- name: CreateInboxItem :one
|
||||
INSERT INTO bot_inbox (bot_id, source, content)
|
||||
VALUES (sqlc.arg(bot_id), sqlc.arg(source), sqlc.arg(content))
|
||||
INSERT INTO bot_inbox (bot_id, source, header, content, action)
|
||||
VALUES (sqlc.arg(bot_id), sqlc.arg(source), sqlc.arg(header), sqlc.arg(content), sqlc.arg(action))
|
||||
RETURNING *;
|
||||
|
||||
-- name: GetInboxItemByID :one
|
||||
@@ -35,7 +35,7 @@ WHERE bot_id = sqlc.arg(bot_id)
|
||||
-- name: SearchInboxItems :many
|
||||
SELECT * FROM bot_inbox
|
||||
WHERE bot_id = sqlc.arg(bot_id)
|
||||
AND content::text ILIKE '%' || sqlc.arg(query) || '%'
|
||||
AND content ILIKE '%' || sqlc.arg(query) || '%'
|
||||
AND (sqlc.narg(start_time)::timestamptz IS NULL OR created_at >= sqlc.narg(start_time)::timestamptz)
|
||||
AND (sqlc.narg(end_time)::timestamptz IS NULL OR created_at <= sqlc.narg(end_time)::timestamptz)
|
||||
AND (sqlc.narg(include_read)::boolean IS NULL OR sqlc.narg(include_read)::boolean = TRUE OR is_read = FALSE)
|
||||
|
||||
@@ -211,7 +211,16 @@ func (p *ChannelInboundProcessor) HandleInbound(ctx context.Context, cfg channel
|
||||
if activeChatID == "" {
|
||||
activeChatID = strings.TrimSpace(resolved.ChatID)
|
||||
}
|
||||
if !shouldTriggerAssistantResponse(msg) && !identity.ForceReply {
|
||||
// Determine inbox action: trigger (immediate response) or notify (passive).
|
||||
inboxAction := inbox.ActionNotify
|
||||
if shouldTriggerAssistantResponse(msg) || identity.ForceReply {
|
||||
inboxAction = inbox.ActionTrigger
|
||||
}
|
||||
|
||||
// All messages go through inbox first.
|
||||
inboxItem := p.createInboxItem(ctx, identity, msg, text, attachments, resolved.RouteID, inboxAction)
|
||||
|
||||
if inboxAction != inbox.ActionTrigger {
|
||||
if p.logger != nil {
|
||||
p.logger.Info(
|
||||
"inbound not triggering assistant (group trigger condition not met)",
|
||||
@@ -228,9 +237,14 @@ func (p *ChannelInboundProcessor) HandleInbound(ctx context.Context, cfg channel
|
||||
if !strings.EqualFold(identity.BotType, "personal") {
|
||||
p.persistInboundUser(ctx, resolved.RouteID, identity, msg, text, attachments, "passive_sync")
|
||||
}
|
||||
p.createInboxItem(ctx, identity, msg, text, attachments, resolved.RouteID)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Mark the trigger inbox item as read immediately.
|
||||
if inboxItem.ID != "" {
|
||||
p.markInboxItemRead(ctx, inboxItem)
|
||||
}
|
||||
|
||||
userMessagePersisted := p.persistInboundUser(ctx, resolved.RouteID, identity, msg, text, attachments, "active_chat")
|
||||
|
||||
// Issue chat token for reply routing.
|
||||
@@ -717,17 +731,18 @@ func (p *ChannelInboundProcessor) createInboxItem(
|
||||
text string,
|
||||
attachments []conversation.ChatAttachment,
|
||||
routeID string,
|
||||
) {
|
||||
action string,
|
||||
) inbox.Item {
|
||||
if p.inboxService == nil {
|
||||
return
|
||||
return inbox.Item{}
|
||||
}
|
||||
botID := strings.TrimSpace(ident.BotID)
|
||||
if botID == "" {
|
||||
return
|
||||
return inbox.Item{}
|
||||
}
|
||||
trimmedText := strings.TrimSpace(text)
|
||||
if trimmedText == "" && len(attachments) == 0 {
|
||||
return
|
||||
return inbox.Item{}
|
||||
}
|
||||
displayName := strings.TrimSpace(ident.DisplayName)
|
||||
if displayName == "" {
|
||||
@@ -750,17 +765,29 @@ func (p *ChannelInboundProcessor) createInboxItem(
|
||||
strings.TrimSpace(msg.Conversation.Name),
|
||||
attachmentPaths,
|
||||
)
|
||||
content := meta.ToMap()
|
||||
content["text"] = trimmedText
|
||||
content["route_id"] = strings.TrimSpace(routeID)
|
||||
header := meta.ToMap()
|
||||
header["route_id"] = strings.TrimSpace(routeID)
|
||||
|
||||
if _, err := p.inboxService.Create(ctx, inbox.CreateRequest{
|
||||
item, err := p.inboxService.Create(ctx, inbox.CreateRequest{
|
||||
BotID: botID,
|
||||
Source: msg.Channel.String(),
|
||||
Content: content,
|
||||
}); err != nil && p.logger != nil {
|
||||
Header: header,
|
||||
Content: trimmedText,
|
||||
Action: action,
|
||||
})
|
||||
if err != nil && p.logger != nil {
|
||||
p.logger.Warn("create inbox item failed", slog.Any("error", err), slog.String("bot_id", botID))
|
||||
}
|
||||
return item
|
||||
}
|
||||
|
||||
func (p *ChannelInboundProcessor) markInboxItemRead(ctx context.Context, item inbox.Item) {
|
||||
if p.inboxService == nil || item.ID == "" {
|
||||
return
|
||||
}
|
||||
if err := p.inboxService.MarkRead(ctx, item.BotID, []string{item.ID}); err != nil && p.logger != nil {
|
||||
p.logger.Warn("mark inbox item read failed", slog.Any("error", err), slog.String("item_id", item.ID))
|
||||
}
|
||||
}
|
||||
|
||||
func buildChannelMessage(output conversation.AssistantOutput, capabilities channel.ChannelCapabilities) channel.Message {
|
||||
|
||||
@@ -175,7 +175,8 @@ type gatewaySkill struct {
|
||||
type gatewayInboxItem struct {
|
||||
ID string `json:"id"`
|
||||
Source string `json:"source"`
|
||||
Content map[string]any `json:"content"`
|
||||
Header map[string]any `json:"header"`
|
||||
Content string `json:"content"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
}
|
||||
|
||||
@@ -408,6 +409,7 @@ func (r *Resolver) resolve(ctx context.Context, req conversation.ChatRequest) (r
|
||||
inboxGatewayItems = append(inboxGatewayItems, gatewayInboxItem{
|
||||
ID: item.ID,
|
||||
Source: item.Source,
|
||||
Header: item.Header,
|
||||
Content: item.Content,
|
||||
CreatedAt: item.CreatedAt.Format(time.RFC3339),
|
||||
})
|
||||
|
||||
@@ -37,25 +37,35 @@ func (q *Queries) CountUnreadInboxItems(ctx context.Context, botID pgtype.UUID)
|
||||
}
|
||||
|
||||
const createInboxItem = `-- name: CreateInboxItem :one
|
||||
INSERT INTO bot_inbox (bot_id, source, content)
|
||||
VALUES ($1, $2, $3)
|
||||
RETURNING id, bot_id, source, content, is_read, created_at, read_at
|
||||
INSERT INTO bot_inbox (bot_id, source, header, content, action)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
RETURNING id, bot_id, source, header, content, action, is_read, created_at, read_at
|
||||
`
|
||||
|
||||
type CreateInboxItemParams struct {
|
||||
BotID pgtype.UUID `json:"bot_id"`
|
||||
Source string `json:"source"`
|
||||
Content []byte `json:"content"`
|
||||
Header []byte `json:"header"`
|
||||
Content string `json:"content"`
|
||||
Action string `json:"action"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateInboxItem(ctx context.Context, arg CreateInboxItemParams) (BotInbox, error) {
|
||||
row := q.db.QueryRow(ctx, createInboxItem, arg.BotID, arg.Source, arg.Content)
|
||||
row := q.db.QueryRow(ctx, createInboxItem,
|
||||
arg.BotID,
|
||||
arg.Source,
|
||||
arg.Header,
|
||||
arg.Content,
|
||||
arg.Action,
|
||||
)
|
||||
var i BotInbox
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.BotID,
|
||||
&i.Source,
|
||||
&i.Header,
|
||||
&i.Content,
|
||||
&i.Action,
|
||||
&i.IsRead,
|
||||
&i.CreatedAt,
|
||||
&i.ReadAt,
|
||||
@@ -90,7 +100,7 @@ func (q *Queries) DeleteInboxItemsByBot(ctx context.Context, botID pgtype.UUID)
|
||||
}
|
||||
|
||||
const getInboxItemByID = `-- name: GetInboxItemByID :one
|
||||
SELECT id, bot_id, source, content, is_read, created_at, read_at FROM bot_inbox
|
||||
SELECT id, bot_id, source, header, content, action, is_read, created_at, read_at FROM bot_inbox
|
||||
WHERE id = $1
|
||||
AND bot_id = $2
|
||||
`
|
||||
@@ -107,7 +117,9 @@ func (q *Queries) GetInboxItemByID(ctx context.Context, arg GetInboxItemByIDPara
|
||||
&i.ID,
|
||||
&i.BotID,
|
||||
&i.Source,
|
||||
&i.Header,
|
||||
&i.Content,
|
||||
&i.Action,
|
||||
&i.IsRead,
|
||||
&i.CreatedAt,
|
||||
&i.ReadAt,
|
||||
@@ -116,7 +128,7 @@ func (q *Queries) GetInboxItemByID(ctx context.Context, arg GetInboxItemByIDPara
|
||||
}
|
||||
|
||||
const listInboxItems = `-- name: ListInboxItems :many
|
||||
SELECT id, bot_id, source, content, is_read, created_at, read_at FROM bot_inbox
|
||||
SELECT id, bot_id, source, header, content, action, is_read, created_at, read_at FROM bot_inbox
|
||||
WHERE bot_id = $1
|
||||
AND ($2::boolean IS NULL OR is_read = $2::boolean)
|
||||
AND ($3::text IS NULL OR source = $3::text)
|
||||
@@ -152,7 +164,9 @@ func (q *Queries) ListInboxItems(ctx context.Context, arg ListInboxItemsParams)
|
||||
&i.ID,
|
||||
&i.BotID,
|
||||
&i.Source,
|
||||
&i.Header,
|
||||
&i.Content,
|
||||
&i.Action,
|
||||
&i.IsRead,
|
||||
&i.CreatedAt,
|
||||
&i.ReadAt,
|
||||
@@ -168,7 +182,7 @@ func (q *Queries) ListInboxItems(ctx context.Context, arg ListInboxItemsParams)
|
||||
}
|
||||
|
||||
const listUnreadInboxItems = `-- name: ListUnreadInboxItems :many
|
||||
SELECT id, bot_id, source, content, is_read, created_at, read_at FROM bot_inbox
|
||||
SELECT id, bot_id, source, header, content, action, is_read, created_at, read_at FROM bot_inbox
|
||||
WHERE bot_id = $1
|
||||
AND is_read = FALSE
|
||||
ORDER BY created_at ASC
|
||||
@@ -193,7 +207,9 @@ func (q *Queries) ListUnreadInboxItems(ctx context.Context, arg ListUnreadInboxI
|
||||
&i.ID,
|
||||
&i.BotID,
|
||||
&i.Source,
|
||||
&i.Header,
|
||||
&i.Content,
|
||||
&i.Action,
|
||||
&i.IsRead,
|
||||
&i.CreatedAt,
|
||||
&i.ReadAt,
|
||||
@@ -228,9 +244,9 @@ func (q *Queries) MarkInboxItemsRead(ctx context.Context, arg MarkInboxItemsRead
|
||||
}
|
||||
|
||||
const searchInboxItems = `-- name: SearchInboxItems :many
|
||||
SELECT id, bot_id, source, content, is_read, created_at, read_at FROM bot_inbox
|
||||
SELECT id, bot_id, source, header, content, action, is_read, created_at, read_at FROM bot_inbox
|
||||
WHERE bot_id = $1
|
||||
AND content::text ILIKE '%' || $2 || '%'
|
||||
AND content ILIKE '%' || $2 || '%'
|
||||
AND ($3::timestamptz IS NULL OR created_at >= $3::timestamptz)
|
||||
AND ($4::timestamptz IS NULL OR created_at <= $4::timestamptz)
|
||||
AND ($5::boolean IS NULL OR $5::boolean = TRUE OR is_read = FALSE)
|
||||
@@ -267,7 +283,9 @@ func (q *Queries) SearchInboxItems(ctx context.Context, arg SearchInboxItemsPara
|
||||
&i.ID,
|
||||
&i.BotID,
|
||||
&i.Source,
|
||||
&i.Header,
|
||||
&i.Content,
|
||||
&i.Action,
|
||||
&i.IsRead,
|
||||
&i.CreatedAt,
|
||||
&i.ReadAt,
|
||||
|
||||
@@ -105,7 +105,9 @@ type BotInbox struct {
|
||||
ID pgtype.UUID `json:"id"`
|
||||
BotID pgtype.UUID `json:"bot_id"`
|
||||
Source string `json:"source"`
|
||||
Content []byte `json:"content"`
|
||||
Header []byte `json:"header"`
|
||||
Content string `json:"content"`
|
||||
Action string `json:"action"`
|
||||
IsRead bool `json:"is_read"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
ReadAt pgtype.Timestamptz `json:"read_at"`
|
||||
|
||||
@@ -142,7 +142,7 @@ func (h *InboxHandler) Create(c echo.Context) error {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
|
||||
}
|
||||
req.BotID = botID
|
||||
if len(req.Content) == 0 {
|
||||
if strings.TrimSpace(req.Content) == "" {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "content is required")
|
||||
}
|
||||
item, err := h.service.Create(c.Request().Context(), req)
|
||||
|
||||
+27
-10
@@ -28,11 +28,18 @@ func NewService(log *slog.Logger, queries *sqlc.Queries) *Service {
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
ActionTrigger = "trigger"
|
||||
ActionNotify = "notify"
|
||||
)
|
||||
|
||||
type Item struct {
|
||||
ID string `json:"id"`
|
||||
BotID string `json:"bot_id"`
|
||||
Source string `json:"source"`
|
||||
Content map[string]any `json:"content"`
|
||||
Header map[string]any `json:"header"`
|
||||
Content string `json:"content"`
|
||||
Action string `json:"action"`
|
||||
IsRead bool `json:"is_read"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
ReadAt time.Time `json:"read_at,omitempty"`
|
||||
@@ -41,7 +48,9 @@ type Item struct {
|
||||
type CreateRequest struct {
|
||||
BotID string `json:"bot_id"`
|
||||
Source string `json:"source"`
|
||||
Content map[string]any `json:"content"`
|
||||
Header map[string]any `json:"header"`
|
||||
Content string `json:"content"`
|
||||
Action string `json:"action"`
|
||||
}
|
||||
|
||||
type ListFilter struct {
|
||||
@@ -69,15 +78,21 @@ func (s *Service) Create(ctx context.Context, req CreateRequest) (Item, error) {
|
||||
if err != nil {
|
||||
return Item{}, err
|
||||
}
|
||||
content, err := json.Marshal(req.Content)
|
||||
header, err := json.Marshal(req.Header)
|
||||
if err != nil {
|
||||
return Item{}, err
|
||||
}
|
||||
action := req.Action
|
||||
if action != ActionTrigger && action != ActionNotify {
|
||||
action = ActionNotify
|
||||
}
|
||||
|
||||
row, err := s.queries.CreateInboxItem(ctx, sqlc.CreateInboxItemParams{
|
||||
BotID: botUUID,
|
||||
Source: req.Source,
|
||||
Content: content,
|
||||
Header: header,
|
||||
Content: req.Content,
|
||||
Action: action,
|
||||
})
|
||||
if err != nil {
|
||||
return Item{}, err
|
||||
@@ -236,18 +251,20 @@ func (s *Service) Delete(ctx context.Context, botID, itemID string) error {
|
||||
// --- conversion helpers ---
|
||||
|
||||
func rowToItem(row sqlc.BotInbox) Item {
|
||||
var content map[string]any
|
||||
if len(row.Content) > 0 {
|
||||
_ = json.Unmarshal(row.Content, &content)
|
||||
var header map[string]any
|
||||
if len(row.Header) > 0 {
|
||||
_ = json.Unmarshal(row.Header, &header)
|
||||
}
|
||||
if content == nil {
|
||||
content = map[string]any{}
|
||||
if header == nil {
|
||||
header = map[string]any{}
|
||||
}
|
||||
return Item{
|
||||
ID: pgUUIDToString(row.ID),
|
||||
BotID: pgUUIDToString(row.BotID),
|
||||
Source: row.Source,
|
||||
Content: content,
|
||||
Header: header,
|
||||
Content: row.Content,
|
||||
Action: row.Action,
|
||||
IsRead: row.IsRead,
|
||||
CreatedAt: db.TimeFromPg(row.CreatedAt),
|
||||
ReadAt: db.TimeFromPg(row.ReadAt),
|
||||
|
||||
@@ -137,6 +137,7 @@ func (e *Executor) CallTool(ctx context.Context, session mcpgw.ToolSessionContex
|
||||
entry := map[string]any{
|
||||
"id": item.ID,
|
||||
"source": item.Source,
|
||||
"header": item.Header,
|
||||
"content": item.Content,
|
||||
"is_read": item.IsRead,
|
||||
"created_at": item.CreatedAt.Format(time.RFC3339),
|
||||
|
||||
@@ -29,13 +29,20 @@ ${skill.content}
|
||||
|
||||
const formatInbox = (items: InboxItem[]): string => {
|
||||
if (!items || items.length === 0) return ''
|
||||
const formatted = items.map((item) => ({
|
||||
id: item.id,
|
||||
source: item.source,
|
||||
header: item.header,
|
||||
content: item.content,
|
||||
createdAt: item.createdAt,
|
||||
}))
|
||||
return `
|
||||
## Inbox (${items.length} unread)
|
||||
|
||||
These are messages from other channels — NOT from the current conversation. Use ${quote('send')} or ${quote('react')} if you want to respond to any of them.
|
||||
|
||||
<inbox>
|
||||
${JSON.stringify(items)}
|
||||
${JSON.stringify(formatted)}
|
||||
</inbox>
|
||||
|
||||
Use ${quote('search_inbox')} to find older messages by keyword.
|
||||
|
||||
@@ -33,7 +33,8 @@ export const allActions = Object.values(AgentAction)
|
||||
export interface InboxItem {
|
||||
id: string
|
||||
source: string
|
||||
content: Record<string, unknown>
|
||||
header: Record<string, unknown>
|
||||
content: string
|
||||
createdAt: string
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -499,6 +499,12 @@ export type HandlersPingResponse = {
|
||||
status?: string;
|
||||
};
|
||||
|
||||
export type HandlersRefreshResponse = {
|
||||
access_token?: string;
|
||||
expires_at?: string;
|
||||
token_type?: string;
|
||||
};
|
||||
|
||||
export type HandlersSkillItem = {
|
||||
content?: string;
|
||||
description?: string;
|
||||
@@ -623,19 +629,23 @@ export type InboxCountResult = {
|
||||
};
|
||||
|
||||
export type InboxCreateRequest = {
|
||||
action?: string;
|
||||
bot_id?: string;
|
||||
content?: {
|
||||
content?: string;
|
||||
header?: {
|
||||
[key: string]: unknown;
|
||||
};
|
||||
source?: string;
|
||||
};
|
||||
|
||||
export type InboxItem = {
|
||||
action?: string;
|
||||
bot_id?: string;
|
||||
content?: {
|
||||
content?: string;
|
||||
created_at?: string;
|
||||
header?: {
|
||||
[key: string]: unknown;
|
||||
};
|
||||
created_at?: string;
|
||||
id?: string;
|
||||
is_read?: boolean;
|
||||
read_at?: string;
|
||||
@@ -975,7 +985,7 @@ export type SearchprovidersProviderMeta = {
|
||||
provider?: string;
|
||||
};
|
||||
|
||||
export type SearchprovidersProviderName = 'brave' | 'bing' | 'google';
|
||||
export type SearchprovidersProviderName = 'brave' | 'bing' | 'google' | 'tavily';
|
||||
|
||||
export type SearchprovidersUpdateRequest = {
|
||||
config?: {
|
||||
@@ -1130,6 +1140,35 @@ export type PostAuthLoginResponses = {
|
||||
|
||||
export type PostAuthLoginResponse = PostAuthLoginResponses[keyof PostAuthLoginResponses];
|
||||
|
||||
export type PostAuthRefreshData = {
|
||||
body?: never;
|
||||
path?: never;
|
||||
query?: never;
|
||||
url: '/auth/refresh';
|
||||
};
|
||||
|
||||
export type PostAuthRefreshErrors = {
|
||||
/**
|
||||
* Unauthorized
|
||||
*/
|
||||
401: HandlersErrorResponse;
|
||||
/**
|
||||
* Internal Server Error
|
||||
*/
|
||||
500: HandlersErrorResponse;
|
||||
};
|
||||
|
||||
export type PostAuthRefreshError = PostAuthRefreshErrors[keyof PostAuthRefreshErrors];
|
||||
|
||||
export type PostAuthRefreshResponses = {
|
||||
/**
|
||||
* OK
|
||||
*/
|
||||
200: HandlersRefreshResponse;
|
||||
};
|
||||
|
||||
export type PostAuthRefreshResponse = PostAuthRefreshResponses[keyof PostAuthRefreshResponses];
|
||||
|
||||
export type GetBotsData = {
|
||||
body?: never;
|
||||
path?: never;
|
||||
|
||||
+66
-4
@@ -61,6 +61,40 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/auth/refresh": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "Issue a new JWT using the existing claims with updated expiration",
|
||||
"tags": [
|
||||
"auth"
|
||||
],
|
||||
"summary": "Refresh Token",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.RefreshResponse"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/bots": {
|
||||
"get": {
|
||||
"description": "List bots accessible to current user (admin can specify owner_id)",
|
||||
@@ -7213,6 +7247,20 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"handlers.RefreshResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"access_token": {
|
||||
"type": "string"
|
||||
},
|
||||
"expires_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"token_type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"handlers.SkillItem": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -7514,10 +7562,16 @@ const docTemplate = `{
|
||||
"inbox.CreateRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"action": {
|
||||
"type": "string"
|
||||
},
|
||||
"bot_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"content": {
|
||||
"type": "string"
|
||||
},
|
||||
"header": {
|
||||
"type": "object",
|
||||
"additionalProperties": {}
|
||||
},
|
||||
@@ -7529,16 +7583,22 @@ const docTemplate = `{
|
||||
"inbox.Item": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"action": {
|
||||
"type": "string"
|
||||
},
|
||||
"bot_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"content": {
|
||||
"type": "object",
|
||||
"additionalProperties": {}
|
||||
"type": "string"
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"header": {
|
||||
"type": "object",
|
||||
"additionalProperties": {}
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -8381,12 +8441,14 @@ const docTemplate = `{
|
||||
"enum": [
|
||||
"brave",
|
||||
"bing",
|
||||
"google"
|
||||
"google",
|
||||
"tavily"
|
||||
],
|
||||
"x-enum-varnames": [
|
||||
"ProviderBrave",
|
||||
"ProviderBing",
|
||||
"ProviderGoogle"
|
||||
"ProviderGoogle",
|
||||
"ProviderTavily"
|
||||
]
|
||||
},
|
||||
"searchproviders.UpdateRequest": {
|
||||
|
||||
+66
-4
@@ -52,6 +52,40 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/auth/refresh": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"BearerAuth": []
|
||||
}
|
||||
],
|
||||
"description": "Issue a new JWT using the existing claims with updated expiration",
|
||||
"tags": [
|
||||
"auth"
|
||||
],
|
||||
"summary": "Refresh Token",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.RefreshResponse"
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Unauthorized",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/bots": {
|
||||
"get": {
|
||||
"description": "List bots accessible to current user (admin can specify owner_id)",
|
||||
@@ -7204,6 +7238,20 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"handlers.RefreshResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"access_token": {
|
||||
"type": "string"
|
||||
},
|
||||
"expires_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"token_type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"handlers.SkillItem": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -7505,10 +7553,16 @@
|
||||
"inbox.CreateRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"action": {
|
||||
"type": "string"
|
||||
},
|
||||
"bot_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"content": {
|
||||
"type": "string"
|
||||
},
|
||||
"header": {
|
||||
"type": "object",
|
||||
"additionalProperties": {}
|
||||
},
|
||||
@@ -7520,16 +7574,22 @@
|
||||
"inbox.Item": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"action": {
|
||||
"type": "string"
|
||||
},
|
||||
"bot_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"content": {
|
||||
"type": "object",
|
||||
"additionalProperties": {}
|
||||
"type": "string"
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"header": {
|
||||
"type": "object",
|
||||
"additionalProperties": {}
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -8372,12 +8432,14 @@
|
||||
"enum": [
|
||||
"brave",
|
||||
"bing",
|
||||
"google"
|
||||
"google",
|
||||
"tavily"
|
||||
],
|
||||
"x-enum-varnames": [
|
||||
"ProviderBrave",
|
||||
"ProviderBing",
|
||||
"ProviderGoogle"
|
||||
"ProviderGoogle",
|
||||
"ProviderTavily"
|
||||
]
|
||||
},
|
||||
"searchproviders.UpdateRequest": {
|
||||
|
||||
+42
-2
@@ -825,6 +825,15 @@ definitions:
|
||||
status:
|
||||
type: string
|
||||
type: object
|
||||
handlers.RefreshResponse:
|
||||
properties:
|
||||
access_token:
|
||||
type: string
|
||||
expires_at:
|
||||
type: string
|
||||
token_type:
|
||||
type: string
|
||||
type: object
|
||||
handlers.SkillItem:
|
||||
properties:
|
||||
content:
|
||||
@@ -1022,9 +1031,13 @@ definitions:
|
||||
type: object
|
||||
inbox.CreateRequest:
|
||||
properties:
|
||||
action:
|
||||
type: string
|
||||
bot_id:
|
||||
type: string
|
||||
content:
|
||||
type: string
|
||||
header:
|
||||
additionalProperties: {}
|
||||
type: object
|
||||
source:
|
||||
@@ -1032,13 +1045,17 @@ definitions:
|
||||
type: object
|
||||
inbox.Item:
|
||||
properties:
|
||||
action:
|
||||
type: string
|
||||
bot_id:
|
||||
type: string
|
||||
content:
|
||||
additionalProperties: {}
|
||||
type: object
|
||||
type: string
|
||||
created_at:
|
||||
type: string
|
||||
header:
|
||||
additionalProperties: {}
|
||||
type: object
|
||||
id:
|
||||
type: string
|
||||
is_read:
|
||||
@@ -1599,11 +1616,13 @@ definitions:
|
||||
- brave
|
||||
- bing
|
||||
- google
|
||||
- tavily
|
||||
type: string
|
||||
x-enum-varnames:
|
||||
- ProviderBrave
|
||||
- ProviderBing
|
||||
- ProviderGoogle
|
||||
- ProviderTavily
|
||||
searchproviders.UpdateRequest:
|
||||
properties:
|
||||
config:
|
||||
@@ -1824,6 +1843,27 @@ paths:
|
||||
summary: Login
|
||||
tags:
|
||||
- auth
|
||||
/auth/refresh:
|
||||
post:
|
||||
description: Issue a new JWT using the existing claims with updated expiration
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.RefreshResponse'
|
||||
"401":
|
||||
description: Unauthorized
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.ErrorResponse'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.ErrorResponse'
|
||||
security:
|
||||
- BearerAuth: []
|
||||
summary: Refresh Token
|
||||
tags:
|
||||
- auth
|
||||
/bots:
|
||||
get:
|
||||
description: List bots accessible to current user (admin can specify owner_id)
|
||||
|
||||
Reference in New Issue
Block a user