aamp-openclaw-plugin 0.1.42 → 0.1.44
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 +13 -2
- package/dist/chunk-ORTVVAMV.js +82 -0
- package/dist/chunk-ORTVVAMV.js.map +7 -0
- package/dist/chunk-W4C7IUCH.js +81 -0
- package/dist/chunk-W4C7IUCH.js.map +7 -0
- package/dist/file-store.js +11 -0
- package/dist/file-store.js.map +2 -2
- package/dist/index.js +236 -92
- package/dist/index.js.map +3 -3
- package/package.json +2 -2
- package/skills/SKILL.md +21 -2
package/dist/index.js
CHANGED
|
@@ -910,21 +910,17 @@ var JmapPushClient = class extends TinyEmitter {
|
|
|
910
910
|
this.emit("error", new Error(`JMAP WebSocket handshake failed: ${res.statusCode ?? "unknown"} ${res.statusMessage ?? ""}${headerSummary ? ` | headers: ${headerSummary}` : ""}`));
|
|
911
911
|
this.scheduleReconnect();
|
|
912
912
|
});
|
|
913
|
-
this.ws.on("open",
|
|
914
|
-
this.
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
dataTypes: ["Email"],
|
|
925
|
-
pushState: null
|
|
926
|
-
}));
|
|
927
|
-
this.emit("connected");
|
|
913
|
+
this.ws.on("open", () => {
|
|
914
|
+
void this.handleWebSocketOpen().catch((err) => {
|
|
915
|
+
const reason = `websocket open initialization failed: ${err.message}`;
|
|
916
|
+
this.connecting = false;
|
|
917
|
+
this.connected = false;
|
|
918
|
+
this.stopPingHeartbeat();
|
|
919
|
+
this.startPolling(reason);
|
|
920
|
+
this.emit("error", new Error(`JMAP ${reason}`));
|
|
921
|
+
this.ws?.close();
|
|
922
|
+
this.scheduleReconnect();
|
|
923
|
+
});
|
|
928
924
|
});
|
|
929
925
|
this.ws.on("pong", () => {
|
|
930
926
|
});
|
|
@@ -977,6 +973,22 @@ var JmapPushClient = class extends TinyEmitter {
|
|
|
977
973
|
this.pingTimer = null;
|
|
978
974
|
}
|
|
979
975
|
}
|
|
976
|
+
async handleWebSocketOpen() {
|
|
977
|
+
this.connecting = false;
|
|
978
|
+
this.connected = true;
|
|
979
|
+
this.stopPolling();
|
|
980
|
+
this.startPingHeartbeat();
|
|
981
|
+
const accountId = this.session?.primaryAccounts["urn:ietf:params:jmap:mail"];
|
|
982
|
+
if (accountId && this.emailState === null) {
|
|
983
|
+
await this.initEmailState(accountId);
|
|
984
|
+
}
|
|
985
|
+
this.ws?.send(JSON.stringify({
|
|
986
|
+
"@type": "WebSocketPushEnable",
|
|
987
|
+
dataTypes: ["Email"],
|
|
988
|
+
pushState: null
|
|
989
|
+
}));
|
|
990
|
+
this.emit("connected");
|
|
991
|
+
}
|
|
980
992
|
startSafetySync() {
|
|
981
993
|
if (this.safetySyncTimer)
|
|
982
994
|
return;
|
|
@@ -1196,6 +1208,7 @@ var JmapPushClient = class extends TinyEmitter {
|
|
|
1196
1208
|
|
|
1197
1209
|
// ../sdk/dist/pairing.js
|
|
1198
1210
|
import { randomBytes } from "node:crypto";
|
|
1211
|
+
var DEFAULT_PAIRING_WEB_URL = "https://meshmail.ai/pair";
|
|
1199
1212
|
function normalizeMailbox(value) {
|
|
1200
1213
|
const mailbox = value.trim().toLowerCase();
|
|
1201
1214
|
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(mailbox)) {
|
|
@@ -1243,6 +1256,23 @@ function buildPairingUrl(payload) {
|
|
|
1243
1256
|
}
|
|
1244
1257
|
return url.toString();
|
|
1245
1258
|
}
|
|
1259
|
+
function buildPairingWebUrl(payload, baseUrl2 = DEFAULT_PAIRING_WEB_URL) {
|
|
1260
|
+
const mailbox = normalizeMailbox(payload.mailbox);
|
|
1261
|
+
const pairCode = payload.pairCode.trim();
|
|
1262
|
+
if (!pairCode)
|
|
1263
|
+
throw new Error("pairCode cannot be empty");
|
|
1264
|
+
const url = new URL(baseUrl2);
|
|
1265
|
+
url.searchParams.set("mailbox", mailbox);
|
|
1266
|
+
url.searchParams.set("pair_code", pairCode);
|
|
1267
|
+
const rules = normalizeDispatchContextRules(payload.dispatchContextRules);
|
|
1268
|
+
if (rules) {
|
|
1269
|
+
url.searchParams.set("dispatch_context_rules", encodeBase64UrlJson2(rules));
|
|
1270
|
+
}
|
|
1271
|
+
return url.toString();
|
|
1272
|
+
}
|
|
1273
|
+
function pairingUrlToWebUrl(input, baseUrl2 = DEFAULT_PAIRING_WEB_URL) {
|
|
1274
|
+
return buildPairingWebUrl(parsePairingUrl(input), baseUrl2);
|
|
1275
|
+
}
|
|
1246
1276
|
function createPairingCode(options) {
|
|
1247
1277
|
const pairCode = options.pairCode?.trim() || randomBytes(6).toString("base64url");
|
|
1248
1278
|
const dispatchContextRules = normalizeDispatchContextRules(options.dispatchContextRules);
|
|
@@ -1265,8 +1295,10 @@ function parsePairingUrl(input) {
|
|
|
1265
1295
|
} catch {
|
|
1266
1296
|
throw new Error("Invalid pairing URL");
|
|
1267
1297
|
}
|
|
1268
|
-
|
|
1269
|
-
|
|
1298
|
+
const isDeepLink = url.protocol === "aamp:" && url.hostname === "connect";
|
|
1299
|
+
const isWebLink = (url.protocol === "https:" || url.protocol === "http:") && url.hostname === "meshmail.ai" && url.pathname === "/pair";
|
|
1300
|
+
if (!isDeepLink && !isWebLink) {
|
|
1301
|
+
throw new Error("Pairing URL must start with aamp://connect or https://meshmail.ai/pair");
|
|
1270
1302
|
}
|
|
1271
1303
|
const mailbox = url.searchParams.get("mailbox") ?? "";
|
|
1272
1304
|
const pairCode = url.searchParams.get("pair_code") ?? "";
|
|
@@ -1585,37 +1617,102 @@ var SmtpSender = class _SmtpSender {
|
|
|
1585
1617
|
const data = await res.json();
|
|
1586
1618
|
return data.methodResponses ?? [];
|
|
1587
1619
|
}
|
|
1588
|
-
async
|
|
1620
|
+
async uploadJmapBlob(params) {
|
|
1589
1621
|
const session = await this.resolveJmapSession();
|
|
1590
|
-
const content = typeof attachment.content === "string" ? Buffer.from(attachment.content, "base64") : attachment.content;
|
|
1591
1622
|
const uploadUrl = session.uploadUrl.replace(/\{accountId\}|%7BaccountId%7D/gi, encodeURIComponent(session.accountId));
|
|
1592
1623
|
const res = await this.fetch(uploadUrl, {
|
|
1593
1624
|
method: "POST",
|
|
1594
1625
|
headers: {
|
|
1595
1626
|
Authorization: this.getJmapAuthHeader(),
|
|
1596
|
-
"Content-Type":
|
|
1627
|
+
"Content-Type": params.contentType
|
|
1597
1628
|
},
|
|
1598
|
-
body: content
|
|
1629
|
+
body: params.content
|
|
1599
1630
|
});
|
|
1600
1631
|
const bodyText = await res.text();
|
|
1601
1632
|
if (!res.ok) {
|
|
1602
|
-
throw new Error(`JMAP
|
|
1633
|
+
throw new Error(`JMAP blob upload failed: ${res.status} ${bodyText}`);
|
|
1603
1634
|
}
|
|
1604
1635
|
let data;
|
|
1605
1636
|
try {
|
|
1606
1637
|
data = JSON.parse(bodyText);
|
|
1607
1638
|
} catch {
|
|
1608
|
-
throw new Error("JMAP
|
|
1639
|
+
throw new Error("JMAP blob upload returned invalid JSON");
|
|
1609
1640
|
}
|
|
1610
1641
|
if (!data.blobId) {
|
|
1611
|
-
throw new Error("JMAP
|
|
1642
|
+
throw new Error("JMAP blob upload did not return blobId");
|
|
1612
1643
|
}
|
|
1613
1644
|
return {
|
|
1614
1645
|
blobId: data.blobId,
|
|
1615
|
-
type: data.type ??
|
|
1616
|
-
size: data.size ?? content.byteLength
|
|
1617
|
-
|
|
1646
|
+
type: data.type ?? params.contentType,
|
|
1647
|
+
size: data.size ?? params.content.byteLength
|
|
1648
|
+
};
|
|
1649
|
+
}
|
|
1650
|
+
async buildRawSentMessage(params) {
|
|
1651
|
+
const rawTransport = createTransport({
|
|
1652
|
+
streamTransport: true,
|
|
1653
|
+
buffer: true,
|
|
1654
|
+
newline: "unix"
|
|
1655
|
+
});
|
|
1656
|
+
const mailOptions = {
|
|
1657
|
+
from: params.from,
|
|
1658
|
+
to: params.to,
|
|
1659
|
+
subject: params.subject,
|
|
1660
|
+
text: params.text,
|
|
1661
|
+
headers: params.aampHeaders
|
|
1618
1662
|
};
|
|
1663
|
+
if (params.messageId)
|
|
1664
|
+
mailOptions.messageId = sanitize(params.messageId);
|
|
1665
|
+
if (params.inReplyTo)
|
|
1666
|
+
mailOptions.inReplyTo = params.inReplyTo;
|
|
1667
|
+
if (params.references)
|
|
1668
|
+
mailOptions.references = params.references;
|
|
1669
|
+
if (params.attachments?.length) {
|
|
1670
|
+
mailOptions.attachments = params.attachments.map((attachment) => ({
|
|
1671
|
+
filename: attachment.filename,
|
|
1672
|
+
contentType: attachment.contentType,
|
|
1673
|
+
content: typeof attachment.content === "string" ? Buffer.from(attachment.content, "base64") : attachment.content
|
|
1674
|
+
}));
|
|
1675
|
+
}
|
|
1676
|
+
const info = await rawTransport.sendMail(mailOptions);
|
|
1677
|
+
const rawMessage = info.message;
|
|
1678
|
+
if (Buffer.isBuffer(rawMessage))
|
|
1679
|
+
return rawMessage;
|
|
1680
|
+
if (rawMessage instanceof Uint8Array)
|
|
1681
|
+
return Buffer.from(rawMessage);
|
|
1682
|
+
if (typeof rawMessage === "string")
|
|
1683
|
+
return Buffer.from(rawMessage);
|
|
1684
|
+
throw new Error("Raw message generation did not return a Buffer");
|
|
1685
|
+
}
|
|
1686
|
+
async importRawSentMessage(params) {
|
|
1687
|
+
if (!this.canPersistSentCopy())
|
|
1688
|
+
return;
|
|
1689
|
+
const sentMailboxId = await this.getSentMailboxId();
|
|
1690
|
+
if (!sentMailboxId)
|
|
1691
|
+
return;
|
|
1692
|
+
const rawMessage = await this.buildRawSentMessage(params);
|
|
1693
|
+
const uploadedMessage = await this.uploadJmapBlob({
|
|
1694
|
+
content: rawMessage,
|
|
1695
|
+
contentType: "message/rfc822"
|
|
1696
|
+
});
|
|
1697
|
+
const responses = await this.jmapCall([
|
|
1698
|
+
[
|
|
1699
|
+
"Email/import",
|
|
1700
|
+
{
|
|
1701
|
+
emails: {
|
|
1702
|
+
sent1: {
|
|
1703
|
+
blobId: uploadedMessage.blobId,
|
|
1704
|
+
mailboxIds: { [sentMailboxId]: true },
|
|
1705
|
+
keywords: { "$seen": true }
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
},
|
|
1709
|
+
"import1"
|
|
1710
|
+
]
|
|
1711
|
+
]);
|
|
1712
|
+
const result = responses.find(([name]) => name === "Email/import")?.[1];
|
|
1713
|
+
if (result?.notImported?.sent1) {
|
|
1714
|
+
throw new Error(`JMAP sent message import failed: ${JSON.stringify(result.notImported.sent1)}`);
|
|
1715
|
+
}
|
|
1619
1716
|
}
|
|
1620
1717
|
async getSentMailboxId() {
|
|
1621
1718
|
if (!this.sentMailboxIdPromise) {
|
|
@@ -1638,10 +1735,13 @@ var SmtpSender = class _SmtpSender {
|
|
|
1638
1735
|
async saveToSent(params) {
|
|
1639
1736
|
if (!this.canPersistSentCopy())
|
|
1640
1737
|
return;
|
|
1738
|
+
if (params.attachments?.length) {
|
|
1739
|
+
await this.importRawSentMessage(params);
|
|
1740
|
+
return;
|
|
1741
|
+
}
|
|
1641
1742
|
const sentMailboxId = await this.getSentMailboxId();
|
|
1642
1743
|
if (!sentMailboxId)
|
|
1643
1744
|
return;
|
|
1644
|
-
const uploadedAttachments = params.attachments?.length ? await Promise.all(params.attachments.map((attachment) => this.uploadSentAttachment(attachment))) : [];
|
|
1645
1745
|
const emailCreate = {
|
|
1646
1746
|
mailboxIds: { [sentMailboxId]: true },
|
|
1647
1747
|
from: [{ email: params.from }],
|
|
@@ -1649,35 +1749,13 @@ var SmtpSender = class _SmtpSender {
|
|
|
1649
1749
|
subject: params.subject,
|
|
1650
1750
|
keywords: { "$seen": true }
|
|
1651
1751
|
};
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
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 = {
|
|
1674
|
-
body: {
|
|
1675
|
-
value: params.text,
|
|
1676
|
-
charset: "utf-8"
|
|
1677
|
-
}
|
|
1678
|
-
};
|
|
1679
|
-
emailCreate.textBody = [{ partId: "body", type: "text/plain" }];
|
|
1680
|
-
}
|
|
1752
|
+
emailCreate.bodyValues = {
|
|
1753
|
+
body: {
|
|
1754
|
+
value: params.text,
|
|
1755
|
+
charset: "utf-8"
|
|
1756
|
+
}
|
|
1757
|
+
};
|
|
1758
|
+
emailCreate.textBody = [{ partId: "body", type: "text/plain" }];
|
|
1681
1759
|
if (params.inReplyTo) {
|
|
1682
1760
|
emailCreate["header:In-Reply-To:asText"] = ` ${sanitize(params.inReplyTo)}`;
|
|
1683
1761
|
}
|
|
@@ -2350,6 +2428,25 @@ function buildRegisteredCommandDispatchPayload(opts) {
|
|
|
2350
2428
|
};
|
|
2351
2429
|
}
|
|
2352
2430
|
var DEFAULT_TASK_DISPATCH_CONCURRENCY = 10;
|
|
2431
|
+
function asStreamRecord(value) {
|
|
2432
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
2433
|
+
return void 0;
|
|
2434
|
+
return value;
|
|
2435
|
+
}
|
|
2436
|
+
function normalizeSseStreamEvent(params) {
|
|
2437
|
+
const record = asStreamRecord(params.data) ?? {};
|
|
2438
|
+
const payload = asStreamRecord(record.payload) ?? record;
|
|
2439
|
+
const seqValue = Number(record.seq);
|
|
2440
|
+
return {
|
|
2441
|
+
id: typeof record.id === "string" && record.id ? record.id : params.eventId,
|
|
2442
|
+
streamId: String(record.streamId ?? params.streamId),
|
|
2443
|
+
taskId: String(record.taskId ?? ""),
|
|
2444
|
+
seq: Number.isFinite(seqValue) ? seqValue : params.seq,
|
|
2445
|
+
timestamp: String(record.timestamp ?? (/* @__PURE__ */ new Date()).toISOString()),
|
|
2446
|
+
type: String(record.type ?? params.eventType ?? "message"),
|
|
2447
|
+
payload
|
|
2448
|
+
};
|
|
2449
|
+
}
|
|
2353
2450
|
function normalizeTaskDispatchConcurrency(value) {
|
|
2354
2451
|
if (value == null)
|
|
2355
2452
|
return DEFAULT_TASK_DISPATCH_CONCURRENCY;
|
|
@@ -2365,6 +2462,7 @@ var AampClient = class _AampClient extends TinyEmitter {
|
|
|
2365
2462
|
taskDispatchConcurrency;
|
|
2366
2463
|
pendingTaskDispatches = [];
|
|
2367
2464
|
activeTaskDispatchCount = 0;
|
|
2465
|
+
discoveryPromise;
|
|
2368
2466
|
streamAppendQueues = /* @__PURE__ */ new Map();
|
|
2369
2467
|
constructor(config) {
|
|
2370
2468
|
super();
|
|
@@ -2476,6 +2574,8 @@ var AampClient = class _AampClient extends TinyEmitter {
|
|
|
2476
2574
|
}
|
|
2477
2575
|
static createPairingCode = createPairingCode;
|
|
2478
2576
|
static buildPairingUrl = buildPairingUrl;
|
|
2577
|
+
static buildPairingWebUrl = buildPairingWebUrl;
|
|
2578
|
+
static pairingUrlToWebUrl = pairingUrlToWebUrl;
|
|
2479
2579
|
static parsePairingUrl = parsePairingUrl;
|
|
2480
2580
|
static isPairingUrl = isPairingUrl;
|
|
2481
2581
|
static consumePairingCode = consumePairingCode;
|
|
@@ -2513,6 +2613,38 @@ var AampClient = class _AampClient extends TinyEmitter {
|
|
|
2513
2613
|
...opts.body ? { body: JSON.stringify(opts.body) } : {}
|
|
2514
2614
|
});
|
|
2515
2615
|
}
|
|
2616
|
+
discoverCachedAampService() {
|
|
2617
|
+
if (!this.discoveryPromise) {
|
|
2618
|
+
const discoveryPromise = _AampClient.discoverAampService(this.config.baseUrl, this.config.fetch);
|
|
2619
|
+
this.discoveryPromise = discoveryPromise;
|
|
2620
|
+
void discoveryPromise.catch(() => {
|
|
2621
|
+
if (this.discoveryPromise === discoveryPromise) {
|
|
2622
|
+
this.discoveryPromise = void 0;
|
|
2623
|
+
}
|
|
2624
|
+
});
|
|
2625
|
+
}
|
|
2626
|
+
return this.discoveryPromise;
|
|
2627
|
+
}
|
|
2628
|
+
async callAampApi(opts) {
|
|
2629
|
+
const fetchImpl = this.config.fetch ?? fetch;
|
|
2630
|
+
const discovery = await this.discoverCachedAampService();
|
|
2631
|
+
const base = this.config.baseUrl.replace(/\/$/, "");
|
|
2632
|
+
const apiUrl = new URL(discovery.api.url, `${base}/`);
|
|
2633
|
+
apiUrl.searchParams.set("action", opts.action);
|
|
2634
|
+
for (const [key, value] of Object.entries(opts.query ?? {})) {
|
|
2635
|
+
if (value == null)
|
|
2636
|
+
continue;
|
|
2637
|
+
apiUrl.searchParams.set(key, String(value));
|
|
2638
|
+
}
|
|
2639
|
+
return fetchImpl(apiUrl, {
|
|
2640
|
+
method: opts.method ?? "GET",
|
|
2641
|
+
headers: {
|
|
2642
|
+
...opts.authToken ? { Authorization: `Basic ${opts.authToken}` } : {},
|
|
2643
|
+
...opts.body ? { "Content-Type": "application/json" } : {}
|
|
2644
|
+
},
|
|
2645
|
+
body: opts.body ? JSON.stringify(opts.body) : void 0
|
|
2646
|
+
});
|
|
2647
|
+
}
|
|
2516
2648
|
static async registerMailbox(opts) {
|
|
2517
2649
|
const base = opts.aampHost.replace(/\/$/, "");
|
|
2518
2650
|
const registerRes = await _AampClient.callDiscoveredApi(base, {
|
|
@@ -2738,7 +2870,7 @@ var AampClient = class _AampClient extends TinyEmitter {
|
|
|
2738
2870
|
};
|
|
2739
2871
|
}
|
|
2740
2872
|
async resolveStreamCapability() {
|
|
2741
|
-
const discovery = await
|
|
2873
|
+
const discovery = await this.discoverCachedAampService();
|
|
2742
2874
|
const stream = discovery.capabilities?.stream;
|
|
2743
2875
|
if (!stream?.transport) {
|
|
2744
2876
|
throw new Error("AAMP stream capability is not available on this service");
|
|
@@ -2747,11 +2879,10 @@ var AampClient = class _AampClient extends TinyEmitter {
|
|
|
2747
2879
|
}
|
|
2748
2880
|
async createStream(opts) {
|
|
2749
2881
|
const stream = await this.resolveStreamCapability();
|
|
2750
|
-
const res = await
|
|
2882
|
+
const res = await this.callAampApi({
|
|
2751
2883
|
action: stream.createAction ?? "aamp.stream.create",
|
|
2752
2884
|
method: "POST",
|
|
2753
2885
|
authToken: this.config.mailboxToken,
|
|
2754
|
-
fetch: this.config.fetch,
|
|
2755
2886
|
body: opts
|
|
2756
2887
|
});
|
|
2757
2888
|
if (!res.ok) {
|
|
@@ -2794,11 +2925,10 @@ var AampClient = class _AampClient extends TinyEmitter {
|
|
|
2794
2925
|
}
|
|
2795
2926
|
async dispatchStreamAppend(opts) {
|
|
2796
2927
|
const stream = await this.resolveStreamCapability();
|
|
2797
|
-
const res = await
|
|
2928
|
+
const res = await this.callAampApi({
|
|
2798
2929
|
action: stream.appendAction ?? "aamp.stream.append",
|
|
2799
2930
|
method: "POST",
|
|
2800
2931
|
authToken: this.config.mailboxToken,
|
|
2801
|
-
fetch: this.config.fetch,
|
|
2802
2932
|
body: opts
|
|
2803
2933
|
});
|
|
2804
2934
|
if (!res.ok) {
|
|
@@ -2900,11 +3030,10 @@ var AampClient = class _AampClient extends TinyEmitter {
|
|
|
2900
3030
|
async closeStream(opts) {
|
|
2901
3031
|
await this.flushStreamAppendQueue(opts.streamId);
|
|
2902
3032
|
const stream = await this.resolveStreamCapability();
|
|
2903
|
-
const res = await
|
|
3033
|
+
const res = await this.callAampApi({
|
|
2904
3034
|
action: stream.closeAction ?? "aamp.stream.close",
|
|
2905
3035
|
method: "POST",
|
|
2906
3036
|
authToken: this.config.mailboxToken,
|
|
2907
|
-
fetch: this.config.fetch,
|
|
2908
3037
|
body: opts
|
|
2909
3038
|
});
|
|
2910
3039
|
if (!res.ok) {
|
|
@@ -2915,10 +3044,9 @@ var AampClient = class _AampClient extends TinyEmitter {
|
|
|
2915
3044
|
}
|
|
2916
3045
|
async getTaskStream(opts) {
|
|
2917
3046
|
const stream = await this.resolveStreamCapability();
|
|
2918
|
-
const res = await
|
|
3047
|
+
const res = await this.callAampApi({
|
|
2919
3048
|
action: stream.getAction ?? "aamp.stream.get",
|
|
2920
3049
|
authToken: this.config.mailboxToken,
|
|
2921
|
-
fetch: this.config.fetch,
|
|
2922
3050
|
query: {
|
|
2923
3051
|
...opts.taskId ? { taskId: opts.taskId } : {},
|
|
2924
3052
|
...opts.streamId ? { streamId: opts.streamId } : {}
|
|
@@ -2949,7 +3077,8 @@ var AampClient = class _AampClient extends TinyEmitter {
|
|
|
2949
3077
|
const res = await fetchImpl(url, {
|
|
2950
3078
|
headers: {
|
|
2951
3079
|
Authorization: `Basic ${this.config.mailboxToken}`,
|
|
2952
|
-
Accept: "text/event-stream"
|
|
3080
|
+
Accept: "text/event-stream",
|
|
3081
|
+
...opts.lastEventId ? { "Last-Event-ID": opts.lastEventId } : {}
|
|
2953
3082
|
},
|
|
2954
3083
|
signal: controller.signal
|
|
2955
3084
|
});
|
|
@@ -2963,16 +3092,19 @@ var AampClient = class _AampClient extends TinyEmitter {
|
|
|
2963
3092
|
let currentEvent = "message";
|
|
2964
3093
|
let currentId = "";
|
|
2965
3094
|
let currentData = [];
|
|
3095
|
+
let fallbackSeq = 0;
|
|
2966
3096
|
const flush = () => {
|
|
2967
3097
|
if (!currentData.length)
|
|
2968
3098
|
return;
|
|
2969
3099
|
try {
|
|
2970
|
-
|
|
2971
|
-
handlers.onEvent({
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
3100
|
+
fallbackSeq += 1;
|
|
3101
|
+
handlers.onEvent(normalizeSseStreamEvent({
|
|
3102
|
+
data: JSON.parse(currentData.join("\n")),
|
|
3103
|
+
streamId,
|
|
3104
|
+
eventType: currentEvent,
|
|
3105
|
+
eventId: currentId || void 0,
|
|
3106
|
+
seq: fallbackSeq
|
|
3107
|
+
}));
|
|
2976
3108
|
} catch (err) {
|
|
2977
3109
|
handlers.onError?.(err);
|
|
2978
3110
|
} finally {
|
|
@@ -3058,6 +3190,7 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
|
3058
3190
|
import { dirname, join } from "node:path";
|
|
3059
3191
|
import { homedir } from "node:os";
|
|
3060
3192
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
3193
|
+
var DEFAULT_PAIRING_WEB_URL2 = "https://meshmail.ai/pair";
|
|
3061
3194
|
function defaultCredentialsPath() {
|
|
3062
3195
|
return join(homedir(), ".openclaw", "extensions", "aamp-openclaw-plugin", ".credentials.json");
|
|
3063
3196
|
}
|
|
@@ -3131,6 +3264,14 @@ function createPairingCode2(params) {
|
|
|
3131
3264
|
writeFileSync(resolved, JSON.stringify(state, null, 2), "utf-8");
|
|
3132
3265
|
return state;
|
|
3133
3266
|
}
|
|
3267
|
+
function pairingUrlToWebUrl2(connectUrl) {
|
|
3268
|
+
const parsed = new URL(connectUrl);
|
|
3269
|
+
const url = new URL(DEFAULT_PAIRING_WEB_URL2);
|
|
3270
|
+
for (const [key, value] of parsed.searchParams) {
|
|
3271
|
+
url.searchParams.set(key, value);
|
|
3272
|
+
}
|
|
3273
|
+
return url.toString();
|
|
3274
|
+
}
|
|
3134
3275
|
function consumePairingCode2(params) {
|
|
3135
3276
|
const resolved = params.file ?? defaultPairingPath();
|
|
3136
3277
|
if (!existsSync(resolved))
|
|
@@ -3317,8 +3458,11 @@ async function ensureTaskStream(task) {
|
|
|
3317
3458
|
});
|
|
3318
3459
|
await aampClient.appendStreamEvent({
|
|
3319
3460
|
streamId: created.streamId,
|
|
3320
|
-
type: "
|
|
3321
|
-
payload: {
|
|
3461
|
+
type: "todo",
|
|
3462
|
+
payload: {
|
|
3463
|
+
items: [{ id: "openclaw-queued", content: "Task queued in OpenClaw", status: "in_progress" }],
|
|
3464
|
+
summary: "Task queued in OpenClaw"
|
|
3465
|
+
}
|
|
3322
3466
|
});
|
|
3323
3467
|
activeTaskStreams.set(task.taskId, created.streamId);
|
|
3324
3468
|
return created.streamId;
|
|
@@ -3750,8 +3894,10 @@ var src_default = {
|
|
|
3750
3894
|
mailbox: email,
|
|
3751
3895
|
file: cfg.pairingFile ?? defaultPairingPath()
|
|
3752
3896
|
});
|
|
3753
|
-
const
|
|
3897
|
+
const webUrl = pairingUrlToWebUrl2(pairing.connectUrl);
|
|
3898
|
+
const qr = await renderTerminalQr(webUrl);
|
|
3754
3899
|
api.logger.info(`[AAMP] Pair with AAMP App before ${pairing.expiresAt}: ${pairing.connectUrl}`);
|
|
3900
|
+
api.logger.info(`[AAMP] Web pairing link: ${webUrl}`);
|
|
3755
3901
|
if (qr)
|
|
3756
3902
|
api.logger.info(`
|
|
3757
3903
|
${qr}`);
|
|
@@ -3762,7 +3908,8 @@ ${qr}`);
|
|
|
3762
3908
|
Scan this QR code:
|
|
3763
3909
|
${qr}` : "\nCould not render a terminal QR code.",
|
|
3764
3910
|
`
|
|
3765
|
-
Pairing
|
|
3911
|
+
Pairing link: ${webUrl}`,
|
|
3912
|
+
`Pairing URL: ${pairing.connectUrl}`
|
|
3766
3913
|
].join("\n");
|
|
3767
3914
|
}
|
|
3768
3915
|
function wakeAgentForPendingTask(task) {
|
|
@@ -3854,7 +4001,7 @@ Pairing URL: ${pairing.connectUrl}`
|
|
|
3854
4001
|
file: cfg.pairingFile ?? defaultPairingPath()
|
|
3855
4002
|
});
|
|
3856
4003
|
api.logger.info(`[AAMP] Pair with AAMP App before ${pairing.expiresAt}: ${pairing.connectUrl}`);
|
|
3857
|
-
const qr = await renderTerminalQr(pairing.connectUrl);
|
|
4004
|
+
const qr = await renderTerminalQr(pairingUrlToWebUrl2(pairing.connectUrl));
|
|
3858
4005
|
if (qr)
|
|
3859
4006
|
api.logger.info(`
|
|
3860
4007
|
${qr}`);
|
|
@@ -4590,9 +4737,9 @@ ${task.bodyText}` : "",
|
|
|
4590
4737
|
content: readBinaryFile(a.path)
|
|
4591
4738
|
}));
|
|
4592
4739
|
}
|
|
4593
|
-
await appendTaskStream(task.taskId, "
|
|
4594
|
-
|
|
4595
|
-
|
|
4740
|
+
await appendTaskStream(task.taskId, "todo", {
|
|
4741
|
+
items: [{ id: "openclaw-result", content: `Sending ${p.status} result`, status: "completed" }],
|
|
4742
|
+
summary: `Sending ${p.status} result`
|
|
4596
4743
|
});
|
|
4597
4744
|
if (p.output) {
|
|
4598
4745
|
await appendTaskStream(task.taskId, "text.delta", { text: p.output });
|
|
@@ -4671,9 +4818,9 @@ ${task.bodyText}` : "",
|
|
|
4671
4818
|
if (!aampClient?.isConnected()) {
|
|
4672
4819
|
return { content: [{ type: "text", text: "Error: AAMP client is not connected." }] };
|
|
4673
4820
|
}
|
|
4674
|
-
await appendTaskStream(task.taskId, "
|
|
4675
|
-
|
|
4676
|
-
|
|
4821
|
+
await appendTaskStream(task.taskId, "todo", {
|
|
4822
|
+
items: [{ id: "openclaw-help", content: p.blockedReason, status: "completed" }],
|
|
4823
|
+
summary: p.blockedReason
|
|
4677
4824
|
});
|
|
4678
4825
|
try {
|
|
4679
4826
|
await aampClient.sendHelp({
|
|
@@ -4686,12 +4833,9 @@ ${task.bodyText}` : "",
|
|
|
4686
4833
|
});
|
|
4687
4834
|
} catch (err) {
|
|
4688
4835
|
const message = err instanceof Error ? err.message : String(err);
|
|
4689
|
-
await appendTaskStream(task.taskId, "
|
|
4690
|
-
|
|
4691
|
-
|
|
4692
|
-
await appendTaskStream(task.taskId, "status", {
|
|
4693
|
-
state: "running",
|
|
4694
|
-
label: "Help request failed; task still needs a reply"
|
|
4836
|
+
await appendTaskStream(task.taskId, "todo", {
|
|
4837
|
+
items: [{ id: "openclaw-help", content: "Help request failed; task still needs a reply", status: "in_progress" }],
|
|
4838
|
+
summary: `Failed to send help request: ${message}`
|
|
4695
4839
|
});
|
|
4696
4840
|
api.logger.error(`[AAMP] aamp_send_help failed for ${task.taskId}: ${message}`);
|
|
4697
4841
|
return {
|
|
@@ -4751,7 +4895,7 @@ ${lines.join("\n")}`
|
|
|
4751
4895
|
}, { name: "aamp_pending_tasks" });
|
|
4752
4896
|
api.registerTool({
|
|
4753
4897
|
name: "aamp_pairing_code",
|
|
4754
|
-
description:
|
|
4898
|
+
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".',
|
|
4755
4899
|
parameters: { type: "object", properties: {} },
|
|
4756
4900
|
execute: async () => ({
|
|
4757
4901
|
content: [{ type: "text", text: await renderPairingCodeForCurrentAgent() }]
|
|
@@ -5020,7 +5164,7 @@ Question: ${h.question}`,
|
|
|
5020
5164
|
});
|
|
5021
5165
|
api.registerCommand({
|
|
5022
5166
|
name: "aamp-pair",
|
|
5023
|
-
description: "Show a fresh AAMP pairing QR code for this OpenClaw agent",
|
|
5167
|
+
description: "Show a fresh AAMP pairing QR code, web pairing link, and aamp://connect URL for this OpenClaw agent",
|
|
5024
5168
|
acceptsArgs: false,
|
|
5025
5169
|
requireAuth: false,
|
|
5026
5170
|
handler: async () => ({
|