@bobfrankston/rmfmail 1.2.136 → 1.2.139
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 +79 -7
- package/bin/mailx.js.map +1 -1
- package/bin/mailx.ts +75 -7
- package/client/app.bundle.js +40 -5
- package/client/app.bundle.js.map +3 -3
- package/client/app.js +31 -0
- package/client/app.js.map +1 -1
- package/client/app.ts +24 -0
- package/client/components/message-list.js +30 -4
- package/client/components/message-list.js.map +1 -1
- package/client/components/message-list.ts +30 -4
- package/client/compose/compose.bundle.js +4 -0
- package/client/compose/compose.bundle.js.map +2 -2
- package/client/lib/api-client.js +5 -0
- package/client/lib/api-client.js.map +1 -1
- package/client/lib/api-client.ts +6 -0
- package/client/lib/mailxapi.js +4 -0
- package/package.json +1 -1
- package/packages/mailx-service/jsonrpc.d.ts +1 -0
- package/packages/mailx-service/jsonrpc.d.ts.map +1 -1
- package/packages/mailx-service/jsonrpc.js +15 -0
- package/packages/mailx-service/jsonrpc.js.map +1 -1
- package/packages/mailx-service/jsonrpc.ts +17 -0
- package/packages/mailx-store/db.d.ts.map +1 -1
- package/packages/mailx-store/db.js +28 -2
- package/packages/mailx-store/db.js.map +1 -1
- package/packages/mailx-store/db.ts +27 -2
package/bin/mailx.js
CHANGED
|
@@ -1830,10 +1830,31 @@ async function main() {
|
|
|
1830
1830
|
const d = new Date();
|
|
1831
1831
|
return `${pad2(d.getHours())}:${pad2(d.getMinutes())}:${pad2(d.getSeconds())}.${pad3(d.getMilliseconds())}`;
|
|
1832
1832
|
};
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1833
|
+
// Day-aware stream: recompute the filename on every write and swap
|
|
1834
|
+
// the stream at midnight. The old code resolved the path ONCE at
|
|
1835
|
+
// boot, so a daemon that ran past midnight kept appending to the
|
|
1836
|
+
// boot-day file forever — while the sync worker's lines (written
|
|
1837
|
+
// through its own channel) landed in the new day's file. Result: the
|
|
1838
|
+
// morning of 2026-07-14 had ALL main-thread diagnostics ([ipc],
|
|
1839
|
+
// [client], [popout]) hiding in rmfmail-2026-07-13.log and the
|
|
1840
|
+
// current file showing only [w] worker lines — which made the
|
|
1841
|
+
// popout-send data-loss incident look untraceable (S68).
|
|
1842
|
+
let logDate = localDate();
|
|
1843
|
+
let logStream = fs.createWriteStream(path.join(logDir, `rmfmail-${logDate}.log`), { flags: "a" });
|
|
1844
|
+
const logWrite = (line) => {
|
|
1845
|
+
const d = localDate();
|
|
1846
|
+
if (d !== logDate) {
|
|
1847
|
+
try {
|
|
1848
|
+
logStream.end();
|
|
1849
|
+
}
|
|
1850
|
+
catch { /* old handle already dead */ }
|
|
1851
|
+
logDate = d;
|
|
1852
|
+
logStream = fs.createWriteStream(path.join(logDir, `rmfmail-${d}.log`), { flags: "a" });
|
|
1853
|
+
}
|
|
1854
|
+
logStream.write(line);
|
|
1855
|
+
};
|
|
1856
|
+
console.log = (...a) => { logWrite(`${ts()} ${a.join(" ")}\n`); };
|
|
1857
|
+
console.error = (...a) => { logWrite(`${ts()} ERROR ${a.join(" ")}\n`); };
|
|
1837
1858
|
// Redirect daemon's process.stderr to the log too. msger forwards its
|
|
1838
1859
|
// Rust child's stderr to our process.stderr; in daemon mode that's
|
|
1839
1860
|
// stdio:"ignore" → /dev/null, which buries diagnostics like
|
|
@@ -1843,7 +1864,7 @@ async function main() {
|
|
|
1843
1864
|
process.stderr.write = ((chunk, ...rest) => {
|
|
1844
1865
|
try {
|
|
1845
1866
|
const s = typeof chunk === "string" ? chunk : chunk.toString();
|
|
1846
|
-
|
|
1867
|
+
logWrite(`${ts()} STDERR ${s}${s.endsWith("\n") ? "" : "\n"}`);
|
|
1847
1868
|
}
|
|
1848
1869
|
catch { /* ignore — best-effort logging */ }
|
|
1849
1870
|
return origStderrWrite(chunk, ...rest);
|
|
@@ -1858,7 +1879,7 @@ async function main() {
|
|
|
1858
1879
|
// for nothing — each was independent. Same for node-tcp-transport
|
|
1859
1880
|
// and mailx-store/file-store.js further down; folded in here so the
|
|
1860
1881
|
// resolution + module-init cost happens once, in parallel.
|
|
1861
|
-
const [{ MailxDB, prewarmParseWorker, storeBus, Store }, { ImapManager }, { MailxService, spawnSyncWorker }, { dispatch }, { loadSettings, loadAccountsAsync, loadAllowlistAsync, getConfigDir, getStorageInfo, getStorePath }, { NodeTcpTransport }, { FileMessageStore },] = await Promise.all([
|
|
1882
|
+
const [{ MailxDB, prewarmParseWorker, storeBus, Store }, { ImapManager }, { MailxService, spawnSyncWorker }, { dispatch, setDebugEvalSink }, { loadSettings, loadAccountsAsync, loadAllowlistAsync, getConfigDir, getStorageInfo, getStorePath }, { NodeTcpTransport }, { FileMessageStore },] = await Promise.all([
|
|
1862
1883
|
import("@bobfrankston/mailx-store"),
|
|
1863
1884
|
import("@bobfrankston/mailx-imap"),
|
|
1864
1885
|
import("@bobfrankston/mailx-service"),
|
|
@@ -2089,7 +2110,10 @@ RFC 5322 with CRLF line endings. Bodies are quoted-printable encoded (readable i
|
|
|
2089
2110
|
// see memory: feedback_cross_platform_strong_rule). Remove on
|
|
2090
2111
|
// resolution so the file doesn't accumulate stale PIDs.
|
|
2091
2112
|
svc.setPopupFn(async (opts) => {
|
|
2092
|
-
|
|
2113
|
+
// Own WebView2 profile so a reminder popup can never share (and
|
|
2114
|
+
// crash) the main window's browser process — see S67 note on the
|
|
2115
|
+
// popout windows below. Service-supplied opts may override.
|
|
2116
|
+
const h = showMessageBoxEx({ profile: "popout", ...opts });
|
|
2093
2117
|
if (typeof h.pid === "number")
|
|
2094
2118
|
addChildPid(h.pid);
|
|
2095
2119
|
try {
|
|
@@ -2116,6 +2140,41 @@ RFC 5322 with CRLF line endings. Bodies are quoted-printable encoded (readable i
|
|
|
2116
2140
|
const { ports } = await import("@bobfrankston/miscinfo");
|
|
2117
2141
|
const dbgApp = express();
|
|
2118
2142
|
dbgApp.use(express.json({ limit: "25mb" }));
|
|
2143
|
+
// Live-page eval: POST /api/eval {code} → daemon pushes a
|
|
2144
|
+
// `debugEval` event to the main window → app.ts runs the code and
|
|
2145
|
+
// answers via the `debugEvalResult` action → we reply here. The
|
|
2146
|
+
// expression may be async (it's wrapped in `(async () => (…))()`),
|
|
2147
|
+
// must evaluate as an EXPRESSION, and should return JSON-able
|
|
2148
|
+
// data. Lets an agent inspect the real DOM (elementFromPoint,
|
|
2149
|
+
// computed styles, scroll geometry) without devtools. Registered
|
|
2150
|
+
// BEFORE the generic /api router so it isn't shadowed.
|
|
2151
|
+
const pendingEvals = new Map();
|
|
2152
|
+
setDebugEvalSink((id, result, error) => {
|
|
2153
|
+
const p = pendingEvals.get(id);
|
|
2154
|
+
if (!p)
|
|
2155
|
+
return;
|
|
2156
|
+
pendingEvals.delete(id);
|
|
2157
|
+
clearTimeout(p.timer);
|
|
2158
|
+
p.finish(error ? { error } : { result });
|
|
2159
|
+
});
|
|
2160
|
+
dbgApp.post("/api/eval", (req, res) => {
|
|
2161
|
+
const code = req.body?.code;
|
|
2162
|
+
if (typeof code !== "string" || !code.trim()) {
|
|
2163
|
+
res.status(400).json({ error: "body must be { code: string } — a JS expression" });
|
|
2164
|
+
return;
|
|
2165
|
+
}
|
|
2166
|
+
if (!sendToClient) {
|
|
2167
|
+
res.status(503).json({ error: "main window not connected yet" });
|
|
2168
|
+
return;
|
|
2169
|
+
}
|
|
2170
|
+
const id = `ev-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
2171
|
+
const timer = setTimeout(() => {
|
|
2172
|
+
pendingEvals.delete(id);
|
|
2173
|
+
res.status(504).json({ error: "eval timeout (10s) — window busy, or client bundle predates debugEval" });
|
|
2174
|
+
}, 10_000);
|
|
2175
|
+
pendingEvals.set(id, { finish: (v) => res.json(v), timer });
|
|
2176
|
+
sendToClient({ _event: "debugEval", type: "debugEval", id, code });
|
|
2177
|
+
});
|
|
2119
2178
|
dbgApp.use("/api", createApiRouter(store, imapManager));
|
|
2120
2179
|
// Port: `--debug-server=<n>` / `--debug-server <n>` if given,
|
|
2121
2180
|
// else the reserved `ports.rmfmaildbg` from miscinfo (the named
|
|
@@ -2205,6 +2264,13 @@ RFC 5322 with CRLF line endings. Bodies are quoted-printable encoded (readable i
|
|
|
2205
2264
|
// window shows unactivated BEHIND the focused main window
|
|
2206
2265
|
// and reads as "nothing opened" (Bob 2026-07-09).
|
|
2207
2266
|
focusOnCreate: true,
|
|
2267
|
+
// Own WebView2 profile: a window sharing the main window's
|
|
2268
|
+
// user-data dir shares its browser process, and a crash
|
|
2269
|
+
// there blacks out the MAIN window permanently (S67,
|
|
2270
|
+
// 2026-07-14 + 2026-07-15: BROWSER_PROCESS_EXITED during
|
|
2271
|
+
// popout spawn). One shared "popout" profile for all
|
|
2272
|
+
// secondary windows — decoupled from main, bounded disk.
|
|
2273
|
+
profile: "popout",
|
|
2208
2274
|
});
|
|
2209
2275
|
popoutWindows.set(key, h);
|
|
2210
2276
|
if (typeof h.pid === "number")
|
|
@@ -2239,6 +2305,8 @@ RFC 5322 with CRLF line endings. Bodies are quoted-printable encoded (readable i
|
|
|
2239
2305
|
// Same as the message popout: the daemon is background,
|
|
2240
2306
|
// so an unactivated window lands behind the main window.
|
|
2241
2307
|
focusOnCreate: true,
|
|
2308
|
+
// Own profile — same S67 rationale as the message popout.
|
|
2309
|
+
profile: "popout",
|
|
2242
2310
|
// App entries on the NATIVE right-click menu (native stays
|
|
2243
2311
|
// so spellcheck suggestions survive). compose.ts maps ids
|
|
2244
2312
|
// to editor commands via window.__msgerContextCommand.
|
|
@@ -2294,6 +2362,10 @@ RFC 5322 with CRLF line endings. Bodies are quoted-printable encoded (readable i
|
|
|
2294
2362
|
aumid: "com.frankston.rmfmail",
|
|
2295
2363
|
escapeCloses: false,
|
|
2296
2364
|
focusOnCreate: true,
|
|
2365
|
+
// Own profile — same S67 rationale as the message popout.
|
|
2366
|
+
// Note: localStorage view prefs won't be shared with the
|
|
2367
|
+
// main window; preferences.jsonc (the real store) is.
|
|
2368
|
+
profile: "popout",
|
|
2297
2369
|
});
|
|
2298
2370
|
popoutWindows.set(key, h);
|
|
2299
2371
|
if (typeof h.pid === "number")
|