@bobfrankston/rmfmail 1.1.235 → 1.1.237

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.
@@ -4131,11 +4131,28 @@ export class ImapManager extends EventEmitter {
4131
4131
  const [, original, host, pidStr] = m;
4132
4132
  if (host !== this.hostname) continue;
4133
4133
  const pid = parseInt(pidStr);
4134
- if (pid === myPid) continue; // it's us
4135
- let alive = false;
4136
- try { process.kill(pid, 0); alive = true; } catch { /* dead */ }
4137
4134
  let ageMs = Infinity;
4138
4135
  try { ageMs = Date.now() - fs.statSync(path.join(dir, f)).mtimeMs; } catch { /* */ }
4136
+ if (pid === myPid) {
4137
+ // Our own claim. Normally we're actively sending it — leave
4138
+ // it. But the send is now bounded (60s APPEND timeout), so
4139
+ // an OWN claim older than STALE_CLAIM_MS means the send
4140
+ // hung past its timeout, or a prior tick orphaned it (the
4141
+ // release rename failed). Without reclaiming it the file
4142
+ // sits in `.sending-` forever — recovery used to skip every
4143
+ // own-PID claim unconditionally, so a transient connection
4144
+ // wedge pinned the message even after the link recovered
4145
+ // (Bob 2026-06-11: two messages stuck .sending-<ourpid>
4146
+ // while SELECT Outbox was already succeeding again).
4147
+ if (ageMs < STALE_CLAIM_MS) continue;
4148
+ try {
4149
+ fs.renameSync(path.join(dir, f), path.join(dir, original));
4150
+ console.log(` [outbox] Recovered our own stale claim ${f} → ${original} (hung ${Math.round(ageMs / 60_000)}m)`);
4151
+ } catch { /* ignore */ }
4152
+ continue;
4153
+ }
4154
+ let alive = false;
4155
+ try { process.kill(pid, 0); alive = true; } catch { /* dead */ }
4139
4156
  // Live PID + recent mtime → genuine sibling owner, leave it.
4140
4157
  // Live PID + ancient mtime → recycled PID, sweep. Dead PID → sweep.
4141
4158
  if (alive && ageMs < STALE_CLAIM_MS) continue;
@@ -4261,7 +4278,17 @@ export class ImapManager extends EventEmitter {
4261
4278
  }
4262
4279
  try {
4263
4280
  const raw = fs.readFileSync(claimedPath, "utf-8");
4264
- await client.appendMessage(outboxPath, raw, ["\\Seen"]);
4281
+ // Bound the APPEND. On a wedged connection (Dovecot
4282
+ // ETIMEDOUT storm) the bare await can hang the full
4283
+ // 300s inactivity timeout, pinning the file in
4284
+ // `.sending-` state the whole time and reading to the
4285
+ // user as "stuck, not sending" (Bob 2026-06-11). A
4286
+ // 60s cap force-closes the socket and throws, so the
4287
+ // catch below releases the claim and the next tick
4288
+ // retries instead of hanging for 5 minutes.
4289
+ await withTimeout(
4290
+ client.appendMessage(outboxPath, raw, ["\\Seen"]),
4291
+ 60_000, client, `outbox APPEND ${file}`);
4265
4292
  fs.renameSync(claimedPath, path.join(sentDir, file));
4266
4293
  console.log(` [outbox] Moved ${file} to IMAP Outbox → sent/`);
4267
4294
  } catch (e: any) {
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bobfrankston/mailx-imap",
3
- "version": "0.1.85",
3
+ "version": "0.1.86",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bobfrankston/mailx-imap",
9
- "version": "0.1.85",
9
+ "version": "0.1.86",
10
10
  "license": "ISC",
11
11
  "dependencies": {
12
12
  "@bobfrankston/iflow-direct": "^0.1.27",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/mailx-imap",
3
- "version": "0.1.85",
3
+ "version": "0.1.86",
4
4
  "type": "module",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/mailx-store",
3
- "version": "0.1.45",
3
+ "version": "0.1.46",
4
4
  "type": "module",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/mailx-types",
3
- "version": "0.1.18",
3
+ "version": "0.1.19",
4
4
  "type": "module",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",