@bobfrankston/mailx 1.0.146 → 1.0.147
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/bin/mailx.js
CHANGED
|
@@ -488,8 +488,17 @@ async function main() {
|
|
|
488
488
|
console.log("No mailx configuration found.");
|
|
489
489
|
await runSetup();
|
|
490
490
|
}
|
|
491
|
-
//
|
|
492
|
-
|
|
491
|
+
// Redirect console to log file — keep terminal clean
|
|
492
|
+
if (!verbose) {
|
|
493
|
+
const home = process.env.USERPROFILE || process.env.HOME || ".";
|
|
494
|
+
const logDir = path.join(home, ".mailx", "logs");
|
|
495
|
+
fs.mkdirSync(logDir, { recursive: true });
|
|
496
|
+
const logDate = new Date().toISOString().slice(0, 10);
|
|
497
|
+
const logPath = path.join(logDir, `mailx-${logDate}.log`);
|
|
498
|
+
const logStream = fs.createWriteStream(logPath, { flags: "a" });
|
|
499
|
+
console.log = (...a) => { logStream.write(a.join(" ") + "\n"); };
|
|
500
|
+
console.error = (...a) => { logStream.write("ERROR " + a.join(" ") + "\n"); };
|
|
501
|
+
}
|
|
493
502
|
// --server mode: Express + HTTP (for dev/remote access)
|
|
494
503
|
if (serverMode) {
|
|
495
504
|
console.log("Starting mailx HTTP server...");
|
|
@@ -520,20 +529,34 @@ async function main() {
|
|
|
520
529
|
const imapManager = new ImapManager(db);
|
|
521
530
|
imapManager.useNativeClient = true;
|
|
522
531
|
const svc = new MailxService(db, imapManager);
|
|
523
|
-
// Open msger in service mode —
|
|
532
|
+
// Open msger in service mode — custom protocol serves files from client dir
|
|
524
533
|
const clientDir = path.join(import.meta.dirname, "..", "client");
|
|
525
|
-
const indexPath = path.join(clientDir, "index.html");
|
|
526
534
|
const mailxapiPath = path.join(clientDir, "lib", "mailxapi.js");
|
|
527
535
|
const mailxapiScript = fs.readFileSync(mailxapiPath, "utf-8");
|
|
528
536
|
const handle = showService({
|
|
529
|
-
url:
|
|
537
|
+
url: "index.html",
|
|
538
|
+
contentDir: clientDir,
|
|
530
539
|
initScript: mailxapiScript,
|
|
531
540
|
size: { width: 1400, height: 900 },
|
|
541
|
+
escapeCloses: false,
|
|
532
542
|
});
|
|
533
543
|
// Handle requests from WebView → dispatch to MailxService
|
|
544
|
+
// Pass server version to dispatch so getVersion returns it
|
|
545
|
+
const rootPkg = JSON.parse(fs.readFileSync(path.join(import.meta.dirname, "..", "package.json"), "utf-8"));
|
|
534
546
|
handle.onRequest(async (req) => {
|
|
535
|
-
|
|
536
|
-
|
|
547
|
+
if (verbose)
|
|
548
|
+
console.error(`[ipc] ← ${req._action} (${req._cbid})`);
|
|
549
|
+
req._version = rootPkg.version;
|
|
550
|
+
try {
|
|
551
|
+
const response = await dispatch(svc, req);
|
|
552
|
+
if (verbose)
|
|
553
|
+
console.error(`[ipc] → ${req._action} (${req._cbid}) ok`);
|
|
554
|
+
handle.send(response);
|
|
555
|
+
}
|
|
556
|
+
catch (e) {
|
|
557
|
+
console.error(`[ipc] → ${req._action} (${req._cbid}) error: ${e.message}`);
|
|
558
|
+
handle.send({ _cbid: req._cbid, error: e.message });
|
|
559
|
+
}
|
|
537
560
|
});
|
|
538
561
|
// Wire IMAP events → push to WebView
|
|
539
562
|
imapManager.on("syncProgress", (accountId, phase, progress) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobfrankston/mailx",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.147",
|
|
4
4
|
"description": "Local-first email client with IMAP sync and standalone native app",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "bin/mailx.js",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"@bobfrankston/iflow": "^1.0.52",
|
|
24
24
|
"@bobfrankston/miscinfo": "^1.0.7",
|
|
25
25
|
"@bobfrankston/oauthsupport": "^1.0.20",
|
|
26
|
-
"@bobfrankston/msger": "^0.1.
|
|
26
|
+
"@bobfrankston/msger": "^0.1.199",
|
|
27
27
|
"@capacitor/android": "^8.3.0",
|
|
28
28
|
"@capacitor/cli": "^8.3.0",
|
|
29
29
|
"@capacitor/core": "^8.3.0",
|
|
@@ -164,6 +164,7 @@ export declare class ImapManager extends EventEmitter {
|
|
|
164
164
|
processOutbox(accountId: string): Promise<void>;
|
|
165
165
|
/** Start background Outbox worker — runs immediately then every 10 seconds */
|
|
166
166
|
private outboxBackoff;
|
|
167
|
+
private outboxBackoffDelay;
|
|
167
168
|
startOutboxWorker(): void;
|
|
168
169
|
/** Stop Outbox worker */
|
|
169
170
|
stopOutboxWorker(): void;
|
|
@@ -1592,7 +1592,8 @@ export class ImapManager extends EventEmitter {
|
|
|
1592
1592
|
}
|
|
1593
1593
|
}
|
|
1594
1594
|
/** Start background Outbox worker — runs immediately then every 10 seconds */
|
|
1595
|
-
outboxBackoff = new Map(); // accountId → next retry
|
|
1595
|
+
outboxBackoff = new Map(); // accountId → next retry timestamp
|
|
1596
|
+
outboxBackoffDelay = new Map(); // accountId → current delay ms
|
|
1596
1597
|
startOutboxWorker() {
|
|
1597
1598
|
if (this.outboxInterval)
|
|
1598
1599
|
return;
|
|
@@ -1606,12 +1607,15 @@ export class ImapManager extends EventEmitter {
|
|
|
1606
1607
|
try {
|
|
1607
1608
|
await this.processLocalQueue(accountId);
|
|
1608
1609
|
await this.processOutbox(accountId);
|
|
1609
|
-
|
|
1610
|
+
// Success — clear backoff
|
|
1611
|
+
this.outboxBackoff.delete(accountId);
|
|
1612
|
+
this.outboxBackoffDelay.delete(accountId);
|
|
1610
1613
|
}
|
|
1611
1614
|
catch (e) {
|
|
1612
|
-
// Exponential backoff: 30s
|
|
1613
|
-
const
|
|
1614
|
-
const delay =
|
|
1615
|
+
// Exponential backoff: 30s → 60s → 120s → 300s (max 5min)
|
|
1616
|
+
const prevDelay = this.outboxBackoffDelay.get(accountId) || 0;
|
|
1617
|
+
const delay = prevDelay ? Math.min(prevDelay * 2, 300000) : 30000;
|
|
1618
|
+
this.outboxBackoffDelay.set(accountId, delay);
|
|
1615
1619
|
this.outboxBackoff.set(accountId, now + delay);
|
|
1616
1620
|
console.error(` [outbox] Error for ${accountId}: ${imapError(e)} (retry in ${Math.round(delay / 1000)}s)`);
|
|
1617
1621
|
}
|
|
@@ -93,8 +93,11 @@ async function dispatchAction(svc, action, p) {
|
|
|
93
93
|
case "allowRemoteContent":
|
|
94
94
|
svc.allowRemoteContent(p.type, p.value);
|
|
95
95
|
return { ok: true };
|
|
96
|
-
case "getVersion":
|
|
97
|
-
|
|
96
|
+
case "getVersion": {
|
|
97
|
+
const settings = svc.getSettings();
|
|
98
|
+
const storage = svc.getStorageInfo();
|
|
99
|
+
return { version: p._version || "dev", theme: settings.ui?.theme || "system", storage };
|
|
100
|
+
}
|
|
98
101
|
// Autocomplete
|
|
99
102
|
case "autocomplete":
|
|
100
103
|
return svc.autocomplete(p);
|