@delexec/ops 0.1.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 +3 -0
- package/README.zh-CN.md +6 -0
- package/node_modules/@delexec/caller-controller/README.md +3 -0
- package/node_modules/@delexec/caller-controller/README.zh-CN.md +6 -0
- package/node_modules/@delexec/caller-controller/package.json +53 -0
- package/node_modules/@delexec/caller-controller/src/server.js +127 -0
- package/node_modules/@delexec/caller-controller-core/README.md +3 -0
- package/node_modules/@delexec/caller-controller-core/README.zh-CN.md +6 -0
- package/node_modules/@delexec/caller-controller-core/package.json +26 -0
- package/node_modules/@delexec/caller-controller-core/src/index.js +1612 -0
- package/node_modules/@delexec/caller-skill-adapter/package.json +12 -0
- package/node_modules/@delexec/caller-skill-adapter/src/server.js +1042 -0
- package/node_modules/@delexec/caller-skill-mcp-adapter/README.md +65 -0
- package/node_modules/@delexec/caller-skill-mcp-adapter/package.json +16 -0
- package/node_modules/@delexec/caller-skill-mcp-adapter/src/server.js +527 -0
- package/node_modules/@delexec/responder-controller/README.md +3 -0
- package/node_modules/@delexec/responder-controller/README.zh-CN.md +6 -0
- package/node_modules/@delexec/responder-controller/package.json +53 -0
- package/node_modules/@delexec/responder-controller/src/server.js +254 -0
- package/node_modules/@delexec/responder-runtime-core/README.md +3 -0
- package/node_modules/@delexec/responder-runtime-core/README.zh-CN.md +6 -0
- package/node_modules/@delexec/responder-runtime-core/package.json +26 -0
- package/node_modules/@delexec/responder-runtime-core/src/executors.js +326 -0
- package/node_modules/@delexec/responder-runtime-core/src/index.js +1202 -0
- package/node_modules/@delexec/runtime-utils/README.md +3 -0
- package/node_modules/@delexec/runtime-utils/README.zh-CN.md +6 -0
- package/node_modules/@delexec/runtime-utils/package.json +23 -0
- package/node_modules/@delexec/runtime-utils/src/index.js +338 -0
- package/node_modules/@delexec/sqlite-store/README.md +3 -0
- package/node_modules/@delexec/sqlite-store/README.zh-CN.md +6 -0
- package/node_modules/@delexec/sqlite-store/package.json +26 -0
- package/node_modules/@delexec/sqlite-store/src/index.js +68 -0
- package/node_modules/@delexec/transport-email/README.md +3 -0
- package/node_modules/@delexec/transport-email/README.zh-CN.md +6 -0
- package/node_modules/@delexec/transport-email/package.json +23 -0
- package/node_modules/@delexec/transport-email/src/index.js +185 -0
- package/node_modules/@delexec/transport-emailengine/README.md +3 -0
- package/node_modules/@delexec/transport-emailengine/README.zh-CN.md +6 -0
- package/node_modules/@delexec/transport-emailengine/package.json +26 -0
- package/node_modules/@delexec/transport-emailengine/src/index.js +210 -0
- package/node_modules/@delexec/transport-gmail/README.md +3 -0
- package/node_modules/@delexec/transport-gmail/README.zh-CN.md +6 -0
- package/node_modules/@delexec/transport-gmail/package.json +26 -0
- package/node_modules/@delexec/transport-gmail/src/index.js +295 -0
- package/node_modules/@delexec/transport-relay-http/README.md +3 -0
- package/node_modules/@delexec/transport-relay-http/README.zh-CN.md +6 -0
- package/node_modules/@delexec/transport-relay-http/package.json +23 -0
- package/node_modules/@delexec/transport-relay-http/src/index.js +124 -0
- package/package.json +64 -0
- package/src/cli.js +1571 -0
- package/src/config.js +1180 -0
- package/src/example-hotline-worker.js +65 -0
- package/src/example-hotline.js +196 -0
- package/src/logging.js +56 -0
- package/src/supervisor.js +3070 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
|
|
3
|
+
export const RSP_EMAIL_SUBJECT_PREFIX = "[RSP]";
|
|
4
|
+
|
|
5
|
+
function nowIso() {
|
|
6
|
+
return new Date().toISOString();
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function randomInt(maxInclusive) {
|
|
10
|
+
if (maxInclusive <= 0) {
|
|
11
|
+
return 0;
|
|
12
|
+
}
|
|
13
|
+
return Math.floor(Math.random() * (maxInclusive + 1));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function deepClone(value) {
|
|
17
|
+
return value == null ? value : JSON.parse(JSON.stringify(value));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function normalizeAttachments(attachments = []) {
|
|
21
|
+
return (Array.isArray(attachments) ? attachments : []).map((attachment, index) => {
|
|
22
|
+
const contentBase64 = Buffer.isBuffer(attachment?.content)
|
|
23
|
+
? attachment.content.toString("base64")
|
|
24
|
+
: attachment?.content_base64 || Buffer.from(String(attachment?.content || ""), "utf8").toString("base64");
|
|
25
|
+
const byteSize = Buffer.from(contentBase64 || "", "base64").length;
|
|
26
|
+
return {
|
|
27
|
+
attachment_id: attachment?.attachment_id || `att_${index}_${crypto.randomUUID()}`,
|
|
28
|
+
name: attachment?.name || `attachment-${index + 1}.bin`,
|
|
29
|
+
media_type: attachment?.media_type || "application/octet-stream",
|
|
30
|
+
content_base64: contentBase64,
|
|
31
|
+
byte_size: Number.isFinite(Number(attachment?.byte_size)) ? Number(attachment.byte_size) : byteSize
|
|
32
|
+
};
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function normalizeEnvelope(envelope = {}) {
|
|
37
|
+
const base = deepClone(envelope);
|
|
38
|
+
const bodyText =
|
|
39
|
+
typeof envelope.body_text === "string"
|
|
40
|
+
? envelope.body_text
|
|
41
|
+
: typeof envelope.result_package === "object"
|
|
42
|
+
? JSON.stringify(envelope.result_package)
|
|
43
|
+
: typeof envelope.payload === "string"
|
|
44
|
+
? envelope.payload
|
|
45
|
+
: envelope.payload?.body_text || "";
|
|
46
|
+
|
|
47
|
+
const to =
|
|
48
|
+
envelope.to ||
|
|
49
|
+
envelope.address ||
|
|
50
|
+
envelope.receiver ||
|
|
51
|
+
(envelope.direction === "responder_to_caller" ? "caller-controller" : "responder-controller");
|
|
52
|
+
const from =
|
|
53
|
+
envelope.from ||
|
|
54
|
+
envelope.sender ||
|
|
55
|
+
(envelope.direction === "responder_to_caller" ? "responder-controller" : "caller-controller");
|
|
56
|
+
|
|
57
|
+
const payload =
|
|
58
|
+
envelope.payload && typeof envelope.payload === "object" && !Array.isArray(envelope.payload)
|
|
59
|
+
? deepClone(envelope.payload)
|
|
60
|
+
: envelope.payload;
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
...base,
|
|
64
|
+
message_id: envelope.message_id || `mail_${crypto.randomUUID()}`,
|
|
65
|
+
request_id: envelope.request_id || payload?.request_id || envelope.result_package?.request_id || null,
|
|
66
|
+
thread_id: envelope.thread_id || envelope.thread_hint || envelope.request_id || `thread_${crypto.randomUUID()}`,
|
|
67
|
+
thread_hint: envelope.thread_hint || envelope.thread_id || envelope.request_id || null,
|
|
68
|
+
from,
|
|
69
|
+
to,
|
|
70
|
+
type: envelope.type || payload?.type || "email.message",
|
|
71
|
+
direction: envelope.direction || null,
|
|
72
|
+
body_text: bodyText,
|
|
73
|
+
payload,
|
|
74
|
+
result_package: envelope.result_package ? deepClone(envelope.result_package) : null,
|
|
75
|
+
attachments: normalizeAttachments(envelope.attachments || payload?.attachments || []),
|
|
76
|
+
sent_at: nowIso()
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function buildEmailSubject(envelope = {}) {
|
|
81
|
+
const normalized = normalizeEnvelope(envelope);
|
|
82
|
+
const type = normalized.type || "message";
|
|
83
|
+
const requestId = normalized.request_id || normalized.thread_id || normalized.message_id;
|
|
84
|
+
return `${RSP_EMAIL_SUBJECT_PREFIX} ${type} ${requestId}`;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function isRspEmailSubject(subject) {
|
|
88
|
+
return typeof subject === "string" && subject.startsWith(`${RSP_EMAIL_SUBJECT_PREFIX} `);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function buildRspHeaders(envelope = {}) {
|
|
92
|
+
const normalized = normalizeEnvelope(envelope);
|
|
93
|
+
return {
|
|
94
|
+
"X-RSP-Transport": "1",
|
|
95
|
+
"X-RSP-Request-Id": normalized.request_id || "",
|
|
96
|
+
"X-RSP-Thread-Id": normalized.thread_id || "",
|
|
97
|
+
"X-RSP-Message-Id": normalized.message_id || "",
|
|
98
|
+
"X-RSP-Type": normalized.type || "",
|
|
99
|
+
"X-RSP-From": normalized.from || "",
|
|
100
|
+
"X-RSP-To": normalized.to || ""
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export class InMemoryEmailTransport {
|
|
105
|
+
constructor(options = {}) {
|
|
106
|
+
this.messages = [];
|
|
107
|
+
this.options = {
|
|
108
|
+
duplicateRate: Number(options.duplicateRate || 0),
|
|
109
|
+
outOfOrderRate: Number(options.outOfOrderRate || 0),
|
|
110
|
+
minDelayMs: Number(options.minDelayMs || 0),
|
|
111
|
+
maxDelayMs: Number(options.maxDelayMs || 0)
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async send(envelope) {
|
|
116
|
+
const delayRange = Math.max(this.options.maxDelayMs - this.options.minDelayMs, 0);
|
|
117
|
+
const delayMs = this.options.minDelayMs + randomInt(delayRange);
|
|
118
|
+
if (delayMs > 0) {
|
|
119
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const record = normalizeEnvelope(envelope);
|
|
123
|
+
this.messages.push(record);
|
|
124
|
+
|
|
125
|
+
if (Math.random() < this.options.duplicateRate) {
|
|
126
|
+
this.messages.push({ ...deepClone(record), message_id: `dup_${crypto.randomUUID()}` });
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (Math.random() < this.options.outOfOrderRate && this.messages.length >= 2) {
|
|
130
|
+
const last = this.messages.length - 1;
|
|
131
|
+
const swapWith = randomInt(last - 1);
|
|
132
|
+
const temp = this.messages[last];
|
|
133
|
+
this.messages[last] = this.messages[swapWith];
|
|
134
|
+
this.messages[swapWith] = temp;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return deepClone(record);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async poll({ receiver, limit = 50 } = {}) {
|
|
141
|
+
const items = this.messages
|
|
142
|
+
.filter((item) => !receiver || item.to === receiver)
|
|
143
|
+
.slice(0, limit)
|
|
144
|
+
.map((item) => deepClone(item));
|
|
145
|
+
return { items };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async ack(messageId, { receiver } = {}) {
|
|
149
|
+
const index = this.messages.findIndex((item) => item.message_id === messageId && (!receiver || item.to === receiver));
|
|
150
|
+
if (index === -1) {
|
|
151
|
+
return { acked: false };
|
|
152
|
+
}
|
|
153
|
+
this.messages.splice(index, 1);
|
|
154
|
+
return { acked: true };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async sendTaskEmail(message) {
|
|
158
|
+
return this.send(message);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async pollThreadReplies({ request_id, direction = "responder_to_caller", limit = 50 } = {}) {
|
|
162
|
+
return this.messages
|
|
163
|
+
.filter((item) => (!request_id || item.request_id === request_id) && item.direction === direction)
|
|
164
|
+
.slice(0, limit)
|
|
165
|
+
.map((item) => deepClone(item));
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
clear() {
|
|
169
|
+
this.messages.length = 0;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export async function sendTaskEmail(transport, message) {
|
|
174
|
+
if (!transport || typeof transport.sendTaskEmail !== "function") {
|
|
175
|
+
throw new Error("TRANSPORT_SEND_NOT_AVAILABLE");
|
|
176
|
+
}
|
|
177
|
+
return transport.sendTaskEmail(message);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export async function pollThreadReplies(transport, query) {
|
|
181
|
+
if (!transport || typeof transport.pollThreadReplies !== "function") {
|
|
182
|
+
throw new Error("TRANSPORT_POLL_NOT_AVAILABLE");
|
|
183
|
+
}
|
|
184
|
+
return transport.pollThreadReplies(query);
|
|
185
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@delexec/transport-emailengine",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "EmailEngine transport adapter for delegated execution",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/index.js",
|
|
9
|
+
"./package.json": "./package.json"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"src",
|
|
13
|
+
"README.md"
|
|
14
|
+
],
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "public"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"delegated-execution",
|
|
20
|
+
"transport",
|
|
21
|
+
"emailengine"
|
|
22
|
+
],
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@delexec/transport-email": "0.1.0"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildEmailSubject,
|
|
3
|
+
buildRspHeaders,
|
|
4
|
+
isRspEmailSubject,
|
|
5
|
+
normalizeEnvelope
|
|
6
|
+
} from "@delexec/transport-email";
|
|
7
|
+
|
|
8
|
+
function base64ToUtf8(value) {
|
|
9
|
+
return Buffer.from(value || "", "base64").toString("utf8");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async function request(baseUrl, pathname, { method = "GET", headers = {}, body, parse = "json" } = {}) {
|
|
13
|
+
const response = await fetch(new URL(pathname, baseUrl), {
|
|
14
|
+
method,
|
|
15
|
+
headers,
|
|
16
|
+
body
|
|
17
|
+
});
|
|
18
|
+
if (parse === "buffer") {
|
|
19
|
+
return {
|
|
20
|
+
status: response.status,
|
|
21
|
+
headers: response.headers,
|
|
22
|
+
body: Buffer.from(await response.arrayBuffer())
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
const text = await response.text();
|
|
26
|
+
return {
|
|
27
|
+
status: response.status,
|
|
28
|
+
headers: response.headers,
|
|
29
|
+
body: text ? JSON.parse(text) : null
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function authHeaders(accessToken, extra = {}) {
|
|
34
|
+
return {
|
|
35
|
+
Authorization: `Bearer ${accessToken}`,
|
|
36
|
+
...extra
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function fetchMessageAttachments(baseUrl, account, accessToken, attachments = []) {
|
|
41
|
+
const results = [];
|
|
42
|
+
for (const attachment of attachments) {
|
|
43
|
+
const response = await request(baseUrl, `/v1/account/${encodeURIComponent(account)}/attachment/${encodeURIComponent(attachment.id)}`, {
|
|
44
|
+
headers: authHeaders(accessToken),
|
|
45
|
+
parse: "buffer"
|
|
46
|
+
});
|
|
47
|
+
if (response.status !== 200) {
|
|
48
|
+
throw new Error(`emailengine_attachment_fetch_failed:${response.status}`);
|
|
49
|
+
}
|
|
50
|
+
const content = response.body;
|
|
51
|
+
results.push({
|
|
52
|
+
attachment_id: attachment.id,
|
|
53
|
+
name: attachment.filename || attachment.name || `attachment-${results.length + 1}.bin`,
|
|
54
|
+
media_type: attachment.contentType || attachment.mimeType || "application/octet-stream",
|
|
55
|
+
content_base64: content.toString("base64"),
|
|
56
|
+
byte_size: content.length
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
return results;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function loadMessage(baseUrl, account, accessToken, message) {
|
|
63
|
+
const response = await request(baseUrl, `/v1/account/${encodeURIComponent(account)}/message/${encodeURIComponent(message.id)}?textType=*`, {
|
|
64
|
+
headers: authHeaders(accessToken)
|
|
65
|
+
});
|
|
66
|
+
if (response.status !== 200) {
|
|
67
|
+
throw new Error(`emailengine_message_fetch_failed:${response.status}`);
|
|
68
|
+
}
|
|
69
|
+
const body = response.body || {};
|
|
70
|
+
const attachments = await fetchMessageAttachments(baseUrl, account, accessToken, body.attachments || []);
|
|
71
|
+
const textBody = Array.isArray(body.text?.plain)
|
|
72
|
+
? body.text.plain.join("\n")
|
|
73
|
+
: typeof body.text?.plain === "string"
|
|
74
|
+
? body.text.plain
|
|
75
|
+
: "";
|
|
76
|
+
let payload = null;
|
|
77
|
+
try {
|
|
78
|
+
payload = textBody ? JSON.parse(textBody) : null;
|
|
79
|
+
} catch {
|
|
80
|
+
payload = null;
|
|
81
|
+
}
|
|
82
|
+
return normalizeEnvelope({
|
|
83
|
+
message_id: body.messageId || message.messageId || message.id || null,
|
|
84
|
+
thread_id: body.threadId || message.threadId || null,
|
|
85
|
+
thread_hint: body.threadId || message.threadId || null,
|
|
86
|
+
request_id: body.headers?.["x-rsp-request-id"] || body.headers?.["X-RSP-Request-Id"] || payload?.request_id || null,
|
|
87
|
+
from: body.from?.address || body.from?.[0]?.address || null,
|
|
88
|
+
to: body.to?.[0]?.address || body.to?.address || null,
|
|
89
|
+
type: body.headers?.["x-rsp-type"] || body.headers?.["X-RSP-Type"] || payload?.type || "email.message",
|
|
90
|
+
body_text: textBody,
|
|
91
|
+
payload,
|
|
92
|
+
attachments
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function createEmailEngineTransportAdapter({
|
|
97
|
+
baseUrl,
|
|
98
|
+
account,
|
|
99
|
+
accessToken,
|
|
100
|
+
receiver = account,
|
|
101
|
+
sender = account,
|
|
102
|
+
pageSize = 20
|
|
103
|
+
}) {
|
|
104
|
+
if (!baseUrl) {
|
|
105
|
+
throw new Error("emailengine_base_url_required");
|
|
106
|
+
}
|
|
107
|
+
if (!account) {
|
|
108
|
+
throw new Error("emailengine_account_required");
|
|
109
|
+
}
|
|
110
|
+
if (!accessToken) {
|
|
111
|
+
throw new Error("emailengine_access_token_required");
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
async send(envelope) {
|
|
116
|
+
const normalized = normalizeEnvelope({
|
|
117
|
+
...envelope,
|
|
118
|
+
from: envelope.from || sender,
|
|
119
|
+
to: envelope.to || receiver
|
|
120
|
+
});
|
|
121
|
+
const response = await request(baseUrl, `/v1/account/${encodeURIComponent(account)}/submit`, {
|
|
122
|
+
method: "POST",
|
|
123
|
+
headers: authHeaders(accessToken, {
|
|
124
|
+
"content-type": "application/json; charset=utf-8"
|
|
125
|
+
}),
|
|
126
|
+
body: JSON.stringify({
|
|
127
|
+
from: normalized.from,
|
|
128
|
+
to: [normalized.to],
|
|
129
|
+
subject: buildEmailSubject(normalized),
|
|
130
|
+
text: normalized.body_text,
|
|
131
|
+
headers: buildRspHeaders(normalized),
|
|
132
|
+
attachments: normalized.attachments.map((attachment) => ({
|
|
133
|
+
filename: attachment.name,
|
|
134
|
+
contentType: attachment.media_type,
|
|
135
|
+
content: attachment.content_base64,
|
|
136
|
+
encoding: "base64"
|
|
137
|
+
}))
|
|
138
|
+
})
|
|
139
|
+
});
|
|
140
|
+
if (response.status !== 200 && response.status !== 202) {
|
|
141
|
+
throw new Error(`emailengine_send_failed:${response.status}`);
|
|
142
|
+
}
|
|
143
|
+
return normalized;
|
|
144
|
+
},
|
|
145
|
+
|
|
146
|
+
async poll({ limit = 10 } = {}) {
|
|
147
|
+
const response = await request(baseUrl, `/v1/account/${encodeURIComponent(account)}/search?path=INBOX`, {
|
|
148
|
+
method: "POST",
|
|
149
|
+
headers: authHeaders(accessToken, {
|
|
150
|
+
"content-type": "application/json; charset=utf-8"
|
|
151
|
+
}),
|
|
152
|
+
body: JSON.stringify({
|
|
153
|
+
page: 0,
|
|
154
|
+
pageSize: Math.max(limit, pageSize),
|
|
155
|
+
search: {
|
|
156
|
+
unseen: true
|
|
157
|
+
}
|
|
158
|
+
})
|
|
159
|
+
});
|
|
160
|
+
if (response.status !== 200) {
|
|
161
|
+
throw new Error(`emailengine_poll_failed:${response.status}`);
|
|
162
|
+
}
|
|
163
|
+
const messages = Array.isArray(response.body?.messages) ? response.body.messages : [];
|
|
164
|
+
const items = [];
|
|
165
|
+
for (const message of messages) {
|
|
166
|
+
if (!isRspEmailSubject(message.subject || "")) {
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
items.push(await loadMessage(baseUrl, account, accessToken, message));
|
|
170
|
+
if (items.length >= limit) {
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return { items };
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
async ack(messageId) {
|
|
178
|
+
const response = await request(baseUrl, `/v1/account/${encodeURIComponent(account)}/message/${encodeURIComponent(messageId)}`, {
|
|
179
|
+
method: "PUT",
|
|
180
|
+
headers: authHeaders(accessToken, {
|
|
181
|
+
"content-type": "application/json; charset=utf-8"
|
|
182
|
+
}),
|
|
183
|
+
body: JSON.stringify({
|
|
184
|
+
flags: {
|
|
185
|
+
add: ["\\Seen"]
|
|
186
|
+
}
|
|
187
|
+
})
|
|
188
|
+
});
|
|
189
|
+
if (response.status !== 200) {
|
|
190
|
+
throw new Error(`emailengine_ack_failed:${response.status}`);
|
|
191
|
+
}
|
|
192
|
+
return { acked: true };
|
|
193
|
+
},
|
|
194
|
+
|
|
195
|
+
async health() {
|
|
196
|
+
const response = await request(baseUrl, `/v1/account/${encodeURIComponent(account)}`, {
|
|
197
|
+
headers: authHeaders(accessToken)
|
|
198
|
+
});
|
|
199
|
+
if (response.status !== 200) {
|
|
200
|
+
throw new Error(`emailengine_health_failed:${response.status}`);
|
|
201
|
+
}
|
|
202
|
+
return {
|
|
203
|
+
ok: true,
|
|
204
|
+
provider: "emailengine",
|
|
205
|
+
version: "API v1",
|
|
206
|
+
account
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@delexec/transport-gmail",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Gmail transport adapter for delegated execution",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/index.js",
|
|
9
|
+
"./package.json": "./package.json"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"src",
|
|
13
|
+
"README.md"
|
|
14
|
+
],
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "public"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"delegated-execution",
|
|
20
|
+
"transport",
|
|
21
|
+
"gmail"
|
|
22
|
+
],
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@delexec/transport-email": "0.1.0"
|
|
25
|
+
}
|
|
26
|
+
}
|