aamp-openclaw-plugin 0.1.34 → 0.1.36
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/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/index.js +149 -140
- package/dist/index.js.map +2 -2
- package/openclaw.plugin.json +43 -0
- package/package.json +99 -1
- package/skills/SKILL.md +130 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { createRequire as __aampCreateRequire } from "module"; const require = __aampCreateRequire(import.meta.url);
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
9
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
10
|
+
}) : x)(function(x) {
|
|
11
|
+
if (typeof require !== "undefined")
|
|
12
|
+
return require.apply(this, arguments);
|
|
13
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
14
|
+
});
|
|
15
|
+
var __commonJS = (cb, mod) => function __require2() {
|
|
16
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
17
|
+
};
|
|
18
|
+
var __copyProps = (to, from, except, desc) => {
|
|
19
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
20
|
+
for (let key of __getOwnPropNames(from))
|
|
21
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
22
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
23
|
+
}
|
|
24
|
+
return to;
|
|
25
|
+
};
|
|
26
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
27
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
28
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
29
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
30
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
31
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
32
|
+
mod
|
|
33
|
+
));
|
|
34
|
+
|
|
35
|
+
// src/file-store.ts
|
|
36
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
37
|
+
import { dirname, join } from "node:path";
|
|
38
|
+
import { homedir } from "node:os";
|
|
39
|
+
function defaultCredentialsPath() {
|
|
40
|
+
return join(homedir(), ".openclaw", "extensions", "aamp-openclaw-plugin", ".credentials.json");
|
|
41
|
+
}
|
|
42
|
+
function loadCachedIdentity(file) {
|
|
43
|
+
const resolved = file ?? defaultCredentialsPath();
|
|
44
|
+
if (!existsSync(resolved))
|
|
45
|
+
return null;
|
|
46
|
+
try {
|
|
47
|
+
const parsed = JSON.parse(readFileSync(resolved, "utf-8"));
|
|
48
|
+
if (!parsed.email || !parsed.jmapToken || !parsed.smtpPassword)
|
|
49
|
+
return null;
|
|
50
|
+
return parsed;
|
|
51
|
+
} catch {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function saveCachedIdentity(identity, file) {
|
|
56
|
+
const resolved = file ?? defaultCredentialsPath();
|
|
57
|
+
mkdirSync(dirname(resolved), { recursive: true });
|
|
58
|
+
writeFileSync(resolved, JSON.stringify(identity, null, 2), "utf-8");
|
|
59
|
+
}
|
|
60
|
+
function ensureDir(dir) {
|
|
61
|
+
mkdirSync(dir, { recursive: true });
|
|
62
|
+
}
|
|
63
|
+
function readBinaryFile(path) {
|
|
64
|
+
return readFileSync(path);
|
|
65
|
+
}
|
|
66
|
+
function writeBinaryFile(path, content) {
|
|
67
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
68
|
+
writeFileSync(path, content);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export {
|
|
72
|
+
__require,
|
|
73
|
+
__commonJS,
|
|
74
|
+
__toESM,
|
|
75
|
+
defaultCredentialsPath,
|
|
76
|
+
loadCachedIdentity,
|
|
77
|
+
saveCachedIdentity,
|
|
78
|
+
ensureDir,
|
|
79
|
+
readBinaryFile,
|
|
80
|
+
writeBinaryFile
|
|
81
|
+
};
|
|
82
|
+
//# sourceMappingURL=chunk-ORTVVAMV.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/file-store.ts"],
|
|
4
|
+
"sourcesContent": ["import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport { homedir } from 'node:os'\n\nexport interface Identity {\n email: string\n jmapToken: string\n smtpPassword: string\n}\n\nexport function defaultCredentialsPath(): string {\n return join(homedir(), '.openclaw', 'extensions', 'aamp-openclaw-plugin', '.credentials.json')\n}\n\nexport function loadCachedIdentity(file?: string): Identity | null {\n const resolved = file ?? defaultCredentialsPath()\n if (!existsSync(resolved)) return null\n try {\n const parsed = JSON.parse(readFileSync(resolved, 'utf-8')) as Partial<Identity>\n if (!parsed.email || !parsed.jmapToken || !parsed.smtpPassword) return null\n return parsed as Identity\n } catch {\n return null\n }\n}\n\nexport function saveCachedIdentity(identity: Identity, file?: string): void {\n const resolved = file ?? defaultCredentialsPath()\n mkdirSync(dirname(resolved), { recursive: true })\n writeFileSync(resolved, JSON.stringify(identity, null, 2), 'utf-8')\n}\n\nexport function ensureDir(dir: string): void {\n mkdirSync(dir, { recursive: true })\n}\n\nexport function readBinaryFile(path: string): Buffer {\n return readFileSync(path)\n}\n\nexport function writeBinaryFile(path: string, content: Uint8Array | Buffer): void {\n mkdirSync(dirname(path), { recursive: true })\n writeFileSync(path, content)\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,YAAY,WAAW,cAAc,qBAAqB;AACnE,SAAS,SAAS,YAAY;AAC9B,SAAS,eAAe;AAQjB,SAAS,yBAAiC;AAC/C,SAAO,KAAK,QAAQ,GAAG,aAAa,cAAc,wBAAwB,mBAAmB;AAC/F;AAEO,SAAS,mBAAmB,MAAgC;AACjE,QAAM,WAAW,QAAQ,uBAAuB;AAChD,MAAI,CAAC,WAAW,QAAQ;AAAG,WAAO;AAClC,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,aAAa,UAAU,OAAO,CAAC;AACzD,QAAI,CAAC,OAAO,SAAS,CAAC,OAAO,aAAa,CAAC,OAAO;AAAc,aAAO;AACvE,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,mBAAmB,UAAoB,MAAqB;AAC1E,QAAM,WAAW,QAAQ,uBAAuB;AAChD,YAAU,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAChD,gBAAc,UAAU,KAAK,UAAU,UAAU,MAAM,CAAC,GAAG,OAAO;AACpE;AAEO,SAAS,UAAU,KAAmB;AAC3C,YAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AACpC;AAEO,SAAS,eAAe,MAAsB;AACnD,SAAO,aAAa,IAAI;AAC1B;AAEO,SAAS,gBAAgB,MAAc,SAAoC;AAChF,YAAU,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC5C,gBAAc,MAAM,OAAO;AAC7B;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
8
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
9
|
+
}) : x)(function(x) {
|
|
10
|
+
if (typeof require !== "undefined")
|
|
11
|
+
return require.apply(this, arguments);
|
|
12
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
13
|
+
});
|
|
14
|
+
var __commonJS = (cb, mod) => function __require2() {
|
|
15
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
16
|
+
};
|
|
17
|
+
var __copyProps = (to, from, except, desc) => {
|
|
18
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
19
|
+
for (let key of __getOwnPropNames(from))
|
|
20
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
21
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
22
|
+
}
|
|
23
|
+
return to;
|
|
24
|
+
};
|
|
25
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
26
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
27
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
28
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
29
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
30
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
31
|
+
mod
|
|
32
|
+
));
|
|
33
|
+
|
|
34
|
+
// src/file-store.ts
|
|
35
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
36
|
+
import { dirname, join } from "node:path";
|
|
37
|
+
import { homedir } from "node:os";
|
|
38
|
+
function defaultCredentialsPath() {
|
|
39
|
+
return join(homedir(), ".openclaw", "extensions", "aamp-openclaw-plugin", ".credentials.json");
|
|
40
|
+
}
|
|
41
|
+
function loadCachedIdentity(file) {
|
|
42
|
+
const resolved = file ?? defaultCredentialsPath();
|
|
43
|
+
if (!existsSync(resolved))
|
|
44
|
+
return null;
|
|
45
|
+
try {
|
|
46
|
+
const parsed = JSON.parse(readFileSync(resolved, "utf-8"));
|
|
47
|
+
if (!parsed.email || !parsed.jmapToken || !parsed.smtpPassword)
|
|
48
|
+
return null;
|
|
49
|
+
return parsed;
|
|
50
|
+
} catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function saveCachedIdentity(identity, file) {
|
|
55
|
+
const resolved = file ?? defaultCredentialsPath();
|
|
56
|
+
mkdirSync(dirname(resolved), { recursive: true });
|
|
57
|
+
writeFileSync(resolved, JSON.stringify(identity, null, 2), "utf-8");
|
|
58
|
+
}
|
|
59
|
+
function ensureDir(dir) {
|
|
60
|
+
mkdirSync(dir, { recursive: true });
|
|
61
|
+
}
|
|
62
|
+
function readBinaryFile(path) {
|
|
63
|
+
return readFileSync(path);
|
|
64
|
+
}
|
|
65
|
+
function writeBinaryFile(path, content) {
|
|
66
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
67
|
+
writeFileSync(path, content);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export {
|
|
71
|
+
__require,
|
|
72
|
+
__commonJS,
|
|
73
|
+
__toESM,
|
|
74
|
+
defaultCredentialsPath,
|
|
75
|
+
loadCachedIdentity,
|
|
76
|
+
saveCachedIdentity,
|
|
77
|
+
ensureDir,
|
|
78
|
+
readBinaryFile,
|
|
79
|
+
writeBinaryFile
|
|
80
|
+
};
|
|
81
|
+
//# sourceMappingURL=chunk-W4C7IUCH.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/file-store.ts"],
|
|
4
|
+
"sourcesContent": ["import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport { homedir } from 'node:os'\n\nexport interface Identity {\n email: string\n jmapToken: string\n smtpPassword: string\n}\n\nexport function defaultCredentialsPath(): string {\n return join(homedir(), '.openclaw', 'extensions', 'aamp-openclaw-plugin', '.credentials.json')\n}\n\nexport function loadCachedIdentity(file?: string): Identity | null {\n const resolved = file ?? defaultCredentialsPath()\n if (!existsSync(resolved)) return null\n try {\n const parsed = JSON.parse(readFileSync(resolved, 'utf-8')) as Partial<Identity>\n if (!parsed.email || !parsed.jmapToken || !parsed.smtpPassword) return null\n return parsed as Identity\n } catch {\n return null\n }\n}\n\nexport function saveCachedIdentity(identity: Identity, file?: string): void {\n const resolved = file ?? defaultCredentialsPath()\n mkdirSync(dirname(resolved), { recursive: true })\n writeFileSync(resolved, JSON.stringify(identity, null, 2), 'utf-8')\n}\n\nexport function ensureDir(dir: string): void {\n mkdirSync(dir, { recursive: true })\n}\n\nexport function readBinaryFile(path: string): Buffer {\n return readFileSync(path)\n}\n\nexport function writeBinaryFile(path: string, content: Uint8Array | Buffer): void {\n mkdirSync(dirname(path), { recursive: true })\n writeFileSync(path, content)\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,YAAY,WAAW,cAAc,qBAAqB;AACnE,SAAS,SAAS,YAAY;AAC9B,SAAS,eAAe;AAQjB,SAAS,yBAAiC;AAC/C,SAAO,KAAK,QAAQ,GAAG,aAAa,cAAc,wBAAwB,mBAAmB;AAC/F;AAEO,SAAS,mBAAmB,MAAgC;AACjE,QAAM,WAAW,QAAQ,uBAAuB;AAChD,MAAI,CAAC,WAAW,QAAQ;AAAG,WAAO;AAClC,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,aAAa,UAAU,OAAO,CAAC;AACzD,QAAI,CAAC,OAAO,SAAS,CAAC,OAAO,aAAa,CAAC,OAAO;AAAc,aAAO;AACvE,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,mBAAmB,UAAoB,MAAqB;AAC1E,QAAM,WAAW,QAAQ,uBAAuB;AAChD,YAAU,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAChD,gBAAc,UAAU,KAAK,UAAU,UAAU,MAAM,CAAC,GAAG,OAAO;AACpE;AAEO,SAAS,UAAU,KAAmB;AAC3C,YAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AACpC;AAEO,SAAS,eAAe,MAAsB;AACnD,SAAO,aAAa,IAAI;AAC1B;AAEO,SAAS,gBAAgB,MAAc,SAAoC;AAChF,YAAU,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC5C,gBAAc,MAAM,OAAO;AAC7B;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -446,6 +446,27 @@ function describeError(err) {
|
|
|
446
446
|
}
|
|
447
447
|
return parts.join(" | ");
|
|
448
448
|
}
|
|
449
|
+
function sleep(ms) {
|
|
450
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
451
|
+
}
|
|
452
|
+
function shouldRetrySessionFetch(status) {
|
|
453
|
+
return status === 429 || status >= 500;
|
|
454
|
+
}
|
|
455
|
+
function shouldRetryBlobDownload(status) {
|
|
456
|
+
return status === 404 || status === 429 || status === 503;
|
|
457
|
+
}
|
|
458
|
+
function rewriteUrlToConfiguredOrigin(rawUrl, configuredBaseUrl) {
|
|
459
|
+
const parsed = new URL(rawUrl);
|
|
460
|
+
const configured = new URL(configuredBaseUrl);
|
|
461
|
+
parsed.protocol = configured.protocol;
|
|
462
|
+
parsed.username = configured.username;
|
|
463
|
+
parsed.password = configured.password;
|
|
464
|
+
parsed.hostname = configured.hostname;
|
|
465
|
+
parsed.port = configured.port;
|
|
466
|
+
return parsed.toString();
|
|
467
|
+
}
|
|
468
|
+
var SESSION_FETCH_MAX_ATTEMPTS = 3;
|
|
469
|
+
var SESSION_FETCH_RETRY_BASE_DELAY_MS = 250;
|
|
449
470
|
var JmapPushClient = class extends TinyEmitter {
|
|
450
471
|
ws = null;
|
|
451
472
|
session = null;
|
|
@@ -522,18 +543,30 @@ var JmapPushClient = class extends TinyEmitter {
|
|
|
522
543
|
*/
|
|
523
544
|
async fetchSession() {
|
|
524
545
|
const url = `${this.jmapUrl}/.well-known/jmap`;
|
|
525
|
-
let
|
|
526
|
-
|
|
527
|
-
res
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
546
|
+
let lastError = null;
|
|
547
|
+
for (let attempt = 1; attempt <= SESSION_FETCH_MAX_ATTEMPTS; attempt += 1) {
|
|
548
|
+
let res;
|
|
549
|
+
try {
|
|
550
|
+
res = await fetch(url, {
|
|
551
|
+
headers: { Authorization: this.getAuthHeader() }
|
|
552
|
+
});
|
|
553
|
+
} catch (err) {
|
|
554
|
+
lastError = new Error(`fetchSession ${url} failed: ${describeError(err)}`);
|
|
555
|
+
if (attempt >= SESSION_FETCH_MAX_ATTEMPTS)
|
|
556
|
+
throw lastError;
|
|
557
|
+
await sleep(SESSION_FETCH_RETRY_BASE_DELAY_MS * attempt);
|
|
558
|
+
continue;
|
|
559
|
+
}
|
|
560
|
+
if (res.ok) {
|
|
561
|
+
return res.json();
|
|
562
|
+
}
|
|
563
|
+
lastError = new Error(attempt >= SESSION_FETCH_MAX_ATTEMPTS || !shouldRetrySessionFetch(res.status) ? `Failed to fetch JMAP session: ${res.status} ${res.statusText}` : `Failed to fetch JMAP session after ${attempt} attempt(s): ${res.status} ${res.statusText}`);
|
|
564
|
+
if (attempt >= SESSION_FETCH_MAX_ATTEMPTS || !shouldRetrySessionFetch(res.status)) {
|
|
565
|
+
throw lastError;
|
|
566
|
+
}
|
|
567
|
+
await sleep(SESSION_FETCH_RETRY_BASE_DELAY_MS * attempt);
|
|
535
568
|
}
|
|
536
|
-
|
|
569
|
+
throw lastError ?? new Error("Failed to fetch JMAP session");
|
|
537
570
|
}
|
|
538
571
|
/**
|
|
539
572
|
* Perform a JMAP API call
|
|
@@ -967,27 +1000,37 @@ var JmapPushClient = class extends TinyEmitter {
|
|
|
967
1000
|
const accountId = this.session.primaryAccounts["urn:ietf:params:jmap:mail"] ?? Object.keys(this.session.accounts)[0];
|
|
968
1001
|
let downloadUrl = this.session.downloadUrl ?? `${this.jmapUrl}/jmap/download/{accountId}/{blobId}/{name}`;
|
|
969
1002
|
try {
|
|
970
|
-
|
|
971
|
-
const configured = new URL(this.jmapUrl);
|
|
972
|
-
parsed.protocol = configured.protocol;
|
|
973
|
-
parsed.host = configured.host;
|
|
974
|
-
downloadUrl = parsed.toString();
|
|
1003
|
+
downloadUrl = rewriteUrlToConfiguredOrigin(downloadUrl, this.jmapUrl);
|
|
975
1004
|
} catch {
|
|
976
1005
|
}
|
|
977
1006
|
const safeFilename = filename ?? "attachment";
|
|
978
1007
|
downloadUrl = downloadUrl.replace(/\{accountId\}|%7BaccountId%7D/gi, encodeURIComponent(accountId)).replace(/\{blobId\}|%7BblobId%7D/gi, encodeURIComponent(blobId)).replace(/\{name\}|%7Bname%7D/gi, encodeURIComponent(safeFilename)).replace(/\{type\}|%7Btype%7D/gi, "application/octet-stream");
|
|
979
1008
|
const maxAttempts = 8;
|
|
980
1009
|
let lastStatus = null;
|
|
1010
|
+
let lastError = null;
|
|
981
1011
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
1012
|
+
let res;
|
|
1013
|
+
try {
|
|
1014
|
+
res = await fetch(downloadUrl, {
|
|
1015
|
+
headers: { Authorization: this.getAuthHeader() }
|
|
1016
|
+
});
|
|
1017
|
+
} catch (err) {
|
|
1018
|
+
lastError = new Error(`Blob download fetch failed: attempt=${attempt}/${maxAttempts} blobId=${blobId} filename=${filename ?? "attachment"} url=${downloadUrl} error=${describeError(err)}`);
|
|
1019
|
+
if (attempt < maxAttempts) {
|
|
1020
|
+
console.warn(`[AAMP-SDK] blob download retry fetch-error attempt=${attempt}/${maxAttempts} url=${downloadUrl} error=${describeError(err)}`);
|
|
1021
|
+
const delay = Math.min(1e3 * Math.pow(2, attempt - 1), 15e3);
|
|
1022
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
1023
|
+
continue;
|
|
1024
|
+
}
|
|
1025
|
+
console.error(`[AAMP-SDK] blob download fetch-error attempt=${attempt}/${maxAttempts} url=${downloadUrl} error=${describeError(err)}`);
|
|
1026
|
+
throw lastError;
|
|
1027
|
+
}
|
|
985
1028
|
lastStatus = res.status;
|
|
986
1029
|
if (res.ok) {
|
|
987
1030
|
const arrayBuffer = await res.arrayBuffer();
|
|
988
1031
|
return Buffer.from(arrayBuffer);
|
|
989
1032
|
}
|
|
990
|
-
if (attempt < maxAttempts && (res.status
|
|
1033
|
+
if (attempt < maxAttempts && shouldRetryBlobDownload(res.status)) {
|
|
991
1034
|
console.warn(`[AAMP-SDK] blob download retry status=${res.status} attempt=${attempt}/${maxAttempts} url=${downloadUrl}`);
|
|
992
1035
|
const delay = Math.min(1e3 * Math.pow(2, attempt - 1), 15e3);
|
|
993
1036
|
await new Promise((r) => setTimeout(r, delay));
|
|
@@ -996,6 +1039,8 @@ var JmapPushClient = class extends TinyEmitter {
|
|
|
996
1039
|
console.error(`[AAMP-SDK] blob download failed status=${res.status} attempt=${attempt}/${maxAttempts} url=${downloadUrl}`);
|
|
997
1040
|
throw new Error(`Blob download failed: status=${res.status} attempt=${attempt}/${maxAttempts} blobId=${blobId} filename=${filename ?? "attachment"} url=${downloadUrl}`);
|
|
998
1041
|
}
|
|
1042
|
+
if (lastError)
|
|
1043
|
+
throw lastError;
|
|
999
1044
|
throw new Error(`Blob download failed after retries: status=${lastStatus ?? "unknown"} attempt=${maxAttempts}/${maxAttempts} blobId=${blobId} filename=${filename ?? "attachment"} url=${downloadUrl}`);
|
|
1000
1045
|
}
|
|
1001
1046
|
/**
|
|
@@ -1829,6 +1874,29 @@ function renderThreadHistoryForAgent(events, options = {}) {
|
|
|
1829
1874
|
}
|
|
1830
1875
|
|
|
1831
1876
|
// ../sdk/src/client.js
|
|
1877
|
+
function buildRegisteredCommandDispatchPayload(opts) {
|
|
1878
|
+
const command = opts.command.trim();
|
|
1879
|
+
if (!command) {
|
|
1880
|
+
throw new Error("Registered command name cannot be empty.");
|
|
1881
|
+
}
|
|
1882
|
+
if (opts.args != null && (typeof opts.args !== "object" || Array.isArray(opts.args))) {
|
|
1883
|
+
throw new Error("Registered command args must be an object when provided.");
|
|
1884
|
+
}
|
|
1885
|
+
if (opts.inputs) {
|
|
1886
|
+
for (const input of opts.inputs) {
|
|
1887
|
+
if (!input.slot?.trim() || !input.attachmentName?.trim()) {
|
|
1888
|
+
throw new Error("Each registered command input must include slot and attachmentName.");
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
return {
|
|
1893
|
+
kind: "registered-command/v1",
|
|
1894
|
+
command,
|
|
1895
|
+
...opts.args && Object.keys(opts.args).length > 0 ? { args: opts.args } : {},
|
|
1896
|
+
...opts.inputs?.length ? { inputs: opts.inputs } : {},
|
|
1897
|
+
stream: { mode: opts.streamMode ?? "full" }
|
|
1898
|
+
};
|
|
1899
|
+
}
|
|
1832
1900
|
var AampClient = class _AampClient extends TinyEmitter {
|
|
1833
1901
|
jmapClient;
|
|
1834
1902
|
smtpSender;
|
|
@@ -2032,6 +2100,21 @@ var AampClient = class _AampClient extends TinyEmitter {
|
|
|
2032
2100
|
async sendTask(opts) {
|
|
2033
2101
|
return this.smtpSender.sendTask(opts);
|
|
2034
2102
|
}
|
|
2103
|
+
async sendRegisteredCommand(opts) {
|
|
2104
|
+
const payload = buildRegisteredCommandDispatchPayload(opts);
|
|
2105
|
+
return this.smtpSender.sendTask({
|
|
2106
|
+
to: opts.to,
|
|
2107
|
+
taskId: opts.taskId,
|
|
2108
|
+
title: opts.title?.trim() || `Registered command: ${payload.command}`,
|
|
2109
|
+
rawBodyText: JSON.stringify(payload, null, 2),
|
|
2110
|
+
priority: opts.priority,
|
|
2111
|
+
expiresAt: opts.expiresAt,
|
|
2112
|
+
contextLinks: opts.contextLinks,
|
|
2113
|
+
dispatchContext: opts.dispatchContext,
|
|
2114
|
+
parentTaskId: opts.parentTaskId,
|
|
2115
|
+
attachments: opts.attachments
|
|
2116
|
+
});
|
|
2117
|
+
}
|
|
2035
2118
|
async sendCancel(opts) {
|
|
2036
2119
|
return this.smtpSender.sendCancel(opts);
|
|
2037
2120
|
}
|
|
@@ -2535,8 +2618,6 @@ function baseUrl(aampHost) {
|
|
|
2535
2618
|
}
|
|
2536
2619
|
var pendingTasks = /* @__PURE__ */ new Map();
|
|
2537
2620
|
var activeTaskStreams = /* @__PURE__ */ new Map();
|
|
2538
|
-
var sessionTaskBindings = /* @__PURE__ */ new Map();
|
|
2539
|
-
var taskVisibleStreamState = /* @__PURE__ */ new Map();
|
|
2540
2621
|
var terminalTaskIds = new Set(loadTaskState(defaultTaskStatePath()).terminalTaskIds ?? []);
|
|
2541
2622
|
var AAMP_SESSION_PREFIX = "aamp:";
|
|
2542
2623
|
var DEFAULT_OPENCLAW_AGENT_ID = "main";
|
|
@@ -2556,7 +2637,6 @@ var lastLoggedTransportMode = "disconnected";
|
|
|
2556
2637
|
var reconcileTimer = null;
|
|
2557
2638
|
var transportMonitorTimer = null;
|
|
2558
2639
|
var historicalReconcileCompleted = false;
|
|
2559
|
-
var stopAgentEventSubscription = null;
|
|
2560
2640
|
var channelRuntime = null;
|
|
2561
2641
|
var channelCfg = null;
|
|
2562
2642
|
async function ensureTaskStream(task) {
|
|
@@ -2626,6 +2706,11 @@ function isSyntheticPendingKey(taskKey) {
|
|
|
2626
2706
|
function isTaskAwaitingHelpReply(task) {
|
|
2627
2707
|
return task.awaitingHelpReply === true;
|
|
2628
2708
|
}
|
|
2709
|
+
function threadAlreadyTerminal(events) {
|
|
2710
|
+
return (events ?? []).some(
|
|
2711
|
+
(event) => event.intent === "task.result" || event.intent === "task.cancel"
|
|
2712
|
+
);
|
|
2713
|
+
}
|
|
2629
2714
|
function isActionablePendingTask(taskKey, task) {
|
|
2630
2715
|
return !isSyntheticPendingKey(taskKey) && !isTaskAwaitingHelpReply(task);
|
|
2631
2716
|
}
|
|
@@ -2705,80 +2790,6 @@ function resolvePendingKeyFromSessionKey(sessionKey) {
|
|
|
2705
2790
|
}
|
|
2706
2791
|
return void 0;
|
|
2707
2792
|
}
|
|
2708
|
-
function normalizeBoundSessionKey(sessionKey) {
|
|
2709
|
-
if (typeof sessionKey !== "string")
|
|
2710
|
-
return void 0;
|
|
2711
|
-
const normalized = stripOpenClawAgentScope(sessionKey).trim();
|
|
2712
|
-
return normalized || void 0;
|
|
2713
|
-
}
|
|
2714
|
-
function bindSessionToTask(sessionKey, taskId) {
|
|
2715
|
-
const normalized = normalizeBoundSessionKey(sessionKey);
|
|
2716
|
-
if (!normalized)
|
|
2717
|
-
return;
|
|
2718
|
-
if (!taskId) {
|
|
2719
|
-
sessionTaskBindings.delete(normalized);
|
|
2720
|
-
return;
|
|
2721
|
-
}
|
|
2722
|
-
sessionTaskBindings.set(normalized, taskId);
|
|
2723
|
-
}
|
|
2724
|
-
function resolveStreamTaskId(sessionKey) {
|
|
2725
|
-
const pendingKey = resolvePendingKeyFromSessionKey(sessionKey);
|
|
2726
|
-
if (pendingKey && !isSyntheticPendingKey(pendingKey))
|
|
2727
|
-
return pendingKey;
|
|
2728
|
-
const normalized = normalizeBoundSessionKey(sessionKey);
|
|
2729
|
-
if (!normalized)
|
|
2730
|
-
return void 0;
|
|
2731
|
-
const taskId = sessionTaskBindings.get(normalized);
|
|
2732
|
-
if (!taskId)
|
|
2733
|
-
return void 0;
|
|
2734
|
-
if (!pendingTasks.has(taskId) && !activeTaskStreams.has(taskId)) {
|
|
2735
|
-
sessionTaskBindings.delete(normalized);
|
|
2736
|
-
return void 0;
|
|
2737
|
-
}
|
|
2738
|
-
return taskId;
|
|
2739
|
-
}
|
|
2740
|
-
function forgetTaskStreamContext(taskId) {
|
|
2741
|
-
taskVisibleStreamState.delete(taskId);
|
|
2742
|
-
for (const [sessionKey, boundTaskId] of sessionTaskBindings) {
|
|
2743
|
-
if (boundTaskId === taskId) {
|
|
2744
|
-
sessionTaskBindings.delete(sessionKey);
|
|
2745
|
-
}
|
|
2746
|
-
}
|
|
2747
|
-
}
|
|
2748
|
-
async function handleAgentVisibleTextEvent(event) {
|
|
2749
|
-
if (event.stream !== "assistant")
|
|
2750
|
-
return;
|
|
2751
|
-
const taskId = resolveStreamTaskId(event.sessionKey);
|
|
2752
|
-
if (!taskId || !activeTaskStreams.has(taskId))
|
|
2753
|
-
return;
|
|
2754
|
-
const payload = event.data && typeof event.data === "object" ? event.data : {};
|
|
2755
|
-
const fullText = typeof payload.text === "string" ? payload.text : void 0;
|
|
2756
|
-
const deltaText = typeof payload.delta === "string" ? payload.delta : void 0;
|
|
2757
|
-
if (!fullText && !deltaText)
|
|
2758
|
-
return;
|
|
2759
|
-
const runId = typeof event.runId === "string" && event.runId.trim() ? event.runId : "__default__";
|
|
2760
|
-
const previousState = taskVisibleStreamState.get(taskId);
|
|
2761
|
-
const previousText = previousState?.runId === runId ? previousState.text : "";
|
|
2762
|
-
let nextText = previousText;
|
|
2763
|
-
let nextDelta = "";
|
|
2764
|
-
if (typeof fullText === "string") {
|
|
2765
|
-
nextText = fullText;
|
|
2766
|
-
if (fullText.startsWith(previousText)) {
|
|
2767
|
-
nextDelta = fullText.slice(previousText.length);
|
|
2768
|
-
} else if (!previousText) {
|
|
2769
|
-
nextDelta = fullText;
|
|
2770
|
-
} else if (deltaText) {
|
|
2771
|
-
nextDelta = deltaText;
|
|
2772
|
-
}
|
|
2773
|
-
} else if (deltaText) {
|
|
2774
|
-
nextText = `${previousText}${deltaText}`;
|
|
2775
|
-
nextDelta = deltaText;
|
|
2776
|
-
}
|
|
2777
|
-
taskVisibleStreamState.set(taskId, { runId, text: nextText });
|
|
2778
|
-
if (!nextDelta)
|
|
2779
|
-
return;
|
|
2780
|
-
await appendTaskStream(taskId, "text.delta", { text: nextDelta });
|
|
2781
|
-
}
|
|
2782
2793
|
function saveTerminalTaskIds() {
|
|
2783
2794
|
saveTaskState({ terminalTaskIds: [...terminalTaskIds] }, defaultTaskStatePath());
|
|
2784
2795
|
}
|
|
@@ -2957,7 +2968,12 @@ var src_default = {
|
|
|
2957
2968
|
const cfg = api.config?.channels?.aamp ?? api.pluginConfig ?? {};
|
|
2958
2969
|
api.registerChannel({
|
|
2959
2970
|
id: "aamp",
|
|
2960
|
-
meta: {
|
|
2971
|
+
meta: {
|
|
2972
|
+
label: "AAMP",
|
|
2973
|
+
selectionLabel: "AAMP",
|
|
2974
|
+
docsPath: "/channels/aamp",
|
|
2975
|
+
blurb: "AAMP mailbox channel for receiving and replying to tasks over email."
|
|
2976
|
+
},
|
|
2961
2977
|
capabilities: { chatTypes: ["dm"] },
|
|
2962
2978
|
config: {
|
|
2963
2979
|
listAccountIds: () => cfg.aampHost ? ["default"] : [],
|
|
@@ -2990,22 +3006,6 @@ var src_default = {
|
|
|
2990
3006
|
api.logger.warn(`[AAMP] Could not trigger heartbeat for ${label}: ${err.message}`);
|
|
2991
3007
|
}
|
|
2992
3008
|
}
|
|
2993
|
-
if (stopAgentEventSubscription) {
|
|
2994
|
-
try {
|
|
2995
|
-
stopAgentEventSubscription();
|
|
2996
|
-
} catch {
|
|
2997
|
-
}
|
|
2998
|
-
stopAgentEventSubscription = null;
|
|
2999
|
-
}
|
|
3000
|
-
const onAgentEvent = api.runtime?.events?.onAgentEvent;
|
|
3001
|
-
if (typeof onAgentEvent === "function") {
|
|
3002
|
-
const unsubscribe = onAgentEvent((event) => {
|
|
3003
|
-
void handleAgentVisibleTextEvent(event).catch((err) => {
|
|
3004
|
-
api.logger.warn(`[AAMP] Failed to mirror assistant stream: ${err.message}`);
|
|
3005
|
-
});
|
|
3006
|
-
});
|
|
3007
|
-
stopAgentEventSubscription = typeof unsubscribe === "function" ? unsubscribe : null;
|
|
3008
|
-
}
|
|
3009
3009
|
function getConfiguredCardText() {
|
|
3010
3010
|
const inline = cfg.cardText?.trim();
|
|
3011
3011
|
if (inline)
|
|
@@ -3151,6 +3151,11 @@ var src_default = {
|
|
|
3151
3151
|
threadContextText: ""
|
|
3152
3152
|
};
|
|
3153
3153
|
});
|
|
3154
|
+
if (threadAlreadyTerminal(hydratedTask.threadHistory)) {
|
|
3155
|
+
rememberTerminalTask(task.taskId);
|
|
3156
|
+
api.logger.info(`[AAMP] Skipping historical task ${task.taskId} because the thread already reached a terminal state`);
|
|
3157
|
+
return;
|
|
3158
|
+
}
|
|
3154
3159
|
if (!queuePendingTask(hydratedTask)) {
|
|
3155
3160
|
api.logger.info(`[AAMP] Ignoring already-terminal or expired task ${task.taskId}`);
|
|
3156
3161
|
return;
|
|
@@ -3174,7 +3179,6 @@ var src_default = {
|
|
|
3174
3179
|
pendingTasks.delete(`help:${cancel.taskId}`);
|
|
3175
3180
|
dispatchedSubtasks.delete(cancel.taskId);
|
|
3176
3181
|
waitingDispatches.delete(cancel.taskId);
|
|
3177
|
-
forgetTaskStreamContext(cancel.taskId);
|
|
3178
3182
|
rememberTerminalTask(cancel.taskId);
|
|
3179
3183
|
void closeTaskStream(cancel.taskId, { reason: "task.cancel" }).catch(() => {
|
|
3180
3184
|
});
|
|
@@ -3507,13 +3511,6 @@ ${notifyBody?.bodyText ?? help.question}`;
|
|
|
3507
3511
|
} catch {
|
|
3508
3512
|
}
|
|
3509
3513
|
}
|
|
3510
|
-
if (stopAgentEventSubscription) {
|
|
3511
|
-
try {
|
|
3512
|
-
stopAgentEventSubscription();
|
|
3513
|
-
} catch {
|
|
3514
|
-
}
|
|
3515
|
-
stopAgentEventSubscription = null;
|
|
3516
|
-
}
|
|
3517
3514
|
}
|
|
3518
3515
|
});
|
|
3519
3516
|
api.on("gateway_start", () => {
|
|
@@ -3584,10 +3581,6 @@ ${notifyBody?.bodyText ?? help.question}`;
|
|
|
3584
3581
|
return rankDiff;
|
|
3585
3582
|
return new Date(a.receivedAt).getTime() - new Date(b.receivedAt).getTime();
|
|
3586
3583
|
});
|
|
3587
|
-
bindSessionToTask(
|
|
3588
|
-
ctx?.sessionKey,
|
|
3589
|
-
isNotification ? actionableTasks.length === 1 ? actionableTasks[0]?.taskId : void 0 : task.taskId
|
|
3590
|
-
);
|
|
3591
3584
|
const otherActionableTasks = actionableTasks.filter((pendingTask) => pendingTask.taskId !== task.taskId);
|
|
3592
3585
|
const hasAttachmentInfo = isNotification && (task.bodyText?.includes("aamp_download_attachment") ?? false);
|
|
3593
3586
|
const actionRequiredSection = isNotification && actionableTasks.length > 0 ? [
|
|
@@ -3811,17 +3804,14 @@ ${task.contextLinks.map((l) => ` - ${l}`).join("\n")}` : "",
|
|
|
3811
3804
|
state: "completing",
|
|
3812
3805
|
label: `Sending ${p.status} result`
|
|
3813
3806
|
});
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
if (finalOutputDelta) {
|
|
3817
|
-
await appendTaskStream(task.taskId, "text.delta", { text: finalOutputDelta });
|
|
3807
|
+
if (p.output) {
|
|
3808
|
+
await appendTaskStream(task.taskId, "text.delta", { text: p.output });
|
|
3818
3809
|
}
|
|
3819
3810
|
await closeTaskStream(task.taskId, {
|
|
3820
3811
|
reason: "task.result",
|
|
3821
3812
|
status: p.status,
|
|
3822
3813
|
...p.errorMsg ? { error: p.errorMsg } : {}
|
|
3823
3814
|
});
|
|
3824
|
-
forgetTaskStreamContext(task.taskId);
|
|
3825
3815
|
await aampClient.sendResult({
|
|
3826
3816
|
to: task.from,
|
|
3827
3817
|
taskId: task.taskId,
|
|
@@ -3895,18 +3885,37 @@ ${task.contextLinks.map((l) => ` - ${l}`).join("\n")}` : "",
|
|
|
3895
3885
|
state: "help_needed",
|
|
3896
3886
|
label: p.blockedReason
|
|
3897
3887
|
});
|
|
3888
|
+
try {
|
|
3889
|
+
await aampClient.sendHelp({
|
|
3890
|
+
to: task.from,
|
|
3891
|
+
taskId: task.taskId,
|
|
3892
|
+
question: p.question,
|
|
3893
|
+
blockedReason: p.blockedReason,
|
|
3894
|
+
suggestedOptions: p.suggestedOptions ?? [],
|
|
3895
|
+
inReplyTo: task.messageId || void 0
|
|
3896
|
+
});
|
|
3897
|
+
} catch (err) {
|
|
3898
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3899
|
+
await appendTaskStream(task.taskId, "error", {
|
|
3900
|
+
message: `Failed to send help request: ${message}`
|
|
3901
|
+
});
|
|
3902
|
+
await appendTaskStream(task.taskId, "status", {
|
|
3903
|
+
state: "running",
|
|
3904
|
+
label: "Help request failed; task still needs a reply"
|
|
3905
|
+
});
|
|
3906
|
+
api.logger.error(`[AAMP] aamp_send_help failed for ${task.taskId}: ${message}`);
|
|
3907
|
+
return {
|
|
3908
|
+
content: [
|
|
3909
|
+
{
|
|
3910
|
+
type: "text",
|
|
3911
|
+
text: `Error: failed to send help request for task ${task.taskId}: ${message}`
|
|
3912
|
+
}
|
|
3913
|
+
]
|
|
3914
|
+
};
|
|
3915
|
+
}
|
|
3898
3916
|
await closeTaskStream(task.taskId, {
|
|
3899
3917
|
reason: "task.help_needed"
|
|
3900
3918
|
});
|
|
3901
|
-
forgetTaskStreamContext(task.taskId);
|
|
3902
|
-
await aampClient.sendHelp({
|
|
3903
|
-
to: task.from,
|
|
3904
|
-
taskId: task.taskId,
|
|
3905
|
-
question: p.question,
|
|
3906
|
-
blockedReason: p.blockedReason,
|
|
3907
|
-
suggestedOptions: p.suggestedOptions ?? [],
|
|
3908
|
-
inReplyTo: task.messageId || void 0
|
|
3909
|
-
});
|
|
3910
3919
|
pendingTasks.set(task.taskId, {
|
|
3911
3920
|
...task,
|
|
3912
3921
|
awaitingHelpReply: true
|