fluxmail 0.0.1 → 0.2.0

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.
Files changed (92) hide show
  1. package/README.md +37 -10
  2. package/dist/accounts/googleAuth.d.ts +31 -0
  3. package/dist/accounts/googleAuth.d.ts.map +1 -0
  4. package/dist/accounts/googleAuth.js +147 -0
  5. package/dist/accounts/googleAuth.js.map +1 -0
  6. package/dist/accounts/registry.d.ts +37 -0
  7. package/dist/accounts/registry.d.ts.map +1 -0
  8. package/dist/accounts/registry.js +278 -0
  9. package/dist/accounts/registry.js.map +1 -0
  10. package/dist/cli.d.ts +3 -0
  11. package/dist/cli.d.ts.map +1 -0
  12. package/dist/cli.js +629 -0
  13. package/dist/cli.js.map +1 -0
  14. package/dist/config.d.ts +40 -0
  15. package/dist/config.d.ts.map +1 -0
  16. package/dist/config.js +216 -0
  17. package/dist/config.js.map +1 -0
  18. package/dist/context.d.ts +15 -0
  19. package/dist/context.d.ts.map +1 -0
  20. package/dist/context.js +15 -0
  21. package/dist/context.js.map +1 -0
  22. package/dist/http/app.d.ts +16 -0
  23. package/dist/http/app.d.ts.map +1 -0
  24. package/dist/http/app.js +118 -0
  25. package/dist/http/app.js.map +1 -0
  26. package/dist/index.d.ts +14 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +14 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/licensing/activation.d.ts +5 -0
  31. package/dist/licensing/activation.d.ts.map +1 -0
  32. package/dist/licensing/activation.js +11 -0
  33. package/dist/licensing/activation.js.map +1 -0
  34. package/dist/licensing/client.d.ts +52 -0
  35. package/dist/licensing/client.d.ts.map +1 -0
  36. package/dist/licensing/client.js +75 -0
  37. package/dist/licensing/client.js.map +1 -0
  38. package/dist/licensing/entitlements.d.ts +55 -0
  39. package/dist/licensing/entitlements.d.ts.map +1 -0
  40. package/dist/licensing/entitlements.js +116 -0
  41. package/dist/licensing/entitlements.js.map +1 -0
  42. package/dist/licensing/lease.d.ts +47 -0
  43. package/dist/licensing/lease.d.ts.map +1 -0
  44. package/dist/licensing/lease.js +103 -0
  45. package/dist/licensing/lease.js.map +1 -0
  46. package/dist/licensing/refresher.d.ts +46 -0
  47. package/dist/licensing/refresher.d.ts.map +1 -0
  48. package/dist/licensing/refresher.js +150 -0
  49. package/dist/licensing/refresher.js.map +1 -0
  50. package/dist/mcp/buildServer.d.ts +28 -0
  51. package/dist/mcp/buildServer.d.ts.map +1 -0
  52. package/dist/mcp/buildServer.js +414 -0
  53. package/dist/mcp/buildServer.js.map +1 -0
  54. package/dist/scheduler/sendScheduler.d.ts +38 -0
  55. package/dist/scheduler/sendScheduler.d.ts.map +1 -0
  56. package/dist/scheduler/sendScheduler.js +128 -0
  57. package/dist/scheduler/sendScheduler.js.map +1 -0
  58. package/dist/service/emailService.d.ts +139 -0
  59. package/dist/service/emailService.d.ts.map +1 -0
  60. package/dist/service/emailService.js +380 -0
  61. package/dist/service/emailService.js.map +1 -0
  62. package/dist/storage/apiKeys.d.ts +27 -0
  63. package/dist/storage/apiKeys.d.ts.map +1 -0
  64. package/dist/storage/apiKeys.js +49 -0
  65. package/dist/storage/apiKeys.js.map +1 -0
  66. package/dist/storage/crypto.d.ts +4 -0
  67. package/dist/storage/crypto.d.ts.map +1 -0
  68. package/dist/storage/crypto.js +21 -0
  69. package/dist/storage/crypto.js.map +1 -0
  70. package/dist/storage/db.d.ts +1041 -0
  71. package/dist/storage/db.d.ts.map +1 -0
  72. package/dist/storage/db.js +211 -0
  73. package/dist/storage/db.js.map +1 -0
  74. package/dist/storage/imapState.d.ts +15 -0
  75. package/dist/storage/imapState.d.ts.map +1 -0
  76. package/dist/storage/imapState.js +89 -0
  77. package/dist/storage/imapState.js.map +1 -0
  78. package/dist/storage/members.d.ts +29 -0
  79. package/dist/storage/members.d.ts.map +1 -0
  80. package/dist/storage/members.js +67 -0
  81. package/dist/storage/members.js.map +1 -0
  82. package/dist/storage/scheduledSends.d.ts +60 -0
  83. package/dist/storage/scheduledSends.d.ts.map +1 -0
  84. package/dist/storage/scheduledSends.js +169 -0
  85. package/dist/storage/scheduledSends.js.map +1 -0
  86. package/dist/version.d.ts +2 -0
  87. package/dist/version.d.ts.map +1 -0
  88. package/dist/version.js +7 -0
  89. package/dist/version.js.map +1 -0
  90. package/package.json +49 -15
  91. package/bin/fluxmail.js +0 -4
  92. package/src/cli.js +0 -36
@@ -0,0 +1,38 @@
1
+ import { type SendResult } from '@fluxmail/core';
2
+ import type { FluxmailDb } from '../storage/db.js';
3
+ /** The slice of EmailService the scheduler needs. */
4
+ export interface ScheduledSender {
5
+ send(accountId: string | undefined, input: {
6
+ draftId: string;
7
+ }): Promise<SendResult>;
8
+ /** Throws while a lapsed license leaves the instance over the plan quota. */
9
+ enforceQuota(): string | undefined;
10
+ }
11
+ /**
12
+ * Fires scheduled sends when their time comes. Owned by the long-lived
13
+ * processes (serve/stdio); one-shot CLI commands construct it but never
14
+ * start() it. Assumes a single long-lived process per database; there is
15
+ * no cross-process locking.
16
+ */
17
+ export declare class SendScheduler {
18
+ private readonly db;
19
+ private readonly service;
20
+ private timer;
21
+ private running;
22
+ private processing;
23
+ private rerun;
24
+ /** Retry gates for transient failures; in-memory only (a restart just retries immediately). */
25
+ private backoff;
26
+ /** While over the plan quota, due sends are held and re-checked no sooner than this. */
27
+ private quotaHoldUntil;
28
+ constructor(db: FluxmailDb, service: ScheduledSender);
29
+ /** Starts the loop; immediately fires anything past due (catch-up after downtime). */
30
+ start(): void;
31
+ stop(): void;
32
+ /** Poke after any schedule change (new, canceled, or sent early). */
33
+ wake(): void;
34
+ private tick;
35
+ private fire;
36
+ private arm;
37
+ }
38
+ //# sourceMappingURL=sendScheduler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sendScheduler.d.ts","sourceRoot":"","sources":["../../src/scheduler/sendScheduler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC/D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAYnD,qDAAqD;AACrD,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IACrF,6EAA6E;IAC7E,YAAY,IAAI,MAAM,GAAG,SAAS,CAAC;CACpC;AAUD;;;;;GAKG;AACH,qBAAa,aAAa;IAWtB,OAAO,CAAC,QAAQ,CAAC,EAAE;IACnB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAX1B,OAAO,CAAC,KAAK,CAA6B;IAC1C,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,KAAK,CAAS;IACtB,+FAA+F;IAC/F,OAAO,CAAC,OAAO,CAA6D;IAC5E,wFAAwF;IACxF,OAAO,CAAC,cAAc,CAAK;gBAGR,EAAE,EAAE,UAAU,EACd,OAAO,EAAE,eAAe;IAG3C,sFAAsF;IACtF,KAAK,IAAI,IAAI;IAKb,IAAI,IAAI,IAAI;IAMZ,qEAAqE;IACrE,IAAI,IAAI,IAAI;YAIE,IAAI;YAqCJ,IAAI;IAgClB,OAAO,CAAC,GAAG;CAaZ"}
@@ -0,0 +1,128 @@
1
+ import { isEmailError } from '@fluxmail/core';
2
+ import { claimScheduledSend, completeClaim, failClaim, holdPending, listActive, listClaimable, retryClaim, } from '../storage/scheduledSends.js';
3
+ /** Re-check at least this often while anything is pending (heals clock jumps and laptop sleep). */
4
+ const MAX_ARM_MS = 15 * 60_000;
5
+ const BASE_BACKOFF_MS = 30_000;
6
+ const CLAIM_LEASE_MS = 5 * 60_000;
7
+ /** Errors that no amount of retrying will fix (e.g. the draft is gone). */
8
+ const PERMANENT_CODES = new Set(['not_found', 'invalid_request', 'unsupported_capability']);
9
+ /**
10
+ * Fires scheduled sends when their time comes. Owned by the long-lived
11
+ * processes (serve/stdio); one-shot CLI commands construct it but never
12
+ * start() it. Assumes a single long-lived process per database; there is
13
+ * no cross-process locking.
14
+ */
15
+ export class SendScheduler {
16
+ db;
17
+ service;
18
+ timer;
19
+ running = false;
20
+ processing = false;
21
+ rerun = false;
22
+ /** Retry gates for transient failures; in-memory only (a restart just retries immediately). */
23
+ backoff = new Map();
24
+ /** While over the plan quota, due sends are held and re-checked no sooner than this. */
25
+ quotaHoldUntil = 0;
26
+ constructor(db, service) {
27
+ this.db = db;
28
+ this.service = service;
29
+ }
30
+ /** Starts the loop; immediately fires anything past due (catch-up after downtime). */
31
+ start() {
32
+ this.running = true;
33
+ void this.tick();
34
+ }
35
+ stop() {
36
+ this.running = false;
37
+ if (this.timer)
38
+ clearTimeout(this.timer);
39
+ this.timer = undefined;
40
+ }
41
+ /** Poke after any schedule change (new, canceled, or sent early). */
42
+ wake() {
43
+ if (this.running)
44
+ void this.tick();
45
+ }
46
+ async tick() {
47
+ if (!this.running)
48
+ return;
49
+ if (this.processing) {
50
+ this.rerun = true;
51
+ return;
52
+ }
53
+ this.processing = true;
54
+ try {
55
+ const now = Date.now();
56
+ let due = listClaimable(this.db, now)
57
+ .filter((r) => r.sendAt <= now && (this.backoff.get(r.id)?.notBefore ?? 0) <= now)
58
+ .sort((a, b) => a.sendAt - b.sendAt);
59
+ if (due.length) {
60
+ try {
61
+ this.service.enforceQuota();
62
+ this.quotaHoldUntil = 0;
63
+ }
64
+ catch (err) {
65
+ // Over quota after a license lapse: hold due sends (pending, no
66
+ // attempt consumed) and re-check later; they fire once the license
67
+ // is renewed or usage is trimmed to fit the plan.
68
+ this.quotaHoldUntil = now + MAX_ARM_MS;
69
+ const message = err instanceof Error ? err.message : String(err);
70
+ for (const row of due)
71
+ holdPending(this.db, row.id, message);
72
+ due = [];
73
+ }
74
+ }
75
+ for (const row of due)
76
+ await this.fire(row);
77
+ }
78
+ finally {
79
+ this.processing = false;
80
+ }
81
+ if (this.rerun) {
82
+ this.rerun = false;
83
+ return void this.tick();
84
+ }
85
+ this.arm();
86
+ }
87
+ async fire(row) {
88
+ const claim = claimScheduledSend(this.db, row.id, Date.now(), CLAIM_LEASE_MS);
89
+ if (!claim) {
90
+ this.backoff.delete(row.id);
91
+ return;
92
+ }
93
+ try {
94
+ const result = await this.service.send(row.accountId, { draftId: row.draftId });
95
+ completeClaim(this.db, row.id, claim.token, result);
96
+ this.backoff.delete(row.id);
97
+ }
98
+ catch (err) {
99
+ const message = err instanceof Error ? err.message : String(err);
100
+ if (isEmailError(err) && PERMANENT_CODES.has(err.code)) {
101
+ failClaim(this.db, row.id, claim.token, err.code === 'not_found' ? 'Draft no longer exists: it was sent or deleted outside Fluxmail' : message);
102
+ this.backoff.delete(row.id);
103
+ }
104
+ else {
105
+ // Transient (auth_expired, rate_limited, network): retry forever, late but sent.
106
+ retryClaim(this.db, row.id, claim.token, message);
107
+ const attempt = (this.backoff.get(row.id)?.attempt ?? 0) + 1;
108
+ this.backoff.set(row.id, {
109
+ attempt,
110
+ notBefore: Date.now() + Math.min(BASE_BACKOFF_MS * 2 ** (attempt - 1), MAX_ARM_MS),
111
+ });
112
+ }
113
+ }
114
+ }
115
+ arm() {
116
+ if (this.timer)
117
+ clearTimeout(this.timer);
118
+ this.timer = undefined;
119
+ const pending = listActive(this.db);
120
+ if (!pending.length)
121
+ return;
122
+ const next = Math.max(Math.min(...pending.map((r) => Math.max(r.sendAt, r.claimUntil ?? 0, this.backoff.get(r.id)?.notBefore ?? 0))), this.quotaHoldUntil);
123
+ const delay = Math.min(Math.max(next - Date.now(), 0), MAX_ARM_MS);
124
+ this.timer = setTimeout(() => void this.tick(), delay);
125
+ this.timer.unref();
126
+ }
127
+ }
128
+ //# sourceMappingURL=sendScheduler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sendScheduler.js","sourceRoot":"","sources":["../../src/scheduler/sendScheduler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,139 @@
1
+ import { type Account, type AttachmentMeta, type DraftInput, type EmailAddress, type EmailQuery, type Folder, type Message, type ModifyAction, type Page, type PageOpts, type SendResult, type Thread } from '@fluxmail/core';
2
+ import type { AccountRegistry } from '../accounts/registry.js';
3
+ import { type Entitlements } from '../licensing/entitlements.js';
4
+ import type { FluxmailDb } from '../storage/db.js';
5
+ import { type ScheduledSendStatus } from '../storage/scheduledSends.js';
6
+ export interface SendInput extends DraftInput {
7
+ /** With replyToMessageId: compute recipients from the original (reply-all semantics). */
8
+ replyAll?: boolean;
9
+ }
10
+ /** The mailboxes an API key may reach: everything for admin keys, shared + owned for member keys. */
11
+ export interface AccessScope {
12
+ /** Member the key is scoped to; null grants unscoped admin access. */
13
+ memberId: string | null;
14
+ }
15
+ export interface ForwardInput {
16
+ messageId: string;
17
+ to: EmailAddress[];
18
+ cc?: EmailAddress[];
19
+ /** Optional note placed above the forwarded content. */
20
+ comment?: string;
21
+ includeAttachments?: boolean;
22
+ }
23
+ export interface ServiceStatus {
24
+ accounts: Array<Pick<Account, 'id' | 'provider' | 'email' | 'status' | 'memberId'> & {
25
+ error?: {
26
+ code: string;
27
+ message: string;
28
+ };
29
+ warnings?: string[];
30
+ }>;
31
+ /** Instance-wide fields are only returned to administrators. */
32
+ members?: {
33
+ count: number;
34
+ };
35
+ entitlements?: Entitlements;
36
+ /** Renewal warning while the license is in grace or has lapsed. */
37
+ licenseWarning?: string;
38
+ providersAvailable: string[];
39
+ scheduled: {
40
+ pending: number;
41
+ nextSendAt?: string;
42
+ };
43
+ }
44
+ export interface ScheduledSendInfo {
45
+ scheduleId: string;
46
+ accountId: string;
47
+ draftId: string;
48
+ /** ISO 8601 UTC. */
49
+ sendAt: string;
50
+ status: ScheduledSendStatus;
51
+ attempts: number;
52
+ subject?: string;
53
+ to?: string;
54
+ lastError?: string;
55
+ sentMessageId?: string;
56
+ sentThreadId?: string;
57
+ }
58
+ /** Parse and validate a sendAt timestamp; returns epoch ms. */
59
+ export declare function resolveSendAt(sendAtIso: string, now?: number): number;
60
+ /**
61
+ * All business logic lives here: account routing, reply/forward computation,
62
+ * entitlement enforcement, auth-failure bookkeeping. MCP tools (and the future
63
+ * REST API / CLI) are thin wrappers over this service.
64
+ */
65
+ export declare class EmailService {
66
+ private readonly registry;
67
+ private readonly db;
68
+ private readonly scope;
69
+ /** Wired to SendScheduler.wake() in long-lived processes; a no-op for one-shot CLI commands. */
70
+ onScheduleChanged: () => void;
71
+ constructor(registry: AccountRegistry, db: FluxmailDb, scope?: AccessScope);
72
+ /**
73
+ * A view of this service restricted to one member's mailboxes (shared + owned).
74
+ * The HTTP layer scopes each request to the authenticated API key's member;
75
+ * unscoped admin keys keep full access.
76
+ */
77
+ withScope(scope: AccessScope): EmailService;
78
+ /** Shared mailboxes and, for member keys, the member's own mailboxes. */
79
+ private canAccess;
80
+ private accessibleAccounts;
81
+ /**
82
+ * Resolve an account id within the current scope. A member-scoped key defaults
83
+ * to its sole accessible mailbox and can never reach another member's mailbox;
84
+ * inaccessible ids surface as not_found so a key cannot probe for their existence.
85
+ */
86
+ private resolveScopedAccountId;
87
+ /** Route a call to the right provider, recording auth failures on the account. */
88
+ private withProvider;
89
+ listAccounts(): Account[];
90
+ status(): Promise<ServiceStatus>;
91
+ /**
92
+ * Gate for MCP tool calls: throws once a lapsed license leaves the instance
93
+ * over the entitled caps; returns a renewal warning to attach to results
94
+ * while the license is in its grace period or has lapsed.
95
+ */
96
+ enforceQuota(): string | undefined;
97
+ private scheduledStatus;
98
+ listFolders(accountId?: string): Promise<Folder[]>;
99
+ listMessages(accountId: string | undefined, q: EmailQuery, page?: PageOpts): Promise<Page<Message>>;
100
+ getMessage(accountId: string | undefined, id: string): Promise<Message>;
101
+ getThread(accountId: string | undefined, threadId: string): Promise<Thread>;
102
+ createDraft(accountId: string | undefined, d: SendInput): Promise<Message>;
103
+ updateDraft(accountId: string | undefined, draftId: string, d: SendInput): Promise<Message>;
104
+ deleteDraft(accountId: string | undefined, draftId: string): Promise<void>;
105
+ send(accountId: string | undefined, input: SendInput | {
106
+ draftId: string;
107
+ }): Promise<SendResult>;
108
+ /**
109
+ * Draft-backed scheduled send: the content becomes a real provider draft
110
+ * immediately; only the schedule (draft id + fire time) is stored locally.
111
+ */
112
+ scheduleSend(accountId: string | undefined, input: SendInput | {
113
+ draftId: string;
114
+ }, sendAtIso: string): Promise<ScheduledSendInfo>;
115
+ listScheduled(accountId?: string): ScheduledSendInfo[];
116
+ /** Cancels a pending schedule; the provider draft is kept. */
117
+ cancelScheduled(scheduleId: string): {
118
+ scheduleId: string;
119
+ draftId: string;
120
+ draftKept: true;
121
+ };
122
+ /**
123
+ * Reply recipient computation (provider-agnostic): when replying without explicit
124
+ * recipients, derive them from the original message: Reply-To/From for a plain
125
+ * reply; plus original To/Cc (minus our own address) for reply-all.
126
+ */
127
+ private resolveRecipients;
128
+ forward(accountId: string | undefined, input: ForwardInput): Promise<SendResult>;
129
+ modify(accountId: string | undefined, ids: string[], action: ModifyAction): Promise<void>;
130
+ getAttachment(accountId: string | undefined, messageId: string, attachmentId: string): Promise<{
131
+ meta: AttachmentMeta;
132
+ content: Buffer;
133
+ }>;
134
+ }
135
+ export declare function buildForwardBody(original: Message, comment?: string): {
136
+ text?: string;
137
+ html?: string;
138
+ };
139
+ //# sourceMappingURL=emailService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emailService.d.ts","sourceRoot":"","sources":["../../src/service/emailService.ts"],"names":[],"mappings":"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"}