feat: add Gmail Pub/Sub watcher for inbound email automation

New ChannelAdapter that monitors Gmail via Google Cloud Pub/Sub push
notifications with polling fallback. Supports OAuth2 auth, configurable
watch labels, template rendering with email metadata placeholders
(from, to, subject, snippet, date, id, labels).

Wired into daemon lifecycle and gateway (POST /gmail/push endpoint).
Includes 16 tests covering auth, templates, push notifications, and
channel routing.
This commit is contained in:
William Valentin
2026-02-07 15:39:24 -08:00
parent 131d23989c
commit 06438bb44f
8 changed files with 1008 additions and 1 deletions
+16
View File
@@ -119,6 +119,20 @@ const webhookSchema = z.object({
enabled: z.boolean().default(true),
});
const gmailSchema = z.object({
enabled: z.boolean().default(false),
credentials_file: z.string().optional(),
token_file: z.string().default('~/.config/flynn/gmail-token.json'),
watch_labels: z.array(z.string()).default(['INBOX']),
poll_interval: z.string().default('60s'),
history_start: z.string().optional(), // ISO date string — only process emails after this date
output: z.object({
channel: z.string().min(1),
peer: z.string().min(1),
}),
message: z.string().default('New email from {{from}}: {{subject}}\n\n{{snippet}}'),
}).optional();
const heartbeatCheckSchema = z.enum(['gateway', 'model', 'channels', 'memory', 'disk']);
const heartbeatSchema = z.object({
@@ -136,6 +150,7 @@ const heartbeatSchema = z.object({
const automationSchema = z.object({
cron: z.array(cronJobSchema).default([]),
webhooks: z.array(webhookSchema).default([]),
gmail: gmailSchema,
heartbeat: heartbeatSchema,
}).default({});
@@ -343,6 +358,7 @@ export type TelegramConfig = z.infer<typeof telegramSchema>;
export type ModelConfig = z.infer<typeof modelConfigSchema>;
export type CronJobConfig = z.infer<typeof cronJobSchema>;
export type WebhookConfig = z.infer<typeof webhookSchema>;
export type GmailConfig = z.infer<typeof gmailSchema>;
export type AgentsConfig = z.infer<typeof agentsSchema>;
export type CompactionConfig = z.infer<typeof compactionSchema>;
export type MemoryConfig = z.infer<typeof memorySchema>;