Files
hermes-mcp/scripts/patch-imapflow.cjs
Garfield c7eae2c735 fix(imap): robust error logging + parser-instance.js null guards
- Move client.connect() inside try/catch in withClient
- Add logImapError() writing full stack to /vaults/imap-errors.log for diagnosis
- Extend patch-imapflow.cjs to guard this.remainder.trim() in parser-instance.js
- Root cause of reported crash was undefined args.q (callers passing 'query' not 'q')

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 18:13:26 -04:00

87 lines
3.3 KiB
JavaScript

#!/usr/bin/env node
// Patches imapflow to handle Poste.io servers returning IMAP NO/BAD/BYE
// responses where some TEXT attributes have undefined .value, causing
// `val.value.trim()` to throw "Cannot read properties of undefined".
const fs = require('fs');
const path = require('path');
const file = path.join(__dirname, '../node_modules/imapflow/lib/imap-flow.js');
let src = fs.readFileSync(file, 'utf8');
const patches = [
{
bad: `.map(val => val.value.trim())`,
fixed: `.map(val => (val.value != null ? val.value.trim() : ''))`,
},
{
bad: `section[0].value.toUpperCase().trim()`,
fixed: `(section[0].value || '').toUpperCase().trim()`,
},
{
bad: `attr.value.toUpperCase().trim()`,
fixed: `(attr.value || '').toUpperCase().trim()`,
},
{
bad: `val.value.toUpperCase().trim()`,
fixed: `(val.value || '').toUpperCase().trim()`,
},
{
bad: `contentType.value.toLowerCase().trim()`,
fixed: `(contentType.value || '').toLowerCase().trim()`,
},
{
bad: `disposition.value.toLowerCase().trim()`,
fixed: `(disposition.value || '').toLowerCase().trim()`,
},
];
let total = 0;
for (const p of patches) {
const re = new RegExp(p.bad.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g');
const count = (src.match(re) || []).length;
if (count > 0) {
src = src.replaceAll(p.bad, p.fixed);
total += count;
console.log(`[patch-imapflow] patched ${count} occurrence(s) of: ${p.bad}`);
}
}
if (total > 0) {
fs.writeFileSync(file, src, 'utf8');
console.log(`[patch-imapflow] total patched ${total} occurrence(s) in imap-flow.js`);
} else {
console.log('[patch-imapflow] already patched or patterns not found — no changes made');
}
// Also patch parser-instance.js (this.remainder can be undefined on malformed Poste.io responses)
const parserFile = path.join(__dirname, '../node_modules/imapflow/lib/handler/parser-instance.js');
if (fs.existsSync(parserFile)) {
let parserSrc = fs.readFileSync(parserFile, 'utf8');
let parserPatched = 0;
const parserPatches = [
{ bad: `this.humanReadable = this.remainder.trim();`, fixed: `this.humanReadable = (this.remainder || '').trim();` },
{ bad: `this.humanReadable = this.remainder.substring(i + 1).trim();`, fixed: `this.humanReadable = (this.remainder || '').substring(i + 1).trim();` },
];
for (const p of parserPatches) {
if (parserSrc.includes(p.bad)) {
parserSrc = parserSrc.replaceAll(p.bad, p.fixed);
parserPatched++;
console.log(`[patch-imapflow] patched parser-instance.js: ${p.bad}`);
}
}
if (parserPatched > 0) fs.writeFileSync(parserFile, parserSrc, 'utf8');
}
// Also patch nodemailer inside imapflow (malformed Content-Type headers)
const nodemailerFile = path.join(__dirname, '../node_modules/imapflow/node_modules/nodemailer/lib/mime-node/index.js');
if (fs.existsSync(nodemailerFile)) {
let nodemailerSrc = fs.readFileSync(nodemailerFile, 'utf8');
const nodemailerBad = `this.contentType = structured.value.trim().toLowerCase();`;
const nodemailerFixed = `this.contentType = ((structured && structured.value) || '').trim().toLowerCase();`;
if (nodemailerSrc.includes(nodemailerBad)) {
nodemailerSrc = nodemailerSrc.replaceAll(nodemailerBad, nodemailerFixed);
fs.writeFileSync(nodemailerFile, nodemailerSrc, 'utf8');
console.log('[patch-imapflow] patched nodemailer mime-node/index.js');
}
}