const { execSync } = require("child_process"); const path = require("path"); // Configuration const LIMIT = 50; // Process max 50 emails per account per run const OUTLOOK_SCRIPTS_DIR = "/home/alex/clawd/skills/outlook/scripts"; const OUTLOOK_PROFILES = [ { name: "Personal", dir: process.env.HOME + "/.outlook-mcp-personal" }, { name: "Work", dir: process.env.HOME + "/.outlook-mcp-o365" }, ]; function log(msg) { console.log(`[${new Date().toISOString()}] ${msg}`); } function execCmd(cmd, env = {}) { try { return execSync(cmd, { encoding: "utf8", env: { ...process.env, ...env }, stdio: "pipe" }); } catch (e) { log(`Error executing command: ${cmd}`); log(e.message); return null; } } // --- Gmail --- function processGmail() { log("Checking Gmail..."); try { const res = execCmd( `npx -y mcporter call --server google-workspace --tool "gmail.search" --args '{"query":"is:unread", "maxResults":${LIMIT}}' --output json`, ); if (!res) { return; } const data = JSON.parse(res); if (!data.messages || data.messages.length === 0) { log("Gmail: No unread messages."); return; } log(`Gmail: Found ${data.messages.length} unread messages.`); for (const msgStub of data.messages) { // Get details for summary const detailsJson = execCmd( `npx -y mcporter call --server google-workspace --tool "gmail.get" --args '{"messageId":"${msgStub.id}"}' --output json`, ); if (detailsJson) { let details; try { details = JSON.parse(detailsJson); } catch (err) { log(`Gmail: Failed to parse details JSON for ${msgStub.id}: ${err.message}`); continue; } let subject = "(No Subject)"; let from = "(Unknown)"; // Handle mcporter simplified object if (details.subject) { subject = details.subject; } if (details.from) { from = details.from; } // Handle raw Gmail API object if (details.payload && details.payload.headers) { subject = details.payload.headers.find((h) => h.name === "Subject")?.value || subject; from = details.payload.headers.find((h) => h.name === "From")?.value || from; } else if (!details.subject && !details.from) { log( `Gmail: Unexpected structure for ${msgStub.id}. Keys: ${Object.keys(details).join(", ")}`, ); } log(`[Gmail] ${from}: ${subject}`); } // Mark as read execCmd( `npx -y mcporter call --server google-workspace --tool "gmail.modify" --args '{"messageId":"${msgStub.id}","removeLabelIds":["UNREAD"]}' --output json`, ); } } catch (e) { log(`Gmail Error: ${e.message}`); } } // --- Outlook --- function processOutlook(profile) { log(`Checking Outlook (${profile.name})...`); // Get token const tokenScript = path.join(OUTLOOK_SCRIPTS_DIR, "outlook-token.sh"); // Ensure token is fresh execCmd(`${tokenScript} refresh`, { OUTLOOK_CONFIG_DIR: profile.dir }); const token = execCmd(`${tokenScript} get`, { OUTLOOK_CONFIG_DIR: profile.dir })?.trim(); if (!token) { log(`Outlook (${profile.name}): Could not get token.`); return; } // Fetch unread const url = `https://graph.microsoft.com/v1.0/me/messages?$filter=isRead%20eq%20false&$top=${LIMIT}&$select=id,subject,from,receivedDateTime`; const curlCmd = `curl -s "${url}" -H "Authorization: Bearer ${token}"`; const res = execCmd(curlCmd); if (!res) { return; } try { const data = JSON.parse(res); if (!data.value || data.value.length === 0) { log(`Outlook (${profile.name}): No unread messages.`); return; } log(`Outlook (${profile.name}): Found ${data.value.length} unread messages.`); for (const msg of data.value) { const subject = msg.subject || "(No Subject)"; const from = msg.from?.emailAddress?.address || "(Unknown)"; log(`[Outlook-${profile.name}] ${from}: ${subject}`); // Mark as read const patchCmd = `curl -s -X PATCH "https://graph.microsoft.com/v1.0/me/messages/${msg.id}" -H "Authorization: Bearer ${token}" -H "Content-Type: application/json" -d '{"isRead":true}'`; execCmd(patchCmd); } } catch (e) { log(`Outlook (${profile.name}) Error: ${e.message}`); } } // --- Main --- function main() { log("Starting Email Sweep..."); processGmail(); for (const profile of OUTLOOK_PROFILES) { processOutlook(profile); } log("Email Sweep Completed."); } main();