{ "updatedAt": "2026-05-01T20:02:33.035Z", "createdAt": "2026-03-27T23:10:47.862Z", "id": "75JCevkdgkyCr2qH", "name": "Nightly Obsidian Vault Sync", "description": null, "active": false, "isArchived": false, "nodes": [ { "id": "schedule-node", "name": "Schedule Trigger", "type": "n8n-nodes-base.scheduleTrigger", "typeVersion": 1.2, "position": [ 240, 300 ], "parameters": { "rule": { "interval": [ { "field": "cronExpression", "expression": "45 23 * * *" } ] } } }, { "id": "nightly-sync", "name": "Generate Nightly Vault Sync", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [ 520, 300 ], "parameters": { "mode": "runOnceForAllItems", "jsCode": "\nconst http = this.helpers.httpRequest;\nconst OBS_BASE = 'http://192.168.153.130:27123';\nconst OBS_KEY = '698cfc8b00b93c41480e7e1cb84d77b75176be87507256a5fae9a5b53b5a20cb';\nconst MODEL = 'gemma-4-26B-A4B-it-UD-IQ2_M.gguf';\nconst TZ = 'America/Los_Angeles';\n\nconst enc = (p) => encodeURIComponent(p).replace(/%2F/g, '/');\nconst getDate = () => new Intl.DateTimeFormat('en-CA', { timeZone: TZ, year: 'numeric', month: '2-digit', day: '2-digit' }).format(new Date()).replaceAll('/', '-');\n\nconst notePaths = [\n 'Infrastructure/Architecture.md',\n 'Infrastructure/Automation/n8n Workflows.md',\n 'Infrastructure/Automation/Cron Jobs.md',\n 'Infrastructure/Services/Docker Services.md'\n];\n\nconst obsHeaders = {\n 'Authorization': `Bearer ${OBS_KEY}`,\n 'User-Agent': 'n8n-nightly-vault-sync'\n};\n\nconst notes = {};\nfor (const p of notePaths) {\n notes[p] = await http({\n method: 'GET',\n url: `${OBS_BASE}/vault/${enc(p)}`,\n headers: obsHeaders,\n timeout: 15000,\n });\n}\n\nconst n8nHealth = await http({\n method: 'GET',\n url: 'http://192.168.153.130:18808/healthz',\n json: true,\n timeout: 10000,\n});\n\nconst modelInfo = await http({\n method: 'GET',\n url: 'http://192.168.153.130:18806/v1/models',\n json: true,\n timeout: 10000,\n});\n\nconst prompt = [\n 'Write a concise nightly operational sync note for an Obsidian shared vault.',\n 'Return markdown body only. No code fences.',\n 'Start with heading: # Nightly Vault Sync',\n 'Then sections: ## Summary, ## Current State, ## Follow-ups',\n 'Keep it factual, low-noise, and under 250 words.',\n 'Mention that this is an automated nightly note generated by n8n using the local LLM.',\n '',\n 'Live health:',\n `- n8n health: ${JSON.stringify(n8nHealth)}`,\n `- local model ids: ${JSON.stringify((modelInfo.data || []).map(m => m.id))}`,\n '',\n 'Source note contents:',\n ...notePaths.flatMap(p => [`\\n--- ${p} ---`, String(notes[p]).slice(0, 1800)])\n].join('\\n');\n\nconst llm = await http({\n method: 'POST',\n url: 'http://192.168.153.130:18806/v1/chat/completions',\n headers: { 'Content-Type': 'application/json' },\n body: {\n model: MODEL,\n temperature: 0.2,\n max_tokens: 260,\n messages: [\n {\n role: 'system',\n content: 'You create concise nightly operations notes for an Obsidian vault. Prefer concrete facts from the provided sources. If there are unresolved drifts or follow-ups, mention them briefly. Do not invent incidents.'\n },\n { role: 'user', content: prompt }\n ]\n },\n json: true,\n timeout: 60000,\n});\n\nlet body = (((llm || {}).choices || [])[0] || {}).message?.content || '';\nbody = body.replace(/^```(?:markdown)?\\s*/i, '').replace(/```\\s*$/i, '').trim();\nif (!body) {\n body = '# Nightly Vault Sync\\n\\n## Summary\\n\\nAutomated nightly note ran, but the local LLM returned an empty response.\\n\\n## Current State\\n\\n- n8n health: ok\\n- local model endpoint reachable\\n\\n## Follow-ups\\n\\n- Check the local LLM response path if this repeats.';\n}\n\nconst date = getDate();\nconst notePath = `Notes/${date} Nightly Vault Sync.md`;\nconst full = `---\\ntitle: Nightly Vault Sync\\narea: infrastructure\\ntags: [infrastructure, obsidian, automation, nightly, assistant]\\ncreated: ${date}\\nupdated: ${date}\\nstatus: active\\nrelated: [[Infrastructure/Architecture]], [[Infrastructure/Automation/n8n Workflows]], [[Infrastructure/Automation/Cron Jobs]], [[Infrastructure/Services/Docker Services]]\\n---\\n\\n${body}\\n`;\n\nawait http({\n method: 'PUT',\n url: `${OBS_BASE}/vault/${enc(notePath)}`,\n headers: { ...obsHeaders, 'Content-Type': 'text/markdown' },\n body: full,\n timeout: 20000,\n});\n\nreturn [{ json: { notePath, model: MODEL, sourceNotes: notePaths, n8nHealth, modelCount: (modelInfo.data || []).length } }];\n" } } ], "connections": { "Schedule Trigger": { "main": [ [ { "node": "Generate Nightly Vault Sync", "type": "main", "index": 0 } ] ] } }, "settings": { "executionOrder": "v1", "callerPolicy": "workflowsFromSameOwner", "availableInMCP": false }, "staticData": { "node:Schedule Trigger": { "recurrenceRules": [] } }, "meta": null, "pinData": null, "versionId": "9585256a-29c2-444a-aa55-0eaf259f032d", "activeVersionId": null, "versionCounter": 66, "triggerCount": 1, "shared": [ { "updatedAt": "2026-03-27T23:10:47.871Z", "createdAt": "2026-03-27T23:10:47.871Z", "role": "workflow:owner", "workflowId": "75JCevkdgkyCr2qH", "projectId": "WGdp8QunI1tHpjXa", "project": { "updatedAt": "2026-03-11T21:08:10.005Z", "createdAt": "2026-03-11T21:05:11.541Z", "id": "WGdp8QunI1tHpjXa", "name": "will will ", "type": "personal", "icon": null, "description": null, "creatorId": "5ad50ead-6e6a-4d12-ab5b-e5db15835bb5" } } ], "tags": [], "activeVersion": null }