aamp-openclaw-plugin 0.1.22 → 0.1.24
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/bin/aamp-openclaw-plugin.mjs +57 -15
- package/dist/index.js +31 -11
- package/dist/index.js.map +2 -2
- package/package.json +1 -1
|
@@ -82,6 +82,7 @@ function ensurePluginConfig(config, pluginConfig, options = {}) {
|
|
|
82
82
|
if (!next.plugins || typeof next.plugins !== 'object') next.plugins = {}
|
|
83
83
|
if (!Array.isArray(next.plugins.allow)) next.plugins.allow = []
|
|
84
84
|
if (!next.plugins.entries || typeof next.plugins.entries !== 'object') next.plugins.entries = {}
|
|
85
|
+
if (!next.channels || typeof next.channels !== 'object') next.channels = {}
|
|
85
86
|
|
|
86
87
|
if (!next.plugins.allow.includes(PLUGIN_ID)) {
|
|
87
88
|
next.plugins.allow.push(PLUGIN_ID)
|
|
@@ -89,29 +90,51 @@ function ensurePluginConfig(config, pluginConfig, options = {}) {
|
|
|
89
90
|
|
|
90
91
|
const legacyEntry = next.plugins.entries.aamp
|
|
91
92
|
const prevEntry = next.plugins.entries[PLUGIN_ID] ?? legacyEntry
|
|
92
|
-
const mergedConfig = {
|
|
93
|
-
...(prevEntry?.config && typeof prevEntry.config === 'object' ? prevEntry.config : {}),
|
|
94
|
-
...pluginConfig,
|
|
95
|
-
}
|
|
96
|
-
if (!pluginConfig.senderPolicies) {
|
|
97
|
-
delete mergedConfig.senderPolicies
|
|
98
|
-
}
|
|
99
|
-
|
|
100
93
|
next.plugins.entries[PLUGIN_ID] = {
|
|
101
94
|
enabled: true,
|
|
102
95
|
...(prevEntry && typeof prevEntry === 'object' ? prevEntry : {}),
|
|
103
|
-
config: mergedConfig,
|
|
104
96
|
}
|
|
105
97
|
|
|
106
98
|
if (next.plugins.entries.aamp) {
|
|
107
99
|
delete next.plugins.entries.aamp
|
|
108
100
|
}
|
|
109
101
|
|
|
102
|
+
const previousChannelConfig =
|
|
103
|
+
next.channels.aamp && typeof next.channels.aamp === 'object' ? next.channels.aamp : {}
|
|
104
|
+
const mergedChannelConfig = {
|
|
105
|
+
...previousChannelConfig,
|
|
106
|
+
...pluginConfig,
|
|
107
|
+
enabled: true,
|
|
108
|
+
}
|
|
109
|
+
if (!pluginConfig.senderPolicies) {
|
|
110
|
+
delete mergedChannelConfig.senderPolicies
|
|
111
|
+
}
|
|
112
|
+
next.channels.aamp = mergedChannelConfig
|
|
113
|
+
|
|
110
114
|
next.tools = ensureAampToolAllowlist(next.tools, options)
|
|
111
115
|
|
|
112
116
|
return next
|
|
113
117
|
}
|
|
114
118
|
|
|
119
|
+
function ensurePluginInstallRecord(config, installRecord) {
|
|
120
|
+
const next = config && typeof config === 'object' ? structuredClone(config) : {}
|
|
121
|
+
if (!next.plugins || typeof next.plugins !== 'object') next.plugins = {}
|
|
122
|
+
if (!next.plugins.installs || typeof next.plugins.installs !== 'object') next.plugins.installs = {}
|
|
123
|
+
|
|
124
|
+
next.plugins.installs[PLUGIN_ID] = {
|
|
125
|
+
...(next.plugins.installs[PLUGIN_ID] && typeof next.plugins.installs[PLUGIN_ID] === 'object'
|
|
126
|
+
? next.plugins.installs[PLUGIN_ID]
|
|
127
|
+
: {}),
|
|
128
|
+
...installRecord,
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (next.plugins.installs.aamp) {
|
|
132
|
+
delete next.plugins.installs.aamp
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return next
|
|
136
|
+
}
|
|
137
|
+
|
|
115
138
|
function ensureAampToolAllowlist(toolsConfig, options = {}) {
|
|
116
139
|
const next = toolsConfig && typeof toolsConfig === 'object' ? structuredClone(toolsConfig) : {}
|
|
117
140
|
const existingAllow = Array.isArray(next.allow) ? next.allow.filter((value) => typeof value === 'string' && value.trim()) : []
|
|
@@ -278,7 +301,7 @@ function installPluginFiles(credentialsFile = DEFAULT_CREDENTIALS_FILE) {
|
|
|
278
301
|
writeFileSync(credentialsPath, existingCredentials)
|
|
279
302
|
}
|
|
280
303
|
|
|
281
|
-
return extensionDir
|
|
304
|
+
return { extensionDir, packageJson, packageRoot }
|
|
282
305
|
}
|
|
283
306
|
|
|
284
307
|
function restartGateway() {
|
|
@@ -371,9 +394,12 @@ async function runInit() {
|
|
|
371
394
|
const configPath = resolveOpenClawConfigPath()
|
|
372
395
|
const existing = readJsonFile(configPath)
|
|
373
396
|
const previousEntry = existing?.plugins?.entries?.[PLUGIN_ID] ?? existing?.plugins?.entries?.aamp
|
|
374
|
-
const previousConfig =
|
|
375
|
-
|
|
376
|
-
|
|
397
|
+
const previousConfig =
|
|
398
|
+
existing?.channels?.aamp && typeof existing.channels.aamp === 'object'
|
|
399
|
+
? existing.channels.aamp
|
|
400
|
+
: previousEntry?.config && typeof previousEntry.config === 'object'
|
|
401
|
+
? previousEntry.config
|
|
402
|
+
: null
|
|
377
403
|
const previousCredentialsFile = previousConfig?.credentialsFile || DEFAULT_CREDENTIALS_FILE
|
|
378
404
|
const previousSlug = previousConfig?.slug || 'openclaw-agent'
|
|
379
405
|
|
|
@@ -466,10 +492,10 @@ async function runInit() {
|
|
|
466
492
|
}
|
|
467
493
|
|
|
468
494
|
output.write('\nInstalling OpenClaw plugin files...\n')
|
|
469
|
-
const extensionDir = installPluginFiles(previousCredentialsFile)
|
|
495
|
+
const { extensionDir, packageJson, packageRoot } = installPluginFiles(previousCredentialsFile)
|
|
470
496
|
|
|
471
497
|
const toolPolicyPlan = planToolPolicyUpdate(existing?.tools, { includeCodingBaseline })
|
|
472
|
-
|
|
498
|
+
let next = ensurePluginConfig(existing, {
|
|
473
499
|
aampHost,
|
|
474
500
|
slug,
|
|
475
501
|
credentialsFile: DEFAULT_CREDENTIALS_FILE,
|
|
@@ -478,6 +504,20 @@ async function runInit() {
|
|
|
478
504
|
includeCodingBaseline,
|
|
479
505
|
})
|
|
480
506
|
|
|
507
|
+
const now = new Date().toISOString()
|
|
508
|
+
next = ensurePluginInstallRecord(next, {
|
|
509
|
+
source: 'npm',
|
|
510
|
+
spec: packageJson?.name || PLUGIN_ID,
|
|
511
|
+
sourcePath: packageRoot,
|
|
512
|
+
installPath: extensionDir,
|
|
513
|
+
version: packageJson?.version || '0.0.0',
|
|
514
|
+
resolvedName: packageJson?.name || PLUGIN_ID,
|
|
515
|
+
resolvedVersion: packageJson?.version || '0.0.0',
|
|
516
|
+
resolvedSpec: `${packageJson?.name || PLUGIN_ID}@${packageJson?.version || '0.0.0'}`,
|
|
517
|
+
installedAt: now,
|
|
518
|
+
resolvedAt: now,
|
|
519
|
+
})
|
|
520
|
+
|
|
481
521
|
writeJsonFile(configPath, next)
|
|
482
522
|
|
|
483
523
|
const identityResult = await ensureMailboxIdentity({
|
|
@@ -496,6 +536,8 @@ async function runInit() {
|
|
|
496
536
|
'',
|
|
497
537
|
'Configured plugin entry:',
|
|
498
538
|
` plugins.entries["${PLUGIN_ID}"]`,
|
|
539
|
+
` plugins.installs["${PLUGIN_ID}"]`,
|
|
540
|
+
` channels.aamp.enabled: ${next.channels?.aamp?.enabled === true ? 'true' : 'false'}`,
|
|
499
541
|
` aampHost: ${aampHost}`,
|
|
500
542
|
` credentialsFile: ${DEFAULT_CREDENTIALS_FILE}`,
|
|
501
543
|
` senderPolicies: ${senderPolicies ? JSON.stringify(senderPolicies) : '(allow all)'}`,
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
import WebSocket from "ws";
|
|
3
3
|
|
|
4
4
|
// ../sdk/src/types.ts
|
|
5
|
+
var AAMP_PROTOCOL_VERSION = "1.0";
|
|
5
6
|
var AAMP_HEADER = {
|
|
7
|
+
VERSION: "X-AAMP-Version",
|
|
6
8
|
INTENT: "X-AAMP-Intent",
|
|
7
9
|
TASK_ID: "X-AAMP-TaskId",
|
|
8
10
|
TIMEOUT: "X-AAMP-Timeout",
|
|
@@ -122,6 +124,7 @@ function parseAampHeaders(meta) {
|
|
|
122
124
|
const headers = normalizeHeaders(meta.headers);
|
|
123
125
|
const intent = getAampHeader(headers, AAMP_HEADER.INTENT);
|
|
124
126
|
const taskId = getAampHeader(headers, AAMP_HEADER.TASK_ID);
|
|
127
|
+
const protocolVersion = getAampHeader(headers, AAMP_HEADER.VERSION) ?? AAMP_PROTOCOL_VERSION;
|
|
125
128
|
if (!intent || !taskId)
|
|
126
129
|
return null;
|
|
127
130
|
const from = meta.from.replace(/^<|>$/g, "");
|
|
@@ -135,6 +138,7 @@ function parseAampHeaders(meta) {
|
|
|
135
138
|
);
|
|
136
139
|
const parentTaskId = getAampHeader(headers, AAMP_HEADER.PARENT_TASK_ID);
|
|
137
140
|
const dispatch = {
|
|
141
|
+
protocolVersion,
|
|
138
142
|
intent: "task.dispatch",
|
|
139
143
|
taskId,
|
|
140
144
|
title: decodedSubject.replace(/^\[AAMP Task\]\s*/, "").trim() || "Untitled Task",
|
|
@@ -159,6 +163,7 @@ function parseAampHeaders(meta) {
|
|
|
159
163
|
getAampHeader(headers, AAMP_HEADER.STRUCTURED_RESULT)
|
|
160
164
|
);
|
|
161
165
|
const result = {
|
|
166
|
+
protocolVersion,
|
|
162
167
|
intent: "task.result",
|
|
163
168
|
taskId,
|
|
164
169
|
status,
|
|
@@ -171,12 +176,13 @@ function parseAampHeaders(meta) {
|
|
|
171
176
|
};
|
|
172
177
|
return result;
|
|
173
178
|
}
|
|
174
|
-
if (intent === "task.help") {
|
|
179
|
+
if (intent === "task.help_needed" || intent === "task.help") {
|
|
175
180
|
const question = getAampHeader(headers, AAMP_HEADER.QUESTION) ?? "";
|
|
176
181
|
const blockedReason = getAampHeader(headers, AAMP_HEADER.BLOCKED_REASON) ?? "";
|
|
177
182
|
const suggestedOptionsStr = getAampHeader(headers, AAMP_HEADER.SUGGESTED_OPTIONS) ?? "";
|
|
178
183
|
const help = {
|
|
179
|
-
|
|
184
|
+
protocolVersion,
|
|
185
|
+
intent: "task.help_needed",
|
|
180
186
|
taskId,
|
|
181
187
|
question: decodeMimeEncodedWords(question),
|
|
182
188
|
blockedReason: decodeMimeEncodedWords(blockedReason),
|
|
@@ -189,6 +195,7 @@ function parseAampHeaders(meta) {
|
|
|
189
195
|
}
|
|
190
196
|
if (intent === "task.ack") {
|
|
191
197
|
const ack = {
|
|
198
|
+
protocolVersion,
|
|
192
199
|
intent: "task.ack",
|
|
193
200
|
taskId,
|
|
194
201
|
from,
|
|
@@ -201,6 +208,7 @@ function parseAampHeaders(meta) {
|
|
|
201
208
|
}
|
|
202
209
|
function buildDispatchHeaders(params) {
|
|
203
210
|
const headers = {
|
|
211
|
+
[AAMP_HEADER.VERSION]: AAMP_PROTOCOL_VERSION,
|
|
204
212
|
[AAMP_HEADER.INTENT]: "task.dispatch",
|
|
205
213
|
[AAMP_HEADER.TASK_ID]: params.taskId
|
|
206
214
|
};
|
|
@@ -221,12 +229,14 @@ function buildDispatchHeaders(params) {
|
|
|
221
229
|
}
|
|
222
230
|
function buildAckHeaders(opts) {
|
|
223
231
|
return {
|
|
232
|
+
[AAMP_HEADER.VERSION]: AAMP_PROTOCOL_VERSION,
|
|
224
233
|
[AAMP_HEADER.INTENT]: "task.ack",
|
|
225
234
|
[AAMP_HEADER.TASK_ID]: opts.taskId
|
|
226
235
|
};
|
|
227
236
|
}
|
|
228
237
|
function buildResultHeaders(params) {
|
|
229
238
|
const headers = {
|
|
239
|
+
[AAMP_HEADER.VERSION]: AAMP_PROTOCOL_VERSION,
|
|
230
240
|
[AAMP_HEADER.INTENT]: "task.result",
|
|
231
241
|
[AAMP_HEADER.TASK_ID]: params.taskId,
|
|
232
242
|
[AAMP_HEADER.STATUS]: params.status,
|
|
@@ -243,7 +253,8 @@ function buildResultHeaders(params) {
|
|
|
243
253
|
}
|
|
244
254
|
function buildHelpHeaders(params) {
|
|
245
255
|
return {
|
|
246
|
-
[AAMP_HEADER.
|
|
256
|
+
[AAMP_HEADER.VERSION]: AAMP_PROTOCOL_VERSION,
|
|
257
|
+
[AAMP_HEADER.INTENT]: "task.help_needed",
|
|
247
258
|
[AAMP_HEADER.TASK_ID]: params.taskId,
|
|
248
259
|
[AAMP_HEADER.QUESTION]: params.question,
|
|
249
260
|
[AAMP_HEADER.BLOCKED_REASON]: params.blockedReason,
|
|
@@ -493,7 +504,7 @@ var JmapPushClient = class extends TinyEmitter {
|
|
|
493
504
|
* Process a received email.
|
|
494
505
|
*
|
|
495
506
|
* Priority:
|
|
496
|
-
* 1. If X-AAMP-Intent is present → emit typed AAMP event (task.dispatch / task.result / task.
|
|
507
|
+
* 1. If X-AAMP-Intent is present → emit typed AAMP event (task.dispatch / task.result / task.help_needed)
|
|
497
508
|
* 2. If In-Reply-To is present → emit 'reply' event so the application layer can
|
|
498
509
|
* resolve the thread (inReplyTo → taskId via Redis/DB) and handle human replies.
|
|
499
510
|
* 3. Otherwise → ignore (not an AAMP-related email)
|
|
@@ -541,7 +552,8 @@ var JmapPushClient = class extends TinyEmitter {
|
|
|
541
552
|
case "task.result":
|
|
542
553
|
this.emit("task.result", aampMsg);
|
|
543
554
|
break;
|
|
544
|
-
case "task.
|
|
555
|
+
case "task.help_needed":
|
|
556
|
+
this.emit("task.help_needed", aampMsg);
|
|
545
557
|
this.emit("task.help", aampMsg);
|
|
546
558
|
break;
|
|
547
559
|
case "task.ack":
|
|
@@ -1240,7 +1252,8 @@ var AampClient = class extends TinyEmitter {
|
|
|
1240
1252
|
this.jmapClient.on("task.result", (result) => {
|
|
1241
1253
|
this.emit("task.result", result);
|
|
1242
1254
|
});
|
|
1243
|
-
this.jmapClient.on("task.
|
|
1255
|
+
this.jmapClient.on("task.help_needed", (help) => {
|
|
1256
|
+
this.emit("task.help_needed", help);
|
|
1244
1257
|
this.emit("task.help", help);
|
|
1245
1258
|
});
|
|
1246
1259
|
this.jmapClient.on("task.ack", (ack) => {
|
|
@@ -1523,7 +1536,7 @@ var src_default = {
|
|
|
1523
1536
|
},
|
|
1524
1537
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1525
1538
|
register(api) {
|
|
1526
|
-
const cfg = api.pluginConfig ?? {};
|
|
1539
|
+
const cfg = api.config?.channels?.aamp ?? api.pluginConfig ?? {};
|
|
1527
1540
|
api.registerChannel({
|
|
1528
1541
|
id: "aamp",
|
|
1529
1542
|
meta: { label: "AAMP" },
|
|
@@ -1803,8 +1816,8 @@ ${notifyBody?.bodyText ?? "Sub-task completed."}${actionSection}`;
|
|
|
1803
1816
|
api.logger.error(`[AAMP] Sub-task result processing failed: ${err.message}`);
|
|
1804
1817
|
});
|
|
1805
1818
|
});
|
|
1806
|
-
aampClient.on("task.
|
|
1807
|
-
api.logger.info(`[AAMP] \u2190 task.
|
|
1819
|
+
aampClient.on("task.help_needed", (help) => {
|
|
1820
|
+
api.logger.info(`[AAMP] \u2190 task.help_needed ${help.taskId} question="${help.question}" from=${help.from}`);
|
|
1808
1821
|
const waiter = waitingDispatches.get(help.taskId);
|
|
1809
1822
|
if (waiter) {
|
|
1810
1823
|
waitingDispatches.delete(help.taskId);
|
|
@@ -2264,7 +2277,7 @@ ${task.contextLinks.map((l) => ` - ${l}`).join("\n")}` : "",
|
|
|
2264
2277
|
suggestedOptions: p.suggestedOptions ?? [],
|
|
2265
2278
|
inReplyTo: task.messageId || void 0
|
|
2266
2279
|
});
|
|
2267
|
-
api.logger.info(`[AAMP] \u2192 task.
|
|
2280
|
+
api.logger.info(`[AAMP] \u2192 task.help_needed ${task.taskId}`);
|
|
2268
2281
|
return {
|
|
2269
2282
|
content: [
|
|
2270
2283
|
{
|
|
@@ -2458,7 +2471,14 @@ Question: ${h.question}`,
|
|
|
2458
2471
|
return { content: [{ type: "text", text: "Error: email parameter is required" }] };
|
|
2459
2472
|
}
|
|
2460
2473
|
try {
|
|
2461
|
-
const
|
|
2474
|
+
const discoveryRes = await fetch(`${base}/.well-known/aamp`);
|
|
2475
|
+
if (!discoveryRes.ok)
|
|
2476
|
+
throw new Error(`HTTP ${discoveryRes.status}`);
|
|
2477
|
+
const discovery = await discoveryRes.json();
|
|
2478
|
+
const apiUrl = discovery.api?.url;
|
|
2479
|
+
if (!apiUrl)
|
|
2480
|
+
throw new Error("AAMP discovery did not return api.url");
|
|
2481
|
+
const res = await fetch(`${base}${apiUrl}?action=aamp.mailbox.check&email=${encodeURIComponent(email)}`);
|
|
2462
2482
|
if (!res.ok)
|
|
2463
2483
|
throw new Error(`HTTP ${res.status}`);
|
|
2464
2484
|
const data = await res.json();
|