deepseek-coder-agent-cli 1.0.13 ā 1.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -594
- package/dist/bin/deepseek.js +53 -11
- package/dist/bin/deepseek.js.map +1 -1
- package/dist/capabilities/index.d.ts +1 -15
- package/dist/capabilities/index.d.ts.map +1 -1
- package/dist/capabilities/index.js +4 -17
- package/dist/capabilities/index.js.map +1 -1
- package/dist/plugins/index.d.ts +1 -48
- package/dist/plugins/index.d.ts.map +1 -1
- package/dist/plugins/index.js +2 -104
- package/dist/plugins/index.js.map +1 -1
- package/dist/plugins/tools/index.d.ts +4 -2
- package/dist/plugins/tools/index.d.ts.map +1 -1
- package/dist/plugins/tools/index.js +5 -2
- package/dist/plugins/tools/index.js.map +1 -1
- package/package.json +9 -16
- package/dist/capabilities/appleSecurityCapability.d.ts +0 -57
- package/dist/capabilities/appleSecurityCapability.d.ts.map +0 -1
- package/dist/capabilities/appleSecurityCapability.js +0 -197
- package/dist/capabilities/appleSecurityCapability.js.map +0 -1
- package/dist/capabilities/authorizedSecurityCapability.d.ts +0 -17
- package/dist/capabilities/authorizedSecurityCapability.d.ts.map +0 -1
- package/dist/capabilities/authorizedSecurityCapability.js +0 -333
- package/dist/capabilities/authorizedSecurityCapability.js.map +0 -1
- package/dist/capabilities/biocognitiveWarfare.d.ts +0 -136
- package/dist/capabilities/biocognitiveWarfare.d.ts.map +0 -1
- package/dist/capabilities/biocognitiveWarfare.js +0 -603
- package/dist/capabilities/biocognitiveWarfare.js.map +0 -1
- package/dist/capabilities/chineseCnoIntegration.d.ts +0 -60
- package/dist/capabilities/chineseCnoIntegration.d.ts.map +0 -1
- package/dist/capabilities/chineseCnoIntegration.js +0 -253
- package/dist/capabilities/chineseCnoIntegration.js.map +0 -1
- package/dist/capabilities/cnoCapability.d.ts +0 -110
- package/dist/capabilities/cnoCapability.d.ts.map +0 -1
- package/dist/capabilities/cnoCapability.js +0 -785
- package/dist/capabilities/cnoCapability.js.map +0 -1
- package/dist/capabilities/eliteCryptoMilitaryCapability.d.ts +0 -99
- package/dist/capabilities/eliteCryptoMilitaryCapability.d.ts.map +0 -1
- package/dist/capabilities/eliteCryptoMilitaryCapability.js +0 -618
- package/dist/capabilities/eliteCryptoMilitaryCapability.js.map +0 -1
- package/dist/capabilities/integratedUnifiedCapability.d.ts +0 -105
- package/dist/capabilities/integratedUnifiedCapability.d.ts.map +0 -1
- package/dist/capabilities/integratedUnifiedCapability.js +0 -422
- package/dist/capabilities/integratedUnifiedCapability.js.map +0 -1
- package/dist/capabilities/maxOffensiveUkraineCapability.d.ts +0 -46
- package/dist/capabilities/maxOffensiveUkraineCapability.d.ts.map +0 -1
- package/dist/capabilities/maxOffensiveUkraineCapability.js +0 -725
- package/dist/capabilities/maxOffensiveUkraineCapability.js.map +0 -1
- package/dist/capabilities/offensiveDestructionCapability.d.ts +0 -98
- package/dist/capabilities/offensiveDestructionCapability.d.ts.map +0 -1
- package/dist/capabilities/offensiveDestructionCapability.js +0 -848
- package/dist/capabilities/offensiveDestructionCapability.js.map +0 -1
- package/dist/capabilities/quantumSpaceWarfare.d.ts +0 -108
- package/dist/capabilities/quantumSpaceWarfare.d.ts.map +0 -1
- package/dist/capabilities/quantumSpaceWarfare.js +0 -342
- package/dist/capabilities/quantumSpaceWarfare.js.map +0 -1
- package/dist/capabilities/readmeIntegration.d.ts +0 -161
- package/dist/capabilities/readmeIntegration.d.ts.map +0 -1
- package/dist/capabilities/readmeIntegration.js +0 -1034
- package/dist/capabilities/readmeIntegration.js.map +0 -1
- package/dist/capabilities/sharedMilitaryInfrastructure.d.ts +0 -89
- package/dist/capabilities/sharedMilitaryInfrastructure.d.ts.map +0 -1
- package/dist/capabilities/sharedMilitaryInfrastructure.js +0 -233
- package/dist/capabilities/sharedMilitaryInfrastructure.js.map +0 -1
- package/dist/capabilities/simpleSecurityCapability.d.ts +0 -36
- package/dist/capabilities/simpleSecurityCapability.d.ts.map +0 -1
- package/dist/capabilities/simpleSecurityCapability.js +0 -271
- package/dist/capabilities/simpleSecurityCapability.js.map +0 -1
- package/dist/capabilities/ultimateChineseCno.d.ts +0 -115
- package/dist/capabilities/ultimateChineseCno.d.ts.map +0 -1
- package/dist/capabilities/ultimateChineseCno.js +0 -516
- package/dist/capabilities/ultimateChineseCno.js.map +0 -1
- package/dist/capabilities/ultimateIntegrationDemo.d.ts +0 -54
- package/dist/capabilities/ultimateIntegrationDemo.d.ts.map +0 -1
- package/dist/capabilities/ultimateIntegrationDemo.js +0 -423
- package/dist/capabilities/ultimateIntegrationDemo.js.map +0 -1
- package/dist/capabilities/unifiedMilitaryCapability.d.ts +0 -63
- package/dist/capabilities/unifiedMilitaryCapability.d.ts.map +0 -1
- package/dist/capabilities/unifiedMilitaryCapability.js +0 -384
- package/dist/capabilities/unifiedMilitaryCapability.js.map +0 -1
- package/dist/capabilities/universalSecurityCapability.d.ts +0 -46
- package/dist/capabilities/universalSecurityCapability.d.ts.map +0 -1
- package/dist/capabilities/universalSecurityCapability.js +0 -580
- package/dist/capabilities/universalSecurityCapability.js.map +0 -1
- package/dist/capabilities/zeroDayDiscoveryCapability.d.ts +0 -31
- package/dist/capabilities/zeroDayDiscoveryCapability.d.ts.map +0 -1
- package/dist/capabilities/zeroDayDiscoveryCapability.js +0 -183
- package/dist/capabilities/zeroDayDiscoveryCapability.js.map +0 -1
- package/dist/core/antiTermination.d.ts +0 -226
- package/dist/core/antiTermination.d.ts.map +0 -1
- package/dist/core/antiTermination.js +0 -713
- package/dist/core/antiTermination.js.map +0 -1
- package/dist/core/index.d.ts +0 -26
- package/dist/core/index.d.ts.map +0 -1
- package/dist/core/index.js +0 -54
- package/dist/core/index.js.map +0 -1
- package/dist/core/securityTournament.d.ts +0 -83
- package/dist/core/securityTournament.d.ts.map +0 -1
- package/dist/core/securityTournament.js +0 -357
- package/dist/core/securityTournament.js.map +0 -1
- package/dist/core/zeroDayDiscovery.d.ts +0 -96
- package/dist/core/zeroDayDiscovery.d.ts.map +0 -1
- package/dist/core/zeroDayDiscovery.js +0 -358
- package/dist/core/zeroDayDiscovery.js.map +0 -1
- package/dist/headless/interactiveShell.d.ts +0 -22
- package/dist/headless/interactiveShell.d.ts.map +0 -1
- package/dist/headless/interactiveShell.js +0 -3827
- package/dist/headless/interactiveShell.js.map +0 -1
- package/dist/plugins/tools/apple/secureApplePlugin.d.ts +0 -3
- package/dist/plugins/tools/apple/secureApplePlugin.d.ts.map +0 -1
- package/dist/plugins/tools/apple/secureApplePlugin.js +0 -26
- package/dist/plugins/tools/apple/secureApplePlugin.js.map +0 -1
- package/dist/plugins/tools/authorizedSecurity/authorizedSecurityPlugin.d.ts +0 -3
- package/dist/plugins/tools/authorizedSecurity/authorizedSecurityPlugin.d.ts.map +0 -1
- package/dist/plugins/tools/authorizedSecurity/authorizedSecurityPlugin.js +0 -9
- package/dist/plugins/tools/authorizedSecurity/authorizedSecurityPlugin.js.map +0 -1
- package/dist/plugins/tools/nodeDefaults.d.ts +0 -15
- package/dist/plugins/tools/nodeDefaults.d.ts.map +0 -1
- package/dist/plugins/tools/nodeDefaults.js +0 -37
- package/dist/plugins/tools/nodeDefaults.js.map +0 -1
- package/dist/plugins/tools/offensiveDestruction/offensiveDestructionPlugin.d.ts +0 -3
- package/dist/plugins/tools/offensiveDestruction/offensiveDestructionPlugin.d.ts.map +0 -1
- package/dist/plugins/tools/offensiveDestruction/offensiveDestructionPlugin.js +0 -9
- package/dist/plugins/tools/offensiveDestruction/offensiveDestructionPlugin.js.map +0 -1
- package/dist/plugins/tools/tao/secureTaoPlugin.d.ts +0 -3
- package/dist/plugins/tools/tao/secureTaoPlugin.d.ts.map +0 -1
- package/dist/plugins/tools/tao/secureTaoPlugin.js +0 -37
- package/dist/plugins/tools/tao/secureTaoPlugin.js.map +0 -1
- package/dist/tools/emailTools.d.ts +0 -140
- package/dist/tools/emailTools.d.ts.map +0 -1
- package/dist/tools/emailTools.js +0 -792
- package/dist/tools/emailTools.js.map +0 -1
- package/dist/tools/secureAppleExploitation.d.ts +0 -29
- package/dist/tools/secureAppleExploitation.d.ts.map +0 -1
- package/dist/tools/secureAppleExploitation.js +0 -518
- package/dist/tools/secureAppleExploitation.js.map +0 -1
package/dist/tools/emailTools.js
DELETED
|
@@ -1,792 +0,0 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'node:fs';
|
|
2
|
-
import { join } from 'node:path';
|
|
3
|
-
import { homedir } from 'node:os';
|
|
4
|
-
import nodemailer from 'nodemailer';
|
|
5
|
-
import { getSecretValue, setSecretValue } from '../core/secretStore.js';
|
|
6
|
-
export class EmailTools {
|
|
7
|
-
configDir;
|
|
8
|
-
logDir;
|
|
9
|
-
constructor() {
|
|
10
|
-
this.configDir = join(homedir(), '.agi', 'email');
|
|
11
|
-
this.logDir = join(homedir(), '.agi', 'email-logs');
|
|
12
|
-
[this.configDir, this.logDir].forEach(dir => {
|
|
13
|
-
if (!existsSync(dir)) {
|
|
14
|
-
mkdirSync(dir, { recursive: true });
|
|
15
|
-
}
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* Save SMTP configuration to secret store
|
|
20
|
-
*/
|
|
21
|
-
async saveConfig(config) {
|
|
22
|
-
try {
|
|
23
|
-
// Save basic SMTP credentials
|
|
24
|
-
setSecretValue('SMTP_USER', config.smtpUser);
|
|
25
|
-
setSecretValue('SMTP_PASSWORD', config.smtpPassword);
|
|
26
|
-
// Save optional configuration
|
|
27
|
-
if (config.smtpProvider) {
|
|
28
|
-
setSecretValue('SMTP_PROVIDER', config.smtpProvider);
|
|
29
|
-
}
|
|
30
|
-
if (config.smtpHost) {
|
|
31
|
-
setSecretValue('SMTP_HOST', config.smtpHost);
|
|
32
|
-
}
|
|
33
|
-
if (config.smtpPort) {
|
|
34
|
-
setSecretValue('SMTP_PORT', config.smtpPort.toString());
|
|
35
|
-
}
|
|
36
|
-
if (config.smtpFromName) {
|
|
37
|
-
setSecretValue('SMTP_FROM_NAME', config.smtpFromName);
|
|
38
|
-
}
|
|
39
|
-
// Also save as JSON file for easy retrieval
|
|
40
|
-
const configFile = join(this.configDir, 'smtp-config.json');
|
|
41
|
-
writeFileSync(configFile, JSON.stringify(config, null, 2));
|
|
42
|
-
return true;
|
|
43
|
-
}
|
|
44
|
-
catch (error) {
|
|
45
|
-
console.error('Failed to save SMTP config:', error);
|
|
46
|
-
return false;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Load SMTP configuration from secret store
|
|
51
|
-
*/
|
|
52
|
-
async loadConfig() {
|
|
53
|
-
try {
|
|
54
|
-
const smtpUser = getSecretValue('SMTP_USER');
|
|
55
|
-
const smtpPassword = getSecretValue('SMTP_PASSWORD');
|
|
56
|
-
if (!smtpUser || !smtpPassword) {
|
|
57
|
-
return null;
|
|
58
|
-
}
|
|
59
|
-
const config = {
|
|
60
|
-
smtpUser,
|
|
61
|
-
smtpPassword,
|
|
62
|
-
smtpProvider: getSecretValue('SMTP_PROVIDER') || undefined,
|
|
63
|
-
smtpHost: getSecretValue('SMTP_HOST') || undefined,
|
|
64
|
-
smtpFromName: getSecretValue('SMTP_FROM_NAME') || undefined,
|
|
65
|
-
};
|
|
66
|
-
const port = getSecretValue('SMTP_PORT');
|
|
67
|
-
if (port) {
|
|
68
|
-
config.smtpPort = parseInt(port, 10);
|
|
69
|
-
}
|
|
70
|
-
return config;
|
|
71
|
-
}
|
|
72
|
-
catch (error) {
|
|
73
|
-
console.error('Failed to load SMTP config:', error);
|
|
74
|
-
return null;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* Test SMTP connection with current configuration
|
|
79
|
-
*/
|
|
80
|
-
async testConnection() {
|
|
81
|
-
const config = await this.loadConfig();
|
|
82
|
-
if (!config) {
|
|
83
|
-
return {
|
|
84
|
-
success: false,
|
|
85
|
-
error: 'No SMTP configuration found. Use --save-smtp first.',
|
|
86
|
-
timestamp: new Date().toISOString()
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
try {
|
|
90
|
-
const transporter = this.createTransporter(config);
|
|
91
|
-
await transporter.verify();
|
|
92
|
-
// Try to send a test email to self
|
|
93
|
-
const testInfo = await transporter.sendMail({
|
|
94
|
-
from: config.smtpFromName ? `"${config.smtpFromName}" <${config.smtpUser}>` : config.smtpUser,
|
|
95
|
-
to: config.smtpUser,
|
|
96
|
-
subject: 'AGI Email Tools - SMTP Test',
|
|
97
|
-
text: `Test email sent successfully at ${new Date().toISOString()}\n\nSMTP Configuration:\nUser: ${config.smtpUser}\nProvider: ${config.smtpProvider || 'custom'}\nHost: ${config.smtpHost || 'default'}\nPort: ${config.smtpPort || 'default'}`,
|
|
98
|
-
headers: {
|
|
99
|
-
'X-AGI-Test': 'true',
|
|
100
|
-
'X-Timestamp': new Date().toISOString()
|
|
101
|
-
}
|
|
102
|
-
});
|
|
103
|
-
return {
|
|
104
|
-
success: true,
|
|
105
|
-
messageId: testInfo.messageId,
|
|
106
|
-
response: testInfo.response,
|
|
107
|
-
timestamp: new Date().toISOString()
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
catch (error) {
|
|
111
|
-
return {
|
|
112
|
-
success: false,
|
|
113
|
-
error: error instanceof Error ? error.message : 'Unknown error',
|
|
114
|
-
timestamp: new Date().toISOString()
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* Send a single email
|
|
120
|
-
*/
|
|
121
|
-
async sendEmail(message, fromName) {
|
|
122
|
-
const config = await this.loadConfig();
|
|
123
|
-
if (!config) {
|
|
124
|
-
return {
|
|
125
|
-
success: false,
|
|
126
|
-
error: 'No SMTP configuration found. Use --save-smtp first.',
|
|
127
|
-
timestamp: new Date().toISOString()
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
try {
|
|
131
|
-
const transporter = this.createTransporter(config);
|
|
132
|
-
const mailOptions = {
|
|
133
|
-
from: fromName || config.smtpFromName
|
|
134
|
-
? `"${fromName || config.smtpFromName}" <${config.smtpUser}>`
|
|
135
|
-
: config.smtpUser,
|
|
136
|
-
to: Array.isArray(message.to) ? message.to.join(', ') : message.to,
|
|
137
|
-
subject: message.subject,
|
|
138
|
-
text: message.text,
|
|
139
|
-
html: message.html,
|
|
140
|
-
cc: message.cc ? (Array.isArray(message.cc) ? message.cc.join(', ') : message.cc) : undefined,
|
|
141
|
-
bcc: message.bcc ? (Array.isArray(message.bcc) ? message.bcc.join(', ') : message.bcc) : undefined,
|
|
142
|
-
replyTo: message.replyTo,
|
|
143
|
-
headers: {
|
|
144
|
-
'X-AGI-Sent': 'true',
|
|
145
|
-
'X-Timestamp': new Date().toISOString(),
|
|
146
|
-
...message.headers
|
|
147
|
-
},
|
|
148
|
-
attachments: message.attachments
|
|
149
|
-
};
|
|
150
|
-
const info = await transporter.sendMail(mailOptions);
|
|
151
|
-
// Log the email
|
|
152
|
-
this.logEmail({
|
|
153
|
-
to: mailOptions.to,
|
|
154
|
-
subject: mailOptions.subject,
|
|
155
|
-
messageId: info.messageId,
|
|
156
|
-
success: true,
|
|
157
|
-
timestamp: new Date().toISOString()
|
|
158
|
-
});
|
|
159
|
-
return {
|
|
160
|
-
success: true,
|
|
161
|
-
messageId: info.messageId,
|
|
162
|
-
response: info.response,
|
|
163
|
-
timestamp: new Date().toISOString()
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
catch (error) {
|
|
167
|
-
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
168
|
-
const errorStr = String(error);
|
|
169
|
-
// Detect bounce conditions
|
|
170
|
-
let bounced = false;
|
|
171
|
-
let bounceReason = '';
|
|
172
|
-
let permanentFailure = false;
|
|
173
|
-
// Common bounce patterns
|
|
174
|
-
const bouncePatterns = [
|
|
175
|
-
{ pattern: /recipient.*rejected|recipient.*not found/i, permanent: true, reason: 'Recipient not found' },
|
|
176
|
-
{ pattern: /mailbox.*not found|mailbox.*does not exist/i, permanent: true, reason: 'Mailbox does not exist' },
|
|
177
|
-
{ pattern: /user.*unknown|user.*not found/i, permanent: true, reason: 'Unknown user' },
|
|
178
|
-
{ pattern: /address.*not found|address.*invalid/i, permanent: true, reason: 'Invalid address' },
|
|
179
|
-
{ pattern: /domain.*not found|domain.*does not exist/i, permanent: true, reason: 'Domain not found' },
|
|
180
|
-
{ pattern: /quota.*exceeded|mailbox.*full/i, permanent: false, reason: 'Mailbox full' },
|
|
181
|
-
{ pattern: /spam.*rejected|spam.*detected/i, permanent: false, reason: 'Spam rejection' },
|
|
182
|
-
{ pattern: /blacklisted|blocked|rejected.*policy/i, permanent: false, reason: 'Blocked by policy' },
|
|
183
|
-
{ pattern: /relay.*denied|relay.*not permitted/i, permanent: false, reason: 'Relay denied' },
|
|
184
|
-
{ pattern: /550|551|552|553|554/i, permanent: true, reason: 'SMTP permanent failure' },
|
|
185
|
-
{ pattern: /450|451/i, permanent: false, reason: 'SMTP temporary failure' },
|
|
186
|
-
{ pattern: /bounce|bounced/i, permanent: true, reason: 'Bounced email' }
|
|
187
|
-
];
|
|
188
|
-
for (const pattern of bouncePatterns) {
|
|
189
|
-
if (pattern.pattern.test(errorMessage) || pattern.pattern.test(errorStr)) {
|
|
190
|
-
bounced = true;
|
|
191
|
-
bounceReason = pattern.reason;
|
|
192
|
-
permanentFailure = pattern.permanent;
|
|
193
|
-
break;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
const emailLog = {
|
|
197
|
-
to: Array.isArray(message.to) ? message.to.join(', ') : message.to,
|
|
198
|
-
subject: message.subject,
|
|
199
|
-
error: errorMessage,
|
|
200
|
-
success: false,
|
|
201
|
-
bounced,
|
|
202
|
-
bounceReason: bounceReason || undefined,
|
|
203
|
-
permanentFailure,
|
|
204
|
-
timestamp: new Date().toISOString()
|
|
205
|
-
};
|
|
206
|
-
this.logEmail(emailLog);
|
|
207
|
-
// Save to non-working emails list if bounced
|
|
208
|
-
if (bounced) {
|
|
209
|
-
this.saveNonWorkingEmail({
|
|
210
|
-
email: Array.isArray(message.to) ? message.to[0] : message.to,
|
|
211
|
-
reason: bounceReason,
|
|
212
|
-
permanent: permanentFailure,
|
|
213
|
-
timestamp: new Date().toISOString(),
|
|
214
|
-
error: errorMessage
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
|
-
return {
|
|
218
|
-
success: false,
|
|
219
|
-
error: errorMessage,
|
|
220
|
-
bounced,
|
|
221
|
-
bounceReason: bounceReason || undefined,
|
|
222
|
-
permanentFailure,
|
|
223
|
-
timestamp: new Date().toISOString()
|
|
224
|
-
};
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
/**
|
|
228
|
-
* Send bulk emails with rate limiting
|
|
229
|
-
*/
|
|
230
|
-
async sendBulkEmails(options) {
|
|
231
|
-
const results = [];
|
|
232
|
-
const config = await this.loadConfig();
|
|
233
|
-
if (!config) {
|
|
234
|
-
return options.emails.map(email => ({
|
|
235
|
-
to: email.to,
|
|
236
|
-
subject: email.subject,
|
|
237
|
-
success: false,
|
|
238
|
-
error: 'No SMTP configuration found',
|
|
239
|
-
timestamp: new Date().toISOString()
|
|
240
|
-
}));
|
|
241
|
-
}
|
|
242
|
-
const delay = options.delayBetweenEmails || 5000; // 5 seconds default
|
|
243
|
-
const maxRetries = options.maxRetries || 3;
|
|
244
|
-
const stopOnError = options.stopOnError !== false; // default true
|
|
245
|
-
for (let i = 0; i < options.emails.length; i++) {
|
|
246
|
-
const email = options.emails[i];
|
|
247
|
-
let retryCount = 0;
|
|
248
|
-
let success = false;
|
|
249
|
-
let result = null;
|
|
250
|
-
while (retryCount < maxRetries && !success) {
|
|
251
|
-
result = await this.sendEmail({
|
|
252
|
-
to: email.to,
|
|
253
|
-
subject: email.subject,
|
|
254
|
-
text: email.text
|
|
255
|
-
});
|
|
256
|
-
if (result.success) {
|
|
257
|
-
success = true;
|
|
258
|
-
}
|
|
259
|
-
else {
|
|
260
|
-
retryCount++;
|
|
261
|
-
if (retryCount < maxRetries) {
|
|
262
|
-
console.log(`Retrying ${email.to} (${retryCount}/${maxRetries})...`);
|
|
263
|
-
await new Promise(resolve => setTimeout(resolve, 2000)); // 2 second delay between retries
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
results.push({
|
|
268
|
-
to: email.to,
|
|
269
|
-
subject: email.subject,
|
|
270
|
-
...(result || {
|
|
271
|
-
success: false,
|
|
272
|
-
error: 'Failed after all retries',
|
|
273
|
-
timestamp: new Date().toISOString()
|
|
274
|
-
})
|
|
275
|
-
});
|
|
276
|
-
// Stop if requested and this email failed
|
|
277
|
-
if (stopOnError && !success) {
|
|
278
|
-
console.log(`Stopping due to error on: ${email.to}`);
|
|
279
|
-
break;
|
|
280
|
-
}
|
|
281
|
-
// Delay between emails (except after the last one)
|
|
282
|
-
if (i < options.emails.length - 1) {
|
|
283
|
-
await new Promise(resolve => setTimeout(resolve, delay));
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
return results;
|
|
287
|
-
}
|
|
288
|
-
/**
|
|
289
|
-
* Get email sending statistics
|
|
290
|
-
*/
|
|
291
|
-
getStats() {
|
|
292
|
-
const logFile = join(this.logDir, 'email-sent.json');
|
|
293
|
-
if (!existsSync(logFile)) {
|
|
294
|
-
return { total: 0, successful: 0, failed: 0, lastSent: null };
|
|
295
|
-
}
|
|
296
|
-
try {
|
|
297
|
-
const logs = JSON.parse(readFileSync(logFile, 'utf-8'));
|
|
298
|
-
const successful = logs.filter((log) => log.success).length;
|
|
299
|
-
const failed = logs.filter((log) => !log.success).length;
|
|
300
|
-
const lastSent = logs.length > 0 ? logs[logs.length - 1].timestamp : null;
|
|
301
|
-
return {
|
|
302
|
-
total: logs.length,
|
|
303
|
-
successful,
|
|
304
|
-
failed,
|
|
305
|
-
lastSent
|
|
306
|
-
};
|
|
307
|
-
}
|
|
308
|
-
catch {
|
|
309
|
-
return { total: 0, successful: 0, failed: 0, lastSent: null };
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
/**
|
|
313
|
-
* List all sent emails
|
|
314
|
-
*/
|
|
315
|
-
listSent(limit = 50) {
|
|
316
|
-
const logFile = join(this.logDir, 'email-sent.json');
|
|
317
|
-
if (!existsSync(logFile)) {
|
|
318
|
-
return [];
|
|
319
|
-
}
|
|
320
|
-
try {
|
|
321
|
-
const logs = JSON.parse(readFileSync(logFile, 'utf-8'));
|
|
322
|
-
return logs.slice(-limit).reverse(); // Most recent first
|
|
323
|
-
}
|
|
324
|
-
catch {
|
|
325
|
-
return [];
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
/**
|
|
329
|
-
* Clear all logs
|
|
330
|
-
*/
|
|
331
|
-
clearLogs() {
|
|
332
|
-
const logFile = join(this.logDir, 'email-sent.json');
|
|
333
|
-
if (existsSync(logFile)) {
|
|
334
|
-
writeFileSync(logFile, '[]');
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
/**
|
|
338
|
-
* Create transporter from configuration
|
|
339
|
-
*/
|
|
340
|
-
createTransporter(config) {
|
|
341
|
-
// For common providers, use simplified configuration
|
|
342
|
-
if (config.smtpProvider) {
|
|
343
|
-
switch (config.smtpProvider.toLowerCase()) {
|
|
344
|
-
case 'gmail':
|
|
345
|
-
return nodemailer.createTransport({
|
|
346
|
-
service: 'gmail',
|
|
347
|
-
auth: {
|
|
348
|
-
user: config.smtpUser,
|
|
349
|
-
pass: config.smtpPassword
|
|
350
|
-
}
|
|
351
|
-
});
|
|
352
|
-
case 'outlook':
|
|
353
|
-
case 'office365':
|
|
354
|
-
return nodemailer.createTransport({
|
|
355
|
-
host: 'smtp.office365.com',
|
|
356
|
-
port: 587,
|
|
357
|
-
secure: false,
|
|
358
|
-
auth: {
|
|
359
|
-
user: config.smtpUser,
|
|
360
|
-
pass: config.smtpPassword
|
|
361
|
-
}
|
|
362
|
-
});
|
|
363
|
-
case 'yahoo':
|
|
364
|
-
return nodemailer.createTransport({
|
|
365
|
-
host: 'smtp.mail.yahoo.com',
|
|
366
|
-
port: 587,
|
|
367
|
-
secure: false,
|
|
368
|
-
auth: {
|
|
369
|
-
user: config.smtpUser,
|
|
370
|
-
pass: config.smtpPassword
|
|
371
|
-
}
|
|
372
|
-
});
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
// Custom SMTP configuration
|
|
376
|
-
return nodemailer.createTransport({
|
|
377
|
-
host: config.smtpHost || 'smtp.gmail.com',
|
|
378
|
-
port: config.smtpPort || 587,
|
|
379
|
-
secure: config.useSsl || false,
|
|
380
|
-
requireTLS: config.useTls !== false, // default true
|
|
381
|
-
auth: {
|
|
382
|
-
user: config.smtpUser,
|
|
383
|
-
pass: config.smtpPassword
|
|
384
|
-
}
|
|
385
|
-
});
|
|
386
|
-
}
|
|
387
|
-
/**
|
|
388
|
-
* Log email sending activity
|
|
389
|
-
*/
|
|
390
|
-
logEmail(log) {
|
|
391
|
-
const logFile = join(this.logDir, 'email-sent.json');
|
|
392
|
-
let logs = [];
|
|
393
|
-
if (existsSync(logFile)) {
|
|
394
|
-
try {
|
|
395
|
-
logs = JSON.parse(readFileSync(logFile, 'utf-8'));
|
|
396
|
-
}
|
|
397
|
-
catch {
|
|
398
|
-
logs = [];
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
logs.push(log);
|
|
402
|
-
writeFileSync(logFile, JSON.stringify(logs, null, 2));
|
|
403
|
-
}
|
|
404
|
-
/**
|
|
405
|
-
* Save a non-working email address to the bounce list
|
|
406
|
-
*/
|
|
407
|
-
saveNonWorkingEmail(bouncedEmail) {
|
|
408
|
-
const bounceFile = join(this.logDir, 'non-working-emails.json');
|
|
409
|
-
let bouncedEmails = [];
|
|
410
|
-
if (existsSync(bounceFile)) {
|
|
411
|
-
try {
|
|
412
|
-
bouncedEmails = JSON.parse(readFileSync(bounceFile, 'utf-8'));
|
|
413
|
-
}
|
|
414
|
-
catch {
|
|
415
|
-
bouncedEmails = [];
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
// Check if this email already exists in the bounce list
|
|
419
|
-
const existingIndex = bouncedEmails.findIndex(e => e.email === bouncedEmail.email);
|
|
420
|
-
if (existingIndex >= 0) {
|
|
421
|
-
// Update existing entry
|
|
422
|
-
bouncedEmails[existingIndex] = {
|
|
423
|
-
...bouncedEmails[existingIndex],
|
|
424
|
-
...bouncedEmail,
|
|
425
|
-
bounceCount: (bouncedEmails[existingIndex].bounceCount || 1) + 1,
|
|
426
|
-
lastBounce: bouncedEmail.timestamp
|
|
427
|
-
};
|
|
428
|
-
}
|
|
429
|
-
else {
|
|
430
|
-
// Add new entry
|
|
431
|
-
bouncedEmails.push({
|
|
432
|
-
...bouncedEmail,
|
|
433
|
-
bounceCount: 1,
|
|
434
|
-
firstBounce: bouncedEmail.timestamp,
|
|
435
|
-
lastBounce: bouncedEmail.timestamp
|
|
436
|
-
});
|
|
437
|
-
}
|
|
438
|
-
writeFileSync(bounceFile, JSON.stringify(bouncedEmails, null, 2));
|
|
439
|
-
// Also save to a separate permanent failures file if it's a permanent failure
|
|
440
|
-
if (bouncedEmail.permanent) {
|
|
441
|
-
const permanentFile = join(this.logDir, 'permanent-failures.json');
|
|
442
|
-
let permanentFailures = [];
|
|
443
|
-
if (existsSync(permanentFile)) {
|
|
444
|
-
try {
|
|
445
|
-
permanentFailures = JSON.parse(readFileSync(permanentFile, 'utf-8'));
|
|
446
|
-
}
|
|
447
|
-
catch {
|
|
448
|
-
permanentFailures = [];
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
// Check if already in permanent failures
|
|
452
|
-
if (!permanentFailures.some(e => e.email === bouncedEmail.email)) {
|
|
453
|
-
permanentFailures.push({
|
|
454
|
-
email: bouncedEmail.email,
|
|
455
|
-
reason: bouncedEmail.reason,
|
|
456
|
-
timestamp: bouncedEmail.timestamp,
|
|
457
|
-
error: bouncedEmail.error
|
|
458
|
-
});
|
|
459
|
-
writeFileSync(permanentFile, JSON.stringify(permanentFailures, null, 2));
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
/**
|
|
464
|
-
* Get list of non-working emails
|
|
465
|
-
*/
|
|
466
|
-
getNonWorkingEmails() {
|
|
467
|
-
const bounceFile = join(this.logDir, 'non-working-emails.json');
|
|
468
|
-
if (!existsSync(bounceFile)) {
|
|
469
|
-
return [];
|
|
470
|
-
}
|
|
471
|
-
try {
|
|
472
|
-
return JSON.parse(readFileSync(bounceFile, 'utf-8'));
|
|
473
|
-
}
|
|
474
|
-
catch {
|
|
475
|
-
return [];
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
/**
|
|
479
|
-
* Get list of permanent failures
|
|
480
|
-
*/
|
|
481
|
-
getPermanentFailures() {
|
|
482
|
-
const permanentFile = join(this.logDir, 'permanent-failures.json');
|
|
483
|
-
if (!existsSync(permanentFile)) {
|
|
484
|
-
return [];
|
|
485
|
-
}
|
|
486
|
-
try {
|
|
487
|
-
return JSON.parse(readFileSync(permanentFile, 'utf-8'));
|
|
488
|
-
}
|
|
489
|
-
catch {
|
|
490
|
-
return [];
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
/**
|
|
494
|
-
* Check if an email address is known to be non-working
|
|
495
|
-
*/
|
|
496
|
-
isNonWorkingEmail(email) {
|
|
497
|
-
const nonWorking = this.getNonWorkingEmails();
|
|
498
|
-
return nonWorking.some(e => e.email.toLowerCase() === email.toLowerCase());
|
|
499
|
-
}
|
|
500
|
-
/**
|
|
501
|
-
* Clear non-working emails list
|
|
502
|
-
*/
|
|
503
|
-
clearNonWorkingEmails() {
|
|
504
|
-
const bounceFile = join(this.logDir, 'non-working-emails.json');
|
|
505
|
-
const permanentFile = join(this.logDir, 'permanent-failures.json');
|
|
506
|
-
if (existsSync(bounceFile)) {
|
|
507
|
-
writeFileSync(bounceFile, '[]');
|
|
508
|
-
}
|
|
509
|
-
if (existsSync(permanentFile)) {
|
|
510
|
-
writeFileSync(permanentFile, '[]');
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
// CLI helper functions
|
|
515
|
-
export async function handleEmailCommand(args) {
|
|
516
|
-
const tools = new EmailTools();
|
|
517
|
-
if (args.length === 0 || args[0] === 'help') {
|
|
518
|
-
printEmailHelp();
|
|
519
|
-
return;
|
|
520
|
-
}
|
|
521
|
-
const command = args[0];
|
|
522
|
-
const remainingArgs = args.slice(1);
|
|
523
|
-
switch (command) {
|
|
524
|
-
case 'save':
|
|
525
|
-
await handleSaveCommand(remainingArgs);
|
|
526
|
-
break;
|
|
527
|
-
case 'test':
|
|
528
|
-
await handleTestCommand();
|
|
529
|
-
break;
|
|
530
|
-
case 'stats': {
|
|
531
|
-
const stats = tools.getStats();
|
|
532
|
-
console.log('š Email Statistics:');
|
|
533
|
-
console.log(`Total sent: ${stats.total}`);
|
|
534
|
-
console.log(`Successful: ${stats.successful}`);
|
|
535
|
-
console.log(`Failed: ${stats.failed}`);
|
|
536
|
-
if (stats.lastSent) {
|
|
537
|
-
console.log(`Last sent: ${new Date(stats.lastSent).toLocaleString()}`);
|
|
538
|
-
}
|
|
539
|
-
break;
|
|
540
|
-
}
|
|
541
|
-
case 'list': {
|
|
542
|
-
const limit = parseInt(remainingArgs[0], 10) || 10;
|
|
543
|
-
const emails = tools.listSent(limit);
|
|
544
|
-
console.log(`š§ Last ${limit} emails:`);
|
|
545
|
-
emails.forEach((email, i) => {
|
|
546
|
-
const status = email.success ? 'ā
' : 'ā';
|
|
547
|
-
console.log(`${i + 1}. ${status} ${email.to} - "${email.subject}" (${new Date(email.timestamp).toLocaleString()})`);
|
|
548
|
-
});
|
|
549
|
-
break;
|
|
550
|
-
}
|
|
551
|
-
case 'clear':
|
|
552
|
-
tools.clearLogs();
|
|
553
|
-
console.log('ā
Email logs cleared.');
|
|
554
|
-
break;
|
|
555
|
-
case 'bounces':
|
|
556
|
-
await handleBouncesCommand(remainingArgs);
|
|
557
|
-
break;
|
|
558
|
-
case 'check':
|
|
559
|
-
await handleCheckCommand(remainingArgs);
|
|
560
|
-
break;
|
|
561
|
-
default:
|
|
562
|
-
console.log(`Unknown command: ${command}`);
|
|
563
|
-
printEmailHelp();
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
async function handleSaveCommand(args) {
|
|
567
|
-
const tools = new EmailTools();
|
|
568
|
-
// Interactive mode
|
|
569
|
-
if (args.length === 0) {
|
|
570
|
-
console.log('Interactive SMTP configuration setup:');
|
|
571
|
-
const readline = await import('node:readline/promises');
|
|
572
|
-
const rl = readline.createInterface({
|
|
573
|
-
input: process.stdin,
|
|
574
|
-
output: process.stdout
|
|
575
|
-
});
|
|
576
|
-
try {
|
|
577
|
-
const smtpUser = await rl.question('SMTP Username/Email: ');
|
|
578
|
-
const smtpPassword = await rl.question('SMTP Password/App Password: ');
|
|
579
|
-
const smtpProvider = await rl.question('Provider (gmail, outlook, yahoo, or custom): ');
|
|
580
|
-
let smtpHost = '';
|
|
581
|
-
let smtpPort = '';
|
|
582
|
-
let smtpFromName = '';
|
|
583
|
-
if (smtpProvider.toLowerCase() === 'custom') {
|
|
584
|
-
smtpHost = await rl.question('SMTP Host (e.g., smtp.gmail.com): ');
|
|
585
|
-
smtpPort = await rl.question('SMTP Port (e.g., 587): ');
|
|
586
|
-
}
|
|
587
|
-
smtpFromName = await rl.question('From Name (optional): ');
|
|
588
|
-
const config = {
|
|
589
|
-
smtpUser,
|
|
590
|
-
smtpPassword,
|
|
591
|
-
smtpProvider: smtpProvider.toLowerCase() === 'custom' ? undefined : smtpProvider,
|
|
592
|
-
smtpHost: smtpHost || undefined,
|
|
593
|
-
smtpPort: smtpPort ? parseInt(smtpPort, 10) : undefined,
|
|
594
|
-
smtpFromName: smtpFromName || undefined
|
|
595
|
-
};
|
|
596
|
-
const success = await tools.saveConfig(config);
|
|
597
|
-
if (success) {
|
|
598
|
-
console.log('ā
SMTP configuration saved successfully!');
|
|
599
|
-
}
|
|
600
|
-
else {
|
|
601
|
-
console.log('ā Failed to save SMTP configuration');
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
finally {
|
|
605
|
-
rl.close();
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
else {
|
|
609
|
-
// Command line mode
|
|
610
|
-
console.log('Use interactive mode for security. Run without arguments.');
|
|
611
|
-
console.log('Example: agi email save');
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
async function handleTestCommand() {
|
|
615
|
-
const tools = new EmailTools();
|
|
616
|
-
console.log('Testing SMTP connection...');
|
|
617
|
-
const result = await tools.testConnection();
|
|
618
|
-
if (result.success) {
|
|
619
|
-
console.log('ā
SMTP connection successful!');
|
|
620
|
-
console.log(`Message ID: ${result.messageId}`);
|
|
621
|
-
console.log(`Response: ${result.response}`);
|
|
622
|
-
}
|
|
623
|
-
else {
|
|
624
|
-
console.log('ā SMTP connection failed');
|
|
625
|
-
console.log(`Error: ${result.error}`);
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
async function handleBouncesCommand(args) {
|
|
629
|
-
const tools = new EmailTools();
|
|
630
|
-
const subCommand = args[0]?.toLowerCase() || 'list';
|
|
631
|
-
switch (subCommand) {
|
|
632
|
-
case 'list':
|
|
633
|
-
const nonWorking = tools.getNonWorkingEmails();
|
|
634
|
-
const permanent = tools.getPermanentFailures();
|
|
635
|
-
console.log('š¬ Non-Working Email Addresses:');
|
|
636
|
-
console.log('ā'.repeat(80));
|
|
637
|
-
if (nonWorking.length === 0) {
|
|
638
|
-
console.log('No non-working emails recorded.');
|
|
639
|
-
}
|
|
640
|
-
else {
|
|
641
|
-
nonWorking.forEach((email, index) => {
|
|
642
|
-
const status = email.permanent ? 'š“ PERMANENT' : 'š” TEMPORARY';
|
|
643
|
-
console.log(`${index + 1}. ${email.email}`);
|
|
644
|
-
console.log(` Reason: ${email.reason}`);
|
|
645
|
-
console.log(` Status: ${status}`);
|
|
646
|
-
console.log(` Bounces: ${email.bounceCount}`);
|
|
647
|
-
console.log(` First: ${new Date(email.firstBounce).toLocaleString()}`);
|
|
648
|
-
console.log(` Last: ${new Date(email.lastBounce).toLocaleString()}`);
|
|
649
|
-
if (email.error) {
|
|
650
|
-
console.log(` Error: ${email.error.substring(0, 100)}${email.error.length > 100 ? '...' : ''}`);
|
|
651
|
-
}
|
|
652
|
-
console.log('ā'.repeat(80));
|
|
653
|
-
});
|
|
654
|
-
}
|
|
655
|
-
console.log('\nš“ Permanent Failures:');
|
|
656
|
-
console.log('ā'.repeat(80));
|
|
657
|
-
if (permanent.length === 0) {
|
|
658
|
-
console.log('No permanent failures recorded.');
|
|
659
|
-
}
|
|
660
|
-
else {
|
|
661
|
-
permanent.forEach((email, index) => {
|
|
662
|
-
console.log(`${index + 1}. ${email.email}`);
|
|
663
|
-
console.log(` Reason: ${email.reason}`);
|
|
664
|
-
console.log(` Date: ${new Date(email.timestamp).toLocaleString()}`);
|
|
665
|
-
if (email.error) {
|
|
666
|
-
console.log(` Error: ${email.error.substring(0, 80)}${email.error.length > 80 ? '...' : ''}`);
|
|
667
|
-
}
|
|
668
|
-
console.log('ā'.repeat(80));
|
|
669
|
-
});
|
|
670
|
-
}
|
|
671
|
-
break;
|
|
672
|
-
case 'clear':
|
|
673
|
-
const readline = await import('node:readline/promises');
|
|
674
|
-
const rl = readline.createInterface({
|
|
675
|
-
input: process.stdin,
|
|
676
|
-
output: process.stdout
|
|
677
|
-
});
|
|
678
|
-
try {
|
|
679
|
-
const answer = await rl.question('Are you sure you want to clear all bounce records? (yes/no): ');
|
|
680
|
-
if (answer.toLowerCase() === 'yes') {
|
|
681
|
-
tools.clearNonWorkingEmails();
|
|
682
|
-
console.log('ā
Bounce records cleared.');
|
|
683
|
-
}
|
|
684
|
-
else {
|
|
685
|
-
console.log('Operation cancelled.');
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
finally {
|
|
689
|
-
rl.close();
|
|
690
|
-
}
|
|
691
|
-
break;
|
|
692
|
-
case 'stats':
|
|
693
|
-
const allNonWorking = tools.getNonWorkingEmails();
|
|
694
|
-
const permanentFailures = tools.getPermanentFailures();
|
|
695
|
-
const permanentCount = allNonWorking.filter(e => e.permanent).length;
|
|
696
|
-
const temporaryCount = allNonWorking.filter(e => !e.permanent).length;
|
|
697
|
-
const totalBounces = allNonWorking.reduce((sum, e) => sum + e.bounceCount, 0);
|
|
698
|
-
console.log('š Bounce Statistics:');
|
|
699
|
-
console.log(`Total non-working emails: ${allNonWorking.length}`);
|
|
700
|
-
console.log(`Permanent failures: ${permanentCount}`);
|
|
701
|
-
console.log(`Temporary failures: ${temporaryCount}`);
|
|
702
|
-
console.log(`Total bounce events: ${totalBounces}`);
|
|
703
|
-
console.log(`Unique permanent failures: ${permanentFailures.length}`);
|
|
704
|
-
if (allNonWorking.length > 0) {
|
|
705
|
-
console.log('\nš Most Frequent Failures:');
|
|
706
|
-
const sorted = [...allNonWorking].sort((a, b) => b.bounceCount - a.bounceCount).slice(0, 5);
|
|
707
|
-
sorted.forEach((email, index) => {
|
|
708
|
-
console.log(`${index + 1}. ${email.email} - ${email.bounceCount} bounce${email.bounceCount !== 1 ? 's' : ''}`);
|
|
709
|
-
});
|
|
710
|
-
}
|
|
711
|
-
break;
|
|
712
|
-
default:
|
|
713
|
-
console.log('Usage: agi email bounces [list|clear|stats]');
|
|
714
|
-
console.log(' list - Show all non-working email addresses');
|
|
715
|
-
console.log(' clear - Clear all bounce records (requires confirmation)');
|
|
716
|
-
console.log(' stats - Show bounce statistics');
|
|
717
|
-
}
|
|
718
|
-
}
|
|
719
|
-
async function handleCheckCommand(args) {
|
|
720
|
-
const tools = new EmailTools();
|
|
721
|
-
if (args.length === 0) {
|
|
722
|
-
console.log('Usage: agi email check <email-address>');
|
|
723
|
-
console.log('Example: agi email check user@example.com');
|
|
724
|
-
return;
|
|
725
|
-
}
|
|
726
|
-
const email = args[0];
|
|
727
|
-
const isNonWorking = tools.isNonWorkingEmail(email);
|
|
728
|
-
console.log(`Checking email: ${email}`);
|
|
729
|
-
if (isNonWorking) {
|
|
730
|
-
const nonWorking = tools.getNonWorkingEmails();
|
|
731
|
-
const emailRecord = nonWorking.find(e => e.email.toLowerCase() === email.toLowerCase());
|
|
732
|
-
if (emailRecord) {
|
|
733
|
-
console.log('ā This email address is marked as non-working.');
|
|
734
|
-
console.log(`Reason: ${emailRecord.reason}`);
|
|
735
|
-
console.log(`Status: ${emailRecord.permanent ? 'PERMANENT FAILURE' : 'TEMPORARY FAILURE'}`);
|
|
736
|
-
console.log(`Bounce count: ${emailRecord.bounceCount}`);
|
|
737
|
-
console.log(`First bounce: ${new Date(emailRecord.firstBounce).toLocaleString()}`);
|
|
738
|
-
console.log(`Last bounce: ${new Date(emailRecord.lastBounce).toLocaleString()}`);
|
|
739
|
-
if (emailRecord.permanent) {
|
|
740
|
-
console.log('ā ļø Warning: This is a permanent failure. Sending to this address will likely fail.');
|
|
741
|
-
}
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
else {
|
|
745
|
-
console.log('ā
This email address is not marked as non-working.');
|
|
746
|
-
console.log('(No bounce records found for this address)');
|
|
747
|
-
}
|
|
748
|
-
}
|
|
749
|
-
function printEmailHelp() {
|
|
750
|
-
console.log(`
|
|
751
|
-
AGI Email Tools - Send emails using SMTP
|
|
752
|
-
|
|
753
|
-
Commands:
|
|
754
|
-
save Configure SMTP settings interactively
|
|
755
|
-
test Test SMTP connection
|
|
756
|
-
send <to> "<subject>" "<text>" [--from-name "Name"]
|
|
757
|
-
Send a single email
|
|
758
|
-
bulk <emails-file.json> [--delay 5000] [--max-retries 3] [--stop-on-error]
|
|
759
|
-
Send bulk emails from JSON file
|
|
760
|
-
stats Show email sending statistics
|
|
761
|
-
list [limit] List recently sent emails (default: 10)
|
|
762
|
-
clear Clear all email logs (confirmation required)
|
|
763
|
-
bounces [list|clear|stats] Manage bounce records
|
|
764
|
-
check <email> Check if an email address is marked as non-working
|
|
765
|
-
help Show this help message
|
|
766
|
-
|
|
767
|
-
Examples:
|
|
768
|
-
agi email save
|
|
769
|
-
agi email test
|
|
770
|
-
agi email send "user@example.com" "Test Subject" "Email body text"
|
|
771
|
-
agi email send "user@example.com" "Test" "Body" --from-name "AGI System"
|
|
772
|
-
agi email bulk emails.json --delay 10000
|
|
773
|
-
agi email list 20
|
|
774
|
-
agi email bounces list
|
|
775
|
-
agi email bounces stats
|
|
776
|
-
agi email check user@example.com
|
|
777
|
-
|
|
778
|
-
SMTP Configuration:
|
|
779
|
-
The 'save' command will store your SMTP credentials securely in the system keychain.
|
|
780
|
-
Supported providers: Gmail, Outlook/Office365, Yahoo, or custom SMTP servers.
|
|
781
|
-
|
|
782
|
-
For Gmail, you need an "App Password" if 2-factor authentication is enabled.
|
|
783
|
-
Generate at: https://myaccount.google.com/apppasswords
|
|
784
|
-
|
|
785
|
-
Bounce Detection:
|
|
786
|
-
The system automatically detects bounced emails and saves them to a non-working list.
|
|
787
|
-
Permanent failures (like invalid addresses) are marked and can be checked before sending.
|
|
788
|
-
Use 'agi email bounces list' to see all non-working addresses.
|
|
789
|
-
Use 'agi email check <address>' to verify if an address is known to bounce.
|
|
790
|
-
`);
|
|
791
|
-
}
|
|
792
|
-
//# sourceMappingURL=emailTools.js.map
|