62a1f57c1f
Daily 06:30 PT scheduled workflow that: - Collects weather (wttr.in/Seattle), swarm health, n8n/litellm health - Fetches email highlights from IMAP Triage executions - Attempts Google Calendar (graceful skip if OAuth expired) - Synthesizes via local LLM (gemma-4-26B on llama.cpp) - Delivers to Telegram + Obsidian All data collection nodes have continueOnFail for resilience. Workflow ID: g3IdGZCK1EtTsv9T, active: true
1 line
10 KiB
JSON
1 line
10 KiB
JSON
[{"updatedAt":"2026-05-13T21:41:17.798Z","createdAt":"2026-05-13T21:41:17.798Z","id":"g3IdGZCK1EtTsv9T","name":"Morning Brief","description":null,"active":true,"isArchived":false,"nodes":[{"parameters":{"rule":{"interval":[{"field":"cronExpression","expression":"30 6 * * *"}]}},"type":"n8n-nodes-base.scheduleTrigger","typeVersion":1.3,"position":[0,0],"id":"16110cb5-e50a-4d99-a613-448057221422","name":"Daily 06:30 PT"},{"parameters":{"method":"GET","url":"http://wttr.in/Seattle?format=j1","options":{"timeout":10000}},"type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[300,-400],"id":"a119dfe9-46db-43ca-98b2-f0690bc0f6f5","name":"Weather","continueOnFail":true},{"parameters":{"method":"GET","url":"http://172.19.0.1:18809/health","options":{"timeout":10000}},"type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[300,-250],"id":"05f60eba-ab11-4fe0-b761-d1ca9ae557d4","name":"Swarm Health","continueOnFail":true},{"parameters":{"method":"GET","url":"http://127.0.0.1:5678/healthz","options":{"timeout":10000}},"type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[300,-100],"id":"4b5c3f4c-7f11-4e0c-9c56-3b8596a1d25d","name":"n8n Health","continueOnFail":true},{"parameters":{"method":"GET","url":"http://172.19.0.1:18804/health/liveliness","options":{"timeout":10000}},"type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[300,50],"id":"a8e4e45c-60a1-4f90-8ecc-49782d7be900","name":"LiteLLM Health","continueOnFail":true},{"parameters":{"method":"GET","url":"http://127.0.0.1:5678/api/v1/executions","sendQuery":true,"queryParameters":{"parameters":[{"name":"workflowId","value":"9sFwRyUDz51csAp7"},{"name":"limit","value":"5"},{"name":"status","value":"success"}]},"sendHeaders":true,"headerParameters":{"parameters":[{"name":"X-N8N-API-KEY","value":"={{ $env.N8N_API_KEY }}"}]},"options":{"timeout":15000}},"type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[300,200],"id":"c688abdf-9b63-43b4-81da-7c81388b73f8","name":"Email Highlights","continueOnFail":true},{"parameters":{"method":"GET","url":"=https://www.googleapis.com/calendar/v3/calendars/primary/events?timeMin={{ $now.format('yyyy-MM-dd') }}T00:00:00-07:00&timeMax={{ $now.plus({days:1}).format('yyyy-MM-dd') }}T23:59:59-07:00&singleEvents=true&orderBy=startTime","authentication":"oAuth2","options":{"timeout":10000}},"type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[300,350],"id":"d3c5a4ce-9f81-4da8-8dc8-7256bd96285b","name":"Calendar","credentials":{"oAuth2Api":{"id":"458fY4bs1z49OTeZ","name":"Google OAuth"}},"continueOnFail":true},{"parameters":{"mode":"runOnceForAllItems","jsCode":"\nfunction getSafe(nodeName) {\n try {\n const items = $(nodeName).all();\n if (items && items.length > 0 && items[0].json) {\n return items[0].json;\n }\n } catch (e) {}\n return { error: 'Node failed or returned no data' };\n}\n\nconst weather = getSafe('Weather');\nconst swarmHealth = getSafe('Swarm Health');\nconst n8nHealth = getSafe('n8n Health');\nconst litellmHealth = getSafe('LiteLLM Health');\nconst emailData = getSafe('Email Highlights');\nconst calendar = getSafe('Calendar');\n\n// Extract weather summary\nlet weatherSummary = {};\nif (weather.current_condition && weather.current_condition[0]) {\n const c = weather.current_condition[0];\n weatherSummary = {\n temp_F: c.FeelsLikeF || c.temp_F,\n description: c.weatherDesc ? c.weatherDesc[0].value : 'unknown',\n humidity: c.humidity,\n wind_mph: c.windspeedMiles\n };\n} else {\n weatherSummary = { error: 'Weather data unavailable' };\n}\n\n// Count healthy/unhealthy containers\nlet infraSummary = { healthy: 0, unhealthy: 0, details: [] };\nif (Array.isArray(swarmHealth)) {\n for (const c of swarmHealth) {\n if (c.health === 'healthy' || c.status === 'running') {\n infraSummary.healthy++;\n } else {\n infraSummary.unhealthy++;\n }\n infraSummary.details.push({ name: c.name || c.Names, status: c.status, health: c.health });\n }\n} else if (swarmHealth.containers && Array.isArray(swarmHealth.containers)) {\n for (const c of swarmHealth.containers) {\n if (c.health === 'healthy' || c.status === 'running') {\n infraSummary.healthy++;\n } else {\n infraSummary.unhealthy++;\n }\n infraSummary.details.push({ name: c.name, status: c.status, health: c.health });\n }\n} else if (swarmHealth.error) {\n infraSummary = { error: 'Swarm health endpoint unavailable' };\n}\n\nconst n8nOk = (n8nHealth && !n8nHealth.error);\nconst litellmOk = (litellmHealth && !litellmHealth.error);\n\n// Extract email info from execution data\nlet emailHighlights = [];\nif (emailData && emailData.data && Array.isArray(emailData.data)) {\n for (const exec of emailData.data.slice(0, 5)) {\n emailHighlights.push({\n id: exec.id,\n finished: exec.stoppedAt || 'unknown'\n });\n }\n}\n\n// Calendar events\nlet calendarEvents = [];\nif (calendar && calendar.items && Array.isArray(calendar.items)) {\n for (const ev of calendar.items.slice(0, 10)) {\n calendarEvents.push({\n summary: ev.summary || '(no title)',\n start: (ev.start && (ev.start.dateTime || ev.start.date)) || 'unknown',\n end: (ev.end && (ev.end.dateTime || ev.end.date)) || 'unknown'\n });\n }\n}\n\nconst dataForLLM = {\n date: new Date().toISOString().split('T')[0],\n weather: weatherSummary,\n infrastructure: {\n swarm: infraSummary,\n n8n: n8nOk ? 'healthy' : 'unhealthy',\n litellm: litellmOk ? 'healthy' : 'unhealthy'\n },\n email: emailHighlights.length > 0 ? emailHighlights : [{ info: 'No recent email triage data' }],\n calendar: calendarEvents.length > 0 ? calendarEvents : [{ info: 'Calendar unavailable or no events today' }]\n};\n\nreturn [{ json: { dataJson: JSON.stringify(dataForLLM, null, 2) } }];\n"},"type":"n8n-nodes-base.code","typeVersion":2,"position":[650,0],"id":"1d2b39db-3649-4316-8ce9-b5c83c981017","name":"Merge Data"},{"parameters":{"method":"POST","url":"http://172.19.0.1:18806/v1/chat/completions","sendBody":true,"specifyBody":"json","jsonBody":"={\"model\": \"gemma-4-26B-A4B-it-UD-IQ2_M.gguf\", \"messages\": [{\"role\": \"system\", \"content\": \"You are a personal morning brief assistant. Given raw data about weather, infrastructure, email, and calendar, produce a concise morning brief under 400 words. Use emojis for section headers. Format for Telegram using HTML (use <b> for bold, <code> for code). Keep it scannable and actionable. If any section data is missing or shows errors, note it briefly and move on.\"}, {\"role\": \"user\", \"content\": \"Here is today's data:\\n{{ $json.dataJson }}\"}], \"temperature\": 0.3, \"max_tokens\": 800}","options":{"timeout":60000}},"type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[950,0],"id":"f2eb23d3-bf07-46d8-8556-2ba6a0185f5a","name":"Synthesize with LLM","continueOnFail":false},{"parameters":{"mode":"runOnceForAllItems","jsCode":"\nconst response = $input.first().json;\nlet brief = '';\n\nif (response.choices && response.choices[0] && response.choices[0].message) {\n brief = response.choices[0].message.content;\n} else if (typeof response === 'string') {\n brief = response;\n} else {\n brief = 'Error: LLM synthesis failed. Raw data: ' + JSON.stringify(response).substring(0, 500);\n}\n\nconst today = new Date().toISOString().split('T')[0];\nconst yamlFrontmatter = '---\\ncreated: ' + today + '\\ntype: morning-brief\\ntags: [daily, brief]\\n---\\n\\n';\n\nreturn [{\n json: {\n brief: brief,\n briefWithFrontmatter: yamlFrontmatter + '# Morning Brief - ' + today + '\\n\\n' + brief,\n date: today\n }\n}];\n"},"type":"n8n-nodes-base.code","typeVersion":2,"position":[1250,0],"id":"0adac542-7d95-4002-a3e2-080442cfd9e3","name":"Extract Brief"},{"parameters":{"chatId":"8367012007","text":"={{ $json.brief }}","additionalFields":{"parse_mode":"HTML"}},"type":"n8n-nodes-base.telegram","typeVersion":1.2,"position":[1550,-150],"id":"8242ada9-20c8-4689-b00c-3cd2787b2eb5","name":"Send Telegram","credentials":{"telegramApi":{"id":"aox4dyIWVSRdcH5z","name":"Telegram Bot (OpenClaw)"}},"continueOnFail":true},{"parameters":{"method":"PUT","url":"=http://172.19.0.1:27123/vault/Notes/{{ $json.date }} Morning Brief.md","sendHeaders":true,"headerParameters":{"parameters":[{"name":"Content-Type","value":"text/markdown"}]},"sendBody":true,"contentType":"raw","rawContentType":"text/markdown","body":"={{ $json.briefWithFrontmatter }}","options":{"timeout":10000}},"type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[1550,150],"id":"0f1fd6a2-86c0-4d3f-a948-32ce701d9f9f","name":"Save to Obsidian","credentials":{"httpHeaderAuth":{"id":"465Swz2b71O2KRAK","name":"Obsidian Local REST API"}},"continueOnFail":true}],"connections":{"Daily 06:30 PT":{"main":[[{"node":"Weather","type":"main","index":0},{"node":"Swarm Health","type":"main","index":0},{"node":"n8n Health","type":"main","index":0},{"node":"LiteLLM Health","type":"main","index":0},{"node":"Email Highlights","type":"main","index":0},{"node":"Calendar","type":"main","index":0}]]},"Weather":{"main":[[{"node":"Merge Data","type":"main","index":0}]]},"Swarm Health":{"main":[[{"node":"Merge Data","type":"main","index":0}]]},"n8n Health":{"main":[[{"node":"Merge Data","type":"main","index":0}]]},"LiteLLM Health":{"main":[[{"node":"Merge Data","type":"main","index":0}]]},"Email Highlights":{"main":[[{"node":"Merge Data","type":"main","index":0}]]},"Calendar":{"main":[[{"node":"Merge Data","type":"main","index":0}]]},"Merge Data":{"main":[[{"node":"Synthesize with LLM","type":"main","index":0}]]},"Synthesize with LLM":{"main":[[{"node":"Extract Brief","type":"main","index":0}]]},"Extract Brief":{"main":[[{"node":"Send Telegram","type":"main","index":0},{"node":"Save to Obsidian","type":"main","index":0}]]}},"settings":{"executionOrder":"v1","timezone":"America/Los_Angeles","callerPolicy":"workflowsFromSameOwner","availableInMCP":false},"staticData":{"node:Daily 06:30 PT":{"recurrenceRules":[]}},"meta":null,"pinData":null,"versionId":"2e269319-d17c-4d15-8500-43d8e2bbf0b9","activeVersionId":"2e269319-d17c-4d15-8500-43d8e2bbf0b9","versionCounter":4,"triggerCount":1,"tags":[],"shared":[{"updatedAt":"2026-05-13T21:41:17.800Z","createdAt":"2026-05-13T21:41:17.800Z","role":"workflow:owner","workflowId":"g3IdGZCK1EtTsv9T","projectId":"WGdp8QunI1tHpjXa","project":{"updatedAt":"2026-03-11T21:08:10.005Z","createdAt":"2026-03-11T21:05:11.541Z","id":"WGdp8QunI1tHpjXa","name":"will will <will@wills-portal.com>","type":"personal","icon":null,"description":null,"creatorId":"5ad50ead-6e6a-4d12-ab5b-e5db15835bb5"}}],"versionMetadata":{"name":null,"description":null}}] |