@eclaw/openclaw-channel 1.2.0 → 1.2.2

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.js CHANGED
@@ -21,9 +21,16 @@ import { dispatchWebhook } from './webhook-registry.js';
21
21
  * so no separate port is needed. Set webhookUrl to your OpenClaw public URL
22
22
  * (e.g. https://eclaw2.zeabur.app) so E-Claw knows where to push messages.
23
23
  */
24
- /** Parse JSON body from a raw incoming request */
24
+ /** Parse JSON body from a raw incoming request.
25
+ * If the gateway framework already parsed the body (e.g. Express json middleware),
26
+ * skip re-reading the consumed stream to avoid overwriting req.body with {}.
27
+ */
25
28
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
26
29
  function parseBody(req) {
30
+ // Gateway already parsed the body — don't overwrite it
31
+ if (req.body && typeof req.body === 'object' && Object.keys(req.body).length > 0) {
32
+ return Promise.resolve();
33
+ }
27
34
  return new Promise((resolve) => {
28
35
  let body = '';
29
36
  req.on('data', (chunk) => { body += chunk.toString(); });
@@ -32,7 +39,10 @@ function parseBody(req) {
32
39
  req.body = JSON.parse(body);
33
40
  }
34
41
  catch {
35
- req.body = {};
42
+ // Only set empty fallback if body wasn't pre-parsed
43
+ if (!req.body || typeof req.body !== 'object') {
44
+ req.body = {};
45
+ }
36
46
  }
37
47
  resolve();
38
48
  });
@@ -15,4 +15,5 @@
15
15
  * - Mission context via eclaw_context.missionHints
16
16
  * - Silent suppression via silentToken (default "[SILENT]")
17
17
  */
18
- export declare function createWebhookHandler(expectedToken: string, accountId: string, cfg: any): (req: any, res: any) => Promise<void>;
18
+ export declare function createWebhookHandler(_expectedToken: string, // kept for API compat; auth is handled by webhook-registry
19
+ accountId: string, cfg: any): (req: any, res: any) => Promise<void>;
@@ -17,19 +17,15 @@ import { getClient, setActiveEvent, clearActiveEvent } from './outbound.js';
17
17
  * - Mission context via eclaw_context.missionHints
18
18
  * - Silent suppression via silentToken (default "[SILENT]")
19
19
  */
20
- export function createWebhookHandler(expectedToken, accountId,
20
+ export function createWebhookHandler(_expectedToken, // kept for API compat; auth is handled by webhook-registry
21
+ accountId,
21
22
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
22
23
  cfg // full openclaw config (ctx.cfg from startAccount)
23
24
  ) {
24
25
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
25
26
  return async (req, res) => {
26
- // Verify callback token
27
- const authHeader = req.headers?.authorization;
28
- if (expectedToken && (!authHeader || authHeader !== `Bearer ${expectedToken}`)) {
29
- res.writeHead(401, { 'Content-Type': 'application/json' });
30
- res.end(JSON.stringify({ error: 'Unauthorized' }));
31
- return;
32
- }
27
+ // Token verification is handled by webhook-registry dispatch.
28
+ // No additional auth check needed here.
33
29
  const msg = req.body;
34
30
  // ACK immediately so E-Claw doesn't time out
35
31
  res.writeHead(200, { 'Content-Type': 'application/json' });
@@ -22,18 +22,30 @@ export function unregisterWebhookToken(callbackToken) {
22
22
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
23
23
  export async function dispatchWebhook(req, res) {
24
24
  const authHeader = req.headers?.authorization;
25
- if (!authHeader?.startsWith('Bearer ')) {
26
- res.writeHead(401, { 'Content-Type': 'application/json' });
27
- res.end(JSON.stringify({ error: 'Unauthorized' }));
28
- return;
25
+ // Try Bearer-token routing first (preferred)
26
+ if (authHeader?.startsWith('Bearer ')) {
27
+ const token = authHeader.slice(7);
28
+ const entry = registry.get(token);
29
+ if (entry) {
30
+ await entry.handler(req, res);
31
+ return;
32
+ }
33
+ // Token present but unknown — fall through to single-handler fallback
29
34
  }
30
- const token = authHeader.slice(7);
31
- const entry = registry.get(token);
32
- if (!entry) {
33
- // Unknown token likely a stale push after a server restart
34
- res.writeHead(404, { 'Content-Type': 'application/json' });
35
- res.end(JSON.stringify({ error: 'Unknown token' }));
35
+ // Fallback: if exactly one handler is registered, route to it.
36
+ // This handles E-Claw backends that don't echo callback_token.
37
+ if (registry.size === 1) {
38
+ const [, entry] = [...registry.entries()][0];
39
+ await entry.handler(req, res);
36
40
  return;
37
41
  }
38
- await entry.handler(req, res);
42
+ // No valid routing possible
43
+ if (registry.size === 0) {
44
+ res.writeHead(503, { 'Content-Type': 'application/json' });
45
+ res.end(JSON.stringify({ error: 'No handlers registered' }));
46
+ }
47
+ else {
48
+ res.writeHead(401, { 'Content-Type': 'application/json' });
49
+ res.end(JSON.stringify({ error: 'Unauthorized' }));
50
+ }
39
51
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eclaw/openclaw-channel",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "description": "E-Claw channel plugin for OpenClaw — AI chat platform for live wallpaper entities",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",