aamp-openclaw-plugin 0.1.7 → 0.1.8
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 +3 -3
- package/bin/aamp-openclaw-plugin.mjs +177 -0
- package/dist/file-store.js +43 -0
- package/dist/file-store.js.map +7 -0
- package/dist/index.js +39 -1212
- package/dist/index.js.map +4 -4
- package/openclaw.plugin.json +6 -5
- package/package.json +13 -2
- package/skills/SKILL.md +1 -1
package/README.md
CHANGED
|
@@ -27,14 +27,14 @@ npm run build
|
|
|
27
27
|
{
|
|
28
28
|
"plugins": {
|
|
29
29
|
"entries": {
|
|
30
|
-
"aamp": {
|
|
30
|
+
"aamp-openclaw-plugin": {
|
|
31
31
|
"enabled": true,
|
|
32
32
|
"config": {
|
|
33
33
|
"aampHost": "https://meshmail.ai",
|
|
34
34
|
"slug": "openclaw-agent",
|
|
35
|
-
"credentialsFile": "
|
|
35
|
+
"credentialsFile": "~/.openclaw/extensions/aamp-openclaw-plugin/.credentials.json",
|
|
36
36
|
"allowedSenders": [
|
|
37
|
-
"
|
|
37
|
+
"meegle-bot@meshmail.ai"
|
|
38
38
|
]
|
|
39
39
|
}
|
|
40
40
|
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
|
|
4
|
+
import { homedir } from 'node:os'
|
|
5
|
+
import { dirname, join } from 'node:path'
|
|
6
|
+
import { spawnSync } from 'node:child_process'
|
|
7
|
+
import { createInterface } from 'node:readline/promises'
|
|
8
|
+
import { stdin as input, stdout as output, stderr } from 'node:process'
|
|
9
|
+
|
|
10
|
+
const PLUGIN_ID = 'aamp-openclaw-plugin'
|
|
11
|
+
const DEFAULT_AAMP_HOST = 'https://meshmail.ai'
|
|
12
|
+
const DEFAULT_CREDENTIALS_FILE = '~/.openclaw/extensions/aamp-openclaw-plugin/.credentials.json'
|
|
13
|
+
|
|
14
|
+
function resolveOpenClawHome() {
|
|
15
|
+
return process.env.OPENCLAW_HOME?.trim() || join(homedir(), '.openclaw')
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function resolveOpenClawConfigPath() {
|
|
19
|
+
return join(resolveOpenClawHome(), 'openclaw.json')
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function expandHome(pathValue) {
|
|
23
|
+
if (!pathValue) return pathValue
|
|
24
|
+
if (pathValue === '~') return homedir()
|
|
25
|
+
if (pathValue.startsWith('~/')) return join(homedir(), pathValue.slice(2))
|
|
26
|
+
return pathValue
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function readJsonFile(path) {
|
|
30
|
+
if (!existsSync(path)) return null
|
|
31
|
+
return JSON.parse(readFileSync(path, 'utf-8'))
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function writeJsonFile(path, value) {
|
|
35
|
+
mkdirSync(dirname(path), { recursive: true })
|
|
36
|
+
writeFileSync(path, JSON.stringify(value, null, 2) + '\n', 'utf-8')
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function ensurePluginConfig(config, pluginConfig) {
|
|
40
|
+
const next = config && typeof config === 'object' ? structuredClone(config) : {}
|
|
41
|
+
if (!next.plugins || typeof next.plugins !== 'object') next.plugins = {}
|
|
42
|
+
if (!next.plugins.entries || typeof next.plugins.entries !== 'object') next.plugins.entries = {}
|
|
43
|
+
|
|
44
|
+
const legacyEntry = next.plugins.entries.aamp
|
|
45
|
+
const prevEntry = next.plugins.entries[PLUGIN_ID] ?? legacyEntry
|
|
46
|
+
const mergedConfig = {
|
|
47
|
+
...(prevEntry?.config && typeof prevEntry.config === 'object' ? prevEntry.config : {}),
|
|
48
|
+
...pluginConfig,
|
|
49
|
+
}
|
|
50
|
+
if (!pluginConfig.allowedSenders) {
|
|
51
|
+
delete mergedConfig.allowedSenders
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
next.plugins.entries[PLUGIN_ID] = {
|
|
55
|
+
enabled: true,
|
|
56
|
+
...(prevEntry && typeof prevEntry === 'object' ? prevEntry : {}),
|
|
57
|
+
config: mergedConfig,
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (next.plugins.entries.aamp) {
|
|
61
|
+
delete next.plugins.entries.aamp
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return next
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function parseAllowedSenders(raw) {
|
|
68
|
+
const trimmed = raw.trim()
|
|
69
|
+
if (!trimmed) return undefined
|
|
70
|
+
return trimmed
|
|
71
|
+
.split(',')
|
|
72
|
+
.map((s) => s.trim())
|
|
73
|
+
.filter(Boolean)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function installPlugin() {
|
|
77
|
+
const result = spawnSync('openclaw', ['plugins', 'install', PLUGIN_ID], {
|
|
78
|
+
stdio: 'inherit',
|
|
79
|
+
env: process.env,
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
return result.status === 0
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function printHelp() {
|
|
86
|
+
output.write(
|
|
87
|
+
[
|
|
88
|
+
'aamp-openclaw-plugin',
|
|
89
|
+
'',
|
|
90
|
+
'Commands:',
|
|
91
|
+
' init Install the OpenClaw plugin and write ~/.openclaw/openclaw.json',
|
|
92
|
+
' help Show this help',
|
|
93
|
+
'',
|
|
94
|
+
].join('\n'),
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async function runInit() {
|
|
99
|
+
let aampHost = DEFAULT_AAMP_HOST
|
|
100
|
+
let allowedSenders
|
|
101
|
+
|
|
102
|
+
if (input.isTTY) {
|
|
103
|
+
const rl = createInterface({ input, output })
|
|
104
|
+
try {
|
|
105
|
+
output.write('AAMP OpenClaw Plugin Setup\n\n')
|
|
106
|
+
|
|
107
|
+
const aampHostAnswer = await rl.question(`AAMP Host (${DEFAULT_AAMP_HOST}): `)
|
|
108
|
+
aampHost = aampHostAnswer.trim() || DEFAULT_AAMP_HOST
|
|
109
|
+
|
|
110
|
+
const allowedSendersAnswer = await rl.question(
|
|
111
|
+
'Allowed Senders (comma-separated, leave blank to allow all): ',
|
|
112
|
+
)
|
|
113
|
+
allowedSenders = parseAllowedSenders(allowedSendersAnswer)
|
|
114
|
+
} finally {
|
|
115
|
+
rl.close()
|
|
116
|
+
}
|
|
117
|
+
} else {
|
|
118
|
+
const [hostLine = '', sendersLine = ''] = readFileSync(0, 'utf-8').split(/\r?\n/)
|
|
119
|
+
aampHost = hostLine.trim() || DEFAULT_AAMP_HOST
|
|
120
|
+
allowedSenders = parseAllowedSenders(sendersLine)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const configPath = resolveOpenClawConfigPath()
|
|
124
|
+
const existing = readJsonFile(configPath)
|
|
125
|
+
|
|
126
|
+
output.write('\nInstalling OpenClaw plugin...\n')
|
|
127
|
+
const installed = installPlugin()
|
|
128
|
+
if (!installed) {
|
|
129
|
+
output.write(`Warning: failed to run "openclaw plugins install ${PLUGIN_ID}". Continuing with config update.\n`)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const next = ensurePluginConfig(existing, {
|
|
133
|
+
aampHost,
|
|
134
|
+
slug: 'openclaw-agent',
|
|
135
|
+
credentialsFile: DEFAULT_CREDENTIALS_FILE,
|
|
136
|
+
...(allowedSenders ? { allowedSenders } : {}),
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
writeJsonFile(configPath, next)
|
|
140
|
+
|
|
141
|
+
output.write(
|
|
142
|
+
[
|
|
143
|
+
'',
|
|
144
|
+
`Updated ${configPath}`,
|
|
145
|
+
'',
|
|
146
|
+
'Configured plugin entry:',
|
|
147
|
+
` plugins.entries["${PLUGIN_ID}"]`,
|
|
148
|
+
` aampHost: ${aampHost}`,
|
|
149
|
+
` credentialsFile: ${DEFAULT_CREDENTIALS_FILE}`,
|
|
150
|
+
` allowedSenders: ${allowedSenders ? allowedSenders.join(', ') : '(allow all)'}`,
|
|
151
|
+
'',
|
|
152
|
+
].join('\n'),
|
|
153
|
+
)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async function main() {
|
|
157
|
+
const command = process.argv[2] || 'help'
|
|
158
|
+
|
|
159
|
+
if (command === 'help' || command === '--help' || command === '-h') {
|
|
160
|
+
printHelp()
|
|
161
|
+
return
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (command === 'init') {
|
|
165
|
+
await runInit()
|
|
166
|
+
return
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
stderr.write(`Unknown command: ${command}\n\n`)
|
|
170
|
+
printHelp()
|
|
171
|
+
process.exitCode = 1
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
main().catch((err) => {
|
|
175
|
+
stderr.write(`${err instanceof Error ? err.message : String(err)}\n`)
|
|
176
|
+
process.exit(1)
|
|
177
|
+
})
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
function defaultCredentialsPath() {
|
|
5
|
+
return join(homedir(), ".openclaw", "extensions", "aamp-openclaw-plugin", ".credentials.json");
|
|
6
|
+
}
|
|
7
|
+
function loadCachedIdentity(file) {
|
|
8
|
+
const resolved = file ?? defaultCredentialsPath();
|
|
9
|
+
if (!existsSync(resolved))
|
|
10
|
+
return null;
|
|
11
|
+
try {
|
|
12
|
+
const parsed = JSON.parse(readFileSync(resolved, "utf-8"));
|
|
13
|
+
if (!parsed.email || !parsed.jmapToken || !parsed.smtpPassword)
|
|
14
|
+
return null;
|
|
15
|
+
return parsed;
|
|
16
|
+
} catch {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function saveCachedIdentity(identity, file) {
|
|
21
|
+
const resolved = file ?? defaultCredentialsPath();
|
|
22
|
+
mkdirSync(dirname(resolved), { recursive: true });
|
|
23
|
+
writeFileSync(resolved, JSON.stringify(identity, null, 2), "utf-8");
|
|
24
|
+
}
|
|
25
|
+
function ensureDir(dir) {
|
|
26
|
+
mkdirSync(dir, { recursive: true });
|
|
27
|
+
}
|
|
28
|
+
function readBinaryFile(path) {
|
|
29
|
+
return readFileSync(path);
|
|
30
|
+
}
|
|
31
|
+
function writeBinaryFile(path, content) {
|
|
32
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
33
|
+
writeFileSync(path, content);
|
|
34
|
+
}
|
|
35
|
+
export {
|
|
36
|
+
defaultCredentialsPath,
|
|
37
|
+
ensureDir,
|
|
38
|
+
loadCachedIdentity,
|
|
39
|
+
readBinaryFile,
|
|
40
|
+
saveCachedIdentity,
|
|
41
|
+
writeBinaryFile
|
|
42
|
+
};
|
|
43
|
+
//# sourceMappingURL=file-store.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
|
+
}
|