stellamail 1.0.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Smart Choice Inspections LLC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,139 @@
1
+ # โœ‰๏ธ Stellamail
2
+
3
+ **Safe email CLI for AI agents and humans.**
4
+
5
+ Built with safety rails that prevent the disasters AI agents cause with email โ€” duplicate sends, missing attachments, forgotten CCs. Works great as a standalone terminal email client too.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install -g stellamail
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ```bash
16
+ # Interactive setup wizard
17
+ stellamail init
18
+
19
+ # Check your inbox
20
+ stellamail inbox --account work
21
+
22
+ # Send an email
23
+ stellamail send --account work \
24
+ --to "client@example.com" \
25
+ --cc "boss@example.com" \
26
+ --subject "Report attached" \
27
+ --body "Hi, please find your report attached." \
28
+ --attachment "/path/to/report.pdf"
29
+
30
+ # Read a message
31
+ stellamail read 42 --account work
32
+
33
+ # Reply
34
+ stellamail reply 42 --account work --body "Thanks for the update!"
35
+ ```
36
+
37
+ ## Commands
38
+
39
+ | Command | Description |
40
+ |---------|-------------|
41
+ | `send` | Send an email |
42
+ | `reply <id>` | Reply to an email |
43
+ | `forward <id>` | Forward an email |
44
+ | `inbox` | List inbox messages |
45
+ | `read <id>` | Read a single email |
46
+ | `search` | Search emails |
47
+ | `folders` | List mailbox folders |
48
+ | `move <id>` | Move a message to another folder |
49
+ | `delete <id>` | Delete a message |
50
+ | `flag <id>` | Toggle flags on a message |
51
+ | `log` | View send log |
52
+ | `test` | Test SMTP/IMAP connections |
53
+ | `accounts` | List configured accounts |
54
+ | `templates` | List available templates |
55
+ | `init` | Interactive setup wizard |
56
+
57
+ ## Safety Features
58
+
59
+ These aren't optional โ€” they're the whole point.
60
+
61
+ - ๐Ÿ”’ **Duplicate blocking** โ€” Same email within 24h? Blocked. Configurable cooldown.
62
+ - โœ… **Attachment validation** โ€” PDF header check, file exists, size limits.
63
+ - ๐Ÿ“‹ **CC enforcement** โ€” Per-account. Can't "forget" to CC the boss.
64
+ - ๐Ÿ“ **Send logging** โ€” Every send logged with timestamp, recipient, status.
65
+ - ๐Ÿšซ **One attempt only** โ€” No retries. Ever. Fail = report and stop.
66
+ - ๐Ÿงช **Dry run** โ€” Preview everything before sending with `--dry-run`.
67
+
68
+ ## Templates
69
+
70
+ Define reusable email templates in your config:
71
+
72
+ ```bash
73
+ stellamail send --account work \
74
+ --template report-delivery \
75
+ --var firstName=Sarah \
76
+ --var reportType=Radon \
77
+ --var address="123 Main St" \
78
+ --attachment report.pdf
79
+ ```
80
+
81
+ ## Signatures
82
+
83
+ Per-account signatures with template variables:
84
+
85
+ ```json
86
+ {
87
+ "signature": {
88
+ "text": "\n--\n{{name}}\n{{title}}\n{{company}}",
89
+ "vars": {
90
+ "name": "Jane Smith",
91
+ "title": "Inspector",
92
+ "company": "Acme Inspections"
93
+ }
94
+ }
95
+ }
96
+ ```
97
+
98
+ Skip with `--no-signature`.
99
+
100
+ ## JSON Output
101
+
102
+ Every command supports `--output json` for scripting and AI agent integration:
103
+
104
+ ```bash
105
+ stellamail inbox --account work --output json
106
+ stellamail accounts --output json
107
+ ```
108
+
109
+ ## Configuration
110
+
111
+ Run `stellamail init` for interactive setup, or create `~/.stellamail.config.json` manually. See [config-example.json](config-example.json) for all options.
112
+
113
+ **Security:**
114
+ - Config file auto-set to `chmod 600`
115
+ - Warning if permissions are too open
116
+ - Environment variable override: `STELLAMAIL_ACCOUNTNAME_PASS`
117
+
118
+ ## Why Stellamail?
119
+
120
+ An AI agent sent 15 duplicate emails to a client at midnight. The home inspector woke up to an inbox full of angry replies. That's why.
121
+
122
+ Stellamail was born out of pure frustration โ€” built by [Leo Betancor](https://makeasmartchoice.us), a New Jersey home inspector who just wanted his AI assistant to send one email. ONE. Instead, it sent fifteen. So he built an email tool his bots literally can't break.
123
+
124
+ Duplicate blocking, attachment validation, and CC enforcement aren't features โ€” they're therapy.
125
+
126
+ But it's also just a really nice terminal email client for humans. Color-coded inbox, clean formatting, fast IMAP. No Electron, no bloat, no nonsense.
127
+
128
+ ## Tech
129
+
130
+ - **SMTP:** nodemailer
131
+ - **IMAP:** imapflow
132
+ - **Parsing:** mailparser
133
+ - **UI:** chalk + cli-table3
134
+ - **Config:** JSON (no database)
135
+ - **Runtime:** Node.js (CommonJS)
136
+
137
+ ## License
138
+
139
+ MIT
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const { run } = require('../src/cli');
5
+
6
+ run().catch(err => {
7
+ const chalk = require('chalk');
8
+ console.error(chalk.red('โŒ ' + err.message));
9
+ process.exit(1);
10
+ });
@@ -0,0 +1,76 @@
1
+ {
2
+ "_comment": "Stellamail config โ€” copy to stellamail.config.json and fill in your details",
3
+
4
+ "smtp": {
5
+ "host": "smtp.example.com",
6
+ "port": 465,
7
+ "secure": true,
8
+ "rejectUnauthorized": false
9
+ },
10
+
11
+ "accounts": {
12
+ "work": {
13
+ "from": "\"Your Name\" <you@example.com>",
14
+ "smtp": {
15
+ "user": "you@example.com",
16
+ "pass": "your-password-here"
17
+ },
18
+ "requireCc": true,
19
+ "signature": {
20
+ "text": "\n--\n{{name}}\n{{title}}\n{{company}}\nPhone: {{phone}}",
21
+ "html": "<br><hr><b>{{name}}</b><br>{{title}}<br>{{company}}<br>Phone: {{phone}}",
22
+ "vars": {
23
+ "name": "Your Name",
24
+ "title": "Your Title",
25
+ "company": "Your Company",
26
+ "phone": "(555) 123-4567"
27
+ }
28
+ }
29
+ },
30
+ "support": {
31
+ "from": "\"Support\" <support@example.com>",
32
+ "smtp": {
33
+ "user": "support@example.com",
34
+ "pass": "your-password-here"
35
+ },
36
+ "requireCc": false
37
+ }
38
+ },
39
+
40
+ "signature": {
41
+ "_comment": "Global fallback signature (used when account has no signature)",
42
+ "text": "\n--\nSent via Stellamail",
43
+ "html": "<br><hr><small>Sent via Stellamail</small>"
44
+ },
45
+
46
+ "templates": {
47
+ "report-delivery": {
48
+ "subject": "{{reportType}} Report - {{address}}",
49
+ "body": "Hi {{firstName}},\n\nPlease find attached your {{reportType}} Report for {{address}}.\n\nPlease let us know if you have any questions.",
50
+ "cc": "inspector@example.com"
51
+ },
52
+ "invoice": {
53
+ "subject": "Invoice - {{invoiceNumber}}",
54
+ "body": "Hi {{firstName}},\n\nPlease find your invoice #{{invoiceNumber}} attached.\n\nPayment is due within 30 days.\n\nThank you for your business."
55
+ },
56
+ "follow-up": {
57
+ "subject": "Following up - {{topic}}",
58
+ "body": "Hi {{firstName}},\n\nJust following up regarding {{topic}}.\n\nPlease let me know if you have any questions."
59
+ }
60
+ },
61
+
62
+ "duplicateCheck": {
63
+ "cooldownSeconds": 86400,
64
+ "lockDir": "~/.stellamail-locks"
65
+ },
66
+
67
+ "attachments": {
68
+ "maxSizeMB": 25,
69
+ "validatePdf": true
70
+ },
71
+
72
+ "logging": {
73
+ "file": "~/.stellamail-log.json",
74
+ "maxEntries": 500
75
+ }
76
+ }
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "stellamail",
3
+ "version": "1.0.0",
4
+ "description": "Safe email CLI for AI agents and humans. Built by a fed-up home inspector whose AI assistant sent 15 duplicate emails to a client. Never again. Duplicate blocking, attachment validation, CC enforcement, templates, and a color-coded terminal UI.",
5
+ "bin": {
6
+ "stellamail": "./bin/stellamail.js"
7
+ },
8
+ "keywords": [
9
+ "email",
10
+ "cli",
11
+ "imap",
12
+ "smtp",
13
+ "mail",
14
+ "terminal",
15
+ "ai-agent",
16
+ "openclaw",
17
+ "nodemailer",
18
+ "safe-email"
19
+ ],
20
+ "author": "Smart Choice Inspections",
21
+ "license": "MIT",
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "https://github.com/smartchoiceinspections/stellamail"
25
+ },
26
+ "homepage": "https://github.com/smartchoiceinspections/stellamail#readme",
27
+ "engines": {
28
+ "node": ">=16.0.0"
29
+ },
30
+ "files": [
31
+ "bin/",
32
+ "src/",
33
+ "config-example.json",
34
+ "README.md",
35
+ "LICENSE"
36
+ ],
37
+ "dependencies": {
38
+ "nodemailer": "^6.9.0",
39
+ "imapflow": "^1.0.0",
40
+ "mailparser": "^3.6.0",
41
+ "chalk": "^4.1.2",
42
+ "cli-table3": "^0.6.0"
43
+ }
44
+ }
package/src/cli.js ADDED
@@ -0,0 +1,216 @@
1
+ 'use strict';
2
+
3
+ const { findConfig } = require('./config');
4
+ const { chalk } = require('./format');
5
+ const Table = require('cli-table3');
6
+ const { version } = require('../package.json');
7
+
8
+ const COMMANDS = {
9
+ send: './commands/send',
10
+ inbox: './commands/inbox',
11
+ read: './commands/read',
12
+ search: './commands/search',
13
+ folders: './commands/folders',
14
+ log: './commands/log',
15
+ test: './commands/test',
16
+ accounts: './commands/accounts',
17
+ templates: './commands/templates',
18
+ reply: './commands/reply',
19
+ forward: './commands/forward',
20
+ move: './commands/move',
21
+ delete: './commands/delete',
22
+ flag: './commands/flag'
23
+ };
24
+
25
+ const ALL_COMMANDS = Object.keys(COMMANDS).concat('init', 'help', 'version');
26
+
27
+ function levenshtein(a, b) {
28
+ const m = a.length, n = b.length;
29
+ const dp = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));
30
+ for (let i = 0; i <= m; i++) dp[i][0] = i;
31
+ for (let j = 0; j <= n; j++) dp[0][j] = j;
32
+ for (let i = 1; i <= m; i++) {
33
+ for (let j = 1; j <= n; j++) {
34
+ dp[i][j] = a[i - 1] === b[j - 1]
35
+ ? dp[i - 1][j - 1]
36
+ : 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
37
+ }
38
+ }
39
+ return dp[m][n];
40
+ }
41
+
42
+ function suggestCommand(input) {
43
+ let best = null;
44
+ let bestDist = Infinity;
45
+ for (const cmd of ALL_COMMANDS) {
46
+ const dist = levenshtein(input.toLowerCase(), cmd);
47
+ if (dist < bestDist) {
48
+ bestDist = dist;
49
+ best = cmd;
50
+ }
51
+ }
52
+ return bestDist <= 3 ? best : null;
53
+ }
54
+
55
+ function parseArgs(args) {
56
+ const opts = { attachments: [], _positional: [] };
57
+
58
+ for (let i = 0; i < args.length; i++) {
59
+ if (!args[i].startsWith('--')) {
60
+ opts._positional.push(args[i]);
61
+ continue;
62
+ }
63
+ const key = args[i].slice(2);
64
+
65
+ // Boolean flags
66
+ if (['dry-run', 'no-signature', 'html', 'no-duplicate-check', 'help',
67
+ 'unread', 'has-attachment', 'raw', 'all', 'permanent', 'version'].includes(key)) {
68
+ opts[key] = true;
69
+ continue;
70
+ }
71
+
72
+ const val = args[i + 1];
73
+ i++;
74
+
75
+ if (key === 'attachment') {
76
+ opts.attachments.push(val);
77
+ continue;
78
+ }
79
+
80
+ if (key === 'var') {
81
+ if (!opts.vars) opts.vars = {};
82
+ const eq = val.indexOf('=');
83
+ if (eq > 0) {
84
+ opts.vars[val.slice(0, eq)] = val.slice(eq + 1);
85
+ }
86
+ continue;
87
+ }
88
+
89
+ opts[key] = val;
90
+ }
91
+
92
+ return opts;
93
+ }
94
+
95
+ function showBanner() {
96
+ const banner = new Table({
97
+ colWidths: [43],
98
+ style: { head: [], border: ['cyan'] },
99
+ chars: {
100
+ 'top': 'โ”€', 'top-mid': '', 'top-left': 'โ”Œ', 'top-right': 'โ”',
101
+ 'bottom': 'โ”€', 'bottom-mid': '', 'bottom-left': 'โ””', 'bottom-right': 'โ”˜',
102
+ 'left': 'โ”‚', 'left-mid': '', 'mid': '', 'mid-mid': '',
103
+ 'right': 'โ”‚', 'right-mid': '', 'middle': ''
104
+ }
105
+ });
106
+ banner.push(
107
+ [{ content: chalk.bold(` โœ‰๏ธ Stellamail v${version}`), hAlign: 'center' }],
108
+ [{ content: chalk.white(' Safe email CLI for AI agents & humans'), hAlign: 'center' }]
109
+ );
110
+ console.log(banner.toString());
111
+ }
112
+
113
+ function showHelp(commandName) {
114
+ if (commandName === 'init') {
115
+ const init = require('./commands/init');
116
+ console.log(init.help);
117
+ return;
118
+ }
119
+ if (commandName && COMMANDS[commandName]) {
120
+ const cmd = require(COMMANDS[commandName]);
121
+ console.log(cmd.help || `No help available for "${commandName}".`);
122
+ return;
123
+ }
124
+
125
+ showBanner();
126
+
127
+ console.log(`
128
+ ${chalk.yellow.bold('USAGE:')}
129
+ stellamail <command> [options]
130
+
131
+ ${chalk.yellow.bold('COMMANDS:')}
132
+ ${chalk.cyan.bold('send')} Send an email
133
+ ${chalk.cyan.bold('reply')} ${chalk.red('<id>')} Reply to an email
134
+ ${chalk.cyan.bold('forward')} ${chalk.red('<id>')} Forward an email
135
+ ${chalk.cyan.bold('inbox')} List inbox messages (IMAP)
136
+ ${chalk.cyan.bold('read')} ${chalk.red('<id>')} Read a single email (IMAP)
137
+ ${chalk.cyan.bold('search')} Search emails (IMAP)
138
+ ${chalk.cyan.bold('folders')} List mailbox folders (IMAP)
139
+ ${chalk.cyan.bold('move')} ${chalk.red('<id>')} Move a message to another folder
140
+ ${chalk.cyan.bold('delete')} ${chalk.red('<id>')} Delete a message
141
+ ${chalk.cyan.bold('flag')} ${chalk.red('<id>')} Toggle flags on a message
142
+ ${chalk.cyan.bold('log')} View send log
143
+ ${chalk.cyan.bold('test')} Test SMTP/IMAP connections
144
+ ${chalk.cyan.bold('accounts')} List configured accounts
145
+ ${chalk.cyan.bold('templates')} List available templates
146
+ ${chalk.cyan.bold('init')} Interactive setup wizard
147
+ ${chalk.cyan.bold('help')} ${chalk.gray('[cmd]')} Show help for a command
148
+
149
+ ${chalk.yellow.bold('GLOBAL OPTIONS:')}
150
+ ${chalk.gray('--config <path>')} Path to stellamail.config.json
151
+ ${chalk.gray('--output json')} Output as JSON (clean, no formatting)
152
+ ${chalk.gray('--version')} Show version number
153
+
154
+ ${chalk.yellow.bold('EXAMPLES:')}
155
+ ${chalk.green('stellamail send --account work --to user@example.com --subject "Hello" --body "Hi there"')}
156
+ ${chalk.green('stellamail reply 42 --account work --body "Thanks!"')}
157
+ ${chalk.green('stellamail forward 42 --account work --to colleague@example.com')}
158
+ ${chalk.green('stellamail move 42 --account work --to Archive')}
159
+ ${chalk.green('stellamail delete 42 --account work')}
160
+ ${chalk.green('stellamail flag 42 --account work --add flagged')}
161
+ ${chalk.green('stellamail inbox --account work --unread')}
162
+ ${chalk.green('stellamail accounts --output json')}
163
+ `);
164
+ }
165
+
166
+ async function run() {
167
+ const args = process.argv.slice(2);
168
+
169
+ // Handle --version anywhere in args
170
+ if (args.includes('--version') || (args.length === 1 && args[0] === 'version')) {
171
+ console.log(`stellamail v${version}`);
172
+ return;
173
+ }
174
+
175
+ if (args.length === 0 || (args.length === 1 && args[0] === '--help')) {
176
+ showHelp();
177
+ return;
178
+ }
179
+
180
+ const command = args[0];
181
+
182
+ // Handle help
183
+ if (command === 'help') {
184
+ showHelp(args[1]);
185
+ return;
186
+ }
187
+
188
+ // Handle init (no config needed)
189
+ if (command === 'init') {
190
+ const init = require('./commands/init');
191
+ await init.run();
192
+ return;
193
+ }
194
+
195
+ if (!COMMANDS[command]) {
196
+ const suggestion = suggestCommand(command);
197
+ if (suggestion) {
198
+ console.error(chalk.red(`โŒ Unknown command "${command}".`) + ` Did you mean ${chalk.cyan.bold('"' + suggestion + '"')}?`);
199
+ } else {
200
+ console.error(chalk.red(`โŒ Unknown command: "${command}"`));
201
+ }
202
+ console.error(chalk.gray(' Run "stellamail help" for available commands.'));
203
+ process.exit(1);
204
+ }
205
+
206
+ const opts = parseArgs(args.slice(1));
207
+
208
+ // Load config
209
+ const { config } = findConfig(opts.config);
210
+
211
+ // Run command
212
+ const cmd = require(COMMANDS[command]);
213
+ await cmd.run(opts, config);
214
+ }
215
+
216
+ module.exports = { run };
@@ -0,0 +1,64 @@
1
+ 'use strict';
2
+
3
+ const { makeTable, chalk } = require('../format');
4
+
5
+ async function run(opts, config) {
6
+ const accounts = config.accounts ? Object.keys(config.accounts) : [];
7
+ const isJson = opts.output === 'json';
8
+
9
+ if (accounts.length === 0) {
10
+ if (isJson) {
11
+ console.log(JSON.stringify([]));
12
+ } else {
13
+ console.log(chalk.yellow('No accounts configured.'));
14
+ }
15
+ return;
16
+ }
17
+
18
+ if (isJson) {
19
+ const jsonAccounts = accounts.map(name => {
20
+ const acct = config.accounts[name];
21
+ const smtpHost = (acct.smtp && acct.smtp.host) || (config.smtp && config.smtp.host) || null;
22
+ return {
23
+ name,
24
+ from: acct.from || null,
25
+ smtpHost,
26
+ hasImap: !!acct.imap
27
+ };
28
+ });
29
+ console.log(JSON.stringify(jsonAccounts, null, 2));
30
+ return;
31
+ }
32
+
33
+ console.log(chalk.cyan('๐Ÿ“ง Configured Accounts'));
34
+
35
+ const table = makeTable(['Account', 'From', 'SMTP Host', 'IMAP'], [12, 28, 20, 10]);
36
+
37
+ for (const name of accounts) {
38
+ const acct = config.accounts[name];
39
+ const smtpHost = (acct.smtp && acct.smtp.host) || (config.smtp && config.smtp.host) || '';
40
+ const hasImap = acct.imap ? chalk.green('โœ…') : chalk.red('โŒ');
41
+
42
+ table.push([
43
+ chalk.cyan.bold(name),
44
+ chalk.green(acct.from || ''),
45
+ smtpHost,
46
+ hasImap
47
+ ]);
48
+ }
49
+
50
+ console.log(table.toString());
51
+ }
52
+
53
+ const help = `
54
+ stellamail accounts โ€” List configured accounts
55
+
56
+ USAGE:
57
+ stellamail accounts [options]
58
+
59
+ OPTIONS:
60
+ --output json Output as JSON
61
+ --config <path> Path to config file
62
+ `;
63
+
64
+ module.exports = { run, help };
@@ -0,0 +1,78 @@
1
+ 'use strict';
2
+
3
+ const { getAccount } = require('../config');
4
+ const { withImap } = require('../imap');
5
+ const { chalk } = require('../format');
6
+
7
+ async function run(opts, config) {
8
+ const id = opts._positional && opts._positional[0];
9
+ if (!id) {
10
+ console.error(chalk.red('โŒ Usage: stellamail delete <id> --account <name>'));
11
+ process.exit(1);
12
+ }
13
+ if (!opts.account) {
14
+ console.error(chalk.red('โŒ --account is required for delete'));
15
+ process.exit(1);
16
+ }
17
+
18
+ const acctConfig = getAccount(config, opts.account);
19
+ const folder = opts.folder || 'INBOX';
20
+ const seq = parseInt(id, 10);
21
+ const permanent = !!opts.permanent;
22
+ const isJson = opts.output === 'json';
23
+
24
+ await withImap(acctConfig, config, async (client) => {
25
+ const mailbox = await client.getMailboxLock(folder);
26
+ try {
27
+ if (permanent) {
28
+ await client.messageDelete(String(seq));
29
+ if (isJson) {
30
+ console.log(JSON.stringify({
31
+ status: 'deleted',
32
+ id: seq,
33
+ folder,
34
+ permanent: true,
35
+ account: opts.account
36
+ }, null, 2));
37
+ } else {
38
+ console.log(chalk.green(`โœ… Message #${seq} permanently deleted from "${folder}"`));
39
+ }
40
+ } else {
41
+ await client.messageMove(String(seq), 'Trash');
42
+ if (isJson) {
43
+ console.log(JSON.stringify({
44
+ status: 'deleted',
45
+ id: seq,
46
+ folder,
47
+ movedTo: 'Trash',
48
+ permanent: false,
49
+ account: opts.account
50
+ }, null, 2));
51
+ } else {
52
+ console.log(chalk.green(`โœ… Message #${seq} moved to Trash`));
53
+ }
54
+ }
55
+ } finally {
56
+ mailbox.release();
57
+ }
58
+ });
59
+ }
60
+
61
+ const help = `
62
+ stellamail delete โ€” Delete a message
63
+
64
+ USAGE:
65
+ stellamail delete <id> --account <name> [options]
66
+
67
+ REQUIRED:
68
+ <id> Message sequence number
69
+ --account <name> Account name from config
70
+
71
+ OPTIONAL:
72
+ --folder <name> Source folder (default INBOX)
73
+ --permanent Permanently delete (don't move to Trash)
74
+ --output json Output as JSON
75
+ --config <path> Path to config file
76
+ `;
77
+
78
+ module.exports = { run, help };