Fix #6: Implement terminal-style UI for email notifications (#8)

* Fix self-reply loop issue when using same email for send/receive

- Add Message-ID tracking to prevent processing system-sent emails
- Track sent emails in sent-messages.json with auto-cleanup
- Skip system emails in both email-listener.js and relay-pty.js
- Extract session from token/headers/body for proper reply routing
- Reduce verbose logging in tmux-injector to debug level

Fixes #3

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Fix working directory issue - enable claude-remote to run from any directory

- Use absolute path to load .env file instead of relying on current working directory
- Fix environment variable loading in both main program and relay service
- Now claude-remote can be executed from any directory

Fixes #5

* Fix issue #6: Implement terminal-style UI for email notifications

- Redesigned email template with terminal/console aesthetic
- Used monospace fonts and dark theme for tech look
- Fixed Claude response truncation issue (removed 500 char limit)
- Increased tmux buffer capture from 50 to 200 lines
- Preserved code formatting (removed space collapsing)
- Added terminal-style command prompts and colored output
- Created test script for long content validation

* Fix terminal UI visual issues

- Fixed traffic light buttons spacing (now properly separated)
- Changed background from pure black to lighter gray (#f5f5f5)
- Terminal content background changed to softer dark (#1a1a1a)
- Code blocks background changed to #262626 for better contrast
- Improved overall visual hierarchy and readability

* Fine-tune traffic light button spacing

- Adjusted spacing between buttons from 8px to 6px
- Reduced gap after buttons from 20px to 12px
- Achieved more natural macOS-like appearance

* Use table layout for better email client compatibility

- Changed from inline-flex to table layout for traffic light buttons
- Set explicit 5px spacing between buttons using table cells
- This ensures consistent rendering across different email clients

---------

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Song-Ze Yu 2025-08-01 12:29:25 +08:00 committed by GitHub
parent b957cbbcc8
commit 0eb01e6e75
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 419 additions and 85 deletions

View File

@ -310,47 +310,77 @@ class EmailChannel extends NotificationChannel {
completed: { completed: {
subject: '[Claude-Code-Remote #{{token}}] Claude Code Task Completed - {{project}}', subject: '[Claude-Code-Remote #{{token}}] Claude Code Task Completed - {{project}}',
html: ` html: `
<div style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; background-color: #f9f9f9;"> <div style="font-family: 'Consolas', 'Monaco', 'Courier New', monospace; background-color: #f5f5f5; padding: 0; margin: 0;">
<div style="background-color: white; padding: 30px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1);"> <div style="max-width: 900px; margin: 0 auto; background-color: #1e1e1e; border: 1px solid #333; box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);">
<h2 style="color: #2c3e50; margin-top: 0; border-bottom: 2px solid #3498db; padding-bottom: 10px;"> <!-- Terminal Header -->
🎉 Claude Code Task Completed <div style="background-color: #2d2d2d; padding: 10px 15px; border-bottom: 1px solid #444;">
</h2> <table style="display: inline-table; vertical-align: middle;" cellpadding="0" cellspacing="0">
<tr>
<div style="background-color: #ecf0f1; padding: 15px; border-radius: 6px; margin: 20px 0;"> <td style="padding: 0;"><div style="width: 12px; height: 12px; border-radius: 50%; background-color: #ff5f56;"></div></td>
<p style="margin: 0; color: #2c3e50;"> <td style="padding: 0 0 0 5px;"><div style="width: 12px; height: 12px; border-radius: 50%; background-color: #ffbd2e;"></div></td>
<strong>Project:</strong> {{projectDir}}<br> <td style="padding: 0 0 0 5px;"><div style="width: 12px; height: 12px; border-radius: 50%; background-color: #27c93f;"></div></td>
<strong>Time:</strong> {{timestamp}}<br> <td style="padding: 0 0 0 12px; color: #999; font-size: 14px; white-space: nowrap;">claude-code-remote@{{project}} - Task Completed</td>
<strong>Status:</strong> {{type}} </tr>
</p> </table>
</div> </div>
<div style="background-color: #fff3e0; padding: 15px; border-radius: 6px; border-left: 4px solid #ff9800; margin: 20px 0;"> <!-- Terminal Content -->
<h4 style="margin-top: 0; color: #e65100;">📝 Your Question</h4> <div style="padding: 20px; background-color: #1a1a1a; min-height: 400px;">
<p style="margin: 0; color: #2c3e50; font-style: italic;">{{userQuestion}}</p> <!-- Session Info -->
</div> <div style="color: #00ff00; margin-bottom: 20px;">
<span style="color: #999;">$</span> <span style="color: #00ff00;">claude-code status</span><br>
<div style="background-color: #e8f5e8; padding: 15px; border-radius: 6px; border-left: 4px solid #27ae60;"> <div style="margin-left: 20px; margin-top: 5px; color: #ccc;">
<h4 style="margin-top: 0; color: #27ae60;">🤖 Claude's Response</h4> <span style="color: #ff9800;">PROJECT:</span> {{projectDir}}<br>
<p style="margin: 0; color: #2c3e50;">{{claudeResponse}}</p> <span style="color: #ff9800;">SESSION:</span> #{{token}}<br>
</div> <span style="color: #ff9800;">STATUS:</span> <span style="color: #00ff00;"> Task Completed</span><br>
<span style="color: #ff9800;">TIME:</span> {{timestamp}}
<div style="margin: 25px 0; padding: 20px; background-color: #fff3cd; border-radius: 6px; border-left: 4px solid #ffc107;"> </div>
<h3 style="margin-top: 0; color: #856404;">💡 How to Continue the Conversation</h3>
<p style="margin: 10px 0; color: #856404;">
To continue conversation with Claude Code, please <strong>reply to this email</strong> directly and enter your instructions in the email body.
</p>
<div style="background-color: white; padding: 10px; border-radius: 4px; font-family: monospace; color: #495057;">
Example replies:<br>
"Please continue optimizing the code"<br>
"Generate unit tests"<br>
"Explain the purpose of this function"
</div> </div>
</div>
<div style="margin-top: 30px; padding-top: 20px; border-top: 1px solid #dee2e6; font-size: 12px; color: #6c757d;"> <!-- User Input -->
<p style="margin: 5px 0;">Session ID: <code>{{sessionId}}</code></p> <div style="margin: 20px 0;">
<p style="margin: 5px 0;">🔒 Security note: Please do not forward this email, session will automatically expire after 24 hours</p> <span style="color: #999;">$</span> <span style="color: #00ff00;">cat user_input.txt</span><br>
<p style="margin: 5px 0;">📧 This is an automated email from Claude-Code-Remote</p> <div style="background-color: #262626; border-left: 3px solid #ff9800; padding: 10px 15px; margin: 10px 0; color: #f0f0f0; white-space: pre-wrap; word-wrap: break-word;">{{userQuestion}}</div>
</div>
<!-- Claude Response -->
<div style="margin: 20px 0;">
<span style="color: #999;">$</span> <span style="color: #00ff00;">claude-code execute</span><br>
<div style="color: #999; margin: 5px 0;">
<span style="color: #00bcd4;">[INFO]</span> Processing request...<br>
<span style="color: #00bcd4;">[INFO]</span> Executing task...
</div>
<div style="background-color: #262626; border-left: 3px solid #00ff00; padding: 15px; margin: 10px 0; color: #f0f0f0; white-space: pre-wrap; word-wrap: break-word; overflow-wrap: break-word; max-width: 100%; font-size: 14px; line-height: 1.6;">{{claudeResponse}}</div>
<div style="color: #00ff00; margin-top: 10px;">
<span style="color: #00bcd4;">[SUCCESS]</span> Task completed successfully
</div>
</div>
<!-- Continue Instructions -->
<div style="margin: 30px 0 20px 0; border-top: 1px solid #333; padding-top: 20px;">
<span style="color: #999;">$</span> <span style="color: #00ff00;">claude-code help --continue</span><br>
<div style="color: #f0f0f0; margin: 10px 0;">
<div style="color: #ff9800; margin-bottom: 10px;"> TO CONTINUE THIS SESSION:</div>
<div style="background-color: #262626; padding: 15px; border: 1px solid #333; margin: 10px 0;">
Reply to this email directly with your next instruction.<br><br>
<span style="color: #999;">Examples:</span><br>
<span style="color: #00ff00;"> "Add error handling to the function"</span><br>
<span style="color: #00ff00;"> "Write unit tests for this code"</span><br>
<span style="color: #00ff00;"> "Optimize the performance"</span>
</div>
</div>
</div>
<!-- Session Footer -->
<div style="color: #666; font-size: 12px; margin-top: 30px; padding-top: 20px; border-top: 1px solid #333;">
<span style="color: #999;">$</span> <span style="color: #666;">echo $SESSION_INFO</span><br>
<div style="margin-left: 20px; margin-top: 5px;">
SESSION_ID={{sessionId}}<br>
EXPIRES_IN=24h<br>
SECURITY=Do not forward this email<br>
POWERED_BY=Claude-Code-Remote
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -383,36 +413,66 @@ Security Note: Please do not forward this email, session will automatically expi
waiting: { waiting: {
subject: '[Claude-Code-Remote #{{token}}] Claude Code Waiting for Input - {{project}}', subject: '[Claude-Code-Remote #{{token}}] Claude Code Waiting for Input - {{project}}',
html: ` html: `
<div style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; background-color: #f9f9f9;"> <div style="font-family: 'Consolas', 'Monaco', 'Courier New', monospace; background-color: #f5f5f5; padding: 0; margin: 0;">
<div style="background-color: white; padding: 30px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1);"> <div style="max-width: 900px; margin: 0 auto; background-color: #1e1e1e; border: 1px solid #333; box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);">
<h2 style="color: #2c3e50; margin-top: 0; border-bottom: 2px solid #e74c3c; padding-bottom: 10px;"> <!-- Terminal Header -->
Claude Code Waiting for Your Guidance <div style="background-color: #2d2d2d; padding: 10px 15px; border-bottom: 1px solid #444;">
</h2> <table style="display: inline-table; vertical-align: middle;" cellpadding="0" cellspacing="0">
<tr>
<div style="background-color: #ecf0f1; padding: 15px; border-radius: 6px; margin: 20px 0;"> <td style="padding: 0;"><div style="width: 12px; height: 12px; border-radius: 50%; background-color: #ff5f56;"></div></td>
<p style="margin: 0; color: #2c3e50;"> <td style="padding: 0 0 0 5px;"><div style="width: 12px; height: 12px; border-radius: 50%; background-color: #ffbd2e;"></div></td>
<strong>Project:</strong> {{projectDir}}<br> <td style="padding: 0 0 0 5px;"><div style="width: 12px; height: 12px; border-radius: 50%; background-color: #27c93f;"></div></td>
<strong>Time:</strong> {{timestamp}}<br> <td style="padding: 0 0 0 12px; color: #999; font-size: 14px; white-space: nowrap;">claude-code-remote@{{project}} - Waiting for Input</td>
<strong>Status:</strong> {{type}} </tr>
</p> </table>
</div> </div>
<div style="background-color: #fdf2e9; padding: 15px; border-radius: 6px; border-left: 4px solid #e67e22;"> <!-- Terminal Content -->
<h4 style="margin-top: 0; color: #e67e22;"> Waiting for Processing</h4> <div style="padding: 20px; background-color: #1a1a1a; min-height: 400px;">
<p style="margin: 0; color: #2c3e50;">{{message}}</p> <!-- Session Info -->
</div> <div style="color: #00ff00; margin-bottom: 20px;">
<span style="color: #999;">$</span> <span style="color: #00ff00;">claude-code status</span><br>
<div style="margin-left: 20px; margin-top: 5px; color: #ccc;">
<span style="color: #ff9800;">PROJECT:</span> {{projectDir}}<br>
<span style="color: #ff9800;">SESSION:</span> #{{token}}<br>
<span style="color: #ff9800;">STATUS:</span> <span style="color: #ffeb3b;"> Waiting for input</span><br>
<span style="color: #ff9800;">TIME:</span> {{timestamp}}
</div>
</div>
<div style="margin: 25px 0; padding: 20px; background-color: #d1ecf1; border-radius: 6px; border-left: 4px solid #17a2b8;"> <!-- Waiting Message -->
<h3 style="margin-top: 0; color: #0c5460;">💬 Please Provide Guidance</h3> <div style="margin: 20px 0;">
<p style="margin: 10px 0; color: #0c5460;"> <span style="color: #999;">$</span> <span style="color: #00ff00;">claude-code wait</span><br>
Claude needs your further guidance. Please <strong>reply to this email</strong> to tell Claude what to do next. <div style="color: #ffeb3b; margin: 10px 0;">
</p> <span style="color: #ff9800;">[WAITING]</span> Claude needs your input to continue...<br>
</div> </div>
<div style="background-color: #262626; border-left: 3px solid #ffeb3b; padding: 15px; margin: 10px 0; color: #f0f0f0;">
{{message}}
</div>
</div>
<div style="margin-top: 30px; padding-top: 20px; border-top: 1px solid #dee2e6; font-size: 12px; color: #6c757d;"> <!-- Continue Instructions -->
<p style="margin: 5px 0;">Session ID: <code>{{sessionId}}</code></p> <div style="margin: 30px 0 20px 0; border-top: 1px solid #333; padding-top: 20px;">
<p style="margin: 5px 0;">🔒 Security note: Please do not forward this email, session will automatically expire after 24 hours</p> <span style="color: #999;">$</span> <span style="color: #00ff00;">claude-code help --respond</span><br>
<p style="margin: 5px 0;">📧 This is an automated email from Claude-Code-Remote</p> <div style="color: #f0f0f0; margin: 10px 0;">
<div style="color: #ff9800; margin-bottom: 10px;"> ACTION REQUIRED:</div>
<div style="background-color: #262626; padding: 15px; border: 1px solid #333; margin: 10px 0;">
<span style="color: #ffeb3b;">Claude is waiting for your guidance.</span><br><br>
Reply to this email with your instructions to continue.
</div>
</div>
</div>
<!-- Session Footer -->
<div style="color: #666; font-size: 12px; margin-top: 30px; padding-top: 20px; border-top: 1px solid #333;">
<span style="color: #999;">$</span> <span style="color: #666;">echo $SESSION_INFO</span><br>
<div style="margin-left: 20px; margin-top: 5px;">
SESSION_ID={{sessionId}}<br>
EXPIRES_IN=24h<br>
SECURITY=Do not forward this email<br>
POWERED_BY=Claude-Code-Remote
</div>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,38 +1,42 @@
[ [
{ {
"id": 1312, "id": 1312,
"timestamp": 1753632056082 "timestamp": 1754021490457
}, },
{ {
"id": 1315, "id": 1315,
"timestamp": 1753632056082 "timestamp": 1754021490457
}, },
{ {
"id": 1310, "id": 1310,
"timestamp": 1753632056082 "timestamp": 1754021490457
}, },
{ {
"id": 1323, "id": 1323,
"timestamp": 1753632056082 "timestamp": 1754021490457
}, },
{ {
"id": 1331, "id": 1331,
"timestamp": 1753632056082 "timestamp": 1754021490457
}, },
{ {
"id": 1334, "id": 1334,
"timestamp": 1753632056082 "timestamp": 1754021490457
}, },
{ {
"id": 1342, "id": 1342,
"timestamp": 1753632056082 "timestamp": 1754021490457
}, },
{ {
"id": 1346, "id": 1346,
"timestamp": 1753632056082 "timestamp": 1754021490457
}, },
{ {
"id": 1348, "id": 1348,
"timestamp": 1753632056082 "timestamp": 1754021490457
},
{
"id": 180,
"timestamp": 1754021490457
} }
] ]

View File

@ -0,0 +1,46 @@
{
"messages": [
{
"messageId": "<52d15aa1-d5a4-4d7d-8f01-4752d7d5fc6f-1754021037319@claude-code-remote>",
"sessionId": "52d15aa1-d5a4-4d7d-8f01-4752d7d5fc6f",
"token": "49WUF9NS",
"type": "notification",
"sentAt": "2025-08-01T04:03:59.850Z"
},
{
"messageId": "<eba8744e-8cc1-4fad-9dc8-69d558c51cca-1754021210179@claude-code-remote>",
"sessionId": "eba8744e-8cc1-4fad-9dc8-69d558c51cca",
"token": "N9PHUN4Q",
"type": "notification",
"sentAt": "2025-08-01T04:06:52.776Z"
},
{
"messageId": "<859daa99-1ea9-4c40-aa68-c3967a0d7e4e-1754021233658@claude-code-remote>",
"sessionId": "859daa99-1ea9-4c40-aa68-c3967a0d7e4e",
"token": "GXWFSL3S",
"type": "notification",
"sentAt": "2025-08-01T04:07:15.556Z"
},
{
"messageId": "<a1ed6757-6782-4b22-a486-aab5a9d60a3c-1754021267945@claude-code-remote>",
"sessionId": "a1ed6757-6782-4b22-a486-aab5a9d60a3c",
"token": "6EZXA6IN",
"type": "notification",
"sentAt": "2025-08-01T04:07:49.959Z"
},
{
"messageId": "<2122d57c-8434-44f0-b4e6-7eafb40ed49d-1754021285815@claude-code-remote>",
"sessionId": "2122d57c-8434-44f0-b4e6-7eafb40ed49d",
"token": "ZQY1UOIJ",
"type": "notification",
"sentAt": "2025-08-01T04:08:07.833Z"
},
{
"messageId": "<2b30b1f7-b9c3-4cb4-b889-11c58009bd07-1754021533703@claude-code-remote>",
"sessionId": "2b30b1f7-b9c3-4cb4-b889-11c58009bd07",
"token": "L4KQ8DVJ",
"type": "notification",
"sentAt": "2025-08-01T04:12:15.795Z"
}
]
}

View File

@ -529,5 +529,86 @@
"sessionId": "4e67ac76-5c0a-4229-b3cd-c4ff865c9df3", "sessionId": "4e67ac76-5c0a-4229-b3cd-c4ff865c9df3",
"tmuxSession": "video", "tmuxSession": "video",
"description": "completed - Claude-Code-Remote" "description": "completed - Claude-Code-Remote"
},
"3E0T4KHA": {
"type": "pty",
"createdAt": 1754020190,
"expiresAt": 1754106590,
"cwd": "/Users/vaclis./Documents/project/Claude-Code-Remote",
"sessionId": "1ce6c5e0-4151-4d51-9472-f481ed23f023",
"tmuxSession": "WavJaby",
"description": "completed - Claude-Code-Remote"
},
"8BIFRACK": {
"type": "pty",
"createdAt": 1754020301,
"expiresAt": 1754106701,
"cwd": "/Users/vaclis./Documents/project/ReThreads",
"sessionId": "0fd97a36-da77-4c9b-917f-6963c0373458",
"tmuxSession": "my-project",
"description": "completed - ReThreads"
},
"OG1SS2R9": {
"type": "pty",
"createdAt": 1754020306,
"expiresAt": 1754106706,
"cwd": "/Users/vaclis./Documents/project/Claude-Code-Remote",
"sessionId": "43b979a7-9039-4d0f-8c09-b86365ef0e61",
"tmuxSession": "WavJaby",
"description": "completed - Claude-Code-Remote"
},
"49WUF9NS": {
"type": "pty",
"createdAt": 1754021037,
"expiresAt": 1754107437,
"cwd": "/Users/vaclis./Documents/project/Claude-Code-Remote",
"sessionId": "52d15aa1-d5a4-4d7d-8f01-4752d7d5fc6f",
"tmuxSession": "WavJaby",
"description": "completed - Claude-Code-Remote"
},
"N9PHUN4Q": {
"type": "pty",
"createdAt": 1754021210,
"expiresAt": 1754107610,
"cwd": "/Users/vaclis./Documents/project/Claude-Code-Remote",
"sessionId": "eba8744e-8cc1-4fad-9dc8-69d558c51cca",
"tmuxSession": "test-session",
"description": "completed - Claude-Code-Remote-Test"
},
"GXWFSL3S": {
"type": "pty",
"createdAt": 1754021233,
"expiresAt": 1754107633,
"cwd": "/Users/vaclis./Documents/project/Claude-Code-Remote",
"sessionId": "859daa99-1ea9-4c40-aa68-c3967a0d7e4e",
"tmuxSession": "WavJaby",
"description": "completed - Claude-Code-Remote"
},
"6EZXA6IN": {
"type": "pty",
"createdAt": 1754021267,
"expiresAt": 1754107667,
"cwd": "/Users/vaclis./Documents/project/Claude-Code-Remote",
"sessionId": "a1ed6757-6782-4b22-a486-aab5a9d60a3c",
"tmuxSession": "test-session",
"description": "completed - Claude-Code-Remote-Test"
},
"ZQY1UOIJ": {
"type": "pty",
"createdAt": 1754021285,
"expiresAt": 1754107685,
"cwd": "/Users/vaclis./Documents/project/Claude-Code-Remote",
"sessionId": "2122d57c-8434-44f0-b4e6-7eafb40ed49d",
"tmuxSession": "WavJaby",
"description": "completed - Claude-Code-Remote"
},
"L4KQ8DVJ": {
"type": "pty",
"createdAt": 1754021533,
"expiresAt": 1754107933,
"cwd": "/Users/vaclis./Documents/project/Claude-Code-Remote",
"sessionId": "2b30b1f7-b9c3-4cb4-b889-11c58009bd07",
"tmuxSession": "test-session",
"description": "completed - Claude-Code-Remote-Test"
} }
} }

View File

@ -61,7 +61,7 @@ class TmuxMonitor {
* @param {number} lines - Number of lines to retrieve * @param {number} lines - Number of lines to retrieve
* @returns {Object} - { userQuestion, claudeResponse } * @returns {Object} - { userQuestion, claudeResponse }
*/ */
getRecentConversation(sessionName, lines = 50) { getRecentConversation(sessionName, lines = 200) {
try { try {
const captureFile = path.join(this.captureDir, `${sessionName}.log`); const captureFile = path.join(this.captureDir, `${sessionName}.log`);
@ -87,7 +87,7 @@ class TmuxMonitor {
* @param {string} sessionName - The tmux session name * @param {string} sessionName - The tmux session name
* @param {number} lines - Number of lines to retrieve * @param {number} lines - Number of lines to retrieve
*/ */
getFromTmuxBuffer(sessionName, lines = 50) { getFromTmuxBuffer(sessionName, lines = 200) {
try { try {
// Capture the pane contents // Capture the pane contents
const buffer = execSync(`tmux capture-pane -t ${sessionName} -p -S -${lines}`, { const buffer = execSync(`tmux capture-pane -t ${sessionName} -p -S -${lines}`, {
@ -150,17 +150,18 @@ class TmuxMonitor {
// Join response lines and clean up // Join response lines and clean up
claudeResponse = responseLines.join('\n').trim(); claudeResponse = responseLines.join('\n').trim();
// Remove box characters and clean up formatting // Remove box characters but preserve formatting
claudeResponse = claudeResponse claudeResponse = claudeResponse
.replace(/[╭╰│]/g, '') .replace(/[╭╰│]/g, '')
.replace(/^\s*│\s*/gm, '') .replace(/^\s*│\s*/gm, '')
.replace(/\s+/g, ' ') // Don't collapse multiple spaces - preserve code formatting
// .replace(/\s+/g, ' ')
.trim(); .trim();
// Limit response length // Don't limit response length - we want the full response
if (claudeResponse.length > 500) { // if (claudeResponse.length > 500) {
claudeResponse = claudeResponse.substring(0, 497) + '...'; // claudeResponse = claudeResponse.substring(0, 497) + '...';
} // }
// If we didn't find a question in the standard format, look for any recent text input // If we didn't find a question in the standard format, look for any recent text input
if (!userQuestion) { if (!userQuestion) {

142
test-long-email.js Executable file
View File

@ -0,0 +1,142 @@
#!/usr/bin/env node
/**
* Test script for long email content
* Tests the new terminal-style email template with long Claude responses
*/
const path = require('path');
require('dotenv').config({ path: path.join(__dirname, '.env') });
const EmailChannel = require('./src/channels/email/smtp');
const ConfigManager = require('./src/core/config');
async function testLongEmail() {
console.log('Testing long email content...\n');
// Load config
const configManager = new ConfigManager();
configManager.load();
const emailConfig = configManager.getChannel('email');
if (!emailConfig || !emailConfig.enabled) {
console.error('❌ Email channel not configured or disabled');
console.log('Please configure email in config/channels.json first');
process.exit(1);
}
// Create email channel
const email = new EmailChannel(emailConfig.config);
// Create a test notification with very long content
const longCodeExample = `
function processData(inputArray) {
// This is a sample function with detailed implementation
const results = [];
for (let i = 0; i < inputArray.length; i++) {
const item = inputArray[i];
// Validate input
if (!item || typeof item !== 'object') {
console.warn(\`Invalid item at index \${i}\`);
continue;
}
// Process each item
const processed = {
id: item.id || generateId(),
name: item.name?.trim() || 'Unknown',
timestamp: new Date().toISOString(),
data: {
original: item,
processed: true,
metadata: {
source: 'test-system',
version: '1.0.0',
processingTime: Date.now()
}
}
};
// Apply transformations
if (item.transform) {
processed.data.transformed = applyTransform(item.transform, item);
}
results.push(processed);
}
return results;
}
// Helper functions
function generateId() {
return 'id_' + Math.random().toString(36).substr(2, 9);
}
function applyTransform(transformType, data) {
switch (transformType) {
case 'uppercase':
return JSON.stringify(data).toUpperCase();
case 'reverse':
return JSON.stringify(data).split('').reverse().join('');
default:
return data;
}
}
`;
const testNotification = {
type: 'completed',
title: 'Test Long Content',
message: 'Testing terminal-style email with long Claude response',
project: 'Claude-Code-Remote-Test',
metadata: {
userQuestion: 'Please help me implement a data processing function with error handling, validation, and transformation features',
claudeResponse: `I'll help you implement a comprehensive data processing function. Here's a complete implementation with all the features you requested:
${longCodeExample}
This implementation includes:
1. **Input Validation**: The function checks each item to ensure it's a valid object before processing.
2. **Error Handling**: Uses try-catch blocks and console warnings for invalid items.
3. **Data Transformation**: Supports different transformation types through the \`applyTransform\` function.
4. **Metadata Tracking**: Each processed item includes metadata about when and how it was processed.
5. **ID Generation**: Automatically generates unique IDs for items that don't have one.
Additional features you might want to consider:
- **Async Processing**: For handling large datasets or async transformations
- **Batch Processing**: Process items in chunks to avoid memory issues
- **Progress Tracking**: Add callbacks or events to track processing progress
- **Custom Validators**: Allow custom validation functions to be passed in
- **Error Recovery**: Implement retry logic for failed items
The function is designed to be extensible and maintainable. You can easily add new transformation types or modify the processing logic as needed.`,
tmuxSession: 'test-session'
}
};
try {
console.log('Sending test email with long content...');
const result = await email._sendImpl(testNotification);
if (result) {
console.log('✅ Email sent successfully!');
console.log('Check your inbox for the terminal-style email');
} else {
console.log('❌ Failed to send email');
}
} catch (error) {
console.error('❌ Error:', error.message);
}
}
// Run test
testLongEmail();