@steadwing/openalerts 0.2.4 → 0.2.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.
Files changed (124) hide show
  1. package/README.md +198 -137
  2. package/dist/channels/console.d.ts +6 -0
  3. package/dist/channels/console.d.ts.map +1 -0
  4. package/dist/channels/console.js +10 -0
  5. package/dist/channels/console.js.map +1 -0
  6. package/dist/channels/telegram.d.ts +12 -0
  7. package/dist/channels/telegram.d.ts.map +1 -0
  8. package/dist/channels/telegram.js +28 -0
  9. package/dist/channels/telegram.js.map +1 -0
  10. package/dist/channels/webhook.d.ts +8 -0
  11. package/dist/channels/webhook.d.ts.map +1 -0
  12. package/dist/channels/webhook.js +15 -0
  13. package/dist/channels/webhook.js.map +1 -0
  14. package/dist/cli.d.ts +3 -0
  15. package/dist/cli.d.ts.map +1 -0
  16. package/dist/cli.js +234 -0
  17. package/dist/cli.js.map +1 -0
  18. package/dist/config.d.ts +51 -0
  19. package/dist/config.d.ts.map +1 -0
  20. package/dist/config.js +86 -0
  21. package/dist/config.js.map +1 -0
  22. package/dist/core/alert-channel.d.ts +3 -10
  23. package/dist/core/alert-channel.d.ts.map +1 -0
  24. package/dist/core/alert-channel.js +9 -30
  25. package/dist/core/alert-channel.js.map +1 -0
  26. package/dist/core/bounded-map.d.ts +1 -0
  27. package/dist/core/bounded-map.d.ts.map +1 -0
  28. package/dist/core/bounded-map.js +1 -0
  29. package/dist/core/bounded-map.js.map +1 -0
  30. package/dist/core/engine.d.ts +6 -18
  31. package/dist/core/engine.d.ts.map +1 -0
  32. package/dist/core/engine.js +49 -98
  33. package/dist/core/engine.js.map +1 -0
  34. package/dist/core/evaluator.d.ts +1 -0
  35. package/dist/core/evaluator.d.ts.map +1 -0
  36. package/dist/core/evaluator.js +1 -0
  37. package/dist/core/evaluator.js.map +1 -0
  38. package/dist/core/event-bus.d.ts +1 -0
  39. package/dist/core/event-bus.d.ts.map +1 -0
  40. package/dist/core/event-bus.js +1 -0
  41. package/dist/core/event-bus.js.map +1 -0
  42. package/dist/core/formatter.d.ts +1 -0
  43. package/dist/core/formatter.d.ts.map +1 -0
  44. package/dist/core/formatter.js +1 -0
  45. package/dist/core/formatter.js.map +1 -0
  46. package/dist/core/rules.d.ts +1 -0
  47. package/dist/core/rules.d.ts.map +1 -0
  48. package/dist/core/rules.js +98 -0
  49. package/dist/core/rules.js.map +1 -0
  50. package/dist/core/store.d.ts +6 -9
  51. package/dist/core/store.d.ts.map +1 -0
  52. package/dist/core/store.js +43 -96
  53. package/dist/core/store.js.map +1 -0
  54. package/dist/core/types.d.ts +2 -1
  55. package/dist/core/types.d.ts.map +1 -0
  56. package/dist/core/types.js +1 -0
  57. package/dist/core/types.js.map +1 -0
  58. package/dist/db/index.d.ts +6 -0
  59. package/dist/db/index.d.ts.map +1 -0
  60. package/dist/db/index.js +31 -0
  61. package/dist/db/index.js.map +1 -0
  62. package/dist/db/queries.d.ts +157 -0
  63. package/dist/db/queries.d.ts.map +1 -0
  64. package/dist/db/queries.js +221 -0
  65. package/dist/db/queries.js.map +1 -0
  66. package/dist/db/schema.d.ts +5 -0
  67. package/dist/db/schema.d.ts.map +1 -0
  68. package/dist/db/schema.js +177 -0
  69. package/dist/db/schema.js.map +1 -0
  70. package/dist/readers/openclaw.d.ts +11 -0
  71. package/dist/readers/openclaw.d.ts.map +1 -0
  72. package/dist/readers/openclaw.js +267 -0
  73. package/dist/readers/openclaw.js.map +1 -0
  74. package/dist/server/dashboard.d.ts +2 -0
  75. package/dist/server/dashboard.d.ts.map +1 -0
  76. package/dist/server/dashboard.js +765 -0
  77. package/dist/server/dashboard.js.map +1 -0
  78. package/dist/server/index.d.ts +10 -0
  79. package/dist/server/index.d.ts.map +1 -0
  80. package/dist/server/index.js +28 -0
  81. package/dist/server/index.js.map +1 -0
  82. package/dist/server/routes.d.ts +6 -0
  83. package/dist/server/routes.d.ts.map +1 -0
  84. package/dist/server/routes.js +146 -0
  85. package/dist/server/routes.js.map +1 -0
  86. package/dist/server/sse.d.ts +21 -0
  87. package/dist/server/sse.d.ts.map +1 -0
  88. package/dist/server/sse.js +53 -0
  89. package/dist/server/sse.js.map +1 -0
  90. package/dist/watchers/files.d.ts +19 -0
  91. package/dist/watchers/files.d.ts.map +1 -0
  92. package/dist/watchers/files.js +105 -0
  93. package/dist/watchers/files.js.map +1 -0
  94. package/dist/watchers/gateway-adapter.d.ts +18 -0
  95. package/dist/watchers/gateway-adapter.d.ts.map +1 -0
  96. package/dist/watchers/gateway-adapter.js +273 -0
  97. package/dist/watchers/gateway-adapter.js.map +1 -0
  98. package/dist/watchers/gateway.d.ts +27 -0
  99. package/dist/watchers/gateway.d.ts.map +1 -0
  100. package/dist/watchers/gateway.js +131 -0
  101. package/dist/watchers/gateway.js.map +1 -0
  102. package/package.json +29 -43
  103. package/LICENSE +0 -201
  104. package/dist/core/index.d.ts +0 -12
  105. package/dist/core/index.js +0 -23
  106. package/dist/core/llm-enrichment.d.ts +0 -21
  107. package/dist/core/llm-enrichment.js +0 -180
  108. package/dist/core/platform.d.ts +0 -17
  109. package/dist/core/platform.js +0 -93
  110. package/dist/index.d.ts +0 -8
  111. package/dist/index.js +0 -213
  112. package/dist/plugin/adapter.d.ts +0 -150
  113. package/dist/plugin/adapter.js +0 -530
  114. package/dist/plugin/commands.d.ts +0 -18
  115. package/dist/plugin/commands.js +0 -103
  116. package/dist/plugin/dashboard-html.d.ts +0 -7
  117. package/dist/plugin/dashboard-html.js +0 -938
  118. package/dist/plugin/dashboard-routes.d.ts +0 -7
  119. package/dist/plugin/dashboard-routes.js +0 -336
  120. package/dist/plugin/gateway-client.d.ts +0 -39
  121. package/dist/plugin/gateway-client.js +0 -193
  122. package/dist/plugin/log-bridge.d.ts +0 -22
  123. package/dist/plugin/log-bridge.js +0 -363
  124. package/openclaw.plugin.json +0 -57
package/README.md CHANGED
@@ -1,191 +1,252 @@
1
- <p align="center">
2
- <h1 align="center">OpenAlerts</h1>
3
- <p align="center">
4
- An alerting layer for agentic frameworks.
5
- </p>
6
- </p>
7
-
8
- <p align="center">
9
- <a href="https://www.npmjs.com/package/@steadwing/openalerts"><img src="https://img.shields.io/npm/v/@steadwing/openalerts?style=flat&color=blue" alt="npm"></a>
10
- <a href="https://github.com/steadwing/openalerts/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-Apache--2.0-green" alt="License"></a>
11
- <a href="https://github.com/steadwing/openalerts/stargazers"><img src="https://img.shields.io/github/stars/steadwing/openalerts?style=flat" alt="GitHub stars"></a>
12
- <a href="https://discord.gg/4rUP86tSXn"><img src="https://img.shields.io/badge/discord-join-5865F2?style=flat" alt="Discord"></a>
13
- </p>
14
-
15
- <p align="center">
16
- <a href="#quickstart">Quickstart</a> &middot;
17
- <a href="#alert-rules">Alert Rules</a> &middot;
18
- <a href="#llm-enriched-alerts">LLM Enrichment</a> &middot;
19
- <a href="#dashboard">Dashboard</a> &middot;
20
- <a href="#commands">Commands</a>
21
- </p>
1
+ # openalerts
2
+
3
+ Standalone monitoring daemon for [OpenClaw](https://openclaw.dev). Connects to your local OpenClaw gateway in real time, fires alerts via Telegram or webhook when something goes wrong, and serves a live dashboard at `http://localhost:4242`.
4
+
5
+ No code changes to OpenClaw needed — runs as a separate process alongside it.
22
6
 
23
7
  ---
24
8
 
25
- AI agents fail silently. LLM errors, stuck sessions, gateway outages — nobody knows until a user complains.
9
+ ## Install
26
10
 
27
- OpenAlerts watches your agent in real-time and alerts you the moment something goes wrong. A framework-agnostic core with adapter plugins — starting with [OpenClaw](https://github.com/openclaw/openclaw).
11
+ ```bash
12
+ npm install -g @steadwing/openalerts
13
+ ```
28
14
 
29
- ## Quickstart
15
+ > Requires **Node.js ≥ 22.5.0** (uses the built-in `node:sqlite` module — no native builds).
30
16
 
31
- > Currently supports OpenClaw. More framework adapters coming soon.
32
- > This project is under revamp for the next few hours
17
+ ---
33
18
 
34
- ### 1. Install
19
+ ## Quick start
35
20
 
36
21
  ```bash
37
- openclaw plugins install @steadwing/openalerts
22
+ # 1. Create default config (auto-detects your OpenClaw gateway token)
23
+ openalerts init
24
+
25
+ # 2. Edit config to add your alert channel
26
+ # ~/.openalerts/config.json
27
+
28
+ # 3. Start monitoring
29
+ openalerts start
38
30
  ```
39
31
 
40
- ### 2. Configure
32
+ Dashboard at **http://127.0.0.1:4242** — the gateway overlay dismisses automatically once connected.
41
33
 
42
- If you already have a channel paired with OpenClaw (e.g. Telegram via `openclaw pair`), **no config is needed** — OpenAlerts auto-detects where to send alerts.
34
+ ---
43
35
 
44
- Otherwise, set it explicitly in `openclaw.json`:
36
+ ## CLI
45
37
 
46
- ```jsonc
47
- {
48
- "plugins": {
49
- "entries": {
50
- "openalerts": {
51
- "enabled": true,
52
- "config": {
53
- "alertChannel": "telegram", // telegram | discord | slack | whatsapp | signal
54
- "alertTo": "YOUR_CHAT_ID",
55
- },
56
- },
57
- },
58
- },
59
- }
60
- ```
38
+ | Command | Description |
39
+ | -------- | --------------------------------------------------------- |
40
+ | `openalerts init` | Create default config at `~/.openalerts/config.json` |
41
+ | `openalerts start` | Start the monitoring daemon |
42
+ | `openalerts status` | Print live engine state (daemon must be running) |
43
+ | `openalerts test` | Fire a test alert through all configured channels |
61
44
 
62
- **Auto-detection priority:** explicit config > static `allowFrom` in channel config > pairing store.
45
+ **Options for `start`:**
63
46
 
64
- ### 3. Restart & verify
47
+ | Flag | Default | Description |
48
+ | ---- | ------- | ----------- |
49
+ | `--port N` | `4242` | HTTP server port |
50
+ | `--config PATH` | `~/.openalerts/config.json` | Config file path |
65
51
 
66
- ```bash
67
- openclaw gateway stop && openclaw gateway run
68
- ```
52
+ ---
69
53
 
70
- Send `/health` to your bot. You should get a live status report back — zero LLM tokens consumed.
54
+ ## Configuration
71
55
 
72
- That's it. OpenAlerts is now watching your agent.
56
+ `~/.openalerts/config.json` (created by `openalerts init`):
73
57
 
74
- ## Demo
58
+ ```json
59
+ {
60
+ "gatewayUrl": "ws://127.0.0.1:18789",
61
+ "gatewayToken": "<auto-detected from ~/.openclaw/openclaw.json>",
62
+ "stateDir": "~/.openalerts",
63
+ "watch": {
64
+ "openclawDir": "~/.openclaw",
65
+ "workspaces": ["workspace", "workspace-study"]
66
+ },
67
+ "server": {
68
+ "port": 4242,
69
+ "host": "127.0.0.1"
70
+ },
71
+ "channels": [
72
+ { "type": "telegram", "token": "BOT_TOKEN", "chatId": "CHAT_ID" },
73
+ { "type": "webhook", "webhookUrl": "https://your-endpoint" },
74
+ { "type": "console" }
75
+ ],
76
+ "quiet": false
77
+ }
78
+ ```
75
79
 
76
- https://github.com/user-attachments/assets/0b6ed26e-1eb0-47b2-ae4f-947516f024b4
80
+ **Gateway token** is auto-detected from `gateway.auth.token` in `~/.openclaw/openclaw.json` — no manual copy needed.
77
81
 
78
- ## Dashboard
82
+ **Channels**: configure at least one. Falls back to `console` if none are set.
79
83
 
80
- A real-time web dashboard is embedded in the gateway at:
84
+ ---
81
85
 
82
- ```
83
- http://127.0.0.1:18789/openalerts
84
- ```
86
+ ## What it monitors
85
87
 
86
- - **Activity** Live event timeline with session flows, tool calls, LLM usage
87
- - **System Logs** — Filtered, structured logs with search
88
- - **Health** — Rule status, alert history, system stats
88
+ Captures everything from the OpenClaw gateway WebSocket in real time:
89
89
 
90
- ## Alert Rules
90
+ | Event | What's captured |
91
+ | ----- | --------------- |
92
+ | Agent runs | Start, streaming, complete, error, aborted |
93
+ | Tool calls | Tool name, duration, result |
94
+ | LLM usage | Token counts, cost per call |
95
+ | Shell exec | Command, pid, output, exit code |
96
+ | Heartbeats | Gateway health, active sessions, queue depth |
97
+ | Cron jobs | Schedule, last run, next run, errors |
98
+ | Sessions | Active sessions, costs, message counts |
99
+ | Workspace docs | SOUL.md, HEARTBEAT.md, MEMORY.md per agent |
91
100
 
92
- Eight rules run against every event in real-time. All thresholds and cooldowns are configurable.
101
+ ---
93
102
 
94
- | Rule | Watches for | Severity | Threshold (default) |
95
- | ----------------- | ------------------------------------- | -------- | ------------------- |
96
- | `llm-errors` | LLM/agent failures in 1 min window | ERROR | `1` error |
97
- | `infra-errors` | Infrastructure errors in 1 min window | ERROR | `1` error |
98
- | `gateway-down` | No heartbeat received | CRITICAL | `30000` ms (30s) |
99
- | `session-stuck` | Session idle too long | WARN | `120000` ms (2 min) |
100
- | `high-error-rate` | Message failure rate over last 20 | ERROR | `50`% |
101
- | `queue-depth` | Queued items piling up | WARN | `10` items |
102
- | `tool-errors` | Tool failures in 1 min window | WARN | `1` error |
103
- | `heartbeat-fail` | Consecutive heartbeat failures | ERROR | `3` failures |
103
+ ## Alert rules
104
104
 
105
- Every rule also accepts:
105
+ Ten rules run against every event in real time. All thresholds and cooldowns are configurable via the `rules` key in config.
106
106
 
107
- - **`enabled`** `false` to disable the rule (default: `true`)
108
- - **`cooldownMinutes`** minutes before the same rule can fire again (default: `15`)
107
+ | Rule | Triggers when | Threshold | Cooldown |
108
+ | ---- | ------------- | --------- | -------- |
109
+ | `infra-errors` | `infra.error` events in 1 min | 1 | 15 min |
110
+ | `llm-errors` | `llm.error` or `agent.error` in 1 min | 1 | 15 min |
111
+ | `tool-errors` | `tool.error` in 1 min | 1 | 15 min |
112
+ | `heartbeat-fail` | Consecutive heartbeat failures | 3 | 30 min |
113
+ | `session-stuck` | Session idle too long | 120 s | 30 min |
114
+ | `high-error-rate` | Error % of last 20 calls | 50% | 30 min |
115
+ | `queue-depth` | Items in delivery queue | 10 | 15 min |
116
+ | `gateway-down` | No heartbeat from watchdog | 30 s | 60 min |
117
+ | `cost-hourly-spike` | LLM cost per hour | $5 | 30 min |
118
+ | `cost-daily-budget` | LLM cost per day | $20 | 6 h |
109
119
 
110
- To tune rules, add a `rules` object in your plugin config:
120
+ Override a rule threshold in config:
111
121
 
112
- ```jsonc
122
+ ```json
113
123
  {
114
- "plugins": {
115
- "entries": {
116
- "openalerts": {
117
- "config": {
118
- "cooldownMinutes": 10,
119
- "rules": {
120
- "llm-errors": { "threshold": 5 },
121
- "infra-errors": { "cooldownMinutes": 30 },
122
- "high-error-rate": { "enabled": false },
123
- "gateway-down": { "threshold": 60000 },
124
- },
125
- },
126
- },
127
- },
128
- },
124
+ "rules": {
125
+ "llm-errors": { "threshold": 5 },
126
+ "gateway-down": { "enabled": false },
127
+ "heartbeat-fail": { "cooldownMinutes": 60 }
128
+ }
129
129
  }
130
130
  ```
131
131
 
132
- Set `"quiet": true` at the config level for log-only mode (no messages sent).
132
+ ---
133
133
 
134
- ## LLM-Enriched Alerts
134
+ ## Dashboard
135
135
 
136
- OpenAlerts can optionally use your configured LLM to enrich alerts with a human-friendly summary and an actionable suggestion. **This feature is disabled by default** — opt in by setting `"llmEnriched": true` in your plugin config:
136
+ Open **http://127.0.0.1:4242** after starting.
137
137
 
138
- ```jsonc
139
- {
140
- "plugins": {
141
- "entries": {
142
- "openalerts": {
143
- "config": {
144
- "llmEnriched": true,
145
- },
146
- },
147
- },
148
- },
149
- }
150
- ```
138
+ | Tab | What's shown |
139
+ | --- | ------------ |
140
+ | **Overview** | Gateway health log, live activity feed, 24h stats, recent alerts |
141
+ | **Workspaces** | Per-agent SOUL.md, HEARTBEAT.md, MEMORY.md, USER.md previews |
142
+ | **Alerts** | Full alert history with severity, rule ID, fingerprint |
143
+ | **Sessions** | Active sessions with status, token counts, cost |
144
+ | **Live Monitor** | Real-time per-run timeline — steps, tool calls, LLM responses, exec |
145
+ | **Cron Jobs** | Scheduled job status, last/next run, consecutive errors |
146
+ | **Diagnostics** | Raw engine event log |
147
+ | **Delivery Queue** | Pending/failed alert delivery items |
151
148
 
152
- When enabled, alerts include an LLM-generated summary and action:
149
+ Live Monitor sidebar shows sessions colour-coded by status:
150
+ - Green pulse = active (idle, waiting for input)
151
+ - Yellow pulse = thinking (LLM streaming in progress)
152
+ - Grey = no recent activity
153
153
 
154
- ```
155
- 1 agent error(s) on unknown in the last minute. Last: 401 Incorrect API key...
154
+ ---
156
155
 
157
- Summary: Your OpenAI API key is invalid or expired — the agent cannot make LLM calls.
158
- Action: Update your API key in ~/.openclaw/.env with a valid key from platform.openai.com/api-keys
159
- ```
156
+ ## REST API
157
+
158
+ All endpoints return JSON. CORS enabled.
159
+
160
+ | Method | Path | Description |
161
+ | ------ | ---- | ----------- |
162
+ | `GET` | `/api/state` | Full dashboard snapshot |
163
+ | `GET` | `/api/activity` | Unified activity log (actions + diagnostics merged) |
164
+ | `GET` | `/api/logs` | OpenClaw-style log format (`ts`, `level`, `subsystem`, `message`) |
165
+ | `GET` | `/api/actions` | Raw agent/tool/chat action rows |
166
+ | `GET` | `/api/heartbeats` | Gateway heartbeat history |
167
+ | `GET` | `/api/alerts` | Fired alert history |
168
+ | `GET` | `/api/diagnostics` | Engine diagnostic events |
169
+ | `GET` | `/api/engine` | Engine stats, uptime, gateway connection status |
170
+ | `POST` | `/api/test` | Fire a test alert |
171
+ | `GET` | `/events` | SSE stream (live push to dashboard) |
172
+ | `GET` | `/health` | `{ok, ts, sseClients, gatewayConnected}` |
173
+
174
+ Most list endpoints accept `?limit=N` (default 100, max 500) and `/api/activity`, `/api/actions`, `/api/logs` also accept `?session=KEY`.
175
+
176
+ ### SSE events (`GET /events`)
177
+
178
+ | Event | Payload |
179
+ | ----- | ------- |
180
+ | `state` | Full dashboard snapshot — sent on connect and on file changes |
181
+ | `openalerts` | Alert fired: `{rule_id, severity, title, detail, ts, fingerprint}` |
182
+ | `action` | Agent step: `{id, runId, sessionKey, type, toolName, content, ts}` |
183
+ | `health` | Gateway heartbeat: `{queueDepth, activeSessions, sessions, ts}` |
184
+ | `diagnostic` | Engine event: `{event_type, ts, summary, session_key}` |
185
+ | `exec` | Shell command: `{type, runId, pid, command, output, exitCode, ts}` |
160
186
 
161
- - **Model**: reads from `agents.defaults.model.primary` in your `openclaw.json` (e.g. `"openai/gpt-4o-mini"`)
162
- - **API key**: reads from the corresponding environment variable (`OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `GROQ_API_KEY`, etc.)
163
- - **Supported providers**: OpenAI, Anthropic, Groq, Together, DeepSeek (and any OpenAI-compatible API)
164
- - **Graceful fallback**: if the LLM call fails or times out (10s), the original alert is sent unchanged
187
+ ---
165
188
 
166
- ## Commands
189
+ ## Architecture
167
190
 
168
- Zero-token chat commands available in any connected channel:
191
+ ```
192
+ openalerts
193
+ ├── cli.ts Entry point — wires everything together
194
+ ├── config.ts Config loading + gateway token auto-detection
195
+ ├── core/
196
+ │ ├── engine.ts Alert engine — ingests events, evaluates rules, fires alerts
197
+ │ ├── evaluator.ts Sliding-window + cooldown rule state
198
+ │ ├── rules.ts 10 alert rule definitions
199
+ │ └── types.ts Event and alert type definitions
200
+ ├── watchers/
201
+ │ ├── gateway.ts WebSocket client for OpenClaw gateway
202
+ │ ├── gateway-adapter.ts Translates gateway frames → engine events + SSE payloads
203
+ │ └── files.ts node:fs watcher for OpenClaw workspace files
204
+ ├── readers/
205
+ │ └── openclaw.ts Reads SOUL.md, cron jobs, sessions, delivery queue, config
206
+ ├── channels/
207
+ │ ├── telegram.ts Telegram Bot API (no SDK, direct HTTP)
208
+ │ ├── webhook.ts HTTP POST to any webhook URL
209
+ │ └── console.ts Console fallback
210
+ ├── server/
211
+ │ ├── index.ts HTTP server bootstrap
212
+ │ ├── routes.ts All REST + SSE route handlers
213
+ │ ├── sse.ts SSE manager (15s keepalive, broadcast)
214
+ │ └── dashboard.ts Embedded dashboard HTML (vanilla JS, zero framework)
215
+ └── db/
216
+ ├── index.ts SQLite open + periodic prune
217
+ ├── schema.ts 12 tables: sessions, actions, alerts, heartbeats, cron_jobs…
218
+ └── queries.ts Typed query functions including getActivityLog (UNION query)
219
+ ```
169
220
 
170
- | Command | What it does |
171
- | ------------ | ----------------------------------------------------- |
172
- | `/health` | System health snapshot — uptime, active alerts, stats |
173
- | `/alerts` | Recent alert history with severity and timestamps |
174
- | `/dashboard` | Returns the dashboard URL |
221
+ **Storage**: SQLite at `~/.openalerts/openalerts.db` using Node 22's built-in `node:sqlite` — no native build step, no binaries to compile.
175
222
 
176
- ## Roadmap
223
+ **Zero heavy dependencies**: only `ws` (WebSocket client). No Express, no ORM, no native modules.
177
224
 
178
- - [ ] [nanobot](https://github.com/HKUDS/nanobot) adapter
179
- - [ ] [OpenManus](https://github.com/FoundationAgents/OpenManus) adapter
225
+ ---
180
226
 
181
- ## Development
227
+ ## Data flow
182
228
 
183
- ```bash
184
- npm install # install dependencies
185
- npm run build # compile TypeScript
186
- npm run typecheck # type-check without emitting
187
- npm run clean # remove dist/
188
229
  ```
230
+ OpenClaw gateway (ws://127.0.0.1:18789)
231
+ │ health, agent, chat, exec events
232
+
233
+ gateway-adapter.ts ──► engine.ingest() ──► rule evaluation ──► Telegram / webhook
234
+ │ │
235
+ │ SQLite DB JSONL event store
236
+ │ (actions, alerts, (warm-start rule
237
+ │ heartbeats, sessions) state on restart)
238
+
239
+ SSE broadcast ──► dashboard live updates
240
+
241
+ OpenClaw workspace files (~/.openclaw/)
242
+ │ SOUL.md, HEARTBEAT.md, cron/jobs.json, sessions.json…
243
+
244
+ openclaw.ts reader ──► SQLite (agent_info, cron_jobs, sessions)
245
+
246
+ node:fs watcher triggers re-reads on change
247
+ ```
248
+
249
+ ---
189
250
 
190
251
  ## License
191
252
 
@@ -0,0 +1,6 @@
1
+ import type { AlertChannel, AlertEvent } from "../core/types.js";
2
+ export declare class ConsoleChannel implements AlertChannel {
3
+ readonly name = "console";
4
+ send(alert: AlertEvent, formatted: string): void;
5
+ }
6
+ //# sourceMappingURL=console.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"console.d.ts","sourceRoot":"","sources":["../../src/channels/console.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEjE,qBAAa,cAAe,YAAW,YAAY;IACjD,QAAQ,CAAC,IAAI,aAAa;IAC1B,IAAI,CAAC,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;CAMjD"}
@@ -0,0 +1,10 @@
1
+ export class ConsoleChannel {
2
+ name = "console";
3
+ send(alert, formatted) {
4
+ const prefix = alert.severity === "critical" ? "🚨" :
5
+ alert.severity === "error" ? "❌" :
6
+ alert.severity === "warn" ? "⚠️" : "ℹ️";
7
+ console.log(`\n${prefix} ALERT [${alert.ruleId}]\n${formatted}\n`);
8
+ }
9
+ }
10
+ //# sourceMappingURL=console.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"console.js","sourceRoot":"","sources":["../../src/channels/console.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,cAAc;IAChB,IAAI,GAAG,SAAS,CAAC;IAC1B,IAAI,CAAC,KAAiB,EAAE,SAAiB;QACvC,MAAM,MAAM,GAAG,KAAK,CAAC,QAAQ,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YACtC,KAAK,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;gBAClC,KAAK,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;QACvD,OAAO,CAAC,GAAG,CAAC,KAAK,MAAM,WAAW,KAAK,CAAC,MAAM,MAAM,SAAS,IAAI,CAAC,CAAC;IACrE,CAAC;CACF"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Telegram alert channel — uses Bot API directly (no OpenClaw dependency).
3
+ */
4
+ import type { AlertChannel, AlertEvent } from "../core/types.js";
5
+ export declare class TelegramChannel implements AlertChannel {
6
+ readonly name = "telegram";
7
+ private token;
8
+ private chatId;
9
+ constructor(token: string, chatId: string);
10
+ send(_alert: AlertEvent, formatted: string): Promise<void>;
11
+ }
12
+ //# sourceMappingURL=telegram.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telegram.d.ts","sourceRoot":"","sources":["../../src/channels/telegram.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEjE,qBAAa,eAAgB,YAAW,YAAY;IAClD,QAAQ,CAAC,IAAI,cAAc;IAC3B,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,MAAM,CAAS;gBAEX,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAKnC,IAAI,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAoBjE"}
@@ -0,0 +1,28 @@
1
+ export class TelegramChannel {
2
+ name = "telegram";
3
+ token;
4
+ chatId;
5
+ constructor(token, chatId) {
6
+ this.token = token;
7
+ this.chatId = chatId;
8
+ }
9
+ async send(_alert, formatted) {
10
+ const url = `https://api.telegram.org/bot${this.token}/sendMessage`;
11
+ const body = JSON.stringify({
12
+ chat_id: this.chatId,
13
+ text: formatted,
14
+ parse_mode: "HTML",
15
+ disable_web_page_preview: true,
16
+ });
17
+ const res = await fetch(url, {
18
+ method: "POST",
19
+ headers: { "Content-Type": "application/json" },
20
+ body,
21
+ });
22
+ if (!res.ok) {
23
+ const text = await res.text().catch(() => "");
24
+ throw new Error(`Telegram API ${res.status}: ${text.substring(0, 200)}`);
25
+ }
26
+ }
27
+ }
28
+ //# sourceMappingURL=telegram.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telegram.js","sourceRoot":"","sources":["../../src/channels/telegram.ts"],"names":[],"mappings":"AAKA,MAAM,OAAO,eAAe;IACjB,IAAI,GAAG,UAAU,CAAC;IACnB,KAAK,CAAS;IACd,MAAM,CAAS;IAEvB,YAAY,KAAa,EAAE,MAAc;QACvC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,MAAkB,EAAE,SAAiB;QAC9C,MAAM,GAAG,GAAG,+BAA+B,IAAI,CAAC,KAAK,cAAc,CAAC;QACpE,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC;YAC1B,OAAO,EAAE,IAAI,CAAC,MAAM;YACpB,IAAI,EAAE,SAAS;YACf,UAAU,EAAE,MAAM;YAClB,wBAAwB,EAAE,IAAI;SAC/B,CAAC,CAAC;QAEH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAC3B,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI;SACL,CAAC,CAAC;QAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;YAC9C,MAAM,IAAI,KAAK,CAAC,gBAAgB,GAAG,CAAC,MAAM,KAAK,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;QAC3E,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,8 @@
1
+ import type { AlertChannel, AlertEvent } from "../core/types.js";
2
+ export declare class WebhookChannel implements AlertChannel {
3
+ readonly name = "webhook";
4
+ private url;
5
+ constructor(url: string);
6
+ send(alert: AlertEvent, formatted: string): Promise<void>;
7
+ }
8
+ //# sourceMappingURL=webhook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhook.d.ts","sourceRoot":"","sources":["../../src/channels/webhook.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEjE,qBAAa,cAAe,YAAW,YAAY;IACjD,QAAQ,CAAC,IAAI,aAAa;IAC1B,OAAO,CAAC,GAAG,CAAS;gBAER,GAAG,EAAE,MAAM;IAEjB,IAAI,CAAC,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAQhE"}
@@ -0,0 +1,15 @@
1
+ export class WebhookChannel {
2
+ name = "webhook";
3
+ url;
4
+ constructor(url) { this.url = url; }
5
+ async send(alert, formatted) {
6
+ const res = await fetch(this.url, {
7
+ method: "POST",
8
+ headers: { "Content-Type": "application/json" },
9
+ body: JSON.stringify({ alert, formatted }),
10
+ });
11
+ if (!res.ok)
12
+ throw new Error(`Webhook ${res.status}`);
13
+ }
14
+ }
15
+ //# sourceMappingURL=webhook.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhook.js","sourceRoot":"","sources":["../../src/channels/webhook.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,cAAc;IAChB,IAAI,GAAG,SAAS,CAAC;IAClB,GAAG,CAAS;IAEpB,YAAY,GAAW,IAAI,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC;IAE5C,KAAK,CAAC,IAAI,CAAC,KAAiB,EAAE,SAAiB;QAC7C,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE;YAChC,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;SAC3C,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,WAAW,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;IACxD,CAAC;CACF"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}