486 lines
18 KiB
JSON
486 lines
18 KiB
JSON
{
|
|
"updatedAt": "2026-05-14T00:01:22.299Z",
|
|
"createdAt": "2026-05-12T17:48:01.214Z",
|
|
"id": "GSmzuA5dgGgyRg5v",
|
|
"name": "Web-to-Notes Capture (Local LLM + Obsidian)",
|
|
"description": null,
|
|
"active": true,
|
|
"isArchived": false,
|
|
"nodes": [
|
|
{
|
|
"parameters": {
|
|
"httpMethod": "POST",
|
|
"path": "web-to-notes",
|
|
"responseMode": "responseNode",
|
|
"options": {}
|
|
},
|
|
"id": "02979a5e-67e7-43ae-8c9f-4694a5b36e56",
|
|
"name": "Webhook - Capture URL",
|
|
"type": "n8n-nodes-base.webhook",
|
|
"typeVersion": 2.1,
|
|
"position": [
|
|
-900,
|
|
0
|
|
],
|
|
"webhookId": "7958ecbc-c714-41d5-a829-882447ab95f8"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "const body = $json.body ?? $json;\nconst url = String(body.url || body.link || '').trim();\nif (!url || !/^https?:\\/\\//i.test(url)) throw new Error('POST JSON must include url starting with http:// or https://');\nconst title = String(body.title || '').trim();\nconst notes = String(body.notes || body.note || body.comment || '').trim();\nconst tags = Array.isArray(body.tags) ? body.tags : String(body.tags || 'web-capture').split(',').map(s => s.trim()).filter(Boolean);\nreturn [{ json: { url, title, notes, tags, capturedAt: new Date().toISOString() } }];"
|
|
},
|
|
"id": "22ba0ac9-af51-4469-a8bd-b3d3c1dd049b",
|
|
"name": "Normalize Input",
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
-680,
|
|
0
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"method": "POST",
|
|
"url": "http://172.19.0.1:18806/v1/chat/completions",
|
|
"sendBody": true,
|
|
"specifyBody": "json",
|
|
"jsonBody": "={{ JSON.stringify({ model: \"gemma-4-26b\", messages: [{ role: \"system\", content: \"You are a concise summarizer. Extract key points, claims, and notable details. Format as clear markdown with a summary section and key points list.\" }, { role: \"user\", content: `Summarize this ${$json.content_type || \"web\"} content titled \"${$json.title || \"untitled\"}\":\\n\\n${($json.text || \"\").slice(0, 8000)}` }], temperature: 0.3, max_tokens: 1600 }) }}",
|
|
"options": {
|
|
"timeout": 120000
|
|
}
|
|
},
|
|
"id": "2ea254be-4a88-426a-97ff-16a80196b462",
|
|
"name": "Summarize with llama.cpp",
|
|
"type": "n8n-nodes-base.httpRequest",
|
|
"typeVersion": 4.2,
|
|
"position": [
|
|
0,
|
|
0
|
|
],
|
|
"continueOnFail": true
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "const extracted = $('Extract Content').first().json;\nconst input = $('Normalize Input').first().json;\n\nlet summary = '';\ntry { summary = $json.choices?.[0]?.message?.content || $json.body?.choices?.[0]?.message?.content || ''; } catch (e) {}\n// Dedent summary (LLM sometimes returns indented markdown)\nsummary = summary.split('\\n').map(l => l.replace(/^\\s{4}/, '')).join('\\n').trim();\nif (!summary) summary = 'LLM summary unavailable.\\n\\nContent excerpt:\\n\\n> ' + (extracted.text || '').slice(0, 1200);\n\nconst contentType = extracted.content_type || 'web';\nconst title = extracted.title || input.title || 'Untitled';\nconst sourceUrl = extracted.metadata?.source_url || input.url;\nconst notes = input.notes || '';\nconst tags = input.tags || ['web-capture'];\n\nif (contentType === 'youtube') tags.push('youtube', 'video-transcript');\nelse if (contentType === 'pdf') tags.push('pdf', 'document');\n\nconst meta = extracted.metadata || {};\nlet metaSection = '';\nif (contentType === 'youtube') {\n metaSection = `**Video ID:** ${meta.video_id || 'N/A'} \\n**Transcript Entries:** ${meta.transcript_entries || 0}`;\n} else if (contentType === 'pdf') {\n metaSection = `**Author:** ${meta.author || 'N/A'} \\n**Pages:** ${meta.page_count || 'N/A'}`;\n}\n\nfunction slugify(s) { return String(s || 'untitled').toLowerCase().replace(/https?:\\/\\//,'').replace(/[^a-z0-9]+/g,'-').replace(/^-+|-+$/g,'').slice(0,80) || 'untitled'; }\nfunction yamlSafe(s) { return String(s || '').replace(/'/g, \"''\").replace(/\\n/g, ' '); }\n\nconst date = new Date().toISOString().split('T')[0];\nconst notePath = `Clippings/${date}-${slugify(title)}.md`;\n\nconst frontmatter = [\n '---',\n `title: '${yamlSafe(title)}'`,\n `source_url: ${sourceUrl}`,\n `content_type: ${contentType}`,\n `date: ${date}`,\n `tags: [${tags.map(t => \"'\" + t + \"'\").join(', ')}]`,\n '---',\n].join('\\n');\n\nconst body = [\n frontmatter,\n '',\n `# ${title}`,\n '',\n `> Source: [${title}](${sourceUrl})`,\n ...(metaSection ? ['', metaSection] : []),\n ...(notes ? ['', `## Notes\\n${notes}`] : []),\n '',\n '## Summary',\n '',\n summary,\n '',\n '---',\n `*Captured via Web-to-Notes (${contentType})*`,\n].join('\\n');\n\nreturn [{ json: { notePath, body, title, contentType, sourceUrl } }];\n"
|
|
},
|
|
"id": "403dff8b-5789-4018-89ec-69d45569cd25",
|
|
"name": "Build Markdown Note",
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
220,
|
|
0
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"method": "PUT",
|
|
"url": "={{'http://172.19.0.1:27123/vault/' + encodeURIComponent($json.notePath).replace(/%2F/g, '/')}}",
|
|
"sendHeaders": true,
|
|
"headerParameters": {
|
|
"parameters": [
|
|
{
|
|
"name": "Content-Type",
|
|
"value": "text/markdown"
|
|
}
|
|
]
|
|
},
|
|
"sendBody": true,
|
|
"contentType": "raw",
|
|
"rawContentType": "text/markdown",
|
|
"body": "={{$json.body}}",
|
|
"options": {
|
|
"timeout": 30000
|
|
},
|
|
"authentication": "genericCredentialType",
|
|
"genericAuthType": "httpHeaderAuth"
|
|
},
|
|
"id": "1d00b920-985e-415c-b445-4a28674287a0",
|
|
"name": "Write Note to Obsidian",
|
|
"type": "n8n-nodes-base.httpRequest",
|
|
"typeVersion": 4.2,
|
|
"position": [
|
|
460,
|
|
0
|
|
],
|
|
"credentials": {
|
|
"httpHeaderAuth": {
|
|
"id": "465Swz2b71O2KRAK",
|
|
"name": "Obsidian Local REST API"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"parameters": {
|
|
"respondWith": "json",
|
|
"responseBody": "={{JSON.stringify({ok: true, notePath: $json.notePath, title: $json.title, source: $json.url})}}",
|
|
"options": {}
|
|
},
|
|
"id": "c3d45b9e-a4d3-43ee-855a-7a76030e8888",
|
|
"name": "Respond",
|
|
"type": "n8n-nodes-base.respondToWebhook",
|
|
"typeVersion": 1.5,
|
|
"position": [
|
|
700,
|
|
0
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"method": "POST",
|
|
"url": "http://172.19.0.1:18812/extract",
|
|
"sendBody": true,
|
|
"specifyBody": "json",
|
|
"jsonBody": "={{ JSON.stringify({ url: $json.url }) }}",
|
|
"options": {
|
|
"timeout": 120000,
|
|
"fullResponse": false
|
|
}
|
|
},
|
|
"id": "extract-content-v2",
|
|
"name": "Extract Content",
|
|
"type": "n8n-nodes-base.httpRequest",
|
|
"typeVersion": 4.2,
|
|
"position": [
|
|
-240,
|
|
0
|
|
]
|
|
}
|
|
],
|
|
"connections": {
|
|
"Webhook - Capture URL": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Normalize Input",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Normalize Input": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Extract Content",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Extract Content": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Summarize with llama.cpp",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Summarize with llama.cpp": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Build Markdown Note",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Build Markdown Note": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Write Note to Obsidian",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Write Note to Obsidian": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Respond",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
}
|
|
},
|
|
"settings": {
|
|
"executionOrder": "v1",
|
|
"callerPolicy": "workflowsFromSameOwner",
|
|
"availableInMCP": false
|
|
},
|
|
"staticData": null,
|
|
"meta": null,
|
|
"pinData": null,
|
|
"versionId": "f503ca32-52bf-42ef-9dd4-ceecf538ed08",
|
|
"activeVersionId": "f503ca32-52bf-42ef-9dd4-ceecf538ed08",
|
|
"versionCounter": 30,
|
|
"triggerCount": 1,
|
|
"shared": [
|
|
{
|
|
"updatedAt": "2026-05-12T17:48:01.217Z",
|
|
"createdAt": "2026-05-12T17:48:01.217Z",
|
|
"role": "workflow:owner",
|
|
"workflowId": "GSmzuA5dgGgyRg5v",
|
|
"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"
|
|
}
|
|
}
|
|
],
|
|
"tags": [],
|
|
"activeVersion": {
|
|
"updatedAt": "2026-05-14T00:01:22.300Z",
|
|
"createdAt": "2026-05-14T00:01:22.300Z",
|
|
"versionId": "f503ca32-52bf-42ef-9dd4-ceecf538ed08",
|
|
"workflowId": "GSmzuA5dgGgyRg5v",
|
|
"nodes": [
|
|
{
|
|
"parameters": {
|
|
"httpMethod": "POST",
|
|
"path": "web-to-notes",
|
|
"responseMode": "responseNode",
|
|
"options": {}
|
|
},
|
|
"id": "02979a5e-67e7-43ae-8c9f-4694a5b36e56",
|
|
"name": "Webhook - Capture URL",
|
|
"type": "n8n-nodes-base.webhook",
|
|
"typeVersion": 2.1,
|
|
"position": [
|
|
-900,
|
|
0
|
|
],
|
|
"webhookId": "7958ecbc-c714-41d5-a829-882447ab95f8"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "const body = $json.body ?? $json;\nconst url = String(body.url || body.link || '').trim();\nif (!url || !/^https?:\\/\\//i.test(url)) throw new Error('POST JSON must include url starting with http:// or https://');\nconst title = String(body.title || '').trim();\nconst notes = String(body.notes || body.note || body.comment || '').trim();\nconst tags = Array.isArray(body.tags) ? body.tags : String(body.tags || 'web-capture').split(',').map(s => s.trim()).filter(Boolean);\nreturn [{ json: { url, title, notes, tags, capturedAt: new Date().toISOString() } }];"
|
|
},
|
|
"id": "22ba0ac9-af51-4469-a8bd-b3d3c1dd049b",
|
|
"name": "Normalize Input",
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
-680,
|
|
0
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"method": "POST",
|
|
"url": "http://172.19.0.1:18806/v1/chat/completions",
|
|
"sendBody": true,
|
|
"specifyBody": "json",
|
|
"jsonBody": "={{ JSON.stringify({ model: \"gemma-4-26b\", messages: [{ role: \"system\", content: \"You are a concise summarizer. Extract key points, claims, and notable details. Format as clear markdown with a summary section and key points list.\" }, { role: \"user\", content: `Summarize this ${$json.content_type || \"web\"} content titled \"${$json.title || \"untitled\"}\":\\n\\n${($json.text || \"\").slice(0, 8000)}` }], temperature: 0.3, max_tokens: 1600 }) }}",
|
|
"options": {
|
|
"timeout": 120000
|
|
}
|
|
},
|
|
"id": "2ea254be-4a88-426a-97ff-16a80196b462",
|
|
"name": "Summarize with llama.cpp",
|
|
"type": "n8n-nodes-base.httpRequest",
|
|
"typeVersion": 4.2,
|
|
"position": [
|
|
0,
|
|
0
|
|
],
|
|
"continueOnFail": true
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "const extracted = $('Extract Content').first().json;\nconst input = $('Normalize Input').first().json;\n\nlet summary = '';\ntry { summary = $json.choices?.[0]?.message?.content || $json.body?.choices?.[0]?.message?.content || ''; } catch (e) {}\n// Dedent summary (LLM sometimes returns indented markdown)\nsummary = summary.split('\\n').map(l => l.replace(/^\\s{4}/, '')).join('\\n').trim();\nif (!summary) summary = 'LLM summary unavailable.\\n\\nContent excerpt:\\n\\n> ' + (extracted.text || '').slice(0, 1200);\n\nconst contentType = extracted.content_type || 'web';\nconst title = extracted.title || input.title || 'Untitled';\nconst sourceUrl = extracted.metadata?.source_url || input.url;\nconst notes = input.notes || '';\nconst tags = input.tags || ['web-capture'];\n\nif (contentType === 'youtube') tags.push('youtube', 'video-transcript');\nelse if (contentType === 'pdf') tags.push('pdf', 'document');\n\nconst meta = extracted.metadata || {};\nlet metaSection = '';\nif (contentType === 'youtube') {\n metaSection = `**Video ID:** ${meta.video_id || 'N/A'} \\n**Transcript Entries:** ${meta.transcript_entries || 0}`;\n} else if (contentType === 'pdf') {\n metaSection = `**Author:** ${meta.author || 'N/A'} \\n**Pages:** ${meta.page_count || 'N/A'}`;\n}\n\nfunction slugify(s) { return String(s || 'untitled').toLowerCase().replace(/https?:\\/\\//,'').replace(/[^a-z0-9]+/g,'-').replace(/^-+|-+$/g,'').slice(0,80) || 'untitled'; }\nfunction yamlSafe(s) { return String(s || '').replace(/'/g, \"''\").replace(/\\n/g, ' '); }\n\nconst date = new Date().toISOString().split('T')[0];\nconst notePath = `Clippings/${date}-${slugify(title)}.md`;\n\nconst frontmatter = [\n '---',\n `title: '${yamlSafe(title)}'`,\n `source_url: ${sourceUrl}`,\n `content_type: ${contentType}`,\n `date: ${date}`,\n `tags: [${tags.map(t => \"'\" + t + \"'\").join(', ')}]`,\n '---',\n].join('\\n');\n\nconst body = [\n frontmatter,\n '',\n `# ${title}`,\n '',\n `> Source: [${title}](${sourceUrl})`,\n ...(metaSection ? ['', metaSection] : []),\n ...(notes ? ['', `## Notes\\n${notes}`] : []),\n '',\n '## Summary',\n '',\n summary,\n '',\n '---',\n `*Captured via Web-to-Notes (${contentType})*`,\n].join('\\n');\n\nreturn [{ json: { notePath, body, title, contentType, sourceUrl } }];\n"
|
|
},
|
|
"id": "403dff8b-5789-4018-89ec-69d45569cd25",
|
|
"name": "Build Markdown Note",
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
220,
|
|
0
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"method": "PUT",
|
|
"url": "={{'http://172.19.0.1:27123/vault/' + encodeURIComponent($json.notePath).replace(/%2F/g, '/')}}",
|
|
"sendHeaders": true,
|
|
"headerParameters": {
|
|
"parameters": [
|
|
{
|
|
"name": "Content-Type",
|
|
"value": "text/markdown"
|
|
}
|
|
]
|
|
},
|
|
"sendBody": true,
|
|
"contentType": "raw",
|
|
"rawContentType": "text/markdown",
|
|
"body": "={{$json.body}}",
|
|
"options": {
|
|
"timeout": 30000
|
|
},
|
|
"authentication": "genericCredentialType",
|
|
"genericAuthType": "httpHeaderAuth"
|
|
},
|
|
"id": "1d00b920-985e-415c-b445-4a28674287a0",
|
|
"name": "Write Note to Obsidian",
|
|
"type": "n8n-nodes-base.httpRequest",
|
|
"typeVersion": 4.2,
|
|
"position": [
|
|
460,
|
|
0
|
|
],
|
|
"credentials": {
|
|
"httpHeaderAuth": {
|
|
"id": "465Swz2b71O2KRAK",
|
|
"name": "Obsidian Local REST API"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"parameters": {
|
|
"respondWith": "json",
|
|
"responseBody": "={{JSON.stringify({ok: true, notePath: $json.notePath, title: $json.title, source: $json.url})}}",
|
|
"options": {}
|
|
},
|
|
"id": "c3d45b9e-a4d3-43ee-855a-7a76030e8888",
|
|
"name": "Respond",
|
|
"type": "n8n-nodes-base.respondToWebhook",
|
|
"typeVersion": 1.5,
|
|
"position": [
|
|
700,
|
|
0
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"method": "POST",
|
|
"url": "http://172.19.0.1:18812/extract",
|
|
"sendBody": true,
|
|
"specifyBody": "json",
|
|
"jsonBody": "={{ JSON.stringify({ url: $json.url }) }}",
|
|
"options": {
|
|
"timeout": 120000,
|
|
"fullResponse": false
|
|
}
|
|
},
|
|
"id": "extract-content-v2",
|
|
"name": "Extract Content",
|
|
"type": "n8n-nodes-base.httpRequest",
|
|
"typeVersion": 4.2,
|
|
"position": [
|
|
-240,
|
|
0
|
|
]
|
|
}
|
|
],
|
|
"connections": {
|
|
"Webhook - Capture URL": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Normalize Input",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Normalize Input": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Extract Content",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Extract Content": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Summarize with llama.cpp",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Summarize with llama.cpp": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Build Markdown Note",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Build Markdown Note": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Write Note to Obsidian",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Write Note to Obsidian": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Respond",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
}
|
|
},
|
|
"authors": "will will",
|
|
"name": null,
|
|
"description": null,
|
|
"autosaved": false,
|
|
"workflowPublishHistory": [
|
|
{
|
|
"createdAt": "2026-05-14T00:01:22.328Z",
|
|
"id": 1462,
|
|
"workflowId": "GSmzuA5dgGgyRg5v",
|
|
"versionId": "f503ca32-52bf-42ef-9dd4-ceecf538ed08",
|
|
"event": "activated",
|
|
"userId": "5ad50ead-6e6a-4d12-ab5b-e5db15835bb5"
|
|
},
|
|
{
|
|
"createdAt": "2026-05-14T00:01:22.316Z",
|
|
"id": 1461,
|
|
"workflowId": "GSmzuA5dgGgyRg5v",
|
|
"versionId": "f503ca32-52bf-42ef-9dd4-ceecf538ed08",
|
|
"event": "deactivated",
|
|
"userId": "5ad50ead-6e6a-4d12-ab5b-e5db15835bb5"
|
|
}
|
|
]
|
|
}
|
|
}
|