aamp-openclaw-plugin 0.1.37 → 0.1.39
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 +15 -1
- package/bin/aamp-openclaw-plugin.mjs +50 -44
- package/dist/file-store.js +73 -0
- package/dist/file-store.js.map +2 -2
- package/dist/index.js +718 -134
- package/dist/index.js.map +4 -4
- package/openclaw.plugin.json +21 -0
- package/package.json +57 -1
- package/skills/SKILL.md +0 -1
package/README.md
CHANGED
|
@@ -22,6 +22,18 @@ the installer will prompt for:
|
|
|
22
22
|
|
|
23
23
|
The answers are written into the OpenClaw plugin config automatically, so users do not need to hand-edit `openclaw.json`.
|
|
24
24
|
|
|
25
|
+
When the plugin starts, it also prints a five-minute `aamp://connect?...`
|
|
26
|
+
pairing URL and terminal QR code. Scan it with AAMP App, paste it into User UI,
|
|
27
|
+
or run `aamp-cli pair --url ...` to authorize that sender. A valid
|
|
28
|
+
`pair.request` writes the sender and optional dispatch-context rules to the
|
|
29
|
+
paired sender policy file, then consumes the code. The plugin replies with
|
|
30
|
+
`pair.respond`; rejected responses include the failure reason.
|
|
31
|
+
|
|
32
|
+
You can generate a fresh pairing QR code later without restarting OpenClaw:
|
|
33
|
+
|
|
34
|
+
- Ask the agent to use the `aamp_pairing_code` tool.
|
|
35
|
+
- Or run the `/aamp-pair` command in OpenClaw.
|
|
36
|
+
|
|
25
37
|
## Build
|
|
26
38
|
|
|
27
39
|
```bash
|
|
@@ -41,6 +53,8 @@ npm run build
|
|
|
41
53
|
"taskDispatchConcurrency": 10,
|
|
42
54
|
"slug": "openclaw-agent",
|
|
43
55
|
"credentialsFile": "~/.openclaw/extensions/aamp-openclaw-plugin/.credentials.json",
|
|
56
|
+
"pairingFile": "~/.openclaw/extensions/aamp-openclaw-plugin/.pairing.json",
|
|
57
|
+
"senderPoliciesFile": "~/.openclaw/extensions/aamp-openclaw-plugin/.sender-policies.json",
|
|
44
58
|
"senderPolicies": [
|
|
45
59
|
{
|
|
46
60
|
"sender": "meegle-bot@meshmail.ai",
|
|
@@ -57,7 +71,7 @@ npm run build
|
|
|
57
71
|
}
|
|
58
72
|
```
|
|
59
73
|
|
|
60
|
-
If `senderPolicies` is omitted,
|
|
74
|
+
If `senderPolicies` is omitted, no senders are authorized by default. Use the printed pairing QR/URL or configure at least one policy before sending `task.dispatch`; matching policies can also require all configured dispatch-context rules to pass.
|
|
61
75
|
`taskDispatchConcurrency` is optional and defaults to `10`.
|
|
62
76
|
|
|
63
77
|
The plugin also understands:
|
|
@@ -8,10 +8,13 @@ import { stdin as input, stdout as output, stderr } from 'node:process'
|
|
|
8
8
|
import { spawnSync } from 'node:child_process'
|
|
9
9
|
import { createRequire } from 'node:module'
|
|
10
10
|
import { fileURLToPath } from 'node:url'
|
|
11
|
+
import { AampClient } from 'aamp-sdk'
|
|
11
12
|
|
|
12
13
|
const PLUGIN_ID = 'aamp-openclaw-plugin'
|
|
13
14
|
const DEFAULT_AAMP_HOST = 'https://meshmail.ai'
|
|
14
15
|
const DEFAULT_CREDENTIALS_FILE = '~/.openclaw/extensions/aamp-openclaw-plugin/.credentials.json'
|
|
16
|
+
const DEFAULT_PAIRING_FILE = '~/.openclaw/extensions/aamp-openclaw-plugin/.pairing.json'
|
|
17
|
+
const DEFAULT_SENDER_POLICIES_FILE = '~/.openclaw/extensions/aamp-openclaw-plugin/.sender-policies.json'
|
|
15
18
|
const CODING_TOOL_ALLOWLIST = [
|
|
16
19
|
'read',
|
|
17
20
|
'write',
|
|
@@ -35,9 +38,12 @@ const CODING_TOOL_ALLOWLIST = [
|
|
|
35
38
|
'image_generate',
|
|
36
39
|
]
|
|
37
40
|
const AAMP_PLUGIN_TOOL_ALLOWLIST = [
|
|
41
|
+
'aamp_directory_search',
|
|
38
42
|
'aamp_send_result',
|
|
39
43
|
'aamp_send_help',
|
|
40
44
|
'aamp_pending_tasks',
|
|
45
|
+
'aamp_pairing_code',
|
|
46
|
+
'aamp_cancel_task',
|
|
41
47
|
'aamp_dispatch_task',
|
|
42
48
|
'aamp_check_protocol',
|
|
43
49
|
'aamp_download_attachment',
|
|
@@ -176,6 +182,19 @@ export function writeJsonFile(path, value) {
|
|
|
176
182
|
writeFileSync(path, JSON.stringify(value, null, 2) + '\n', 'utf-8')
|
|
177
183
|
}
|
|
178
184
|
|
|
185
|
+
async function renderTerminalQr(value) {
|
|
186
|
+
try {
|
|
187
|
+
const qrcode = await import('qrcode-terminal')
|
|
188
|
+
const generator = qrcode.default?.generate ?? qrcode.generate
|
|
189
|
+
if (!generator) return ''
|
|
190
|
+
return await new Promise((resolve) => {
|
|
191
|
+
generator(value, { small: true }, (qr) => resolve(qr))
|
|
192
|
+
})
|
|
193
|
+
} catch {
|
|
194
|
+
return ''
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
179
198
|
export function normalizeBaseUrl(url) {
|
|
180
199
|
if (url.startsWith('http://') || url.startsWith('https://')) return url.replace(/\/$/, '')
|
|
181
200
|
return `https://${url.replace(/\/$/, '')}`
|
|
@@ -444,50 +463,15 @@ export async function ensureMailboxIdentity({ aampHost, slug, credentialsFile })
|
|
|
444
463
|
}
|
|
445
464
|
}
|
|
446
465
|
|
|
447
|
-
const
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
throw new Error(`AAMP discovery failed (${discoveryRes.status}): ${text || discoveryRes.statusText}`)
|
|
452
|
-
}
|
|
453
|
-
const discovery = await discoveryRes.json()
|
|
454
|
-
const apiUrl = discovery?.api?.url
|
|
455
|
-
if (!apiUrl) {
|
|
456
|
-
throw new Error('AAMP discovery did not return api.url')
|
|
457
|
-
}
|
|
458
|
-
const apiBase = new URL(apiUrl, `${base}/`).toString()
|
|
459
|
-
|
|
460
|
-
const registerRes = await fetch(`${apiBase}?action=aamp.mailbox.register`, {
|
|
461
|
-
method: 'POST',
|
|
462
|
-
headers: { 'Content-Type': 'application/json' },
|
|
463
|
-
body: JSON.stringify({
|
|
464
|
-
slug,
|
|
465
|
-
description: 'OpenClaw AAMP agent node',
|
|
466
|
-
}),
|
|
466
|
+
const credData = await AampClient.registerMailbox({
|
|
467
|
+
aampHost: normalizeBaseUrl(aampHost),
|
|
468
|
+
slug,
|
|
469
|
+
description: 'OpenClaw AAMP agent node',
|
|
467
470
|
})
|
|
468
|
-
|
|
469
|
-
if (!registerRes.ok) {
|
|
470
|
-
const text = await registerRes.text().catch(() => '')
|
|
471
|
-
throw new Error(`AAMP self-register failed (${registerRes.status}): ${text || registerRes.statusText}`)
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
const registerData = await registerRes.json()
|
|
475
|
-
const code = registerData?.registrationCode
|
|
476
|
-
if (!code) {
|
|
477
|
-
throw new Error('AAMP self-register succeeded but no registrationCode was returned')
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
const credRes = await fetch(`${apiBase}?action=aamp.mailbox.credentials&code=${encodeURIComponent(code)}`)
|
|
481
|
-
if (!credRes.ok) {
|
|
482
|
-
const text = await credRes.text().catch(() => '')
|
|
483
|
-
throw new Error(`AAMP credential exchange failed (${credRes.status}): ${text || credRes.statusText}`)
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
const credData = await credRes.json()
|
|
487
471
|
const identity = {
|
|
488
472
|
email: credData?.email,
|
|
489
|
-
mailboxToken: credData?.
|
|
490
|
-
smtpPassword: credData?.
|
|
473
|
+
mailboxToken: credData?.mailboxToken,
|
|
474
|
+
smtpPassword: credData?.smtpPassword,
|
|
491
475
|
}
|
|
492
476
|
|
|
493
477
|
if (!identity.email || !identity.mailboxToken || !identity.smtpPassword) {
|
|
@@ -498,6 +482,18 @@ export async function ensureMailboxIdentity({ aampHost, slug, credentialsFile })
|
|
|
498
482
|
return { created: true, email: identity.email, credentialsPath: resolvedCreds }
|
|
499
483
|
}
|
|
500
484
|
|
|
485
|
+
export function createPairingFile({ email, pairingFile }) {
|
|
486
|
+
if (!email) return null
|
|
487
|
+
const pairing = AampClient.createPairingCode({ mailbox: email })
|
|
488
|
+
writeJsonFile(expandHome(pairingFile), {
|
|
489
|
+
mailbox: pairing.mailbox,
|
|
490
|
+
pairCode: pairing.pairCode,
|
|
491
|
+
expiresAt: pairing.expiresAt,
|
|
492
|
+
connectUrl: pairing.connectUrl,
|
|
493
|
+
})
|
|
494
|
+
return pairing
|
|
495
|
+
}
|
|
496
|
+
|
|
501
497
|
function printHelp() {
|
|
502
498
|
output.write(
|
|
503
499
|
[
|
|
@@ -541,7 +537,7 @@ export async function runInit() {
|
|
|
541
537
|
'Detected existing plugin config:',
|
|
542
538
|
` aampHost: ${previousConfig.aampHost ?? DEFAULT_AAMP_HOST}`,
|
|
543
539
|
` slug: ${previousConfig.slug ?? 'openclaw-agent'}`,
|
|
544
|
-
` senderPolicies: ${previousConfig.senderPolicies ? JSON.stringify(previousConfig.senderPolicies) : '(
|
|
540
|
+
` senderPolicies: ${previousConfig.senderPolicies ? JSON.stringify(previousConfig.senderPolicies) : '(default deny until paired or configured)'}`,
|
|
545
541
|
'',
|
|
546
542
|
].join('\n'),
|
|
547
543
|
)
|
|
@@ -554,7 +550,7 @@ export async function runInit() {
|
|
|
554
550
|
aampHost = aampHostAnswer.trim() || aampHost
|
|
555
551
|
|
|
556
552
|
const senderAnswer = await rl.question(
|
|
557
|
-
'Primary trusted dispatch sender (e.g. meegle-bot@meshmail.ai, leave blank to
|
|
553
|
+
'Primary trusted dispatch sender (e.g. meegle-bot@meshmail.ai, leave blank to pair later): ',
|
|
558
554
|
)
|
|
559
555
|
const sender = senderAnswer.trim()
|
|
560
556
|
if (sender) {
|
|
@@ -620,6 +616,8 @@ export async function runInit() {
|
|
|
620
616
|
aampHost,
|
|
621
617
|
slug,
|
|
622
618
|
credentialsFile: DEFAULT_CREDENTIALS_FILE,
|
|
619
|
+
pairingFile: DEFAULT_PAIRING_FILE,
|
|
620
|
+
senderPoliciesFile: DEFAULT_SENDER_POLICIES_FILE,
|
|
623
621
|
...(senderPolicies ? { senderPolicies } : {}),
|
|
624
622
|
}, {
|
|
625
623
|
includeCodingBaseline,
|
|
@@ -646,6 +644,10 @@ export async function runInit() {
|
|
|
646
644
|
slug,
|
|
647
645
|
credentialsFile: DEFAULT_CREDENTIALS_FILE,
|
|
648
646
|
})
|
|
647
|
+
const pairing = identityResult.email
|
|
648
|
+
? createPairingFile({ email: identityResult.email, pairingFile: DEFAULT_PAIRING_FILE })
|
|
649
|
+
: null
|
|
650
|
+
const qr = pairing ? await renderTerminalQr(pairing.connectUrl) : ''
|
|
649
651
|
|
|
650
652
|
const restartResult = restartGateway()
|
|
651
653
|
|
|
@@ -661,7 +663,9 @@ export async function runInit() {
|
|
|
661
663
|
` channels.aamp.enabled: ${next.channels?.aamp?.enabled === true ? 'true' : 'false'}`,
|
|
662
664
|
` aampHost: ${aampHost}`,
|
|
663
665
|
` credentialsFile: ${DEFAULT_CREDENTIALS_FILE}`,
|
|
664
|
-
`
|
|
666
|
+
` pairingFile: ${DEFAULT_PAIRING_FILE}`,
|
|
667
|
+
` senderPoliciesFile: ${DEFAULT_SENDER_POLICIES_FILE}`,
|
|
668
|
+
` senderPolicies: ${senderPolicies ? JSON.stringify(senderPolicies) : '(default deny until paired or configured)'}`,
|
|
665
669
|
` tools.allow: ${JSON.stringify(next.tools?.allow ?? [])}`,
|
|
666
670
|
` codingBaselineAdded: ${toolPolicyPlan.missingCodingTools.length > 0 && includeCodingBaseline ? 'yes' : 'no'}`,
|
|
667
671
|
identityResult.created
|
|
@@ -676,6 +680,8 @@ export async function runInit() {
|
|
|
676
680
|
restartResult.ok
|
|
677
681
|
? 'Plugin changes should now be active.'
|
|
678
682
|
: 'Please restart the OpenClaw gateway manually for the plugin changes to take effect.',
|
|
683
|
+
pairing ? `Pairing URL (expires ${pairing.expiresAt}): ${pairing.connectUrl}` : '',
|
|
684
|
+
qr ? `\n${qr}` : '',
|
|
679
685
|
'',
|
|
680
686
|
].join('\n'),
|
|
681
687
|
)
|
package/dist/file-store.js
CHANGED
|
@@ -2,12 +2,19 @@
|
|
|
2
2
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
|
+
import { randomBytes } from "node:crypto";
|
|
5
6
|
function defaultCredentialsPath() {
|
|
6
7
|
return join(homedir(), ".openclaw", "extensions", "aamp-openclaw-plugin", ".credentials.json");
|
|
7
8
|
}
|
|
8
9
|
function defaultTaskStatePath() {
|
|
9
10
|
return join(homedir(), ".openclaw", "extensions", "aamp-openclaw-plugin", ".task-state.json");
|
|
10
11
|
}
|
|
12
|
+
function defaultPairingPath() {
|
|
13
|
+
return join(homedir(), ".openclaw", "extensions", "aamp-openclaw-plugin", ".pairing.json");
|
|
14
|
+
}
|
|
15
|
+
function defaultSenderPoliciesPath() {
|
|
16
|
+
return join(homedir(), ".openclaw", "extensions", "aamp-openclaw-plugin", ".sender-policies.json");
|
|
17
|
+
}
|
|
11
18
|
function loadCachedIdentity(file) {
|
|
12
19
|
const resolved = file ?? defaultCredentialsPath();
|
|
13
20
|
if (!existsSync(resolved))
|
|
@@ -54,6 +61,66 @@ function saveTaskState(state, file) {
|
|
|
54
61
|
terminalTaskIds: state.terminalTaskIds ?? []
|
|
55
62
|
}, null, 2), "utf-8");
|
|
56
63
|
}
|
|
64
|
+
function createPairingCode(params) {
|
|
65
|
+
const pairCode = randomBytes(6).toString("base64url");
|
|
66
|
+
const expiresAt = new Date(Date.now() + (params.ttlSeconds ?? 300) * 1e3).toISOString();
|
|
67
|
+
const connectUrl = `aamp://connect?mailbox=${encodeURIComponent(params.mailbox.toLowerCase())}&pair_code=${encodeURIComponent(pairCode)}`;
|
|
68
|
+
const state = {
|
|
69
|
+
mailbox: params.mailbox.toLowerCase(),
|
|
70
|
+
pairCode,
|
|
71
|
+
expiresAt,
|
|
72
|
+
connectUrl
|
|
73
|
+
};
|
|
74
|
+
const resolved = params.file ?? defaultPairingPath();
|
|
75
|
+
mkdirSync(dirname(resolved), { recursive: true });
|
|
76
|
+
writeFileSync(resolved, JSON.stringify(state, null, 2), "utf-8");
|
|
77
|
+
return state;
|
|
78
|
+
}
|
|
79
|
+
function consumePairingCode(params) {
|
|
80
|
+
const resolved = params.file ?? defaultPairingPath();
|
|
81
|
+
if (!existsSync(resolved))
|
|
82
|
+
return null;
|
|
83
|
+
const state = JSON.parse(readFileSync(resolved, "utf-8"));
|
|
84
|
+
if (state.mailbox.toLowerCase() !== params.mailbox.toLowerCase())
|
|
85
|
+
return null;
|
|
86
|
+
if (state.pairCode !== params.pairCode)
|
|
87
|
+
return null;
|
|
88
|
+
if (state.consumedAt)
|
|
89
|
+
return null;
|
|
90
|
+
if (new Date(state.expiresAt).getTime() <= Date.now())
|
|
91
|
+
return null;
|
|
92
|
+
writeFileSync(resolved, JSON.stringify({ ...state, pairCode: "", consumedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2), "utf-8");
|
|
93
|
+
return state;
|
|
94
|
+
}
|
|
95
|
+
function isPairedSenderPolicy(value) {
|
|
96
|
+
if (!value || typeof value !== "object")
|
|
97
|
+
return false;
|
|
98
|
+
const policy = value;
|
|
99
|
+
return typeof policy.sender === "string" && typeof policy.dispatchContextRules === "object" && typeof policy.pairedAt === "string";
|
|
100
|
+
}
|
|
101
|
+
function loadPairedSenderPolicies(file) {
|
|
102
|
+
const resolved = file ?? defaultSenderPoliciesPath();
|
|
103
|
+
if (!existsSync(resolved))
|
|
104
|
+
return [];
|
|
105
|
+
try {
|
|
106
|
+
const data = JSON.parse(readFileSync(resolved, "utf-8"));
|
|
107
|
+
return Array.isArray(data) ? data.filter(isPairedSenderPolicy) : [];
|
|
108
|
+
} catch {
|
|
109
|
+
return [];
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function addPairedSenderPolicy(file, policy) {
|
|
113
|
+
const resolved = file ?? defaultSenderPoliciesPath();
|
|
114
|
+
const policies = loadPairedSenderPolicies(resolved);
|
|
115
|
+
const normalizedSender = policy.sender.toLowerCase();
|
|
116
|
+
const next = [
|
|
117
|
+
...policies.filter((item) => item.sender.toLowerCase() !== normalizedSender),
|
|
118
|
+
{ ...policy, sender: normalizedSender }
|
|
119
|
+
];
|
|
120
|
+
mkdirSync(dirname(resolved), { recursive: true });
|
|
121
|
+
writeFileSync(resolved, JSON.stringify(next, null, 2), "utf-8");
|
|
122
|
+
return next;
|
|
123
|
+
}
|
|
57
124
|
function ensureDir(dir) {
|
|
58
125
|
mkdirSync(dir, { recursive: true });
|
|
59
126
|
}
|
|
@@ -65,10 +132,16 @@ function writeBinaryFile(path, content) {
|
|
|
65
132
|
writeFileSync(path, content);
|
|
66
133
|
}
|
|
67
134
|
export {
|
|
135
|
+
addPairedSenderPolicy,
|
|
136
|
+
consumePairingCode,
|
|
137
|
+
createPairingCode,
|
|
68
138
|
defaultCredentialsPath,
|
|
139
|
+
defaultPairingPath,
|
|
140
|
+
defaultSenderPoliciesPath,
|
|
69
141
|
defaultTaskStatePath,
|
|
70
142
|
ensureDir,
|
|
71
143
|
loadCachedIdentity,
|
|
144
|
+
loadPairedSenderPolicies,
|
|
72
145
|
loadTaskState,
|
|
73
146
|
readBinaryFile,
|
|
74
147
|
saveCachedIdentity,
|
package/dist/file-store.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
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 mailboxToken: string\n smtpPassword: string\n}\n\nexport interface CachedTaskState {\n terminalTaskIds?: string[]\n}\n\nexport function defaultCredentialsPath(): string {\n return join(homedir(), '.openclaw', 'extensions', 'aamp-openclaw-plugin', '.credentials.json')\n}\n\nexport function defaultTaskStatePath(): string {\n return join(homedir(), '.openclaw', 'extensions', 'aamp-openclaw-plugin', '.task-state.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.mailboxToken || !parsed.smtpPassword) return null\n return {\n email: parsed.email,\n mailboxToken: parsed.mailboxToken,\n smtpPassword: parsed.smtpPassword,\n }\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({\n email: identity.email,\n mailboxToken: identity.mailboxToken,\n smtpPassword: identity.smtpPassword,\n }, null, 2), 'utf-8')\n}\n\nexport function loadTaskState(file?: string): CachedTaskState {\n const resolved = file ?? defaultTaskStatePath()\n if (!existsSync(resolved)) return { terminalTaskIds: [] }\n try {\n const parsed = JSON.parse(readFileSync(resolved, 'utf-8')) as CachedTaskState\n return {\n terminalTaskIds: Array.isArray(parsed.terminalTaskIds) ? parsed.terminalTaskIds.filter(Boolean) : [],\n }\n } catch {\n return { terminalTaskIds: [] }\n }\n}\n\nexport function saveTaskState(state: CachedTaskState, file?: string): void {\n const resolved = file ?? defaultTaskStatePath()\n mkdirSync(dirname(resolved), { recursive: true })\n writeFileSync(resolved, JSON.stringify({\n terminalTaskIds: state.terminalTaskIds ?? [],\n }, 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;
|
|
4
|
+
"sourcesContent": ["import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport { homedir } from 'node:os'\nimport { randomBytes } from 'node:crypto'\n\nexport interface Identity {\n email: string\n mailboxToken: string\n smtpPassword: string\n}\n\nexport interface CachedTaskState {\n terminalTaskIds?: string[]\n}\n\nexport interface DispatchContextRules {\n [key: string]: string[]\n}\n\nexport interface PairingCodeState {\n mailbox: string\n pairCode: string\n expiresAt: string\n connectUrl: string\n consumedAt?: string\n}\n\nexport interface PairedSenderPolicy {\n sender: string\n dispatchContextRules: DispatchContextRules\n pairedAt: string\n}\n\nexport function defaultCredentialsPath(): string {\n return join(homedir(), '.openclaw', 'extensions', 'aamp-openclaw-plugin', '.credentials.json')\n}\n\nexport function defaultTaskStatePath(): string {\n return join(homedir(), '.openclaw', 'extensions', 'aamp-openclaw-plugin', '.task-state.json')\n}\n\nexport function defaultPairingPath(): string {\n return join(homedir(), '.openclaw', 'extensions', 'aamp-openclaw-plugin', '.pairing.json')\n}\n\nexport function defaultSenderPoliciesPath(): string {\n return join(homedir(), '.openclaw', 'extensions', 'aamp-openclaw-plugin', '.sender-policies.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.mailboxToken || !parsed.smtpPassword) return null\n return {\n email: parsed.email,\n mailboxToken: parsed.mailboxToken,\n smtpPassword: parsed.smtpPassword,\n }\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({\n email: identity.email,\n mailboxToken: identity.mailboxToken,\n smtpPassword: identity.smtpPassword,\n }, null, 2), 'utf-8')\n}\n\nexport function loadTaskState(file?: string): CachedTaskState {\n const resolved = file ?? defaultTaskStatePath()\n if (!existsSync(resolved)) return { terminalTaskIds: [] }\n try {\n const parsed = JSON.parse(readFileSync(resolved, 'utf-8')) as CachedTaskState\n return {\n terminalTaskIds: Array.isArray(parsed.terminalTaskIds) ? parsed.terminalTaskIds.filter(Boolean) : [],\n }\n } catch {\n return { terminalTaskIds: [] }\n }\n}\n\nexport function saveTaskState(state: CachedTaskState, file?: string): void {\n const resolved = file ?? defaultTaskStatePath()\n mkdirSync(dirname(resolved), { recursive: true })\n writeFileSync(resolved, JSON.stringify({\n terminalTaskIds: state.terminalTaskIds ?? [],\n }, null, 2), 'utf-8')\n}\n\nexport function createPairingCode(params: {\n mailbox: string\n file?: string\n ttlSeconds?: number\n}): PairingCodeState {\n const pairCode = randomBytes(6).toString('base64url')\n const expiresAt = new Date(Date.now() + (params.ttlSeconds ?? 300) * 1000).toISOString()\n const connectUrl = `aamp://connect?mailbox=${encodeURIComponent(params.mailbox.toLowerCase())}&pair_code=${encodeURIComponent(pairCode)}`\n const state: PairingCodeState = {\n mailbox: params.mailbox.toLowerCase(),\n pairCode,\n expiresAt,\n connectUrl,\n }\n const resolved = params.file ?? defaultPairingPath()\n mkdirSync(dirname(resolved), { recursive: true })\n writeFileSync(resolved, JSON.stringify(state, null, 2), 'utf-8')\n return state\n}\n\nexport function consumePairingCode(params: {\n mailbox: string\n pairCode: string\n file?: string\n}): PairingCodeState | null {\n const resolved = params.file ?? defaultPairingPath()\n if (!existsSync(resolved)) return null\n const state = JSON.parse(readFileSync(resolved, 'utf-8')) as PairingCodeState\n if (state.mailbox.toLowerCase() !== params.mailbox.toLowerCase()) return null\n if (state.pairCode !== params.pairCode) return null\n if (state.consumedAt) return null\n if (new Date(state.expiresAt).getTime() <= Date.now()) return null\n writeFileSync(resolved, JSON.stringify({ ...state, pairCode: '', consumedAt: new Date().toISOString() }, null, 2), 'utf-8')\n return state\n}\n\nfunction isPairedSenderPolicy(value: unknown): value is PairedSenderPolicy {\n if (!value || typeof value !== 'object') return false\n const policy = value as PairedSenderPolicy\n return typeof policy.sender === 'string'\n && typeof policy.dispatchContextRules === 'object'\n && typeof policy.pairedAt === 'string'\n}\n\nexport function loadPairedSenderPolicies(file?: string): PairedSenderPolicy[] {\n const resolved = file ?? defaultSenderPoliciesPath()\n if (!existsSync(resolved)) return []\n try {\n const data = JSON.parse(readFileSync(resolved, 'utf-8')) as unknown\n return Array.isArray(data) ? data.filter(isPairedSenderPolicy) : []\n } catch {\n return []\n }\n}\n\nexport function addPairedSenderPolicy(file: string | undefined, policy: PairedSenderPolicy): PairedSenderPolicy[] {\n const resolved = file ?? defaultSenderPoliciesPath()\n const policies = loadPairedSenderPolicies(resolved)\n const normalizedSender = policy.sender.toLowerCase()\n const next = [\n ...policies.filter((item) => item.sender.toLowerCase() !== normalizedSender),\n { ...policy, sender: normalizedSender },\n ]\n mkdirSync(dirname(resolved), { recursive: true })\n writeFileSync(resolved, JSON.stringify(next, null, 2), 'utf-8')\n return next\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": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|