aamp-openclaw-plugin 0.1.40 → 0.1.43
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 +8 -0
- package/bin/aamp-openclaw-plugin.mjs +119 -2
- package/dist/file-store.js +11 -0
- package/dist/file-store.js.map +2 -2
- package/dist/index.js +205 -73
- package/dist/index.js.map +3 -3
- package/package.json +5 -2
- package/skills/SKILL.md +19 -0
- 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/dist/index.js
CHANGED
|
@@ -1196,6 +1196,7 @@ var JmapPushClient = class extends TinyEmitter {
|
|
|
1196
1196
|
|
|
1197
1197
|
// ../sdk/dist/pairing.js
|
|
1198
1198
|
import { randomBytes } from "node:crypto";
|
|
1199
|
+
var DEFAULT_PAIRING_WEB_URL = "https://meshmail.ai/pair";
|
|
1199
1200
|
function normalizeMailbox(value) {
|
|
1200
1201
|
const mailbox = value.trim().toLowerCase();
|
|
1201
1202
|
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(mailbox)) {
|
|
@@ -1243,6 +1244,23 @@ function buildPairingUrl(payload) {
|
|
|
1243
1244
|
}
|
|
1244
1245
|
return url.toString();
|
|
1245
1246
|
}
|
|
1247
|
+
function buildPairingWebUrl(payload, baseUrl2 = DEFAULT_PAIRING_WEB_URL) {
|
|
1248
|
+
const mailbox = normalizeMailbox(payload.mailbox);
|
|
1249
|
+
const pairCode = payload.pairCode.trim();
|
|
1250
|
+
if (!pairCode)
|
|
1251
|
+
throw new Error("pairCode cannot be empty");
|
|
1252
|
+
const url = new URL(baseUrl2);
|
|
1253
|
+
url.searchParams.set("mailbox", mailbox);
|
|
1254
|
+
url.searchParams.set("pair_code", pairCode);
|
|
1255
|
+
const rules = normalizeDispatchContextRules(payload.dispatchContextRules);
|
|
1256
|
+
if (rules) {
|
|
1257
|
+
url.searchParams.set("dispatch_context_rules", encodeBase64UrlJson2(rules));
|
|
1258
|
+
}
|
|
1259
|
+
return url.toString();
|
|
1260
|
+
}
|
|
1261
|
+
function pairingUrlToWebUrl(input, baseUrl2 = DEFAULT_PAIRING_WEB_URL) {
|
|
1262
|
+
return buildPairingWebUrl(parsePairingUrl(input), baseUrl2);
|
|
1263
|
+
}
|
|
1246
1264
|
function createPairingCode(options) {
|
|
1247
1265
|
const pairCode = options.pairCode?.trim() || randomBytes(6).toString("base64url");
|
|
1248
1266
|
const dispatchContextRules = normalizeDispatchContextRules(options.dispatchContextRules);
|
|
@@ -1265,8 +1283,10 @@ function parsePairingUrl(input) {
|
|
|
1265
1283
|
} catch {
|
|
1266
1284
|
throw new Error("Invalid pairing URL");
|
|
1267
1285
|
}
|
|
1268
|
-
|
|
1269
|
-
|
|
1286
|
+
const isDeepLink = url.protocol === "aamp:" && url.hostname === "connect";
|
|
1287
|
+
const isWebLink = (url.protocol === "https:" || url.protocol === "http:") && url.hostname === "meshmail.ai" && url.pathname === "/pair";
|
|
1288
|
+
if (!isDeepLink && !isWebLink) {
|
|
1289
|
+
throw new Error("Pairing URL must start with aamp://connect or https://meshmail.ai/pair");
|
|
1270
1290
|
}
|
|
1271
1291
|
const mailbox = url.searchParams.get("mailbox") ?? "";
|
|
1272
1292
|
const pairCode = url.searchParams.get("pair_code") ?? "";
|
|
@@ -1499,12 +1519,34 @@ var SmtpSender = class _SmtpSender {
|
|
|
1499
1519
|
return false;
|
|
1500
1520
|
return Boolean(this.config.httpBaseUrl && this.config.authToken);
|
|
1501
1521
|
}
|
|
1522
|
+
normalizeAttachments(attachments) {
|
|
1523
|
+
if (!attachments?.length)
|
|
1524
|
+
return void 0;
|
|
1525
|
+
return attachments.map((a) => ({
|
|
1526
|
+
filename: a.filename,
|
|
1527
|
+
contentType: a.contentType,
|
|
1528
|
+
content: typeof a.content === "string" ? Buffer.from(a.content, "base64") : a.content
|
|
1529
|
+
}));
|
|
1530
|
+
}
|
|
1502
1531
|
getJmapAuthHeader() {
|
|
1503
1532
|
if (!this.config.authToken) {
|
|
1504
1533
|
throw new Error("JMAP auth token is not configured");
|
|
1505
1534
|
}
|
|
1506
1535
|
return `Basic ${this.config.authToken}`;
|
|
1507
1536
|
}
|
|
1537
|
+
rewriteUrlToConfiguredOrigin(rawUrl) {
|
|
1538
|
+
const base = this.config.httpBaseUrl?.replace(/\/$/, "");
|
|
1539
|
+
if (!base)
|
|
1540
|
+
return rawUrl;
|
|
1541
|
+
const parsed = new URL(rawUrl, `${base}/`);
|
|
1542
|
+
const configured = new URL(base);
|
|
1543
|
+
parsed.protocol = configured.protocol;
|
|
1544
|
+
parsed.username = configured.username;
|
|
1545
|
+
parsed.password = configured.password;
|
|
1546
|
+
parsed.hostname = configured.hostname;
|
|
1547
|
+
parsed.port = configured.port;
|
|
1548
|
+
return parsed.toString();
|
|
1549
|
+
}
|
|
1508
1550
|
async resolveJmapSession() {
|
|
1509
1551
|
const base = this.config.httpBaseUrl?.replace(/\/$/, "");
|
|
1510
1552
|
if (!base) {
|
|
@@ -1525,7 +1567,8 @@ var SmtpSender = class _SmtpSender {
|
|
|
1525
1567
|
}
|
|
1526
1568
|
return {
|
|
1527
1569
|
accountId,
|
|
1528
|
-
apiUrl: `${base}/jmap
|
|
1570
|
+
apiUrl: `${base}/jmap/`,
|
|
1571
|
+
uploadUrl: this.rewriteUrlToConfiguredOrigin(session.uploadUrl ?? `${base}/jmap/upload/{accountId}/`)
|
|
1529
1572
|
};
|
|
1530
1573
|
})();
|
|
1531
1574
|
}
|
|
@@ -1562,6 +1605,38 @@ var SmtpSender = class _SmtpSender {
|
|
|
1562
1605
|
const data = await res.json();
|
|
1563
1606
|
return data.methodResponses ?? [];
|
|
1564
1607
|
}
|
|
1608
|
+
async uploadSentAttachment(attachment) {
|
|
1609
|
+
const session = await this.resolveJmapSession();
|
|
1610
|
+
const content = typeof attachment.content === "string" ? Buffer.from(attachment.content, "base64") : attachment.content;
|
|
1611
|
+
const uploadUrl = session.uploadUrl.replace(/\{accountId\}|%7BaccountId%7D/gi, encodeURIComponent(session.accountId));
|
|
1612
|
+
const res = await this.fetch(uploadUrl, {
|
|
1613
|
+
method: "POST",
|
|
1614
|
+
headers: {
|
|
1615
|
+
Authorization: this.getJmapAuthHeader(),
|
|
1616
|
+
"Content-Type": attachment.contentType
|
|
1617
|
+
},
|
|
1618
|
+
body: content
|
|
1619
|
+
});
|
|
1620
|
+
const bodyText = await res.text();
|
|
1621
|
+
if (!res.ok) {
|
|
1622
|
+
throw new Error(`JMAP attachment upload failed: ${res.status} ${bodyText}`);
|
|
1623
|
+
}
|
|
1624
|
+
let data;
|
|
1625
|
+
try {
|
|
1626
|
+
data = JSON.parse(bodyText);
|
|
1627
|
+
} catch {
|
|
1628
|
+
throw new Error("JMAP attachment upload returned invalid JSON");
|
|
1629
|
+
}
|
|
1630
|
+
if (!data.blobId) {
|
|
1631
|
+
throw new Error("JMAP attachment upload did not return blobId");
|
|
1632
|
+
}
|
|
1633
|
+
return {
|
|
1634
|
+
blobId: data.blobId,
|
|
1635
|
+
type: data.type ?? attachment.contentType,
|
|
1636
|
+
size: data.size ?? content.byteLength,
|
|
1637
|
+
name: attachment.filename
|
|
1638
|
+
};
|
|
1639
|
+
}
|
|
1565
1640
|
async getSentMailboxId() {
|
|
1566
1641
|
if (!this.sentMailboxIdPromise) {
|
|
1567
1642
|
this.sentMailboxIdPromise = (async () => {
|
|
@@ -1586,20 +1661,43 @@ var SmtpSender = class _SmtpSender {
|
|
|
1586
1661
|
const sentMailboxId = await this.getSentMailboxId();
|
|
1587
1662
|
if (!sentMailboxId)
|
|
1588
1663
|
return;
|
|
1664
|
+
const uploadedAttachments = params.attachments?.length ? await Promise.all(params.attachments.map((attachment) => this.uploadSentAttachment(attachment))) : [];
|
|
1589
1665
|
const emailCreate = {
|
|
1590
1666
|
mailboxIds: { [sentMailboxId]: true },
|
|
1591
1667
|
from: [{ email: params.from }],
|
|
1592
1668
|
to: [{ email: params.to }],
|
|
1593
1669
|
subject: params.subject,
|
|
1594
|
-
|
|
1670
|
+
keywords: { "$seen": true }
|
|
1671
|
+
};
|
|
1672
|
+
if (uploadedAttachments.length) {
|
|
1673
|
+
emailCreate.bodyStructure = {
|
|
1674
|
+
type: "multipart/mixed",
|
|
1675
|
+
subParts: [
|
|
1676
|
+
{ partId: "body", type: "text/plain" },
|
|
1677
|
+
...uploadedAttachments.map((attachment) => ({
|
|
1678
|
+
blobId: attachment.blobId,
|
|
1679
|
+
type: attachment.type,
|
|
1680
|
+
size: attachment.size,
|
|
1681
|
+
name: attachment.name,
|
|
1682
|
+
disposition: "attachment"
|
|
1683
|
+
}))
|
|
1684
|
+
]
|
|
1685
|
+
};
|
|
1686
|
+
emailCreate.bodyValues = {
|
|
1687
|
+
body: {
|
|
1688
|
+
value: params.text,
|
|
1689
|
+
isTruncated: false
|
|
1690
|
+
}
|
|
1691
|
+
};
|
|
1692
|
+
} else {
|
|
1693
|
+
emailCreate.bodyValues = {
|
|
1595
1694
|
body: {
|
|
1596
1695
|
value: params.text,
|
|
1597
1696
|
charset: "utf-8"
|
|
1598
1697
|
}
|
|
1599
|
-
}
|
|
1600
|
-
textBody
|
|
1601
|
-
|
|
1602
|
-
};
|
|
1698
|
+
};
|
|
1699
|
+
emailCreate.textBody = [{ partId: "body", type: "text/plain" }];
|
|
1700
|
+
}
|
|
1603
1701
|
if (params.inReplyTo) {
|
|
1604
1702
|
emailCreate["header:In-Reply-To:asText"] = ` ${sanitize(params.inReplyTo)}`;
|
|
1605
1703
|
}
|
|
@@ -1622,6 +1720,12 @@ var SmtpSender = class _SmtpSender {
|
|
|
1622
1720
|
try {
|
|
1623
1721
|
await this.saveToSent(params);
|
|
1624
1722
|
} catch {
|
|
1723
|
+
if (params.attachments?.length) {
|
|
1724
|
+
try {
|
|
1725
|
+
await this.saveToSent({ ...params, attachments: void 0 });
|
|
1726
|
+
} catch {
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1625
1729
|
}
|
|
1626
1730
|
}
|
|
1627
1731
|
/**
|
|
@@ -1631,6 +1735,7 @@ var SmtpSender = class _SmtpSender {
|
|
|
1631
1735
|
*/
|
|
1632
1736
|
async sendTask(opts) {
|
|
1633
1737
|
const taskId = opts.taskId ?? randomUUID();
|
|
1738
|
+
const attachments = this.normalizeAttachments(opts.attachments);
|
|
1634
1739
|
const aampHeaders = buildDispatchHeaders({
|
|
1635
1740
|
taskId,
|
|
1636
1741
|
priority: opts.priority,
|
|
@@ -1653,12 +1758,8 @@ var SmtpSender = class _SmtpSender {
|
|
|
1653
1758
|
].filter(Boolean).join("\n"),
|
|
1654
1759
|
headers: aampHeaders
|
|
1655
1760
|
};
|
|
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
|
-
}));
|
|
1761
|
+
if (attachments) {
|
|
1762
|
+
sendMailOpts.attachments = attachments;
|
|
1662
1763
|
}
|
|
1663
1764
|
if (this.shouldUseHttpFallback(opts.to)) {
|
|
1664
1765
|
const info2 = await this.sendViaHttp({
|
|
@@ -1666,11 +1767,7 @@ var SmtpSender = class _SmtpSender {
|
|
|
1666
1767
|
subject: sendMailOpts.subject,
|
|
1667
1768
|
text: sendMailOpts.text,
|
|
1668
1769
|
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
|
-
}))
|
|
1770
|
+
attachments
|
|
1674
1771
|
});
|
|
1675
1772
|
await this.saveToSentBestEffort({
|
|
1676
1773
|
from: this.config.user,
|
|
@@ -1678,7 +1775,8 @@ var SmtpSender = class _SmtpSender {
|
|
|
1678
1775
|
subject: sendMailOpts.subject,
|
|
1679
1776
|
text: sendMailOpts.text,
|
|
1680
1777
|
aampHeaders,
|
|
1681
|
-
messageId: info2.messageId
|
|
1778
|
+
messageId: info2.messageId,
|
|
1779
|
+
attachments
|
|
1682
1780
|
});
|
|
1683
1781
|
return { taskId, messageId: info2.messageId ?? "" };
|
|
1684
1782
|
}
|
|
@@ -1689,7 +1787,8 @@ var SmtpSender = class _SmtpSender {
|
|
|
1689
1787
|
subject: sendMailOpts.subject,
|
|
1690
1788
|
text: sendMailOpts.text,
|
|
1691
1789
|
aampHeaders,
|
|
1692
|
-
messageId: info.messageId
|
|
1790
|
+
messageId: info.messageId,
|
|
1791
|
+
attachments
|
|
1693
1792
|
});
|
|
1694
1793
|
return { taskId, messageId: info.messageId ?? "" };
|
|
1695
1794
|
}
|
|
@@ -1697,6 +1796,7 @@ var SmtpSender = class _SmtpSender {
|
|
|
1697
1796
|
* Send a task.result email back to the dispatcher
|
|
1698
1797
|
*/
|
|
1699
1798
|
async sendResult(opts) {
|
|
1799
|
+
const attachments = this.normalizeAttachments(opts.attachments);
|
|
1700
1800
|
const aampHeaders = buildResultHeaders({
|
|
1701
1801
|
taskId: opts.taskId,
|
|
1702
1802
|
status: opts.status,
|
|
@@ -1725,12 +1825,8 @@ Error: ${opts.errorMsg}` : ""
|
|
|
1725
1825
|
mailOpts.inReplyTo = opts.inReplyTo;
|
|
1726
1826
|
mailOpts.references = opts.inReplyTo;
|
|
1727
1827
|
}
|
|
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
|
-
}));
|
|
1828
|
+
if (attachments) {
|
|
1829
|
+
mailOpts.attachments = attachments;
|
|
1734
1830
|
}
|
|
1735
1831
|
if (this.shouldUseHttpFallback(opts.to)) {
|
|
1736
1832
|
const info2 = await this.sendViaHttp({
|
|
@@ -1738,11 +1834,7 @@ Error: ${opts.errorMsg}` : ""
|
|
|
1738
1834
|
subject: mailOpts.subject,
|
|
1739
1835
|
text: mailOpts.text,
|
|
1740
1836
|
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
|
-
}))
|
|
1837
|
+
attachments
|
|
1746
1838
|
});
|
|
1747
1839
|
await this.saveToSentBestEffort({
|
|
1748
1840
|
from: this.config.user,
|
|
@@ -1752,7 +1844,8 @@ Error: ${opts.errorMsg}` : ""
|
|
|
1752
1844
|
aampHeaders,
|
|
1753
1845
|
messageId: info2.messageId,
|
|
1754
1846
|
inReplyTo: opts.inReplyTo,
|
|
1755
|
-
references: opts.inReplyTo
|
|
1847
|
+
references: opts.inReplyTo,
|
|
1848
|
+
attachments
|
|
1756
1849
|
});
|
|
1757
1850
|
return;
|
|
1758
1851
|
}
|
|
@@ -1765,13 +1858,15 @@ Error: ${opts.errorMsg}` : ""
|
|
|
1765
1858
|
aampHeaders,
|
|
1766
1859
|
messageId: info.messageId,
|
|
1767
1860
|
inReplyTo: opts.inReplyTo,
|
|
1768
|
-
references: opts.inReplyTo
|
|
1861
|
+
references: opts.inReplyTo,
|
|
1862
|
+
attachments
|
|
1769
1863
|
});
|
|
1770
1864
|
}
|
|
1771
1865
|
/**
|
|
1772
1866
|
* Send a task.help_needed email when the agent is blocked
|
|
1773
1867
|
*/
|
|
1774
1868
|
async sendHelp(opts) {
|
|
1869
|
+
const attachments = this.normalizeAttachments(opts.attachments);
|
|
1775
1870
|
const aampHeaders = buildHelpHeaders({
|
|
1776
1871
|
taskId: opts.taskId,
|
|
1777
1872
|
question: opts.question,
|
|
@@ -1800,12 +1895,8 @@ ${opts.suggestedOptions.map((o, i) => ` ${i + 1}. ${o}`).join("\n")}` : ""
|
|
|
1800
1895
|
helpMailOpts.inReplyTo = opts.inReplyTo;
|
|
1801
1896
|
helpMailOpts.references = opts.inReplyTo;
|
|
1802
1897
|
}
|
|
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
|
-
}));
|
|
1898
|
+
if (attachments) {
|
|
1899
|
+
helpMailOpts.attachments = attachments;
|
|
1809
1900
|
}
|
|
1810
1901
|
if (this.shouldUseHttpFallback(opts.to)) {
|
|
1811
1902
|
const info2 = await this.sendViaHttp({
|
|
@@ -1813,11 +1904,7 @@ ${opts.suggestedOptions.map((o, i) => ` ${i + 1}. ${o}`).join("\n")}` : ""
|
|
|
1813
1904
|
subject: helpMailOpts.subject,
|
|
1814
1905
|
text: helpMailOpts.text,
|
|
1815
1906
|
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
|
-
}))
|
|
1907
|
+
attachments
|
|
1821
1908
|
});
|
|
1822
1909
|
await this.saveToSentBestEffort({
|
|
1823
1910
|
from: this.config.user,
|
|
@@ -1827,7 +1914,8 @@ ${opts.suggestedOptions.map((o, i) => ` ${i + 1}. ${o}`).join("\n")}` : ""
|
|
|
1827
1914
|
aampHeaders,
|
|
1828
1915
|
messageId: info2.messageId,
|
|
1829
1916
|
inReplyTo: opts.inReplyTo,
|
|
1830
|
-
references: opts.inReplyTo
|
|
1917
|
+
references: opts.inReplyTo,
|
|
1918
|
+
attachments
|
|
1831
1919
|
});
|
|
1832
1920
|
return;
|
|
1833
1921
|
}
|
|
@@ -1840,7 +1928,8 @@ ${opts.suggestedOptions.map((o, i) => ` ${i + 1}. ${o}`).join("\n")}` : ""
|
|
|
1840
1928
|
aampHeaders,
|
|
1841
1929
|
messageId: info.messageId,
|
|
1842
1930
|
inReplyTo: opts.inReplyTo,
|
|
1843
|
-
references: opts.inReplyTo
|
|
1931
|
+
references: opts.inReplyTo,
|
|
1932
|
+
attachments
|
|
1844
1933
|
});
|
|
1845
1934
|
}
|
|
1846
1935
|
/**
|
|
@@ -2296,6 +2385,7 @@ var AampClient = class _AampClient extends TinyEmitter {
|
|
|
2296
2385
|
taskDispatchConcurrency;
|
|
2297
2386
|
pendingTaskDispatches = [];
|
|
2298
2387
|
activeTaskDispatchCount = 0;
|
|
2388
|
+
discoveryPromise;
|
|
2299
2389
|
streamAppendQueues = /* @__PURE__ */ new Map();
|
|
2300
2390
|
constructor(config) {
|
|
2301
2391
|
super();
|
|
@@ -2407,6 +2497,8 @@ var AampClient = class _AampClient extends TinyEmitter {
|
|
|
2407
2497
|
}
|
|
2408
2498
|
static createPairingCode = createPairingCode;
|
|
2409
2499
|
static buildPairingUrl = buildPairingUrl;
|
|
2500
|
+
static buildPairingWebUrl = buildPairingWebUrl;
|
|
2501
|
+
static pairingUrlToWebUrl = pairingUrlToWebUrl;
|
|
2410
2502
|
static parsePairingUrl = parsePairingUrl;
|
|
2411
2503
|
static isPairingUrl = isPairingUrl;
|
|
2412
2504
|
static consumePairingCode = consumePairingCode;
|
|
@@ -2444,6 +2536,38 @@ var AampClient = class _AampClient extends TinyEmitter {
|
|
|
2444
2536
|
...opts.body ? { body: JSON.stringify(opts.body) } : {}
|
|
2445
2537
|
});
|
|
2446
2538
|
}
|
|
2539
|
+
discoverCachedAampService() {
|
|
2540
|
+
if (!this.discoveryPromise) {
|
|
2541
|
+
const discoveryPromise = _AampClient.discoverAampService(this.config.baseUrl, this.config.fetch);
|
|
2542
|
+
this.discoveryPromise = discoveryPromise;
|
|
2543
|
+
void discoveryPromise.catch(() => {
|
|
2544
|
+
if (this.discoveryPromise === discoveryPromise) {
|
|
2545
|
+
this.discoveryPromise = void 0;
|
|
2546
|
+
}
|
|
2547
|
+
});
|
|
2548
|
+
}
|
|
2549
|
+
return this.discoveryPromise;
|
|
2550
|
+
}
|
|
2551
|
+
async callAampApi(opts) {
|
|
2552
|
+
const fetchImpl = this.config.fetch ?? fetch;
|
|
2553
|
+
const discovery = await this.discoverCachedAampService();
|
|
2554
|
+
const base = this.config.baseUrl.replace(/\/$/, "");
|
|
2555
|
+
const apiUrl = new URL(discovery.api.url, `${base}/`);
|
|
2556
|
+
apiUrl.searchParams.set("action", opts.action);
|
|
2557
|
+
for (const [key, value] of Object.entries(opts.query ?? {})) {
|
|
2558
|
+
if (value == null)
|
|
2559
|
+
continue;
|
|
2560
|
+
apiUrl.searchParams.set(key, String(value));
|
|
2561
|
+
}
|
|
2562
|
+
return fetchImpl(apiUrl, {
|
|
2563
|
+
method: opts.method ?? "GET",
|
|
2564
|
+
headers: {
|
|
2565
|
+
...opts.authToken ? { Authorization: `Basic ${opts.authToken}` } : {},
|
|
2566
|
+
...opts.body ? { "Content-Type": "application/json" } : {}
|
|
2567
|
+
},
|
|
2568
|
+
body: opts.body ? JSON.stringify(opts.body) : void 0
|
|
2569
|
+
});
|
|
2570
|
+
}
|
|
2447
2571
|
static async registerMailbox(opts) {
|
|
2448
2572
|
const base = opts.aampHost.replace(/\/$/, "");
|
|
2449
2573
|
const registerRes = await _AampClient.callDiscoveredApi(base, {
|
|
@@ -2669,7 +2793,7 @@ var AampClient = class _AampClient extends TinyEmitter {
|
|
|
2669
2793
|
};
|
|
2670
2794
|
}
|
|
2671
2795
|
async resolveStreamCapability() {
|
|
2672
|
-
const discovery = await
|
|
2796
|
+
const discovery = await this.discoverCachedAampService();
|
|
2673
2797
|
const stream = discovery.capabilities?.stream;
|
|
2674
2798
|
if (!stream?.transport) {
|
|
2675
2799
|
throw new Error("AAMP stream capability is not available on this service");
|
|
@@ -2678,11 +2802,10 @@ var AampClient = class _AampClient extends TinyEmitter {
|
|
|
2678
2802
|
}
|
|
2679
2803
|
async createStream(opts) {
|
|
2680
2804
|
const stream = await this.resolveStreamCapability();
|
|
2681
|
-
const res = await
|
|
2805
|
+
const res = await this.callAampApi({
|
|
2682
2806
|
action: stream.createAction ?? "aamp.stream.create",
|
|
2683
2807
|
method: "POST",
|
|
2684
2808
|
authToken: this.config.mailboxToken,
|
|
2685
|
-
fetch: this.config.fetch,
|
|
2686
2809
|
body: opts
|
|
2687
2810
|
});
|
|
2688
2811
|
if (!res.ok) {
|
|
@@ -2725,11 +2848,10 @@ var AampClient = class _AampClient extends TinyEmitter {
|
|
|
2725
2848
|
}
|
|
2726
2849
|
async dispatchStreamAppend(opts) {
|
|
2727
2850
|
const stream = await this.resolveStreamCapability();
|
|
2728
|
-
const res = await
|
|
2851
|
+
const res = await this.callAampApi({
|
|
2729
2852
|
action: stream.appendAction ?? "aamp.stream.append",
|
|
2730
2853
|
method: "POST",
|
|
2731
2854
|
authToken: this.config.mailboxToken,
|
|
2732
|
-
fetch: this.config.fetch,
|
|
2733
2855
|
body: opts
|
|
2734
2856
|
});
|
|
2735
2857
|
if (!res.ok) {
|
|
@@ -2831,11 +2953,10 @@ var AampClient = class _AampClient extends TinyEmitter {
|
|
|
2831
2953
|
async closeStream(opts) {
|
|
2832
2954
|
await this.flushStreamAppendQueue(opts.streamId);
|
|
2833
2955
|
const stream = await this.resolveStreamCapability();
|
|
2834
|
-
const res = await
|
|
2956
|
+
const res = await this.callAampApi({
|
|
2835
2957
|
action: stream.closeAction ?? "aamp.stream.close",
|
|
2836
2958
|
method: "POST",
|
|
2837
2959
|
authToken: this.config.mailboxToken,
|
|
2838
|
-
fetch: this.config.fetch,
|
|
2839
2960
|
body: opts
|
|
2840
2961
|
});
|
|
2841
2962
|
if (!res.ok) {
|
|
@@ -2846,10 +2967,9 @@ var AampClient = class _AampClient extends TinyEmitter {
|
|
|
2846
2967
|
}
|
|
2847
2968
|
async getTaskStream(opts) {
|
|
2848
2969
|
const stream = await this.resolveStreamCapability();
|
|
2849
|
-
const res = await
|
|
2970
|
+
const res = await this.callAampApi({
|
|
2850
2971
|
action: stream.getAction ?? "aamp.stream.get",
|
|
2851
2972
|
authToken: this.config.mailboxToken,
|
|
2852
|
-
fetch: this.config.fetch,
|
|
2853
2973
|
query: {
|
|
2854
2974
|
...opts.taskId ? { taskId: opts.taskId } : {},
|
|
2855
2975
|
...opts.streamId ? { streamId: opts.streamId } : {}
|
|
@@ -2989,6 +3109,7 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
|
2989
3109
|
import { dirname, join } from "node:path";
|
|
2990
3110
|
import { homedir } from "node:os";
|
|
2991
3111
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
3112
|
+
var DEFAULT_PAIRING_WEB_URL2 = "https://meshmail.ai/pair";
|
|
2992
3113
|
function defaultCredentialsPath() {
|
|
2993
3114
|
return join(homedir(), ".openclaw", "extensions", "aamp-openclaw-plugin", ".credentials.json");
|
|
2994
3115
|
}
|
|
@@ -3062,6 +3183,14 @@ function createPairingCode2(params) {
|
|
|
3062
3183
|
writeFileSync(resolved, JSON.stringify(state, null, 2), "utf-8");
|
|
3063
3184
|
return state;
|
|
3064
3185
|
}
|
|
3186
|
+
function pairingUrlToWebUrl2(connectUrl) {
|
|
3187
|
+
const parsed = new URL(connectUrl);
|
|
3188
|
+
const url = new URL(DEFAULT_PAIRING_WEB_URL2);
|
|
3189
|
+
for (const [key, value] of parsed.searchParams) {
|
|
3190
|
+
url.searchParams.set(key, value);
|
|
3191
|
+
}
|
|
3192
|
+
return url.toString();
|
|
3193
|
+
}
|
|
3065
3194
|
function consumePairingCode2(params) {
|
|
3066
3195
|
const resolved = params.file ?? defaultPairingPath();
|
|
3067
3196
|
if (!existsSync(resolved))
|
|
@@ -3652,24 +3781,24 @@ var src_default = {
|
|
|
3652
3781
|
return;
|
|
3653
3782
|
if (request.to.trim().toLowerCase() !== agentEmail.trim().toLowerCase())
|
|
3654
3783
|
return;
|
|
3784
|
+
const senderPoliciesFile = cfg.senderPoliciesFile ?? defaultSenderPoliciesPath();
|
|
3655
3785
|
const consumed = consumePairingCode2({
|
|
3656
3786
|
file: cfg.pairingFile ?? defaultPairingPath(),
|
|
3657
3787
|
mailbox: agentEmail,
|
|
3658
3788
|
pairCode: request.pairCode
|
|
3659
3789
|
});
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3790
|
+
const pairResponse = consumed ? { success: true } : { success: false, reason: "invalid or expired pair code" };
|
|
3791
|
+
if (pairResponse.success) {
|
|
3792
|
+
pairedSenderPolicies = addPairedSenderPolicy(senderPoliciesFile, {
|
|
3793
|
+
sender: request.from.trim().toLowerCase(),
|
|
3794
|
+
dispatchContextRules: request.dispatchContextRules ?? {},
|
|
3795
|
+
pairedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3796
|
+
});
|
|
3797
|
+
api.logger.info(`[AAMP] Paired sender ${request.from}; sender policy saved to ${senderPoliciesFile}`);
|
|
3798
|
+
} else {
|
|
3799
|
+
api.logger.warn(`[AAMP] Rejected pair.request from ${request.from}: ${pairResponse.reason}`);
|
|
3665
3800
|
}
|
|
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);
|
|
3801
|
+
await sendPairResponse(request, pairResponse.success, pairResponse.reason);
|
|
3673
3802
|
}
|
|
3674
3803
|
async function renderPairingCodeForCurrentAgent() {
|
|
3675
3804
|
const identity = agentEmail ? { email: agentEmail } : loadCachedIdentity(cfg.credentialsFile ?? defaultCredentialsPath());
|
|
@@ -3681,8 +3810,10 @@ var src_default = {
|
|
|
3681
3810
|
mailbox: email,
|
|
3682
3811
|
file: cfg.pairingFile ?? defaultPairingPath()
|
|
3683
3812
|
});
|
|
3684
|
-
const
|
|
3813
|
+
const webUrl = pairingUrlToWebUrl2(pairing.connectUrl);
|
|
3814
|
+
const qr = await renderTerminalQr(webUrl);
|
|
3685
3815
|
api.logger.info(`[AAMP] Pair with AAMP App before ${pairing.expiresAt}: ${pairing.connectUrl}`);
|
|
3816
|
+
api.logger.info(`[AAMP] Web pairing link: ${webUrl}`);
|
|
3686
3817
|
if (qr)
|
|
3687
3818
|
api.logger.info(`
|
|
3688
3819
|
${qr}`);
|
|
@@ -3693,7 +3824,8 @@ ${qr}`);
|
|
|
3693
3824
|
Scan this QR code:
|
|
3694
3825
|
${qr}` : "\nCould not render a terminal QR code.",
|
|
3695
3826
|
`
|
|
3696
|
-
Pairing
|
|
3827
|
+
Pairing link: ${webUrl}`,
|
|
3828
|
+
`Pairing URL: ${pairing.connectUrl}`
|
|
3697
3829
|
].join("\n");
|
|
3698
3830
|
}
|
|
3699
3831
|
function wakeAgentForPendingTask(task) {
|
|
@@ -3785,7 +3917,7 @@ Pairing URL: ${pairing.connectUrl}`
|
|
|
3785
3917
|
file: cfg.pairingFile ?? defaultPairingPath()
|
|
3786
3918
|
});
|
|
3787
3919
|
api.logger.info(`[AAMP] Pair with AAMP App before ${pairing.expiresAt}: ${pairing.connectUrl}`);
|
|
3788
|
-
const qr = await renderTerminalQr(pairing.connectUrl);
|
|
3920
|
+
const qr = await renderTerminalQr(pairingUrlToWebUrl2(pairing.connectUrl));
|
|
3789
3921
|
if (qr)
|
|
3790
3922
|
api.logger.info(`
|
|
3791
3923
|
${qr}`);
|
|
@@ -4682,7 +4814,7 @@ ${lines.join("\n")}`
|
|
|
4682
4814
|
}, { name: "aamp_pending_tasks" });
|
|
4683
4815
|
api.registerTool({
|
|
4684
4816
|
name: "aamp_pairing_code",
|
|
4685
|
-
description:
|
|
4817
|
+
description: 'Generate a fresh five-minute AAMP pairing code for this OpenClaw agent and show a QR code. Use this immediately when the user asks for a pairing code, connect code, QR code, pairing link, invite link, or Chinese requests such as "\u53D1\u5BF9\u63A5\u7801", "\u751F\u6210\u914D\u5BF9\u7801", "\u5F39\u51FA\u4E8C\u7EF4\u7801", or "\u7ED9\u6211\u8FDE\u63A5\u4E8C\u7EF4\u7801".',
|
|
4686
4818
|
parameters: { type: "object", properties: {} },
|
|
4687
4819
|
execute: async () => ({
|
|
4688
4820
|
content: [{ type: "text", text: await renderPairingCodeForCurrentAgent() }]
|
|
@@ -4951,7 +5083,7 @@ Question: ${h.question}`,
|
|
|
4951
5083
|
});
|
|
4952
5084
|
api.registerCommand({
|
|
4953
5085
|
name: "aamp-pair",
|
|
4954
|
-
description: "Show a fresh AAMP pairing QR code for this OpenClaw agent",
|
|
5086
|
+
description: "Show a fresh AAMP pairing QR code, web pairing link, and aamp://connect URL for this OpenClaw agent",
|
|
4955
5087
|
acceptsArgs: false,
|
|
4956
5088
|
requireAuth: false,
|
|
4957
5089
|
handler: async () => ({
|