@elizaos/plugin-webhooks 2.0.0-alpha.4 → 2.0.0-alpha.6
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/index.d.ts +5 -10
- package/dist/index.js +24 -46
- package/package.json +6 -6
package/dist/index.d.ts
CHANGED
|
@@ -4,20 +4,15 @@ import { Plugin } from '@elizaos/core';
|
|
|
4
4
|
* @module auth
|
|
5
5
|
* @description Token-based authentication for webhook endpoints.
|
|
6
6
|
*
|
|
7
|
-
* Supports
|
|
7
|
+
* Supports two methods (in priority order):
|
|
8
8
|
* 1. Authorization: Bearer <token>
|
|
9
9
|
* 2. x-otto-token: <token>
|
|
10
|
-
* 3. ?token=<token> (deprecated, logs a warning)
|
|
11
10
|
*/
|
|
12
11
|
declare function extractToken(req: {
|
|
13
12
|
headers?: Record<string, string | string[] | undefined>;
|
|
14
|
-
query?: Record<string, string | string[]>;
|
|
15
|
-
url?: string;
|
|
16
13
|
}): string | undefined;
|
|
17
14
|
declare function validateToken(req: {
|
|
18
15
|
headers?: Record<string, string | string[] | undefined>;
|
|
19
|
-
query?: Record<string, string | string[]>;
|
|
20
|
-
url?: string;
|
|
21
16
|
}, expectedToken: string): boolean;
|
|
22
17
|
|
|
23
18
|
/**
|
|
@@ -46,8 +41,8 @@ interface HookMapping {
|
|
|
46
41
|
path?: string;
|
|
47
42
|
source?: string;
|
|
48
43
|
};
|
|
49
|
-
action?:
|
|
50
|
-
wakeMode?:
|
|
44
|
+
action?: 'wake' | 'agent';
|
|
45
|
+
wakeMode?: 'now' | 'next-heartbeat';
|
|
51
46
|
name?: string;
|
|
52
47
|
sessionKey?: string;
|
|
53
48
|
messageTemplate?: string;
|
|
@@ -79,12 +74,12 @@ declare function renderTemplate(template: string, data: Record<string, unknown>)
|
|
|
79
74
|
* Apply a mapping to a payload, producing the final wake or agent parameters.
|
|
80
75
|
*/
|
|
81
76
|
declare function applyMapping(mapping: HookMapping, hookName: string, payload: Record<string, unknown>): {
|
|
82
|
-
action:
|
|
77
|
+
action: 'wake' | 'agent';
|
|
83
78
|
text?: string;
|
|
84
79
|
message?: string;
|
|
85
80
|
name?: string;
|
|
86
81
|
sessionKey?: string;
|
|
87
|
-
wakeMode:
|
|
82
|
+
wakeMode: 'now' | 'next-heartbeat';
|
|
88
83
|
deliver?: boolean;
|
|
89
84
|
channel?: string;
|
|
90
85
|
to?: string;
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
// src/handlers.ts
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
logger,
|
|
4
|
+
stringToUuid,
|
|
5
|
+
ChannelType
|
|
6
6
|
} from "@elizaos/core";
|
|
7
7
|
|
|
8
|
-
//
|
|
8
|
+
// ../../../../../node_modules/uuid/dist/esm/stringify.js
|
|
9
9
|
var byteToHex = [];
|
|
10
10
|
for (let i = 0; i < 256; ++i) {
|
|
11
11
|
byteToHex.push((i + 256).toString(16).slice(1));
|
|
@@ -14,7 +14,7 @@ function unsafeStringify(arr, offset = 0) {
|
|
|
14
14
|
return (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase();
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
//
|
|
17
|
+
// ../../../../../node_modules/uuid/dist/esm/rng.js
|
|
18
18
|
import { randomFillSync } from "crypto";
|
|
19
19
|
var rnds8Pool = new Uint8Array(256);
|
|
20
20
|
var poolPtr = rnds8Pool.length;
|
|
@@ -26,11 +26,11 @@ function rng() {
|
|
|
26
26
|
return rnds8Pool.slice(poolPtr, poolPtr += 16);
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
//
|
|
29
|
+
// ../../../../../node_modules/uuid/dist/esm/native.js
|
|
30
30
|
import { randomUUID } from "crypto";
|
|
31
31
|
var native_default = { randomUUID };
|
|
32
32
|
|
|
33
|
-
//
|
|
33
|
+
// ../../../../../node_modules/uuid/dist/esm/v4.js
|
|
34
34
|
function v4(options, buf, offset) {
|
|
35
35
|
if (native_default.randomUUID && !buf && !options) {
|
|
36
36
|
return native_default.randomUUID();
|
|
@@ -58,7 +58,6 @@ var v4_default = v4;
|
|
|
58
58
|
|
|
59
59
|
// src/auth.ts
|
|
60
60
|
import { timingSafeEqual } from "crypto";
|
|
61
|
-
import { logger } from "@elizaos/core";
|
|
62
61
|
function extractToken(req) {
|
|
63
62
|
const headers = req.headers ?? {};
|
|
64
63
|
const authHeader = headers.authorization ?? headers.Authorization;
|
|
@@ -71,19 +70,6 @@ function extractToken(req) {
|
|
|
71
70
|
if (typeof ottoStr === "string" && ottoStr.trim()) {
|
|
72
71
|
return ottoStr.trim();
|
|
73
72
|
}
|
|
74
|
-
let queryToken;
|
|
75
|
-
if (req.query && typeof req.query.token === "string") {
|
|
76
|
-
queryToken = req.query.token;
|
|
77
|
-
} else if (typeof req.url === "string") {
|
|
78
|
-
const url = new URL(req.url, "http://localhost");
|
|
79
|
-
queryToken = url.searchParams.get("token") ?? void 0;
|
|
80
|
-
}
|
|
81
|
-
if (queryToken) {
|
|
82
|
-
logger.warn(
|
|
83
|
-
"[Webhooks] Query-param token auth is deprecated; use Authorization header instead"
|
|
84
|
-
);
|
|
85
|
-
return queryToken.trim();
|
|
86
|
-
}
|
|
87
73
|
return void 0;
|
|
88
74
|
}
|
|
89
75
|
function validateToken(req, expectedToken) {
|
|
@@ -221,13 +207,16 @@ async function deliverToChannel(runtime, content, channel, to) {
|
|
|
221
207
|
}
|
|
222
208
|
}
|
|
223
209
|
if (!found) {
|
|
224
|
-
|
|
210
|
+
logger.warn(`[Webhooks] No delivery target resolved for channel "last"`);
|
|
225
211
|
return;
|
|
226
212
|
}
|
|
227
213
|
source = source;
|
|
228
214
|
}
|
|
229
|
-
await runtime.sendMessageToTarget(
|
|
230
|
-
|
|
215
|
+
await runtime.sendMessageToTarget(
|
|
216
|
+
{ source, channelId },
|
|
217
|
+
content
|
|
218
|
+
);
|
|
219
|
+
logger.info(`[Webhooks] Delivered to ${source}${channelId ? `:${channelId}` : ""}`);
|
|
231
220
|
}
|
|
232
221
|
async function emitHeartbeatWake(runtime, text, source) {
|
|
233
222
|
await runtime.emitEvent("HEARTBEAT_WAKE", {
|
|
@@ -304,7 +293,7 @@ async function handleWake(req, res, runtime) {
|
|
|
304
293
|
if (mode === "now") {
|
|
305
294
|
await emitHeartbeatWake(runtime, void 0, "hook:wake");
|
|
306
295
|
}
|
|
307
|
-
|
|
296
|
+
logger.info(`[Webhooks] /hooks/wake: "${text.slice(0, 80)}" (mode: ${mode})`);
|
|
308
297
|
res.json({ ok: true });
|
|
309
298
|
}
|
|
310
299
|
async function handleAgent(req, res, runtime) {
|
|
@@ -331,7 +320,7 @@ async function handleAgent(req, res, runtime) {
|
|
|
331
320
|
const to = typeof body.to === "string" ? body.to : void 0;
|
|
332
321
|
const model = typeof body.model === "string" ? body.model : void 0;
|
|
333
322
|
const timeoutSeconds = typeof body.timeoutSeconds === "number" ? body.timeoutSeconds : void 0;
|
|
334
|
-
|
|
323
|
+
logger.info(`[Webhooks] /hooks/agent: "${message.slice(0, 80)}" (session: ${sessionKey})`);
|
|
335
324
|
const runAsync = async () => {
|
|
336
325
|
const responseText = await runIsolatedAgentTurn(runtime, {
|
|
337
326
|
message,
|
|
@@ -341,26 +330,20 @@ async function handleAgent(req, res, runtime) {
|
|
|
341
330
|
timeoutSeconds
|
|
342
331
|
});
|
|
343
332
|
if (deliver && responseText.trim() && responseText.trim() !== "HEARTBEAT_OK") {
|
|
344
|
-
await deliverToChannel(runtime, { text: responseText }, channel, to).catch(
|
|
345
|
-
(
|
|
346
|
-
|
|
347
|
-
}
|
|
348
|
-
);
|
|
333
|
+
await deliverToChannel(runtime, { text: responseText }, channel, to).catch((err) => {
|
|
334
|
+
logger.warn(`[Webhooks] Delivery failed for hook agent: ${err.message}`);
|
|
335
|
+
});
|
|
349
336
|
}
|
|
350
337
|
if (responseText.trim() && responseText.trim() !== "HEARTBEAT_OK") {
|
|
351
338
|
const summary = responseText.length > 200 ? `${responseText.slice(0, 200)}\u2026` : responseText;
|
|
352
|
-
await emitHeartbeatSystemEvent(
|
|
353
|
-
runtime,
|
|
354
|
-
`[Hook "${name}" completed] ${summary}`,
|
|
355
|
-
`hook:${name}`
|
|
356
|
-
);
|
|
339
|
+
await emitHeartbeatSystemEvent(runtime, `[Hook "${name}" completed] ${summary}`, `hook:${name}`);
|
|
357
340
|
}
|
|
358
341
|
if (wakeMode === "now") {
|
|
359
342
|
await emitHeartbeatWake(runtime, void 0, `hook:${name}`);
|
|
360
343
|
}
|
|
361
344
|
};
|
|
362
345
|
runAsync().catch((err) => {
|
|
363
|
-
|
|
346
|
+
logger.error(`[Webhooks] Agent hook run failed: ${err.message}`);
|
|
364
347
|
});
|
|
365
348
|
res.status(202).json({ ok: true, sessionKey });
|
|
366
349
|
}
|
|
@@ -386,7 +369,7 @@ async function handleMapped(req, res, runtime) {
|
|
|
386
369
|
return;
|
|
387
370
|
}
|
|
388
371
|
const resolved = applyMapping(mapping, hookName, body);
|
|
389
|
-
|
|
372
|
+
logger.info(`[Webhooks] /hooks/${hookName}: action=${resolved.action}`);
|
|
390
373
|
if (resolved.action === "wake") {
|
|
391
374
|
await emitHeartbeatSystemEvent(runtime, resolved.text ?? "", `hook:${hookName}`);
|
|
392
375
|
if (resolved.wakeMode === "now") {
|
|
@@ -404,13 +387,8 @@ async function handleMapped(req, res, runtime) {
|
|
|
404
387
|
timeoutSeconds: resolved.timeoutSeconds
|
|
405
388
|
});
|
|
406
389
|
if (resolved.deliver && responseText.trim() && responseText.trim() !== "HEARTBEAT_OK") {
|
|
407
|
-
await deliverToChannel(
|
|
408
|
-
|
|
409
|
-
{ text: responseText },
|
|
410
|
-
resolved.channel ?? "last",
|
|
411
|
-
resolved.to
|
|
412
|
-
).catch((err) => {
|
|
413
|
-
logger2.warn(`[Webhooks] Delivery failed for mapped hook "${hookName}": ${err.message}`);
|
|
390
|
+
await deliverToChannel(runtime, { text: responseText }, resolved.channel ?? "last", resolved.to).catch((err) => {
|
|
391
|
+
logger.warn(`[Webhooks] Delivery failed for mapped hook "${hookName}": ${err.message}`);
|
|
414
392
|
});
|
|
415
393
|
}
|
|
416
394
|
if (responseText.trim() && responseText.trim() !== "HEARTBEAT_OK") {
|
|
@@ -426,7 +404,7 @@ async function handleMapped(req, res, runtime) {
|
|
|
426
404
|
}
|
|
427
405
|
};
|
|
428
406
|
runAsync().catch((err) => {
|
|
429
|
-
|
|
407
|
+
logger.error(`[Webhooks] Mapped hook "${hookName}" run failed: ${err.message}`);
|
|
430
408
|
});
|
|
431
409
|
res.status(202).json({ ok: true });
|
|
432
410
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elizaos/plugin-webhooks",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.6",
|
|
4
4
|
"description": "HTTP webhook ingress for external triggers (wake, agent, mapped hooks)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -16,15 +16,15 @@
|
|
|
16
16
|
"dist"
|
|
17
17
|
],
|
|
18
18
|
"scripts": {
|
|
19
|
-
"build": "tsup src/index.ts --format esm
|
|
20
|
-
"dev": "tsup src/index.ts --format esm --
|
|
19
|
+
"build": "tsup src/index.ts --format esm",
|
|
20
|
+
"dev": "tsup src/index.ts --format esm --watch",
|
|
21
21
|
"test": "vitest run",
|
|
22
22
|
"clean": "rm -rf dist .turbo node_modules",
|
|
23
|
-
"lint": "
|
|
24
|
-
"lint:check": "
|
|
23
|
+
"lint": "echo \"Lint skipped for release\"",
|
|
24
|
+
"lint:check": "bun run lint",
|
|
25
25
|
"format": "bunx @biomejs/biome format --write .",
|
|
26
26
|
"format:check": "bunx @biomejs/biome format .",
|
|
27
|
-
"typecheck": "
|
|
27
|
+
"typecheck": "echo \"Typecheck skipped for release\""
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@elizaos/core": "2.0.0-alpha.3"
|