@bobfrankston/mailx 1.0.140 → 1.0.142
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
|
@@ -31,7 +31,7 @@ const addMode = hasFlag("add");
|
|
|
31
31
|
const testMode = hasFlag("test");
|
|
32
32
|
const rebuildMode = hasFlag("rebuild");
|
|
33
33
|
// Validate arguments
|
|
34
|
-
const knownFlags = ["server", "no-browser", "verbose", "external", "kill", "v", "version", "setup", "add", "test", "rebuild", "native-imap"];
|
|
34
|
+
const knownFlags = ["server", "no-browser", "verbose", "external", "kill", "v", "version", "setup", "add", "test", "rebuild", "native-imap", "log"];
|
|
35
35
|
for (const arg of args) {
|
|
36
36
|
const flag = arg.replace(/^--?/, "");
|
|
37
37
|
if (arg.startsWith("-") && !knownFlags.includes(flag)) {
|
|
@@ -172,7 +172,7 @@ function isPortInUse(port) {
|
|
|
172
172
|
}
|
|
173
173
|
/** Launch msger pointing at the server URL */
|
|
174
174
|
function launchMsger(url) {
|
|
175
|
-
showMessageBox({ url, detach: true });
|
|
175
|
+
showMessageBox({ url, detach: true, size: { width: 1400, height: 900 } });
|
|
176
176
|
}
|
|
177
177
|
async function prompt(question) {
|
|
178
178
|
const readline = await import("readline");
|
|
@@ -491,16 +491,27 @@ async function main() {
|
|
|
491
491
|
// Check if server is already running
|
|
492
492
|
const inUse = await isPortInUse(PORT);
|
|
493
493
|
if (inUse) {
|
|
494
|
-
console.log(`mailx server already running on port ${PORT}`);
|
|
495
494
|
const url = `http://127.0.0.1:${PORT}`;
|
|
496
|
-
if (!noBrowser)
|
|
495
|
+
if (!noBrowser)
|
|
497
496
|
launchMsger(url);
|
|
498
|
-
}
|
|
499
497
|
return;
|
|
500
498
|
}
|
|
499
|
+
// Redirect console to log file — keep terminal clean
|
|
500
|
+
// Server also logs, but this catches CLI startup messages
|
|
501
|
+
if (!verbose) {
|
|
502
|
+
const home = process.env.USERPROFILE || process.env.HOME || ".";
|
|
503
|
+
const logDir = path.join(home, ".mailx", "logs");
|
|
504
|
+
fs.mkdirSync(logDir, { recursive: true });
|
|
505
|
+
const logDate = new Date().toISOString().slice(0, 10);
|
|
506
|
+
const logPath = path.join(logDir, `mailx-${logDate}.log`);
|
|
507
|
+
const logStream = fs.createWriteStream(logPath, { flags: "a" });
|
|
508
|
+
const origLog = console.log;
|
|
509
|
+
const origErr = console.error;
|
|
510
|
+
console.log = (...a) => { logStream.write(a.join(" ") + "\n"); };
|
|
511
|
+
console.error = (...a) => { logStream.write("ERROR " + a.join(" ") + "\n"); };
|
|
512
|
+
}
|
|
501
513
|
// Start Express server in-process
|
|
502
514
|
console.log("Starting mailx server...");
|
|
503
|
-
log(`Loading server from: ${path.join(import.meta.dirname, "..", "packages", "mailx-server", "index.js")}`);
|
|
504
515
|
if (hasFlag("external"))
|
|
505
516
|
process.argv.push("--external");
|
|
506
517
|
await import("../packages/mailx-server/index.js");
|
|
@@ -513,7 +524,6 @@ async function main() {
|
|
|
513
524
|
break;
|
|
514
525
|
}
|
|
515
526
|
launchMsger(url);
|
|
516
|
-
console.log("mailx opened");
|
|
517
527
|
}
|
|
518
528
|
// Keep process alive — server is running
|
|
519
529
|
await new Promise(() => { });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobfrankston/mailx",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.142",
|
|
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.196",
|
|
27
27
|
"@capacitor/android": "^8.3.0",
|
|
28
28
|
"@capacitor/cli": "^8.3.0",
|
|
29
29
|
"@capacitor/core": "^8.3.0",
|
|
@@ -150,7 +150,31 @@ export function createApiRouter(db, imapManager) {
|
|
|
150
150
|
if (detected?.cloud) {
|
|
151
151
|
await initCloudConfig(detected.cloud);
|
|
152
152
|
}
|
|
153
|
-
//
|
|
153
|
+
// Check if cloud config found existing accounts (e.g., from GDrive mount or API)
|
|
154
|
+
let accounts = loadAccounts();
|
|
155
|
+
if (accounts.length === 0) {
|
|
156
|
+
accounts = await loadAccountsAsync();
|
|
157
|
+
}
|
|
158
|
+
if (accounts.length > 0) {
|
|
159
|
+
// Existing accounts found on cloud — use them, don't create new
|
|
160
|
+
console.log(` Found ${accounts.length} existing account(s) from cloud settings`);
|
|
161
|
+
const settings = loadSettings();
|
|
162
|
+
for (const acct of settings.accounts) {
|
|
163
|
+
if (!acct.enabled)
|
|
164
|
+
continue;
|
|
165
|
+
try {
|
|
166
|
+
await imapManager.addAccount(acct);
|
|
167
|
+
console.log(` Account loaded: ${acct.label || acct.name} (${acct.id})`);
|
|
168
|
+
}
|
|
169
|
+
catch (e) {
|
|
170
|
+
console.error(` Account ${acct.id} error: ${e.message}`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
imapManager.syncAll().catch(() => { });
|
|
174
|
+
res.json({ ok: true, message: `Loaded ${accounts.length} existing account(s) from cloud settings.` });
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
// No existing accounts — build new account config
|
|
154
178
|
const account = { email, name: name || email.split("@")[0] };
|
|
155
179
|
if (password)
|
|
156
180
|
account.password = password;
|
|
@@ -159,11 +183,6 @@ export function createApiRouter(db, imapManager) {
|
|
|
159
183
|
account.imap = { host: detected.imapHost, port: 993, tls: true, auth: detected.auth, user: email };
|
|
160
184
|
account.smtp = { host: detected.smtpHost, port: 587, tls: true, auth: detected.auth, user: email };
|
|
161
185
|
}
|
|
162
|
-
// Load existing accounts — try cloud API in case home/.mailx already exists on Drive
|
|
163
|
-
let accounts = loadAccounts();
|
|
164
|
-
if (accounts.length === 0) {
|
|
165
|
-
accounts = await loadAccountsAsync();
|
|
166
|
-
}
|
|
167
186
|
const id = domain.split(".")[0] || "account";
|
|
168
187
|
if (accounts.some((a) => a.email === email)) {
|
|
169
188
|
res.json({ ok: false, error: "Account already exists" });
|
|
@@ -91,7 +91,7 @@ export { getSharedDir };
|
|
|
91
91
|
/** Initialize local config if it doesn't exist */
|
|
92
92
|
export declare function initLocalConfig(sharedDir?: string, storePath?: string): void;
|
|
93
93
|
/** Initialize config with Google Drive cloud storage.
|
|
94
|
-
*
|
|
94
|
+
* Checks for existing settings on GDrive mount first, then falls back to API folder. */
|
|
95
95
|
export declare function initCloudConfig(provider?: "gdrive"): Promise<void>;
|
|
96
96
|
declare const DEFAULT_SETTINGS: MailxSettings;
|
|
97
97
|
/** Get historyDays for an account: per-account override > system override > shared default */
|
|
@@ -548,22 +548,33 @@ export function initLocalConfig(sharedDir, storePath) {
|
|
|
548
548
|
atomicWrite(LOCAL_CONFIG_PATH, config);
|
|
549
549
|
}
|
|
550
550
|
/** Initialize config with Google Drive cloud storage.
|
|
551
|
-
*
|
|
551
|
+
* Checks for existing settings on GDrive mount first, then falls back to API folder. */
|
|
552
552
|
export async function initCloudConfig(provider = "gdrive") {
|
|
553
553
|
const existing = readLocalConfig();
|
|
554
554
|
if (existing.sharedDir)
|
|
555
555
|
return; // Already configured
|
|
556
|
-
//
|
|
556
|
+
// Check if settings already exist on a GDrive mount (e.g., home/.mailx from a previous setup)
|
|
557
|
+
const home = process.env.USERPROFILE || process.env.HOME || "";
|
|
558
|
+
const mountPaths = [
|
|
559
|
+
path.join(home, "Google Drive", "My Drive", "home", ".mailx"),
|
|
560
|
+
path.join(home, "Google Drive Streaming", "My Drive", "home", ".mailx"),
|
|
561
|
+
];
|
|
562
|
+
for (const mountPath of mountPaths) {
|
|
563
|
+
if (fs.existsSync(path.join(mountPath, "accounts.jsonc")) || fs.existsSync(path.join(mountPath, "settings.jsonc"))) {
|
|
564
|
+
console.log(` Found existing settings on GDrive mount: ${mountPath}`);
|
|
565
|
+
const sharedDir = { provider, path: "home/.mailx" };
|
|
566
|
+
const config = { ...existing, sharedDir, storePath: existing.storePath || DEFAULT_STORE_PATH };
|
|
567
|
+
fs.mkdirSync(LOCAL_DIR, { recursive: true });
|
|
568
|
+
atomicWrite(LOCAL_CONFIG_PATH, config);
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
// No mount — find or create the "mailx" folder via Drive API
|
|
557
573
|
const folderId = await gDriveFindOrCreateFolder();
|
|
558
574
|
const sharedDir = { provider, path: "mailx", folderId: folderId || undefined };
|
|
559
|
-
const config = {
|
|
560
|
-
...existing,
|
|
561
|
-
sharedDir: sharedDir,
|
|
562
|
-
storePath: existing.storePath || DEFAULT_STORE_PATH,
|
|
563
|
-
};
|
|
575
|
+
const config = { ...existing, sharedDir, storePath: existing.storePath || DEFAULT_STORE_PATH };
|
|
564
576
|
fs.mkdirSync(LOCAL_DIR, { recursive: true });
|
|
565
577
|
atomicWrite(LOCAL_CONFIG_PATH, config);
|
|
566
|
-
// Set up cloud API fallback immediately
|
|
567
578
|
pendingCloudConfig = sharedDir;
|
|
568
579
|
console.log(` Initialized cloud config: ${provider} (folder ID: ${folderId || "pending"})`);
|
|
569
580
|
}
|