feat(skill): add local n8n webhook skill draft

This commit is contained in:
zap
2026-03-12 06:47:00 +00:00
parent ce068eb5b4
commit 7a44225481
4 changed files with 314 additions and 0 deletions

View File

@@ -9,3 +9,4 @@
- `openclaw-ping` webhook path tested end-to-end
- Operating note: prefer narrow webhook-first integration rather than broad n8n admin/API access.
- Will clarified the primary host LAN IP to use/document is `192.168.153.113`.
- Drafted local skill `skills/n8n-webhook` for authenticated webhook-first n8n integration, including `scripts/call-webhook.sh`, payload notes, and a successful package/validation run to `/tmp/n8n-skill-dist/n8n-webhook.skill`.

View File

@@ -0,0 +1,78 @@
---
name: n8n-webhook
description: Trigger authenticated local n8n webhooks on the LAN for OpenClaw-to-n8n integration. Use when calling safe, narrow workflows on the dedicated local n8n-agent instance, such as ping/test endpoints, action-bus style workflows, notifications, logging, or other preapproved webhook entrypoints. Do not use for broad n8n admin/API management, workflow mutation, credential management, or unrestricted orchestration.
---
# N8n Webhook
Use this skill to call the local `n8n-agent` webhook surface on:
- `http://192.168.153.113:18808` (primary LAN)
- `http://100.123.88.127:18808` (Tailscale)
Keep the integration narrow: let OpenClaw decide what to do, and let n8n execute a small set of explicit workflows.
## Policy
1. Prefer named webhook entrypoints over generic admin APIs.
2. Send JSON and expect JSON back.
3. Use header auth by default (`x-openclaw-secret`).
4. Use `/webhook-test/` only while building/editing a workflow.
5. Surface non-2xx responses clearly instead of pretending success.
6. If a new workflow is needed, define its request/response contract before wiring callers.
## Quick usage
Set the shared secret once for the shell session:
```bash
export N8N_WEBHOOK_SECRET='replace-me'
```
Call a production webhook:
```bash
scripts/call-webhook.sh openclaw-ping --data '{"message":"hello from OpenClaw"}'
```
Call a test webhook while editing a flow:
```bash
scripts/call-webhook.sh openclaw-ping --test --data '{"message":"hello from OpenClaw"}'
```
Pretty-print JSON response:
```bash
scripts/call-webhook.sh openclaw-ping --pretty --data '{"message":"hello"}'
```
## Workflow
### Call an existing safe webhook
1. Confirm the target webhook path is already intended for agent use.
2. Use `scripts/call-webhook.sh` with JSON input.
3. Treat any non-2xx response as a failure that needs investigation.
Current known endpoint:
- `openclaw-ping` — basic end-to-end connectivity check
### Add a new webhook-backed capability
1. Write down the webhook path, required auth, request JSON, and response JSON.
2. If the shape is more than trivial, read `references/payloads.md` first.
3. Keep the first version small and explicit.
4. Only add the new endpoint to regular use after a successful `/webhook-test/` run.
## Environment variables
- `N8N_BASE_URL` — override base URL (default `http://192.168.153.113:18808`)
- `N8N_WEBHOOK_SECRET` — required shared secret for authenticated calls
- `N8N_SECRET_HEADER` — header name (default `x-openclaw-secret`)
## Resources
- `scripts/call-webhook.sh` — authenticated POST helper for local n8n webhooks
- `references/payloads.md` — suggested request/response contracts and naming conventions

View File

@@ -0,0 +1,76 @@
# n8n-webhook payload notes
## Current live endpoint
### `openclaw-ping`
Purpose:
- confirm OpenClaw can reach the local n8n webhook surface end-to-end
Typical request body:
```json
{
"message": "hello from OpenClaw"
}
```
Recommended success response:
```json
{
"ok": true,
"service": "n8n-agent",
"message": "openclaw webhook reached"
}
```
## Recommended contract for new endpoints
Prefer a stable JSON shape like:
```json
{
"ok": true,
"request_id": "optional-uuid",
"result": {
"...": "workflow-specific payload"
}
}
```
On failure, return a non-2xx status plus:
```json
{
"ok": false,
"error": {
"code": "short-machine-code",
"message": "human-readable summary"
}
}
```
## Naming guidance
- Use lowercase kebab-case webhook paths.
- Keep names explicit: `openclaw-ping`, `openclaw-action`, `append-log`, `send-notify`.
- Avoid generic names like `run`, `task`, or `webhook1`.
## Suggested action-bus pattern
If multiple agent-safe actions are needed, prefer one router webhook such as `openclaw-action`.
Request shape:
```json
{
"action": "append_log",
"args": {
"text": "backup finished"
},
"request_id": "optional-uuid"
}
```
That keeps the external surface small while letting n8n route internally.

View File

@@ -0,0 +1,159 @@
#!/usr/bin/env bash
set -euo pipefail
usage() {
cat <<'EOF'
Usage:
scripts/call-webhook.sh <path-or-url> [--data '{"k":"v"}'] [--data-file payload.json] [--test] [--pretty] [--dry-run]
Environment:
N8N_BASE_URL Base n8n URL (default: http://192.168.153.113:18808)
N8N_WEBHOOK_SECRET Shared secret value for header auth (required unless --dry-run)
N8N_SECRET_HEADER Header name for shared secret (default: x-openclaw-secret)
Examples:
scripts/call-webhook.sh openclaw-ping --data '{"message":"hello"}'
scripts/call-webhook.sh openclaw-ping --test --data-file payload.json
echo '{"message":"hello"}' | scripts/call-webhook.sh openclaw-ping --pretty
EOF
}
BASE_URL="${N8N_BASE_URL:-http://192.168.153.113:18808}"
SECRET_HEADER="${N8N_SECRET_HEADER:-x-openclaw-secret}"
SECRET_VALUE="${N8N_WEBHOOK_SECRET:-}"
MODE="prod"
PRETTY=0
DRY_RUN=0
PATH_OR_URL=""
DATA=""
DATA_FILE=""
while [[ $# -gt 0 ]]; do
case "$1" in
--help|-h)
usage
exit 0
;;
--test)
MODE="test"
shift
;;
--prod)
MODE="prod"
shift
;;
--pretty)
PRETTY=1
shift
;;
--dry-run)
DRY_RUN=1
shift
;;
--data)
DATA="${2:?missing value for --data}"
shift 2
;;
--data-file)
DATA_FILE="${2:?missing value for --data-file}"
shift 2
;;
--*)
echo "Unknown option: $1" >&2
usage >&2
exit 2
;;
*)
if [[ -z "$PATH_OR_URL" ]]; then
PATH_OR_URL="$1"
else
echo "Unexpected extra argument: $1" >&2
usage >&2
exit 2
fi
shift
;;
esac
done
if [[ -z "$PATH_OR_URL" ]]; then
usage >&2
exit 2
fi
if [[ -n "$DATA" && -n "$DATA_FILE" ]]; then
echo "Use either --data or --data-file, not both." >&2
exit 2
fi
if [[ -n "$DATA_FILE" ]]; then
DATA="$(cat "$DATA_FILE")"
elif [[ -n "$DATA" ]]; then
:
elif ! [[ -t 0 ]]; then
DATA="$(cat)"
else
DATA='{}'
fi
python3 - <<'PY' "$DATA"
import json, sys
json.loads(sys.argv[1])
PY
if [[ "$PATH_OR_URL" =~ ^https?:// ]]; then
URL="$PATH_OR_URL"
else
PREFIX="webhook"
[[ "$MODE" == "test" ]] && PREFIX="webhook-test"
URL="${BASE_URL%/}/${PREFIX}/${PATH_OR_URL#/}"
fi
if [[ "$DRY_RUN" -eq 1 ]]; then
printf 'POST %s\n' "$URL"
printf 'Header: Content-Type: application/json\n'
if [[ -n "$SECRET_VALUE" ]]; then
printf 'Header: %s: [set]\n' "$SECRET_HEADER"
else
printf 'Header: %s: [missing]\n' "$SECRET_HEADER"
fi
printf 'Body: %s\n' "$DATA"
exit 0
fi
if [[ -z "$SECRET_VALUE" ]]; then
echo "N8N_WEBHOOK_SECRET is required for non-dry-run calls." >&2
exit 2
fi
BODY_FILE="$(mktemp)"
trap 'rm -f "$BODY_FILE"' EXIT
HTTP_CODE="$({
curl -sS \
-o "$BODY_FILE" \
-w '%{http_code}' \
-X POST "$URL" \
-H 'Content-Type: application/json' \
-H "$SECRET_HEADER: $SECRET_VALUE" \
--data "$DATA"
} || true)"
if [[ -z "$HTTP_CODE" ]]; then
echo "Request failed before receiving an HTTP status." >&2
cat "$BODY_FILE" >&2 || true
exit 1
fi
if [[ "$HTTP_CODE" =~ ^2[0-9][0-9]$ ]]; then
if [[ "$PRETTY" -eq 1 ]]; then
python3 -m json.tool "$BODY_FILE" 2>/dev/null || cat "$BODY_FILE"
else
cat "$BODY_FILE"
fi
exit 0
fi
echo "n8n webhook call failed with HTTP $HTTP_CODE" >&2
cat "$BODY_FILE" >&2 || true
exit 1