feat(skill): add n8n action-bus helper
This commit is contained in:
@@ -9,4 +9,4 @@
|
|||||||
- `openclaw-ping` webhook path tested end-to-end
|
- `openclaw-ping` webhook path tested end-to-end
|
||||||
- Operating note: prefer narrow webhook-first integration rather than broad n8n admin/API access.
|
- 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`.
|
- 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`.
|
- Drafted local skill `skills/n8n-webhook` for authenticated webhook-first n8n integration, including `scripts/call-webhook.sh`, `scripts/call-action.sh`, payload notes, and a successful package/validation run to `/tmp/n8n-skill-dist/n8n-webhook.skill`.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: n8n-webhook
|
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.
|
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, an action-bus style router workflow, 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
|
# N8n Webhook
|
||||||
@@ -15,11 +15,12 @@ Keep the integration narrow: let OpenClaw decide what to do, and let n8n execute
|
|||||||
## Policy
|
## Policy
|
||||||
|
|
||||||
1. Prefer named webhook entrypoints over generic admin APIs.
|
1. Prefer named webhook entrypoints over generic admin APIs.
|
||||||
2. Send JSON and expect JSON back.
|
2. Prefer one small router webhook (`openclaw-action`) when several agent-safe actions are needed.
|
||||||
3. Use header auth by default (`x-openclaw-secret`).
|
3. Send JSON and expect JSON back.
|
||||||
4. Use `/webhook-test/` only while building/editing a workflow.
|
4. Use header auth by default (`x-openclaw-secret`).
|
||||||
5. Surface non-2xx responses clearly instead of pretending success.
|
5. Use `/webhook-test/` only while building/editing a workflow.
|
||||||
6. If a new workflow is needed, define its request/response contract before wiring callers.
|
6. Surface non-2xx responses clearly instead of pretending success.
|
||||||
|
7. If a new workflow is needed, define its request/response contract before wiring callers.
|
||||||
|
|
||||||
## Quick usage
|
## Quick usage
|
||||||
|
|
||||||
@@ -29,40 +30,56 @@ Set the shared secret once for the shell session:
|
|||||||
export N8N_WEBHOOK_SECRET='replace-me'
|
export N8N_WEBHOOK_SECRET='replace-me'
|
||||||
```
|
```
|
||||||
|
|
||||||
Call a production webhook:
|
Call a production webhook directly:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
scripts/call-webhook.sh openclaw-ping --data '{"message":"hello from OpenClaw"}'
|
scripts/call-webhook.sh openclaw-ping --data '{"message":"hello from OpenClaw"}'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Call the preferred action-bus route:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scripts/call-action.sh append_log --args '{"text":"backup complete"}'
|
||||||
|
```
|
||||||
|
|
||||||
Call a test webhook while editing a flow:
|
Call a test webhook while editing a flow:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
scripts/call-webhook.sh openclaw-ping --test --data '{"message":"hello from OpenClaw"}'
|
scripts/call-action.sh notify --args '{"message":"hello from OpenClaw"}' --test --pretty
|
||||||
```
|
|
||||||
|
|
||||||
Pretty-print JSON response:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
scripts/call-webhook.sh openclaw-ping --pretty --data '{"message":"hello"}'
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Workflow
|
## Workflow
|
||||||
|
|
||||||
### Call an existing safe webhook
|
### Call an existing safe webhook directly
|
||||||
|
|
||||||
1. Confirm the target webhook path is already intended for agent use.
|
Use `scripts/call-webhook.sh` when the path is already defined and there is no benefit to the action-bus wrapper.
|
||||||
2. Use `scripts/call-webhook.sh` with JSON input.
|
|
||||||
3. Treat any non-2xx response as a failure that needs investigation.
|
|
||||||
|
|
||||||
Current known endpoint:
|
Current known direct endpoint:
|
||||||
|
|
||||||
- `openclaw-ping` — basic end-to-end connectivity check
|
- `openclaw-ping` — basic end-to-end connectivity check
|
||||||
|
|
||||||
|
### Call the action bus
|
||||||
|
|
||||||
|
Use `scripts/call-action.sh` when the n8n side exposes a router webhook such as `openclaw-action`.
|
||||||
|
|
||||||
|
Payload shape:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"action": "append_log",
|
||||||
|
"args": {
|
||||||
|
"text": "backup complete"
|
||||||
|
},
|
||||||
|
"request_id": "optional-uuid"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This keeps the external surface small while letting n8n route internally.
|
||||||
|
|
||||||
### Add a new webhook-backed capability
|
### Add a new webhook-backed capability
|
||||||
|
|
||||||
1. Write down the webhook path, required auth, request JSON, and response JSON.
|
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.
|
2. If the path should become part of the shared action bus, document the `action` name and `args` shape in `references/payloads.md`.
|
||||||
3. Keep the first version small and explicit.
|
3. Keep the first version small and explicit.
|
||||||
4. Only add the new endpoint to regular use after a successful `/webhook-test/` run.
|
4. Only add the new endpoint to regular use after a successful `/webhook-test/` run.
|
||||||
|
|
||||||
@@ -71,8 +88,10 @@ Current known endpoint:
|
|||||||
- `N8N_BASE_URL` — override base URL (default `http://192.168.153.113:18808`)
|
- `N8N_BASE_URL` — override base URL (default `http://192.168.153.113:18808`)
|
||||||
- `N8N_WEBHOOK_SECRET` — required shared secret for authenticated calls
|
- `N8N_WEBHOOK_SECRET` — required shared secret for authenticated calls
|
||||||
- `N8N_SECRET_HEADER` — header name (default `x-openclaw-secret`)
|
- `N8N_SECRET_HEADER` — header name (default `x-openclaw-secret`)
|
||||||
|
- `N8N_ACTION_PATH` — router path for `call-action.sh` (default `openclaw-action`)
|
||||||
|
|
||||||
## Resources
|
## Resources
|
||||||
|
|
||||||
- `scripts/call-webhook.sh` — authenticated POST helper for local n8n webhooks
|
- `scripts/call-webhook.sh` — authenticated POST helper for direct local n8n webhooks
|
||||||
- `references/payloads.md` — suggested request/response contracts and naming conventions
|
- `scripts/call-action.sh` — wrapper for action-bus style calls against `openclaw-action`
|
||||||
|
- `references/payloads.md` — request/response contracts and naming conventions
|
||||||
|
|||||||
@@ -25,52 +25,88 @@ Recommended success response:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Recommended contract for new endpoints
|
## Preferred router endpoint
|
||||||
|
|
||||||
Prefer a stable JSON shape like:
|
### `openclaw-action`
|
||||||
|
|
||||||
|
Purpose:
|
||||||
|
- keep the external n8n surface small
|
||||||
|
- route several agent-safe operations behind one authenticated webhook
|
||||||
|
|
||||||
|
Recommended request shape:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"action": "append_log",
|
||||||
|
"args": {
|
||||||
|
"text": "backup complete"
|
||||||
|
},
|
||||||
|
"request_id": "optional-uuid"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Recommended success response:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"ok": true,
|
"ok": true,
|
||||||
"request_id": "optional-uuid",
|
"request_id": "optional-uuid",
|
||||||
"result": {
|
"result": {
|
||||||
"...": "workflow-specific payload"
|
"status": "accepted"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
On failure, return a non-2xx status plus:
|
Recommended failure response:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"ok": false,
|
"ok": false,
|
||||||
"error": {
|
"error": {
|
||||||
"code": "short-machine-code",
|
"code": "unknown_action",
|
||||||
"message": "human-readable summary"
|
"message": "action is not supported"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Naming guidance
|
## Suggested initial actions
|
||||||
|
|
||||||
- Use lowercase kebab-case webhook paths.
|
### `append_log`
|
||||||
- Keep names explicit: `openclaw-ping`, `openclaw-action`, `append-log`, `send-notify`.
|
|
||||||
- Avoid generic names like `run`, `task`, or `webhook1`.
|
|
||||||
|
|
||||||
## Suggested action-bus pattern
|
Request:
|
||||||
|
|
||||||
If multiple agent-safe actions are needed, prefer one router webhook such as `openclaw-action`.
|
|
||||||
|
|
||||||
Request shape:
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"action": "append_log",
|
"action": "append_log",
|
||||||
"args": {
|
"args": {
|
||||||
"text": "backup finished"
|
"text": "backup complete"
|
||||||
},
|
}
|
||||||
"request_id": "optional-uuid"
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
That keeps the external surface small while letting n8n route internally.
|
Purpose:
|
||||||
|
- append a short line to a known log or tracking sink
|
||||||
|
|
||||||
|
### `notify`
|
||||||
|
|
||||||
|
Request:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"action": "notify",
|
||||||
|
"args": {
|
||||||
|
"message": "workflow finished",
|
||||||
|
"title": "optional title"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Purpose:
|
||||||
|
- send a small notification through a known downstream channel
|
||||||
|
|
||||||
|
## Naming guidance
|
||||||
|
|
||||||
|
- Use lowercase kebab-case for webhook paths.
|
||||||
|
- Use lowercase snake_case or kebab-case consistently for action names; prefer snake_case for JSON actions if using switch/router logic.
|
||||||
|
- Keep names explicit: `openclaw-ping`, `openclaw-action`, `append_log`, `notify`.
|
||||||
|
- Avoid generic names like `run`, `task`, or `webhook1`.
|
||||||
|
|||||||
128
skills/n8n-webhook/scripts/call-action.sh
Executable file
128
skills/n8n-webhook/scripts/call-action.sh
Executable file
@@ -0,0 +1,128 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<'EOF'
|
||||||
|
Usage:
|
||||||
|
scripts/call-action.sh <action> [--args '{"k":"v"}'] [--args-file args.json] [--request-id <id>] [--path openclaw-action] [--test] [--pretty] [--dry-run]
|
||||||
|
|
||||||
|
Environment:
|
||||||
|
N8N_ACTION_PATH Default router webhook path (default: openclaw-action)
|
||||||
|
N8N_WEBHOOK_SECRET Shared secret for header auth
|
||||||
|
N8N_BASE_URL Base n8n URL (default: http://192.168.153.113:18808)
|
||||||
|
N8N_SECRET_HEADER Header name (default: x-openclaw-secret)
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
scripts/call-action.sh append_log --args '{"text":"backup complete"}'
|
||||||
|
scripts/call-action.sh notify --args-file notify.json --test --pretty
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
CALL_WEBHOOK="$SCRIPT_DIR/call-webhook.sh"
|
||||||
|
ACTION_PATH="${N8N_ACTION_PATH:-openclaw-action}"
|
||||||
|
ACTION=""
|
||||||
|
ARGS='{}'
|
||||||
|
ARGS_FILE=""
|
||||||
|
REQUEST_ID=""
|
||||||
|
MODE_FLAG=""
|
||||||
|
PRETTY=0
|
||||||
|
DRY_RUN=0
|
||||||
|
EXTRA_ARGS=()
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--help|-h)
|
||||||
|
usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
--args)
|
||||||
|
ARGS="${2:?missing value for --args}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--args-file)
|
||||||
|
ARGS_FILE="${2:?missing value for --args-file}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--request-id)
|
||||||
|
REQUEST_ID="${2:?missing value for --request-id}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--path)
|
||||||
|
ACTION_PATH="${2:?missing value for --path}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--test)
|
||||||
|
MODE_FLAG="--test"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--prod)
|
||||||
|
MODE_FLAG="--prod"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--pretty)
|
||||||
|
PRETTY=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--dry-run)
|
||||||
|
DRY_RUN=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--*)
|
||||||
|
echo "Unknown option: $1" >&2
|
||||||
|
usage >&2
|
||||||
|
exit 2
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
if [[ -z "$ACTION" ]]; then
|
||||||
|
ACTION="$1"
|
||||||
|
else
|
||||||
|
EXTRA_ARGS+=("$1")
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -z "$ACTION" ]]; then
|
||||||
|
usage >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ${#EXTRA_ARGS[@]} -gt 0 ]]; then
|
||||||
|
echo "Unexpected extra arguments: ${EXTRA_ARGS[*]}" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$ARGS_FILE" ]]; then
|
||||||
|
ARGS="$(cat "$ARGS_FILE")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
PAYLOAD="$({
|
||||||
|
python3 - <<'PY' "$ACTION" "$ARGS" "$REQUEST_ID"
|
||||||
|
import json, sys
|
||||||
|
|
||||||
|
action = sys.argv[1]
|
||||||
|
args = json.loads(sys.argv[2])
|
||||||
|
request_id = sys.argv[3]
|
||||||
|
|
||||||
|
if not isinstance(args, dict):
|
||||||
|
raise SystemExit('Action args must decode to a JSON object.')
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
'action': action,
|
||||||
|
'args': args,
|
||||||
|
}
|
||||||
|
if request_id:
|
||||||
|
payload['request_id'] = request_id
|
||||||
|
|
||||||
|
print(json.dumps(payload, separators=(',', ':')))
|
||||||
|
PY
|
||||||
|
} )"
|
||||||
|
|
||||||
|
CMD=("$CALL_WEBHOOK" "$ACTION_PATH" --data "$PAYLOAD")
|
||||||
|
[[ -n "$MODE_FLAG" ]] && CMD+=("$MODE_FLAG")
|
||||||
|
[[ "$PRETTY" -eq 1 ]] && CMD+=(--pretty)
|
||||||
|
[[ "$DRY_RUN" -eq 1 ]] && CMD+=(--dry-run)
|
||||||
|
|
||||||
|
exec "${CMD[@]}"
|
||||||
Reference in New Issue
Block a user