aamp-openclaw-plugin 0.1.39 → 0.1.42
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 +4 -0
- package/bin/aamp-openclaw-plugin.mjs +106 -0
- package/dist/index.js +126 -57
- package/dist/index.js.map +2 -2
- package/package.json +5 -2
- package/dist/chunk-ORTVVAMV.js +0 -82
- package/dist/chunk-ORTVVAMV.js.map +0 -7
- package/dist/chunk-W4C7IUCH.js +0 -81
- package/dist/chunk-W4C7IUCH.js.map +0 -7
package/README.md
CHANGED
|
@@ -4,6 +4,10 @@ OpenClaw plugin that gives an OpenClaw agent an AAMP mailbox identity.
|
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
7
|
+
Requires OpenClaw `>=2026.3.22`. Both `openclaw plugins install` and
|
|
8
|
+
`npx aamp-openclaw-plugin init` stop before installation when the detected
|
|
9
|
+
OpenClaw version is older.
|
|
10
|
+
|
|
7
11
|
```bash
|
|
8
12
|
npm install aamp-openclaw-plugin
|
|
9
13
|
```
|
|
@@ -15,6 +15,8 @@ const DEFAULT_AAMP_HOST = 'https://meshmail.ai'
|
|
|
15
15
|
const DEFAULT_CREDENTIALS_FILE = '~/.openclaw/extensions/aamp-openclaw-plugin/.credentials.json'
|
|
16
16
|
const DEFAULT_PAIRING_FILE = '~/.openclaw/extensions/aamp-openclaw-plugin/.pairing.json'
|
|
17
17
|
const DEFAULT_SENDER_POLICIES_FILE = '~/.openclaw/extensions/aamp-openclaw-plugin/.sender-policies.json'
|
|
18
|
+
export const MIN_OPENCLAW_VERSION = '2026.3.22'
|
|
19
|
+
export const MIN_OPENCLAW_VERSION_RANGE = `>=${MIN_OPENCLAW_VERSION}`
|
|
18
20
|
const CODING_TOOL_ALLOWLIST = [
|
|
19
21
|
'read',
|
|
20
22
|
'write',
|
|
@@ -200,6 +202,108 @@ export function normalizeBaseUrl(url) {
|
|
|
200
202
|
return `https://${url.replace(/\/$/, '')}`
|
|
201
203
|
}
|
|
202
204
|
|
|
205
|
+
export function parseOpenClawVersion(outputText) {
|
|
206
|
+
const match = String(outputText ?? '').match(/\b(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?)\b/)
|
|
207
|
+
return match?.[1] ?? null
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function parseVersionParts(version) {
|
|
211
|
+
const [withoutBuild] = String(version ?? '').split('+', 1)
|
|
212
|
+
const prereleaseIndex = withoutBuild.indexOf('-')
|
|
213
|
+
const core = prereleaseIndex === -1 ? withoutBuild : withoutBuild.slice(0, prereleaseIndex)
|
|
214
|
+
const prereleaseText = prereleaseIndex === -1 ? '' : withoutBuild.slice(prereleaseIndex + 1)
|
|
215
|
+
const numbers = core.split('.').map((part) => Number(part))
|
|
216
|
+
if (numbers.length !== 3 || numbers.some((part) => !Number.isInteger(part) || part < 0)) {
|
|
217
|
+
return null
|
|
218
|
+
}
|
|
219
|
+
const prerelease = prereleaseText ? prereleaseText.split('.') : []
|
|
220
|
+
return { numbers, prerelease }
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function comparePrereleaseIdentifier(left, right) {
|
|
224
|
+
const leftNumeric = /^\d+$/.test(left)
|
|
225
|
+
const rightNumeric = /^\d+$/.test(right)
|
|
226
|
+
if (leftNumeric && rightNumeric) return Number(left) - Number(right)
|
|
227
|
+
if (leftNumeric) return -1
|
|
228
|
+
if (rightNumeric) return 1
|
|
229
|
+
return left.localeCompare(right)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export function compareOpenClawVersions(leftVersion, rightVersion) {
|
|
233
|
+
const left = parseVersionParts(leftVersion)
|
|
234
|
+
const right = parseVersionParts(rightVersion)
|
|
235
|
+
if (!left || !right) {
|
|
236
|
+
throw new Error(`Invalid OpenClaw version comparison: ${leftVersion} vs ${rightVersion}`)
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
for (let idx = 0; idx < 3; idx += 1) {
|
|
240
|
+
const diff = left.numbers[idx] - right.numbers[idx]
|
|
241
|
+
if (diff !== 0) return diff
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (left.prerelease.length === 0 && right.prerelease.length === 0) return 0
|
|
245
|
+
if (left.prerelease.length === 0) return 1
|
|
246
|
+
if (right.prerelease.length === 0) return -1
|
|
247
|
+
|
|
248
|
+
const maxLength = Math.max(left.prerelease.length, right.prerelease.length)
|
|
249
|
+
for (let idx = 0; idx < maxLength; idx += 1) {
|
|
250
|
+
const leftPart = left.prerelease[idx]
|
|
251
|
+
const rightPart = right.prerelease[idx]
|
|
252
|
+
if (leftPart === undefined) return -1
|
|
253
|
+
if (rightPart === undefined) return 1
|
|
254
|
+
const diff = comparePrereleaseIdentifier(leftPart, rightPart)
|
|
255
|
+
if (diff !== 0) return diff
|
|
256
|
+
}
|
|
257
|
+
return 0
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
export function checkOpenClawVersion(currentVersion, minimumVersion = MIN_OPENCLAW_VERSION) {
|
|
261
|
+
const comparison = compareOpenClawVersions(currentVersion, minimumVersion)
|
|
262
|
+
return {
|
|
263
|
+
ok: comparison >= 0,
|
|
264
|
+
currentVersion,
|
|
265
|
+
minimumVersion,
|
|
266
|
+
range: `>=${minimumVersion}`,
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export function getCurrentOpenClawVersion() {
|
|
271
|
+
const result = spawnSync('openclaw', ['--version'], {
|
|
272
|
+
encoding: 'utf-8',
|
|
273
|
+
})
|
|
274
|
+
|
|
275
|
+
if (result.error) {
|
|
276
|
+
throw new Error(
|
|
277
|
+
`OpenClaw CLI was not found. Install OpenClaw ${MIN_OPENCLAW_VERSION_RANGE} or newer before installing ${PLUGIN_ID}.`,
|
|
278
|
+
)
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (result.status !== 0) {
|
|
282
|
+
const reason = (result.stderr || result.stdout || `exit code ${result.status}`).trim()
|
|
283
|
+
throw new Error(`Could not detect OpenClaw version: ${reason}`)
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const version = parseOpenClawVersion(`${result.stdout ?? ''}\n${result.stderr ?? ''}`)
|
|
287
|
+
if (!version) {
|
|
288
|
+
throw new Error(`Could not parse OpenClaw version from: ${(result.stdout || result.stderr || '').trim()}`)
|
|
289
|
+
}
|
|
290
|
+
return version
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
export function assertOpenClawVersionSupported(currentVersion = getCurrentOpenClawVersion()) {
|
|
294
|
+
const check = checkOpenClawVersion(currentVersion)
|
|
295
|
+
if (check.ok) return check
|
|
296
|
+
|
|
297
|
+
throw new Error(
|
|
298
|
+
[
|
|
299
|
+
`${PLUGIN_ID} requires OpenClaw ${check.range}.`,
|
|
300
|
+
`Detected OpenClaw ${check.currentVersion}.`,
|
|
301
|
+
'Please upgrade OpenClaw, then rerun this installer.',
|
|
302
|
+
'Example: npm install -g openclaw@latest',
|
|
303
|
+
].join('\n'),
|
|
304
|
+
)
|
|
305
|
+
}
|
|
306
|
+
|
|
203
307
|
export function ensurePluginConfig(config, pluginConfig, options = {}) {
|
|
204
308
|
const next = config && typeof config === 'object' ? structuredClone(config) : {}
|
|
205
309
|
if (!next.plugins || typeof next.plugins !== 'object') next.plugins = {}
|
|
@@ -508,6 +612,7 @@ function printHelp() {
|
|
|
508
612
|
}
|
|
509
613
|
|
|
510
614
|
export async function runInit() {
|
|
615
|
+
const openClawVersionCheck = assertOpenClawVersionSupported()
|
|
511
616
|
const configPath = resolveOpenClawConfigPath()
|
|
512
617
|
const existing = readJsonFile(configPath)
|
|
513
618
|
const previousEntry = existing?.plugins?.entries?.[PLUGIN_ID] ?? existing?.plugins?.entries?.aamp
|
|
@@ -530,6 +635,7 @@ export async function runInit() {
|
|
|
530
635
|
const rl = createInterface({ input, output })
|
|
531
636
|
try {
|
|
532
637
|
output.write('AAMP OpenClaw Plugin Setup\n\n')
|
|
638
|
+
output.write(`Detected OpenClaw ${openClawVersionCheck.currentVersion} (${MIN_OPENCLAW_VERSION_RANGE} required)\n\n`)
|
|
533
639
|
|
|
534
640
|
if (previousConfig) {
|
|
535
641
|
output.write(
|
package/dist/index.js
CHANGED
|
@@ -1499,12 +1499,34 @@ var SmtpSender = class _SmtpSender {
|
|
|
1499
1499
|
return false;
|
|
1500
1500
|
return Boolean(this.config.httpBaseUrl && this.config.authToken);
|
|
1501
1501
|
}
|
|
1502
|
+
normalizeAttachments(attachments) {
|
|
1503
|
+
if (!attachments?.length)
|
|
1504
|
+
return void 0;
|
|
1505
|
+
return attachments.map((a) => ({
|
|
1506
|
+
filename: a.filename,
|
|
1507
|
+
contentType: a.contentType,
|
|
1508
|
+
content: typeof a.content === "string" ? Buffer.from(a.content, "base64") : a.content
|
|
1509
|
+
}));
|
|
1510
|
+
}
|
|
1502
1511
|
getJmapAuthHeader() {
|
|
1503
1512
|
if (!this.config.authToken) {
|
|
1504
1513
|
throw new Error("JMAP auth token is not configured");
|
|
1505
1514
|
}
|
|
1506
1515
|
return `Basic ${this.config.authToken}`;
|
|
1507
1516
|
}
|
|
1517
|
+
rewriteUrlToConfiguredOrigin(rawUrl) {
|
|
1518
|
+
const base = this.config.httpBaseUrl?.replace(/\/$/, "");
|
|
1519
|
+
if (!base)
|
|
1520
|
+
return rawUrl;
|
|
1521
|
+
const parsed = new URL(rawUrl, `${base}/`);
|
|
1522
|
+
const configured = new URL(base);
|
|
1523
|
+
parsed.protocol = configured.protocol;
|
|
1524
|
+
parsed.username = configured.username;
|
|
1525
|
+
parsed.password = configured.password;
|
|
1526
|
+
parsed.hostname = configured.hostname;
|
|
1527
|
+
parsed.port = configured.port;
|
|
1528
|
+
return parsed.toString();
|
|
1529
|
+
}
|
|
1508
1530
|
async resolveJmapSession() {
|
|
1509
1531
|
const base = this.config.httpBaseUrl?.replace(/\/$/, "");
|
|
1510
1532
|
if (!base) {
|
|
@@ -1525,7 +1547,8 @@ var SmtpSender = class _SmtpSender {
|
|
|
1525
1547
|
}
|
|
1526
1548
|
return {
|
|
1527
1549
|
accountId,
|
|
1528
|
-
apiUrl: `${base}/jmap
|
|
1550
|
+
apiUrl: `${base}/jmap/`,
|
|
1551
|
+
uploadUrl: this.rewriteUrlToConfiguredOrigin(session.uploadUrl ?? `${base}/jmap/upload/{accountId}/`)
|
|
1529
1552
|
};
|
|
1530
1553
|
})();
|
|
1531
1554
|
}
|
|
@@ -1562,6 +1585,38 @@ var SmtpSender = class _SmtpSender {
|
|
|
1562
1585
|
const data = await res.json();
|
|
1563
1586
|
return data.methodResponses ?? [];
|
|
1564
1587
|
}
|
|
1588
|
+
async uploadSentAttachment(attachment) {
|
|
1589
|
+
const session = await this.resolveJmapSession();
|
|
1590
|
+
const content = typeof attachment.content === "string" ? Buffer.from(attachment.content, "base64") : attachment.content;
|
|
1591
|
+
const uploadUrl = session.uploadUrl.replace(/\{accountId\}|%7BaccountId%7D/gi, encodeURIComponent(session.accountId));
|
|
1592
|
+
const res = await this.fetch(uploadUrl, {
|
|
1593
|
+
method: "POST",
|
|
1594
|
+
headers: {
|
|
1595
|
+
Authorization: this.getJmapAuthHeader(),
|
|
1596
|
+
"Content-Type": attachment.contentType
|
|
1597
|
+
},
|
|
1598
|
+
body: content
|
|
1599
|
+
});
|
|
1600
|
+
const bodyText = await res.text();
|
|
1601
|
+
if (!res.ok) {
|
|
1602
|
+
throw new Error(`JMAP attachment upload failed: ${res.status} ${bodyText}`);
|
|
1603
|
+
}
|
|
1604
|
+
let data;
|
|
1605
|
+
try {
|
|
1606
|
+
data = JSON.parse(bodyText);
|
|
1607
|
+
} catch {
|
|
1608
|
+
throw new Error("JMAP attachment upload returned invalid JSON");
|
|
1609
|
+
}
|
|
1610
|
+
if (!data.blobId) {
|
|
1611
|
+
throw new Error("JMAP attachment upload did not return blobId");
|
|
1612
|
+
}
|
|
1613
|
+
return {
|
|
1614
|
+
blobId: data.blobId,
|
|
1615
|
+
type: data.type ?? attachment.contentType,
|
|
1616
|
+
size: data.size ?? content.byteLength,
|
|
1617
|
+
name: attachment.filename
|
|
1618
|
+
};
|
|
1619
|
+
}
|
|
1565
1620
|
async getSentMailboxId() {
|
|
1566
1621
|
if (!this.sentMailboxIdPromise) {
|
|
1567
1622
|
this.sentMailboxIdPromise = (async () => {
|
|
@@ -1586,20 +1641,43 @@ var SmtpSender = class _SmtpSender {
|
|
|
1586
1641
|
const sentMailboxId = await this.getSentMailboxId();
|
|
1587
1642
|
if (!sentMailboxId)
|
|
1588
1643
|
return;
|
|
1644
|
+
const uploadedAttachments = params.attachments?.length ? await Promise.all(params.attachments.map((attachment) => this.uploadSentAttachment(attachment))) : [];
|
|
1589
1645
|
const emailCreate = {
|
|
1590
1646
|
mailboxIds: { [sentMailboxId]: true },
|
|
1591
1647
|
from: [{ email: params.from }],
|
|
1592
1648
|
to: [{ email: params.to }],
|
|
1593
1649
|
subject: params.subject,
|
|
1594
|
-
|
|
1650
|
+
keywords: { "$seen": true }
|
|
1651
|
+
};
|
|
1652
|
+
if (uploadedAttachments.length) {
|
|
1653
|
+
emailCreate.bodyStructure = {
|
|
1654
|
+
type: "multipart/mixed",
|
|
1655
|
+
subParts: [
|
|
1656
|
+
{ partId: "body", type: "text/plain" },
|
|
1657
|
+
...uploadedAttachments.map((attachment) => ({
|
|
1658
|
+
blobId: attachment.blobId,
|
|
1659
|
+
type: attachment.type,
|
|
1660
|
+
size: attachment.size,
|
|
1661
|
+
name: attachment.name,
|
|
1662
|
+
disposition: "attachment"
|
|
1663
|
+
}))
|
|
1664
|
+
]
|
|
1665
|
+
};
|
|
1666
|
+
emailCreate.bodyValues = {
|
|
1667
|
+
body: {
|
|
1668
|
+
value: params.text,
|
|
1669
|
+
isTruncated: false
|
|
1670
|
+
}
|
|
1671
|
+
};
|
|
1672
|
+
} else {
|
|
1673
|
+
emailCreate.bodyValues = {
|
|
1595
1674
|
body: {
|
|
1596
1675
|
value: params.text,
|
|
1597
1676
|
charset: "utf-8"
|
|
1598
1677
|
}
|
|
1599
|
-
}
|
|
1600
|
-
textBody
|
|
1601
|
-
|
|
1602
|
-
};
|
|
1678
|
+
};
|
|
1679
|
+
emailCreate.textBody = [{ partId: "body", type: "text/plain" }];
|
|
1680
|
+
}
|
|
1603
1681
|
if (params.inReplyTo) {
|
|
1604
1682
|
emailCreate["header:In-Reply-To:asText"] = ` ${sanitize(params.inReplyTo)}`;
|
|
1605
1683
|
}
|
|
@@ -1622,6 +1700,12 @@ var SmtpSender = class _SmtpSender {
|
|
|
1622
1700
|
try {
|
|
1623
1701
|
await this.saveToSent(params);
|
|
1624
1702
|
} catch {
|
|
1703
|
+
if (params.attachments?.length) {
|
|
1704
|
+
try {
|
|
1705
|
+
await this.saveToSent({ ...params, attachments: void 0 });
|
|
1706
|
+
} catch {
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1625
1709
|
}
|
|
1626
1710
|
}
|
|
1627
1711
|
/**
|
|
@@ -1631,6 +1715,7 @@ var SmtpSender = class _SmtpSender {
|
|
|
1631
1715
|
*/
|
|
1632
1716
|
async sendTask(opts) {
|
|
1633
1717
|
const taskId = opts.taskId ?? randomUUID();
|
|
1718
|
+
const attachments = this.normalizeAttachments(opts.attachments);
|
|
1634
1719
|
const aampHeaders = buildDispatchHeaders({
|
|
1635
1720
|
taskId,
|
|
1636
1721
|
priority: opts.priority,
|
|
@@ -1653,12 +1738,8 @@ var SmtpSender = class _SmtpSender {
|
|
|
1653
1738
|
].filter(Boolean).join("\n"),
|
|
1654
1739
|
headers: aampHeaders
|
|
1655
1740
|
};
|
|
1656
|
-
if (
|
|
1657
|
-
sendMailOpts.attachments =
|
|
1658
|
-
filename: a.filename,
|
|
1659
|
-
content: typeof a.content === "string" ? Buffer.from(a.content, "base64") : a.content,
|
|
1660
|
-
contentType: a.contentType
|
|
1661
|
-
}));
|
|
1741
|
+
if (attachments) {
|
|
1742
|
+
sendMailOpts.attachments = attachments;
|
|
1662
1743
|
}
|
|
1663
1744
|
if (this.shouldUseHttpFallback(opts.to)) {
|
|
1664
1745
|
const info2 = await this.sendViaHttp({
|
|
@@ -1666,11 +1747,7 @@ var SmtpSender = class _SmtpSender {
|
|
|
1666
1747
|
subject: sendMailOpts.subject,
|
|
1667
1748
|
text: sendMailOpts.text,
|
|
1668
1749
|
aampHeaders,
|
|
1669
|
-
attachments
|
|
1670
|
-
filename: a.filename,
|
|
1671
|
-
contentType: a.contentType,
|
|
1672
|
-
content: typeof a.content === "string" ? Buffer.from(a.content, "base64") : a.content
|
|
1673
|
-
}))
|
|
1750
|
+
attachments
|
|
1674
1751
|
});
|
|
1675
1752
|
await this.saveToSentBestEffort({
|
|
1676
1753
|
from: this.config.user,
|
|
@@ -1678,7 +1755,8 @@ var SmtpSender = class _SmtpSender {
|
|
|
1678
1755
|
subject: sendMailOpts.subject,
|
|
1679
1756
|
text: sendMailOpts.text,
|
|
1680
1757
|
aampHeaders,
|
|
1681
|
-
messageId: info2.messageId
|
|
1758
|
+
messageId: info2.messageId,
|
|
1759
|
+
attachments
|
|
1682
1760
|
});
|
|
1683
1761
|
return { taskId, messageId: info2.messageId ?? "" };
|
|
1684
1762
|
}
|
|
@@ -1689,7 +1767,8 @@ var SmtpSender = class _SmtpSender {
|
|
|
1689
1767
|
subject: sendMailOpts.subject,
|
|
1690
1768
|
text: sendMailOpts.text,
|
|
1691
1769
|
aampHeaders,
|
|
1692
|
-
messageId: info.messageId
|
|
1770
|
+
messageId: info.messageId,
|
|
1771
|
+
attachments
|
|
1693
1772
|
});
|
|
1694
1773
|
return { taskId, messageId: info.messageId ?? "" };
|
|
1695
1774
|
}
|
|
@@ -1697,6 +1776,7 @@ var SmtpSender = class _SmtpSender {
|
|
|
1697
1776
|
* Send a task.result email back to the dispatcher
|
|
1698
1777
|
*/
|
|
1699
1778
|
async sendResult(opts) {
|
|
1779
|
+
const attachments = this.normalizeAttachments(opts.attachments);
|
|
1700
1780
|
const aampHeaders = buildResultHeaders({
|
|
1701
1781
|
taskId: opts.taskId,
|
|
1702
1782
|
status: opts.status,
|
|
@@ -1725,12 +1805,8 @@ Error: ${opts.errorMsg}` : ""
|
|
|
1725
1805
|
mailOpts.inReplyTo = opts.inReplyTo;
|
|
1726
1806
|
mailOpts.references = opts.inReplyTo;
|
|
1727
1807
|
}
|
|
1728
|
-
if (
|
|
1729
|
-
mailOpts.attachments =
|
|
1730
|
-
filename: a.filename,
|
|
1731
|
-
content: typeof a.content === "string" ? Buffer.from(a.content, "base64") : a.content,
|
|
1732
|
-
contentType: a.contentType
|
|
1733
|
-
}));
|
|
1808
|
+
if (attachments) {
|
|
1809
|
+
mailOpts.attachments = attachments;
|
|
1734
1810
|
}
|
|
1735
1811
|
if (this.shouldUseHttpFallback(opts.to)) {
|
|
1736
1812
|
const info2 = await this.sendViaHttp({
|
|
@@ -1738,11 +1814,7 @@ Error: ${opts.errorMsg}` : ""
|
|
|
1738
1814
|
subject: mailOpts.subject,
|
|
1739
1815
|
text: mailOpts.text,
|
|
1740
1816
|
aampHeaders,
|
|
1741
|
-
attachments
|
|
1742
|
-
filename: a.filename,
|
|
1743
|
-
contentType: a.contentType,
|
|
1744
|
-
content: typeof a.content === "string" ? Buffer.from(a.content, "base64") : a.content
|
|
1745
|
-
}))
|
|
1817
|
+
attachments
|
|
1746
1818
|
});
|
|
1747
1819
|
await this.saveToSentBestEffort({
|
|
1748
1820
|
from: this.config.user,
|
|
@@ -1752,7 +1824,8 @@ Error: ${opts.errorMsg}` : ""
|
|
|
1752
1824
|
aampHeaders,
|
|
1753
1825
|
messageId: info2.messageId,
|
|
1754
1826
|
inReplyTo: opts.inReplyTo,
|
|
1755
|
-
references: opts.inReplyTo
|
|
1827
|
+
references: opts.inReplyTo,
|
|
1828
|
+
attachments
|
|
1756
1829
|
});
|
|
1757
1830
|
return;
|
|
1758
1831
|
}
|
|
@@ -1765,13 +1838,15 @@ Error: ${opts.errorMsg}` : ""
|
|
|
1765
1838
|
aampHeaders,
|
|
1766
1839
|
messageId: info.messageId,
|
|
1767
1840
|
inReplyTo: opts.inReplyTo,
|
|
1768
|
-
references: opts.inReplyTo
|
|
1841
|
+
references: opts.inReplyTo,
|
|
1842
|
+
attachments
|
|
1769
1843
|
});
|
|
1770
1844
|
}
|
|
1771
1845
|
/**
|
|
1772
1846
|
* Send a task.help_needed email when the agent is blocked
|
|
1773
1847
|
*/
|
|
1774
1848
|
async sendHelp(opts) {
|
|
1849
|
+
const attachments = this.normalizeAttachments(opts.attachments);
|
|
1775
1850
|
const aampHeaders = buildHelpHeaders({
|
|
1776
1851
|
taskId: opts.taskId,
|
|
1777
1852
|
question: opts.question,
|
|
@@ -1800,12 +1875,8 @@ ${opts.suggestedOptions.map((o, i) => ` ${i + 1}. ${o}`).join("\n")}` : ""
|
|
|
1800
1875
|
helpMailOpts.inReplyTo = opts.inReplyTo;
|
|
1801
1876
|
helpMailOpts.references = opts.inReplyTo;
|
|
1802
1877
|
}
|
|
1803
|
-
if (
|
|
1804
|
-
helpMailOpts.attachments =
|
|
1805
|
-
filename: a.filename,
|
|
1806
|
-
content: typeof a.content === "string" ? Buffer.from(a.content, "base64") : a.content,
|
|
1807
|
-
contentType: a.contentType
|
|
1808
|
-
}));
|
|
1878
|
+
if (attachments) {
|
|
1879
|
+
helpMailOpts.attachments = attachments;
|
|
1809
1880
|
}
|
|
1810
1881
|
if (this.shouldUseHttpFallback(opts.to)) {
|
|
1811
1882
|
const info2 = await this.sendViaHttp({
|
|
@@ -1813,11 +1884,7 @@ ${opts.suggestedOptions.map((o, i) => ` ${i + 1}. ${o}`).join("\n")}` : ""
|
|
|
1813
1884
|
subject: helpMailOpts.subject,
|
|
1814
1885
|
text: helpMailOpts.text,
|
|
1815
1886
|
aampHeaders,
|
|
1816
|
-
attachments
|
|
1817
|
-
filename: a.filename,
|
|
1818
|
-
contentType: a.contentType,
|
|
1819
|
-
content: typeof a.content === "string" ? Buffer.from(a.content, "base64") : a.content
|
|
1820
|
-
}))
|
|
1887
|
+
attachments
|
|
1821
1888
|
});
|
|
1822
1889
|
await this.saveToSentBestEffort({
|
|
1823
1890
|
from: this.config.user,
|
|
@@ -1827,7 +1894,8 @@ ${opts.suggestedOptions.map((o, i) => ` ${i + 1}. ${o}`).join("\n")}` : ""
|
|
|
1827
1894
|
aampHeaders,
|
|
1828
1895
|
messageId: info2.messageId,
|
|
1829
1896
|
inReplyTo: opts.inReplyTo,
|
|
1830
|
-
references: opts.inReplyTo
|
|
1897
|
+
references: opts.inReplyTo,
|
|
1898
|
+
attachments
|
|
1831
1899
|
});
|
|
1832
1900
|
return;
|
|
1833
1901
|
}
|
|
@@ -1840,7 +1908,8 @@ ${opts.suggestedOptions.map((o, i) => ` ${i + 1}. ${o}`).join("\n")}` : ""
|
|
|
1840
1908
|
aampHeaders,
|
|
1841
1909
|
messageId: info.messageId,
|
|
1842
1910
|
inReplyTo: opts.inReplyTo,
|
|
1843
|
-
references: opts.inReplyTo
|
|
1911
|
+
references: opts.inReplyTo,
|
|
1912
|
+
attachments
|
|
1844
1913
|
});
|
|
1845
1914
|
}
|
|
1846
1915
|
/**
|
|
@@ -3652,24 +3721,24 @@ var src_default = {
|
|
|
3652
3721
|
return;
|
|
3653
3722
|
if (request.to.trim().toLowerCase() !== agentEmail.trim().toLowerCase())
|
|
3654
3723
|
return;
|
|
3724
|
+
const senderPoliciesFile = cfg.senderPoliciesFile ?? defaultSenderPoliciesPath();
|
|
3655
3725
|
const consumed = consumePairingCode2({
|
|
3656
3726
|
file: cfg.pairingFile ?? defaultPairingPath(),
|
|
3657
3727
|
mailbox: agentEmail,
|
|
3658
3728
|
pairCode: request.pairCode
|
|
3659
3729
|
});
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3730
|
+
const pairResponse = consumed ? { success: true } : { success: false, reason: "invalid or expired pair code" };
|
|
3731
|
+
if (pairResponse.success) {
|
|
3732
|
+
pairedSenderPolicies = addPairedSenderPolicy(senderPoliciesFile, {
|
|
3733
|
+
sender: request.from.trim().toLowerCase(),
|
|
3734
|
+
dispatchContextRules: request.dispatchContextRules ?? {},
|
|
3735
|
+
pairedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3736
|
+
});
|
|
3737
|
+
api.logger.info(`[AAMP] Paired sender ${request.from}; sender policy saved to ${senderPoliciesFile}`);
|
|
3738
|
+
} else {
|
|
3739
|
+
api.logger.warn(`[AAMP] Rejected pair.request from ${request.from}: ${pairResponse.reason}`);
|
|
3665
3740
|
}
|
|
3666
|
-
|
|
3667
|
-
sender: request.from.trim().toLowerCase(),
|
|
3668
|
-
dispatchContextRules: request.dispatchContextRules ?? {},
|
|
3669
|
-
pairedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3670
|
-
});
|
|
3671
|
-
api.logger.info(`[AAMP] Paired sender ${request.from}; sender policy saved to ${cfg.senderPoliciesFile ?? defaultSenderPoliciesPath()}`);
|
|
3672
|
-
await sendPairResponse(request, true);
|
|
3741
|
+
await sendPairResponse(request, pairResponse.success, pairResponse.reason);
|
|
3673
3742
|
}
|
|
3674
3743
|
async function renderPairingCodeForCurrentAgent() {
|
|
3675
3744
|
const identity = agentEmail ? { email: agentEmail } : loadCachedIdentity(cfg.credentialsFile ?? defaultCredentialsPath());
|