@bobfrankston/mailx 1.0.287 → 1.0.289
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
|
@@ -661,9 +661,17 @@ async function runTest() {
|
|
|
661
661
|
console.log("Test complete. Check your inbox for the test message(s).");
|
|
662
662
|
process.exit(0);
|
|
663
663
|
}
|
|
664
|
-
/** Register this client on GDrive — writes/updates clients.jsonc with device info
|
|
664
|
+
/** Register this client on GDrive — writes/updates clients.jsonc with device info.
|
|
665
|
+
* Best-effort: silently skips when cloud isn't ready (fresh install, expired
|
|
666
|
+
* token). The scary "No cloud configured" banner must NOT fire for this. */
|
|
665
667
|
async function registerClient(settings) {
|
|
666
|
-
const { cloudRead, cloudWrite } = await import("@bobfrankston/mailx-settings");
|
|
668
|
+
const { cloudRead, cloudWrite, getStorageInfo } = await import("@bobfrankston/mailx-settings");
|
|
669
|
+
// Check if cloud is actually ready before attempting a write — cloudWrite
|
|
670
|
+
// calls setCloudError which pushes an error banner to the UI. We'd rather
|
|
671
|
+
// silently skip than show "No cloud configured" at startup.
|
|
672
|
+
const info = getStorageInfo();
|
|
673
|
+
if (info.mode === "local" || !info.provider || info.provider === "local")
|
|
674
|
+
return;
|
|
667
675
|
// Device ID: stable per machine, stored locally
|
|
668
676
|
const deviceIdPath = path.join(os.homedir(), ".mailx", "device-id");
|
|
669
677
|
let deviceId;
|
package/package.json
CHANGED
|
@@ -58,18 +58,25 @@ async function getGoogleDriveToken() {
|
|
|
58
58
|
console.error(" [cloud] No Google credentials found (checked ~/.mailx/google-credentials.json and iflow package)");
|
|
59
59
|
return null;
|
|
60
60
|
}
|
|
61
|
-
// Delete stale token if scope changed (drive → drive.file or vice versa)
|
|
62
61
|
const tokenPath = path.join(GDRIVE_TOKEN_DIR, "token.json");
|
|
62
|
+
// Log token state so failures are diagnosable
|
|
63
63
|
if (fs.existsSync(tokenPath)) {
|
|
64
64
|
try {
|
|
65
65
|
const existing = JSON.parse(fs.readFileSync(tokenPath, "utf-8"));
|
|
66
|
+
const expired = existing.expires_at ? Date.now() >= existing.expires_at : true;
|
|
67
|
+
const hasRefresh = !!existing.refresh_token;
|
|
68
|
+
console.log(` [cloud] GDrive token: ${expired ? "expired" : "valid"}, refresh_token: ${hasRefresh ? "yes" : "NO"}, scope: ${existing.scope || "?"}`);
|
|
69
|
+
// Delete stale token if scope changed (drive → drive.file or vice versa)
|
|
66
70
|
if (existing.scope && existing.scope !== GDRIVE_SCOPES) {
|
|
67
|
-
console.log(` [cloud] Scope changed — re-authenticating...`);
|
|
71
|
+
console.log(` [cloud] Scope changed (${existing.scope} → ${GDRIVE_SCOPES}) — re-authenticating...`);
|
|
68
72
|
fs.unlinkSync(tokenPath);
|
|
69
73
|
}
|
|
70
74
|
}
|
|
71
75
|
catch { /* ignore parse errors */ }
|
|
72
76
|
}
|
|
77
|
+
else {
|
|
78
|
+
console.log(` [cloud] GDrive token: not found at ${tokenPath}`);
|
|
79
|
+
}
|
|
73
80
|
try {
|
|
74
81
|
const token = await authenticateOAuth(creds, {
|
|
75
82
|
scope: GDRIVE_SCOPES,
|
|
@@ -78,10 +85,16 @@ async function getGoogleDriveToken() {
|
|
|
78
85
|
credentialsKey: "installed",
|
|
79
86
|
includeOfflineAccess: true,
|
|
80
87
|
});
|
|
88
|
+
if (token?.access_token) {
|
|
89
|
+
console.log(` [cloud] GDrive auth: success (token refreshed/valid)`);
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
console.error(` [cloud] GDrive auth: returned null — OAuth callback likely timed out or was denied`);
|
|
93
|
+
}
|
|
81
94
|
return token?.access_token || null;
|
|
82
95
|
}
|
|
83
96
|
catch (e) {
|
|
84
|
-
console.error(` [cloud]
|
|
97
|
+
console.error(` [cloud] GDrive auth FAILED: ${e.message}`);
|
|
85
98
|
return null;
|
|
86
99
|
}
|
|
87
100
|
}
|
|
@@ -145,7 +145,8 @@ export async function cloudRead(filename) {
|
|
|
145
145
|
* and the onCloudError listener. */
|
|
146
146
|
export async function cloudWrite(filename, content) {
|
|
147
147
|
if (!pendingCloudConfig) {
|
|
148
|
-
const err = `
|
|
148
|
+
const err = `Cloud not initialized yet — cannot save ${filename} (pendingCloudConfig is null; loadSettings may not have run, or config.jsonc has no sharedDir)`;
|
|
149
|
+
console.error(` [cloud] cloudWrite: ${err}`);
|
|
149
150
|
setCloudError(err, { op: "write", filename });
|
|
150
151
|
throw new Error(err);
|
|
151
152
|
}
|