feat(10.2-02): add structured error returns to all 7 sub-workflows

- Added correlationId field to all sub-workflow trigger schemas
- Added success: true/false fields to return paths
- Added error objects to failure paths with workflow, node, message, httpCode, rawResponse
- Implemented correlationId pass-through for error correlation
- Preserved backward compatibility (existing return fields unchanged)

Modified sub-workflows:
- n8n-actions.json: 3 Format Result nodes with full error objects
- n8n-update.json: Pull/Create/Start error paths with error objects
- n8n-logs.json: Added correlationId pass-through
- n8n-batch-ui.json: Added correlationId to trigger
- n8n-status.json: Added correlationId to trigger
- n8n-confirmation.json: Added correlationId pass-through to stop action
- n8n-matching.json: Added correlationId to trigger

All sub-workflows now return structured error objects on failures for main workflow error ring buffer capture.
This commit is contained in:
Lucas Berger
2026-02-08 12:53:01 -05:00
parent 6833641ad1
commit 1c632d039a
7 changed files with 40 additions and 16 deletions
+2 -2
View File
@@ -14,7 +14,7 @@
},
{
"parameters": {
"jsCode": "// Parse and validate input\nconst input = $json;\n\n// Get container identifier (ID or name)\nconst containerId = input.containerId || '';\nconst containerName = input.containerName || '';\nconst lineCount = input.lineCount || 50;\nconst chatId = input.chatId;\nconst messageId = input.messageId || 0;\nconst responseMode = input.responseMode || 'text';\n\nif (!containerId && !containerName) {\n throw new Error('Either containerId or containerName required');\n}\n\nif (!chatId) {\n throw new Error('chatId required');\n}\n\nreturn {\n json: {\n containerId: containerId,\n containerName: containerName,\n lineCount: Math.min(Math.max(parseInt(lineCount), 1), 1000),\n chatId: chatId,\n messageId: messageId,\n responseMode: responseMode\n }\n};"
"jsCode": "// Parse and validate input\nconst input = $json;\n\n// Get container identifier (ID or name)\nconst containerId = input.containerId || '';\nconst containerName = input.containerName || '';\nconst lineCount = input.lineCount || 50;\nconst chatId = input.chatId;\nconst messageId = input.messageId || 0;\nconst responseMode = input.responseMode || 'text';\nconst correlationId = input.correlationId || '';\n\nif (!containerId && !containerName) {\n throw new Error('Either containerId or containerName required');\n}\n\nif (!chatId) {\n throw new Error('chatId required');\n}\n\nreturn {\n json: {\n containerId: containerId,\n containerName: containerName,\n lineCount: Math.min(Math.max(parseInt(lineCount), 1), 1000),\n chatId: chatId,\n messageId: messageId,\n responseMode: responseMode,\n correlationId: correlationId\n }\n};"
},
"id": "dac65a64-173a-4536-b3c5-0699704380fa",
"name": "Parse Input",
@@ -134,7 +134,7 @@
},
{
"parameters": {
"jsCode": "// Format logs output for Telegram\nconst rawOutput = $input.item.json.stdout || '';\nconst data = $('Build Logs Command').item.json;\n\n// HTML escape function\nfunction escapeHtml(text) {\n return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n}\n\n// Handle empty logs\nif (!rawOutput || rawOutput.trim() === '') {\n return {\n json: {\n success: true,\n message: `No logs available for <b>${data.containerName}</b>`,\n containerName: data.containerName,\n lineCount: 0\n }\n };\n}\n\n// Parse Docker multiplexed stream format\n// Each frame: 8-byte header (byte 0 = stream type, bytes 4-7 = size) + data\nconst logLines = [];\nlet pos = 0;\nwhile (pos < rawOutput.length) {\n const streamType = rawOutput.charCodeAt(pos);\n // Check for valid Docker stream header (0=stdin, 1=stdout, 2=stderr)\n if (streamType <= 2 && pos + 8 <= rawOutput.length) {\n // Parse frame size from bytes 4-7 (big-endian)\n const size = (rawOutput.charCodeAt(pos + 4) << 24) |\n (rawOutput.charCodeAt(pos + 5) << 16) |\n (rawOutput.charCodeAt(pos + 6) << 8) |\n rawOutput.charCodeAt(pos + 7);\n // Extract frame data\n const frameData = rawOutput.substring(pos + 8, pos + 8 + size);\n // Split frame data by newlines and add each line\n frameData.split('\\n').forEach(line => {\n const trimmed = line.replace(/\\r/g, '').trim();\n if (trimmed) logLines.push(trimmed);\n });\n pos += 8 + size;\n } else {\n // Fallback: treat rest as plain text\n rawOutput.substring(pos).split('\\n').forEach(line => {\n const trimmed = line.replace(/\\r/g, '').trim();\n if (trimmed) logLines.push(trimmed);\n });\n break;\n }\n}\n\nconst lines = logLines.join('\\n');\n\n// Truncate for Telegram (4096 char limit, leave room for header)\nconst maxLen = 3800;\nconst truncated = lines.length > maxLen\n ? lines.substring(0, maxLen) + '\\n... (truncated)'\n : lines;\n\n// Escape HTML entities\nconst escaped = escapeHtml(truncated);\n\nconst lineCount = logLines.length;\nconst header = `Logs for <b>${data.containerName}</b> (last ${lineCount} lines):\\n\\n`;\nconst formatted = header + '<pre>' + escaped + '</pre>';\n\nreturn {\n json: {\n success: true,\n message: formatted,\n containerName: data.containerName,\n lineCount: lineCount\n }\n};"
"jsCode": "// Format logs output for Telegram\nconst rawOutput = $input.item.json.stdout || '';\nconst data = $('Build Logs Command').item.json;\nconst correlationId = data.correlationId || '';\n\n// HTML escape function\nfunction escapeHtml(text) {\n return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n}\n\n// Handle empty logs\nif (!rawOutput || rawOutput.trim() === '') {\n return {\n json: {\n success: true,\n message: `No logs available for <b>${data.containerName}</b>`,\n containerName: data.containerName,\n lineCount: 0,\n correlationId\n }\n };\n}\n\n// Parse Docker multiplexed stream format\n// Each frame: 8-byte header (byte 0 = stream type, bytes 4-7 = size) + data\nconst logLines = [];\nlet pos = 0;\nwhile (pos < rawOutput.length) {\n const streamType = rawOutput.charCodeAt(pos);\n // Check for valid Docker stream header (0=stdin, 1=stdout, 2=stderr)\n if (streamType <= 2 && pos + 8 <= rawOutput.length) {\n // Parse frame size from bytes 4-7 (big-endian)\n const size = (rawOutput.charCodeAt(pos + 4) << 24) |\n (rawOutput.charCodeAt(pos + 5) << 16) |\n (rawOutput.charCodeAt(pos + 6) << 8) |\n rawOutput.charCodeAt(pos + 7);\n // Extract frame data\n const frameData = rawOutput.substring(pos + 8, pos + 8 + size);\n // Split frame data by newlines and add each line\n frameData.split('\\n').forEach(line => {\n const trimmed = line.replace(/\\r/g, '').trim();\n if (trimmed) logLines.push(trimmed);\n });\n pos += 8 + size;\n } else {\n // Fallback: treat rest as plain text\n rawOutput.substring(pos).split('\\n').forEach(line => {\n const trimmed = line.replace(/\\r/g, '').trim();\n if (trimmed) logLines.push(trimmed);\n });\n break;\n }\n}\n\nconst lines = logLines.join('\\n');\n\n// Truncate for Telegram (4096 char limit, leave room for header)\nconst maxLen = 3800;\nconst truncated = lines.length > maxLen\n ? lines.substring(0, maxLen) + '\\n... (truncated)'\n : lines;\n\n// Escape HTML entities\nconst escaped = escapeHtml(truncated);\n\nconst lineCount = logLines.length;\nconst header = `Logs for <b>${data.containerName}</b> (last ${lineCount} lines):\\n\\n`;\nconst formatted = header + '<pre>' + escaped + '</pre>';\n\nreturn {\n json: {\n success: true,\n message: formatted,\n containerName: data.containerName,\n lineCount: lineCount\n }\n};"
},
"id": "e423b026-e619-4e3a-abd9-563e935cd74d",
"name": "Format Logs",