surface-cli 0.1.1 → 0.3.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.
- package/README.md +42 -15
- package/dist/cli.js +236 -21
- package/dist/cli.js.map +1 -1
- package/dist/config.js +2 -2
- package/dist/config.js.map +1 -1
- package/dist/contracts/account.js +8 -0
- package/dist/contracts/account.js.map +1 -1
- package/dist/e2e/gmail-v1.js +31 -0
- package/dist/e2e/gmail-v1.js.map +1 -1
- package/dist/e2e/outlook-v1.js +11 -0
- package/dist/e2e/outlook-v1.js.map +1 -1
- package/dist/lib/remote-auth.js +18 -2
- package/dist/lib/remote-auth.js.map +1 -1
- package/dist/lib/stored-mail.js +69 -0
- package/dist/lib/stored-mail.js.map +1 -0
- package/dist/paths.js +2 -0
- package/dist/paths.js.map +1 -1
- package/dist/providers/gmail/adapter.js +336 -53
- package/dist/providers/gmail/adapter.js.map +1 -1
- package/dist/providers/gmail/api.js +68 -0
- package/dist/providers/gmail/api.js.map +1 -1
- package/dist/providers/gmail/normalize.js.map +1 -1
- package/dist/providers/gmail/oauth.js +4 -0
- package/dist/providers/gmail/oauth.js.map +1 -1
- package/dist/providers/outlook/adapter.js +185 -97
- package/dist/providers/outlook/adapter.js.map +1 -1
- package/dist/providers/outlook/extract.js +7 -0
- package/dist/providers/outlook/extract.js.map +1 -1
- package/dist/providers/shared/inline-attachments.js +17 -0
- package/dist/providers/shared/inline-attachments.js.map +1 -0
- package/dist/refs.js +3 -0
- package/dist/refs.js.map +1 -1
- package/dist/session-daemon.js +218 -0
- package/dist/session-daemon.js.map +1 -0
- package/dist/session.js +283 -0
- package/dist/session.js.map +1 -0
- package/dist/state/database.js +542 -8
- package/dist/state/database.js.map +1 -1
- package/dist/summarizer.js +259 -76
- package/dist/summarizer.js.map +1 -1
- package/package.json +1 -1
|
@@ -3,13 +3,33 @@ import { join } from "node:path";
|
|
|
3
3
|
import { SurfaceError } from "../../lib/errors.js";
|
|
4
4
|
import { assertWriteAllowed, collectWriteRecipients } from "../../lib/write-safety.js";
|
|
5
5
|
import { makeAttachmentId, makeMessageRef, makeThreadRef } from "../../refs.js";
|
|
6
|
-
import {
|
|
6
|
+
import { summarizeAndPersistThreads } from "../../summarizer.js";
|
|
7
|
+
import { annotateBodyWithInlineAttachments } from "../shared/inline-attachments.js";
|
|
7
8
|
import { launchOutlookSession, probeOutlookAuth, promptForOutlookLogin } from "./session.js";
|
|
8
|
-
import { applySearchQuery, applyUnreadFilter, captureOutlookServiceSession, collectSearchConversationIds, collectUnreadConversationIds, fetchConversationBundle, waitForOutlookMailboxReady, } from "./extract.js";
|
|
9
|
+
import { applySearchQuery, applyUnreadFilter, captureOutlookServiceSession, collectCurrentConversationIds, collectSearchConversationIds, collectUnreadConversationIds, fetchConversationBundle, waitForOutlookMailboxReady, } from "./extract.js";
|
|
9
10
|
import { buildOutlookInvite, itemIdData, mailboxFromExchange, mailboxesFromExchange, messageIdentity, normalizeOutlookBody, } from "./normalize.js";
|
|
10
11
|
function outlookProfileDir(context) {
|
|
11
12
|
return join(context.accountPaths.authDir, "profile");
|
|
12
13
|
}
|
|
14
|
+
async function withManagedOutlookSession(context, browserSession, work) {
|
|
15
|
+
if (browserSession) {
|
|
16
|
+
return work(browserSession);
|
|
17
|
+
}
|
|
18
|
+
const profileDir = outlookProfileDir(context);
|
|
19
|
+
if (!existsSync(profileDir)) {
|
|
20
|
+
throw new SurfaceError("reauth_required", "Outlook profile directory is missing for this account.", {
|
|
21
|
+
account: null,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
const session = await launchOutlookSession(profileDir, { headless: true });
|
|
25
|
+
try {
|
|
26
|
+
return await work(session);
|
|
27
|
+
}
|
|
28
|
+
finally {
|
|
29
|
+
await session.context.close();
|
|
30
|
+
session.cleanup?.();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
13
33
|
function threadProviderKey(conversationId) {
|
|
14
34
|
return `outlook-thread:${conversationId}`;
|
|
15
35
|
}
|
|
@@ -51,6 +71,38 @@ function uniqueParticipants(messages) {
|
|
|
51
71
|
}
|
|
52
72
|
return participants;
|
|
53
73
|
}
|
|
74
|
+
function quoteOutlookSearchValue(value) {
|
|
75
|
+
const normalized = value.trim();
|
|
76
|
+
if (!normalized) {
|
|
77
|
+
return '""';
|
|
78
|
+
}
|
|
79
|
+
return /[\s"]/u.test(normalized) ? `"${normalized.replace(/"/g, '""')}"` : normalized;
|
|
80
|
+
}
|
|
81
|
+
function buildOutlookSearchQuery(query) {
|
|
82
|
+
const parts = [];
|
|
83
|
+
if (query.text?.trim()) {
|
|
84
|
+
parts.push(query.text.trim());
|
|
85
|
+
}
|
|
86
|
+
if (query.from?.trim()) {
|
|
87
|
+
parts.push(`from:${quoteOutlookSearchValue(query.from)}`);
|
|
88
|
+
}
|
|
89
|
+
if (query.subject?.trim()) {
|
|
90
|
+
parts.push(`subject:${quoteOutlookSearchValue(query.subject)}`);
|
|
91
|
+
}
|
|
92
|
+
return parts.length > 0 ? parts.join(" AND ") : undefined;
|
|
93
|
+
}
|
|
94
|
+
function threadMatchesStructuredFilters(thread, query) {
|
|
95
|
+
if (query.mailbox?.trim() && thread.envelope.mailbox.trim().toLowerCase() !== query.mailbox.trim().toLowerCase()) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
if ((query.labels?.length ?? 0) > 0) {
|
|
99
|
+
const available = new Set(thread.envelope.labels.map((label) => label.trim().toLowerCase()));
|
|
100
|
+
if ((query.labels ?? []).some((label) => !available.has(label.trim().toLowerCase()))) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
54
106
|
function inferThreadRsvpStatus(messages) {
|
|
55
107
|
let latest = null;
|
|
56
108
|
for (const message of messages) {
|
|
@@ -105,7 +157,6 @@ function buildAttachments(entry, messageKey) {
|
|
|
105
157
|
}
|
|
106
158
|
function normalizeMessage(entry, conversationId) {
|
|
107
159
|
const item = entry.item;
|
|
108
|
-
const body = normalizeOutlookBody(item);
|
|
109
160
|
const invite = buildOutlookInvite(item);
|
|
110
161
|
const from = mailboxFromExchange(item.From ?? null)
|
|
111
162
|
?? mailboxFromExchange(item.Sender ?? null);
|
|
@@ -118,6 +169,8 @@ function normalizeMessage(entry, conversationId) {
|
|
|
118
169
|
const instanceKey = typeof item.InstanceKey === "string" ? item.InstanceKey : undefined;
|
|
119
170
|
const key = messageProviderKey(entry, conversationId);
|
|
120
171
|
const attachments = buildAttachments(entry, key);
|
|
172
|
+
const body = normalizeOutlookBody(item);
|
|
173
|
+
const bodyText = annotateBodyWithInlineAttachments(body.text, attachments);
|
|
121
174
|
const envelope = {
|
|
122
175
|
from,
|
|
123
176
|
to,
|
|
@@ -139,12 +192,12 @@ function normalizeMessage(entry, conversationId) {
|
|
|
139
192
|
return {
|
|
140
193
|
message_ref: "",
|
|
141
194
|
envelope,
|
|
142
|
-
snippet: typeof item.Preview === "string" ? item.Preview :
|
|
195
|
+
snippet: typeof item.Preview === "string" ? item.Preview : bodyText.slice(0, 240),
|
|
143
196
|
body: {
|
|
144
|
-
text:
|
|
197
|
+
text: bodyText,
|
|
145
198
|
truncated: false,
|
|
146
199
|
cached: true,
|
|
147
|
-
cached_bytes: Buffer.byteLength(
|
|
200
|
+
cached_bytes: Buffer.byteLength(bodyText, "utf8"),
|
|
148
201
|
},
|
|
149
202
|
attachments,
|
|
150
203
|
...(invite.is_invite
|
|
@@ -269,6 +322,12 @@ function parseOutlookMessageLocator(locatorJson) {
|
|
|
269
322
|
meeting_end: stringOrNull(locator.meeting_end),
|
|
270
323
|
};
|
|
271
324
|
}
|
|
325
|
+
function parseOutlookThreadLocator(locatorJson) {
|
|
326
|
+
const locator = JSON.parse(locatorJson);
|
|
327
|
+
return {
|
|
328
|
+
conversation_id: typeof locator.conversation_id === "string" ? locator.conversation_id : "",
|
|
329
|
+
};
|
|
330
|
+
}
|
|
272
331
|
function parseOutlookAttachmentLocator(locatorJson) {
|
|
273
332
|
const locator = JSON.parse(locatorJson);
|
|
274
333
|
return {
|
|
@@ -447,9 +506,6 @@ async function persistThreads(account, context, threads) {
|
|
|
447
506
|
if (threadInviteStatus) {
|
|
448
507
|
context.db.updateInviteStatusForThread(resolvedThreadRef, threadInviteStatus);
|
|
449
508
|
}
|
|
450
|
-
if (thread.summary) {
|
|
451
|
-
context.db.upsertSummary(resolvedThreadRef, thread.summary);
|
|
452
|
-
}
|
|
453
509
|
persistedThreads.push({
|
|
454
510
|
...thread,
|
|
455
511
|
thread_ref: resolvedThreadRef,
|
|
@@ -459,36 +515,18 @@ async function persistThreads(account, context, threads) {
|
|
|
459
515
|
return persistedThreads;
|
|
460
516
|
});
|
|
461
517
|
}
|
|
462
|
-
async function
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
}
|
|
466
|
-
const
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
summarized.push({
|
|
470
|
-
...thread,
|
|
471
|
-
summary,
|
|
472
|
-
});
|
|
473
|
-
}
|
|
474
|
-
return summarized;
|
|
518
|
+
async function refreshOutlookConversationWithSession(account, conversationId, context, browserSession) {
|
|
519
|
+
const capturedSession = await captureOutlookServiceSession(browserSession.context, browserSession.page, {
|
|
520
|
+
timeoutMs: context.config.providerTimeoutMs,
|
|
521
|
+
});
|
|
522
|
+
const bundle = await fetchConversationBundle(browserSession.context.request, capturedSession, conversationId);
|
|
523
|
+
const persisted = await persistThreads(account, context, [normalizeThread(bundle)]);
|
|
524
|
+
await summarizeAndPersistThreads(persisted, context.config, context.db, context.db.getAccountIdentity(account));
|
|
475
525
|
}
|
|
476
526
|
async function refreshOutlookConversation(account, conversationId, context) {
|
|
477
|
-
|
|
478
|
-
const session = await launchOutlookSession(profileDir, { headless: true });
|
|
479
|
-
try {
|
|
480
|
-
const capturedSession = await captureOutlookServiceSession(session.context, session.page, {
|
|
481
|
-
timeoutMs: context.config.providerTimeoutMs,
|
|
482
|
-
});
|
|
483
|
-
const bundle = await fetchConversationBundle(session.context.request, capturedSession, conversationId);
|
|
484
|
-
await persistThreads(account, context, [normalizeThread(bundle)]);
|
|
485
|
-
}
|
|
486
|
-
finally {
|
|
487
|
-
await session.context.close();
|
|
488
|
-
session.cleanup?.();
|
|
489
|
-
}
|
|
527
|
+
await withManagedOutlookSession(context, undefined, (session) => refreshOutlookConversationWithSession(account, conversationId, context, session));
|
|
490
528
|
}
|
|
491
|
-
async function
|
|
529
|
+
async function refreshStoredMessageWithSession(account, messageRef, context, browserSession) {
|
|
492
530
|
const locatorRow = context.db.findProviderLocator("message", messageRef);
|
|
493
531
|
if (!locatorRow) {
|
|
494
532
|
throw new SurfaceError("cache_miss", `No provider locator exists for message '${messageRef}'.`, {
|
|
@@ -503,7 +541,7 @@ async function refreshStoredMessage(account, messageRef, context) {
|
|
|
503
541
|
messageRef,
|
|
504
542
|
});
|
|
505
543
|
}
|
|
506
|
-
await
|
|
544
|
+
await refreshOutlookConversationWithSession(account, locator.conversation_id, context, browserSession);
|
|
507
545
|
const refreshed = context.db.getStoredMessage(messageRef);
|
|
508
546
|
if (!refreshed) {
|
|
509
547
|
throw new SurfaceError("not_found", `Message '${messageRef}' could not be refreshed from Outlook.`, {
|
|
@@ -513,6 +551,9 @@ async function refreshStoredMessage(account, messageRef, context) {
|
|
|
513
551
|
}
|
|
514
552
|
return refreshed;
|
|
515
553
|
}
|
|
554
|
+
async function refreshStoredMessage(account, messageRef, context) {
|
|
555
|
+
return withManagedOutlookSession(context, undefined, (session) => refreshStoredMessageWithSession(account, messageRef, context, session));
|
|
556
|
+
}
|
|
516
557
|
async function resolveRsvpLocator(account, messageRef, context) {
|
|
517
558
|
let stored = context.db.getStoredMessage(messageRef);
|
|
518
559
|
if (!stored) {
|
|
@@ -1015,6 +1056,35 @@ function buildReadEnvelope(account, messageRef, threadRef, parsed, attachments,
|
|
|
1015
1056
|
},
|
|
1016
1057
|
};
|
|
1017
1058
|
}
|
|
1059
|
+
async function fetchOutlookThreadsWithSession(account, context, browserSession, options) {
|
|
1060
|
+
const { context: playwrightContext, page } = browserSession;
|
|
1061
|
+
const capturedSession = await captureOutlookServiceSession(playwrightContext, page, {
|
|
1062
|
+
timeoutMs: context.config.providerTimeoutMs,
|
|
1063
|
+
});
|
|
1064
|
+
let conversationIds;
|
|
1065
|
+
if (options.kind === "fetch-unread") {
|
|
1066
|
+
await applyUnreadFilter(page);
|
|
1067
|
+
conversationIds = await collectUnreadConversationIds(page, options.fetchLimit ?? options.limit);
|
|
1068
|
+
}
|
|
1069
|
+
else if (options.kind === "browse-current-folder") {
|
|
1070
|
+
conversationIds = await collectCurrentConversationIds(page, options.fetchLimit ?? options.limit);
|
|
1071
|
+
}
|
|
1072
|
+
else {
|
|
1073
|
+
await applySearchQuery(page, options.queryText ?? "");
|
|
1074
|
+
conversationIds = await collectSearchConversationIds(page, options.fetchLimit ?? options.limit);
|
|
1075
|
+
}
|
|
1076
|
+
const bundles = [];
|
|
1077
|
+
for (const conversationId of conversationIds) {
|
|
1078
|
+
bundles.push(await fetchConversationBundle(playwrightContext.request, capturedSession, conversationId));
|
|
1079
|
+
}
|
|
1080
|
+
const normalized = bundles.map((bundle) => normalizeThread(bundle));
|
|
1081
|
+
const filtered = options.postFilter ? normalized.filter(options.postFilter) : normalized;
|
|
1082
|
+
const limited = filtered.slice(0, options.limit);
|
|
1083
|
+
const persisted = await persistThreads(account, context, limited);
|
|
1084
|
+
return options.summarize === false
|
|
1085
|
+
? persisted
|
|
1086
|
+
: await summarizeAndPersistThreads(persisted, context.config, context.db, context.db.getAccountIdentity(account));
|
|
1087
|
+
}
|
|
1018
1088
|
async function fetchOutlookThreads(account, context, options) {
|
|
1019
1089
|
const profileDir = outlookProfileDir(context);
|
|
1020
1090
|
if (!existsSync(profileDir)) {
|
|
@@ -1022,33 +1092,81 @@ async function fetchOutlookThreads(account, context, options) {
|
|
|
1022
1092
|
account: account.name,
|
|
1023
1093
|
});
|
|
1024
1094
|
}
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1095
|
+
return withManagedOutlookSession(context, undefined, (session) => fetchOutlookThreadsWithSession(account, context, session, options));
|
|
1096
|
+
}
|
|
1097
|
+
export async function searchOutlookWithSession(account, query, context, browserSession) {
|
|
1098
|
+
const queryText = buildOutlookSearchQuery(query);
|
|
1099
|
+
const normalizedLabels = new Set((query.labels ?? []).map((label) => label.trim().toLowerCase()).filter(Boolean));
|
|
1100
|
+
const postFilter = (thread) => threadMatchesStructuredFilters(thread, query);
|
|
1101
|
+
if (!queryText && normalizedLabels.has("unread")) {
|
|
1102
|
+
const threads = await withManagedOutlookSession(context, browserSession, (session) => fetchOutlookThreadsWithSession(account, context, session, {
|
|
1103
|
+
kind: "fetch-unread",
|
|
1104
|
+
limit: query.limit,
|
|
1105
|
+
fetchLimit: Math.min(Math.max(query.limit * 3, query.limit), 100),
|
|
1106
|
+
postFilter,
|
|
1107
|
+
}));
|
|
1108
|
+
return threads.slice(0, query.limit);
|
|
1109
|
+
}
|
|
1110
|
+
const threads = await withManagedOutlookSession(context, browserSession, (session) => fetchOutlookThreadsWithSession(account, context, session, {
|
|
1111
|
+
kind: queryText ? "search" : "browse-current-folder",
|
|
1112
|
+
...(queryText ? { queryText } : {}),
|
|
1113
|
+
limit: query.limit,
|
|
1114
|
+
fetchLimit: Math.min(Math.max(query.limit * 3, query.limit), 100),
|
|
1115
|
+
postFilter,
|
|
1116
|
+
}));
|
|
1117
|
+
return threads.slice(0, query.limit);
|
|
1118
|
+
}
|
|
1119
|
+
export async function fetchUnreadOutlookWithSession(account, query, context, browserSession) {
|
|
1120
|
+
return withManagedOutlookSession(context, browserSession, (session) => fetchOutlookThreadsWithSession(account, context, session, {
|
|
1121
|
+
kind: "fetch-unread",
|
|
1122
|
+
limit: query.limit,
|
|
1123
|
+
}));
|
|
1124
|
+
}
|
|
1125
|
+
export async function refreshOutlookThreadWithSession(account, threadRef, context, browserSession) {
|
|
1126
|
+
const locatorRow = context.db.findProviderLocator("thread", threadRef);
|
|
1127
|
+
if (!locatorRow) {
|
|
1128
|
+
throw new SurfaceError("cache_miss", `No provider locator exists for thread '${threadRef}'.`, {
|
|
1129
|
+
account: account.name,
|
|
1130
|
+
threadRef,
|
|
1030
1131
|
});
|
|
1031
|
-
let conversationIds;
|
|
1032
|
-
if (options.kind === "fetch-unread") {
|
|
1033
|
-
await applyUnreadFilter(page);
|
|
1034
|
-
conversationIds = await collectUnreadConversationIds(page, options.limit);
|
|
1035
|
-
}
|
|
1036
|
-
else {
|
|
1037
|
-
await applySearchQuery(page, options.queryText ?? "");
|
|
1038
|
-
conversationIds = await collectSearchConversationIds(page, options.limit);
|
|
1039
|
-
}
|
|
1040
|
-
const bundles = [];
|
|
1041
|
-
for (const conversationId of conversationIds) {
|
|
1042
|
-
bundles.push(await fetchConversationBundle(browserContext.request, capturedSession, conversationId));
|
|
1043
|
-
}
|
|
1044
|
-
const normalized = bundles.map((bundle) => normalizeThread(bundle));
|
|
1045
|
-
const summarized = options.summarize === false ? normalized : await maybeSummarizeThreads(normalized, context);
|
|
1046
|
-
return await persistThreads(account, context, summarized);
|
|
1047
1132
|
}
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1133
|
+
const locator = parseOutlookThreadLocator(locatorRow.locator_json);
|
|
1134
|
+
if (!locator.conversation_id) {
|
|
1135
|
+
throw new SurfaceError("transport_error", `Thread '${threadRef}' is missing an Outlook conversation id.`, {
|
|
1136
|
+
account: account.name,
|
|
1137
|
+
threadRef,
|
|
1138
|
+
});
|
|
1139
|
+
}
|
|
1140
|
+
await withManagedOutlookSession(context, browserSession, (session) => refreshOutlookConversationWithSession(account, locator.conversation_id, context, session));
|
|
1141
|
+
}
|
|
1142
|
+
export async function readOutlookMessageWithSession(account, messageRef, refresh, context, browserSession) {
|
|
1143
|
+
const stored = context.db.getStoredMessage(messageRef);
|
|
1144
|
+
if (!stored) {
|
|
1145
|
+
throw new SurfaceError("not_found", `Message '${messageRef}' was not found.`, {
|
|
1146
|
+
account: account.name,
|
|
1147
|
+
messageRef,
|
|
1148
|
+
});
|
|
1051
1149
|
}
|
|
1150
|
+
const attachments = context.db.listAttachmentsForMessage(messageRef).map((attachment) => ({
|
|
1151
|
+
attachment_id: attachment.attachment_id,
|
|
1152
|
+
filename: attachment.filename,
|
|
1153
|
+
mime_type: attachment.mime_type,
|
|
1154
|
+
size_bytes: attachment.size_bytes,
|
|
1155
|
+
inline: Boolean(attachment.inline),
|
|
1156
|
+
}));
|
|
1157
|
+
const hasReadableCache = Boolean(stored.body_cache_path && existsSync(stored.body_cache_path));
|
|
1158
|
+
if (!refresh && hasReadableCache) {
|
|
1159
|
+
return buildReadEnvelope(account, messageRef, stored.thread_ref, parseStoredMessage(stored), attachments, "hit");
|
|
1160
|
+
}
|
|
1161
|
+
const refreshed = await withManagedOutlookSession(context, browserSession, (session) => refreshStoredMessageWithSession(account, messageRef, context, session));
|
|
1162
|
+
const refreshedAttachments = context.db.listAttachmentsForMessage(messageRef).map((attachment) => ({
|
|
1163
|
+
attachment_id: attachment.attachment_id,
|
|
1164
|
+
filename: attachment.filename,
|
|
1165
|
+
mime_type: attachment.mime_type,
|
|
1166
|
+
size_bytes: attachment.size_bytes,
|
|
1167
|
+
inline: Boolean(attachment.inline),
|
|
1168
|
+
}));
|
|
1169
|
+
return buildReadEnvelope(account, messageRef, refreshed.thread_ref, parseStoredMessage(refreshed), refreshedAttachments, "refreshed");
|
|
1052
1170
|
}
|
|
1053
1171
|
async function mutateOutlookReadState(account, messageRefs, unread, context) {
|
|
1054
1172
|
if (messageRefs.length === 0) {
|
|
@@ -1133,46 +1251,16 @@ export class OutlookWebPlaywrightAdapter {
|
|
|
1133
1251
|
}
|
|
1134
1252
|
}
|
|
1135
1253
|
async search(account, query, context) {
|
|
1136
|
-
return
|
|
1137
|
-
kind: "search",
|
|
1138
|
-
queryText: query.text,
|
|
1139
|
-
limit: query.limit,
|
|
1140
|
-
});
|
|
1254
|
+
return searchOutlookWithSession(account, query, context);
|
|
1141
1255
|
}
|
|
1142
1256
|
async fetchUnread(account, query, context) {
|
|
1143
|
-
return
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1257
|
+
return fetchUnreadOutlookWithSession(account, query, context);
|
|
1258
|
+
}
|
|
1259
|
+
async refreshThread(account, threadRef, context) {
|
|
1260
|
+
await refreshOutlookThreadWithSession(account, threadRef, context);
|
|
1147
1261
|
}
|
|
1148
1262
|
async readMessage(account, messageRef, refresh, context) {
|
|
1149
|
-
|
|
1150
|
-
if (!stored) {
|
|
1151
|
-
throw new SurfaceError("not_found", `Message '${messageRef}' was not found.`, {
|
|
1152
|
-
account: account.name,
|
|
1153
|
-
messageRef,
|
|
1154
|
-
});
|
|
1155
|
-
}
|
|
1156
|
-
const attachments = context.db.listAttachmentsForMessage(messageRef).map((attachment) => ({
|
|
1157
|
-
attachment_id: attachment.attachment_id,
|
|
1158
|
-
filename: attachment.filename,
|
|
1159
|
-
mime_type: attachment.mime_type,
|
|
1160
|
-
size_bytes: attachment.size_bytes,
|
|
1161
|
-
inline: Boolean(attachment.inline),
|
|
1162
|
-
}));
|
|
1163
|
-
const hasReadableCache = Boolean(stored.body_cache_path && existsSync(stored.body_cache_path));
|
|
1164
|
-
if (!refresh && hasReadableCache) {
|
|
1165
|
-
return buildReadEnvelope(account, messageRef, stored.thread_ref, parseStoredMessage(stored), attachments, "hit");
|
|
1166
|
-
}
|
|
1167
|
-
const refreshed = await refreshStoredMessage(account, messageRef, context);
|
|
1168
|
-
const refreshedAttachments = context.db.listAttachmentsForMessage(messageRef).map((attachment) => ({
|
|
1169
|
-
attachment_id: attachment.attachment_id,
|
|
1170
|
-
filename: attachment.filename,
|
|
1171
|
-
mime_type: attachment.mime_type,
|
|
1172
|
-
size_bytes: attachment.size_bytes,
|
|
1173
|
-
inline: Boolean(attachment.inline),
|
|
1174
|
-
}));
|
|
1175
|
-
return buildReadEnvelope(account, messageRef, refreshed.thread_ref, parseStoredMessage(refreshed), refreshedAttachments, "refreshed");
|
|
1263
|
+
return readOutlookMessageWithSession(account, messageRef, refresh, context);
|
|
1176
1264
|
}
|
|
1177
1265
|
async listAttachments(account, messageRef, context) {
|
|
1178
1266
|
const message = context.db.findMessageByRef(messageRef);
|