@steadwing/openalerts 0.2.5 → 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 (134) hide show
  1. package/README.md +198 -141
  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 +48 -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 +1 -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 +1 -0
  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/collections/collection-manager.d.ts +0 -50
  105. package/dist/collections/collection-manager.js +0 -583
  106. package/dist/collections/event-parser.d.ts +0 -27
  107. package/dist/collections/event-parser.js +0 -321
  108. package/dist/collections/index.d.ts +0 -6
  109. package/dist/collections/index.js +0 -6
  110. package/dist/collections/persistence.d.ts +0 -25
  111. package/dist/collections/persistence.js +0 -213
  112. package/dist/collections/types.d.ts +0 -177
  113. package/dist/collections/types.js +0 -15
  114. package/dist/core/index.d.ts +0 -13
  115. package/dist/core/index.js +0 -23
  116. package/dist/core/llm-enrichment.d.ts +0 -21
  117. package/dist/core/llm-enrichment.js +0 -180
  118. package/dist/core/platform.d.ts +0 -17
  119. package/dist/core/platform.js +0 -93
  120. package/dist/index.d.ts +0 -8
  121. package/dist/index.js +0 -600
  122. package/dist/plugin/adapter.d.ts +0 -150
  123. package/dist/plugin/adapter.js +0 -530
  124. package/dist/plugin/commands.d.ts +0 -18
  125. package/dist/plugin/commands.js +0 -103
  126. package/dist/plugin/dashboard-html.d.ts +0 -7
  127. package/dist/plugin/dashboard-html.js +0 -968
  128. package/dist/plugin/dashboard-routes.d.ts +0 -12
  129. package/dist/plugin/dashboard-routes.js +0 -444
  130. package/dist/plugin/gateway-client.d.ts +0 -39
  131. package/dist/plugin/gateway-client.js +0 -200
  132. package/dist/plugin/log-bridge.d.ts +0 -22
  133. package/dist/plugin/log-bridge.js +0 -363
  134. package/openclaw.plugin.json +0 -87
@@ -1,22 +0,0 @@
1
- import type { OpenAlertsEngine } from "../core/index.js";
2
- import type { LogTransportRecord } from "openclaw/plugin-sdk";
3
- /**
4
- * Create a log-transport bridge that parses structured log records and
5
- * synthesizes OpenAlertsEvents to fill gaps left by non-firing plugin hooks.
6
- *
7
- * Covers:
8
- * - Tool calls (tool start → tool end, with duration) — fills `after_tool_call` gap
9
- * - Session lifecycle (idle ↔ processing transitions) — fills `session_start/end` gap
10
- * - Run prompt duration — enriches session.end with durationMs
11
- * - Agent run lifecycle — agent start/end at run level
12
- * - Compaction events — detects costly context compaction
13
- * - Message delivery — "Committed messaging text" fills `message_sent` gap
14
- * - Exec commands — attaches elevated commands to tool events
15
- *
16
- * Returns the transport function to pass to registerLogTransport().
17
- * Call the returned cleanup function to release internal state.
18
- */
19
- export declare function createLogBridge(engine: OpenAlertsEngine): {
20
- transport: (logObj: LogTransportRecord) => void;
21
- cleanup: () => void;
22
- };
@@ -1,363 +0,0 @@
1
- import { BoundedMap } from "../core/index.js";
2
- // ─── Parsing Helpers ─────────────────────────────────────────────────────────
3
- const KV_RE = /(\w+)=([\S]+)/g;
4
- /** Parse key=value pairs from a log message string. */
5
- function parseKvs(message) {
6
- const result = {};
7
- let m;
8
- KV_RE.lastIndex = 0;
9
- while ((m = KV_RE.exec(message)) !== null) {
10
- result[m[1]] = m[2];
11
- }
12
- return result;
13
- }
14
- /** Extract subsystem string from field "0" which may be JSON or raw. */
15
- function extractSubsystem(field0) {
16
- if (typeof field0 !== "string")
17
- return "";
18
- if (field0.startsWith("{")) {
19
- try {
20
- const parsed = JSON.parse(field0);
21
- return typeof parsed.subsystem === "string" ? parsed.subsystem : "";
22
- }
23
- catch {
24
- return "";
25
- }
26
- }
27
- return field0;
28
- }
29
- /** Extract timestamp from a log record. */
30
- function extractTimestamp(logObj) {
31
- const meta = logObj._meta;
32
- if (meta?.date && typeof meta.date === "string") {
33
- const t = new Date(meta.date).getTime();
34
- if (!isNaN(t))
35
- return t;
36
- }
37
- if (typeof logObj.time === "string") {
38
- const t = new Date(logObj.time).getTime();
39
- if (!isNaN(t))
40
- return t;
41
- }
42
- if (typeof logObj.time === "number") {
43
- return logObj.time;
44
- }
45
- return Date.now();
46
- }
47
- /** Subsystems the bridge cares about. Everything else is fast-skipped. */
48
- const WATCHED_SUBSYSTEMS = new Set(["agent/embedded", "diagnostic", "exec"]);
49
- /**
50
- * Parse a raw LogTransportRecord into a structured form.
51
- * Returns null if the record can't be parsed or is from an irrelevant subsystem.
52
- */
53
- function parseLogRecord(logObj) {
54
- const subsystem = extractSubsystem(logObj["0"]);
55
- if (!WATCHED_SUBSYSTEMS.has(subsystem))
56
- return null;
57
- const message = typeof logObj["1"] === "string" ? logObj["1"] : "";
58
- if (!message)
59
- return null;
60
- return {
61
- subsystem,
62
- message,
63
- ts: extractTimestamp(logObj),
64
- kvs: parseKvs(message),
65
- };
66
- }
67
- // ─── Dedup set with periodic pruning ─────────────────────────────────────────
68
- const DEDUP_MAX_SIZE = 2000;
69
- const DEDUP_PRUNE_TARGET = 500;
70
- /** Prune oldest entries from a dedup set when it grows too large. */
71
- function pruneDedupeSet(set) {
72
- if (set.size <= DEDUP_MAX_SIZE)
73
- return;
74
- // Set iterates in insertion order; delete oldest entries
75
- const toDelete = set.size - DEDUP_PRUNE_TARGET;
76
- let deleted = 0;
77
- for (const key of set) {
78
- if (deleted >= toDelete)
79
- break;
80
- set.delete(key);
81
- deleted++;
82
- }
83
- }
84
- // ─── Bridge ──────────────────────────────────────────────────────────────────
85
- /**
86
- * Create a log-transport bridge that parses structured log records and
87
- * synthesizes OpenAlertsEvents to fill gaps left by non-firing plugin hooks.
88
- *
89
- * Covers:
90
- * - Tool calls (tool start → tool end, with duration) — fills `after_tool_call` gap
91
- * - Session lifecycle (idle ↔ processing transitions) — fills `session_start/end` gap
92
- * - Run prompt duration — enriches session.end with durationMs
93
- * - Agent run lifecycle — agent start/end at run level
94
- * - Compaction events — detects costly context compaction
95
- * - Message delivery — "Committed messaging text" fills `message_sent` gap
96
- * - Exec commands — attaches elevated commands to tool events
97
- *
98
- * Returns the transport function to pass to registerLogTransport().
99
- * Call the returned cleanup function to release internal state.
100
- */
101
- export function createLogBridge(engine) {
102
- // ── State ──────────────────────────────────────────────────────────────────
103
- // Use bounded maps to prevent memory leaks (max 1000 entries each)
104
- const toolFlights = new BoundedMap({ maxSize: 1000 });
105
- const sessionStates = new BoundedMap({ maxSize: 500 });
106
- const dedupeSet = new Set();
107
- let pendingCommand = null;
108
- let lastRunDurationMs = null;
109
- let pruneCounter = 0;
110
- function ingest(event) {
111
- engine.ingest(event);
112
- }
113
- // ── Tool call handling (agent/embedded) ────────────────────────────────────
114
- // Fills the `after_tool_call` hook gap (hook is declared but never fires)
115
- function handleToolStart(rec) {
116
- const { toolCallId, tool, runId } = rec.kvs;
117
- if (!toolCallId || !tool)
118
- return;
119
- toolFlights.set(toolCallId, {
120
- tool,
121
- runId: runId ?? "",
122
- startTs: rec.ts,
123
- });
124
- }
125
- function handleToolEnd(rec) {
126
- const { toolCallId, tool, runId } = rec.kvs;
127
- if (!toolCallId)
128
- return;
129
- const dedupeKey = `tool:${toolCallId}`;
130
- if (dedupeSet.has(dedupeKey))
131
- return;
132
- dedupeSet.add(dedupeKey);
133
- const flight = toolFlights.get(toolCallId);
134
- toolFlights.delete(toolCallId);
135
- const durationMs = flight ? rec.ts - flight.startTs : undefined;
136
- const toolName = flight?.tool ?? tool ?? "unknown";
137
- const event = {
138
- type: "tool.call",
139
- ts: rec.ts,
140
- durationMs,
141
- outcome: "success",
142
- meta: {
143
- toolName,
144
- toolCallId,
145
- runId: flight?.runId ?? runId,
146
- source: "log-bridge",
147
- },
148
- };
149
- if (pendingCommand) {
150
- event.meta.command = pendingCommand;
151
- pendingCommand = null;
152
- }
153
- ingest(event);
154
- }
155
- // ── Session lifecycle handling (diagnostic) ────────────────────────────────
156
- // Fills the `session_start/end` hook gap (hooks declared but never fire)
157
- function handleSessionState(rec) {
158
- const { sessionId, sessionKey, prev, new: newState, reason } = rec.kvs;
159
- const sid = sessionId ?? sessionKey;
160
- if (!sid || !newState)
161
- return;
162
- const prevState = prev ?? sessionStates.get(sid);
163
- sessionStates.set(sid, newState);
164
- // session.start: idle → processing
165
- if (newState === "processing" && prevState === "idle") {
166
- const startKey = `session:start:${sid}`;
167
- if (!dedupeSet.has(startKey)) {
168
- dedupeSet.add(startKey);
169
- ingest({
170
- type: "session.start",
171
- ts: rec.ts,
172
- sessionKey: sid,
173
- outcome: "success",
174
- meta: { source: "log-bridge" },
175
- });
176
- }
177
- }
178
- // session.end: processing → idle with reason=run_completed
179
- if (newState === "idle" &&
180
- prevState === "processing" &&
181
- reason === "run_completed") {
182
- const endKey = `session:end:${sid}`;
183
- if (!dedupeSet.has(endKey)) {
184
- dedupeSet.add(endKey);
185
- const event = {
186
- type: "session.end",
187
- ts: rec.ts,
188
- sessionKey: sid,
189
- outcome: "success",
190
- meta: { source: "log-bridge" },
191
- };
192
- if (lastRunDurationMs !== null) {
193
- event.durationMs = lastRunDurationMs;
194
- lastRunDurationMs = null;
195
- }
196
- ingest(event);
197
- }
198
- }
199
- }
200
- // ── Run prompt duration (agent/embedded) ──────────────────────────────────
201
- function handleRunPromptEnd(rec) {
202
- const { durationMs } = rec.kvs;
203
- if (durationMs) {
204
- lastRunDurationMs = parseInt(durationMs, 10) || null;
205
- }
206
- }
207
- // ── Agent run lifecycle (agent/embedded) ──────────────────────────────────
208
- // Complements before_agent_start / agent_end hooks with run-level tracking
209
- function handleAgentRunStart(rec) {
210
- const { runId } = rec.kvs;
211
- ingest({
212
- type: "custom",
213
- ts: rec.ts,
214
- outcome: "success",
215
- meta: { runId, openclawLog: "agent_run_start", source: "log-bridge" },
216
- });
217
- }
218
- function handleAgentRunEnd(rec) {
219
- const { runId } = rec.kvs;
220
- ingest({
221
- type: "custom",
222
- ts: rec.ts,
223
- outcome: "success",
224
- meta: { runId, openclawLog: "agent_run_end", source: "log-bridge" },
225
- });
226
- }
227
- // ── Compaction (agent/embedded) ───────────────────────────────────────────
228
- // OpenClaw compacts when context is full — costly, can lose context.
229
- // No hook exists for this. Only available via logs.
230
- function handleCompactionStart(rec) {
231
- const { runId } = rec.kvs;
232
- ingest({
233
- type: "custom",
234
- ts: rec.ts,
235
- outcome: "success",
236
- meta: {
237
- runId,
238
- compaction: true,
239
- openclawLog: "compaction_start",
240
- source: "log-bridge",
241
- },
242
- });
243
- }
244
- function handleCompactionRetry(rec) {
245
- const { runId } = rec.kvs;
246
- ingest({
247
- type: "custom",
248
- ts: rec.ts,
249
- outcome: "success",
250
- meta: {
251
- runId,
252
- compaction: true,
253
- openclawLog: "compaction_retry",
254
- source: "log-bridge",
255
- },
256
- });
257
- }
258
- // ── Message delivery (agent/embedded) ─────────────────────────────────────
259
- // "Committed messaging text" fires when a messaging tool (Telegram, Discord,
260
- // etc.) successfully delivers a message. Fills the `message_sent` hook gap.
261
- function handleMessageCommitted(rec) {
262
- const { tool, len } = rec.kvs;
263
- ingest({
264
- type: "custom",
265
- ts: rec.ts,
266
- outcome: "success",
267
- meta: {
268
- toolName: tool,
269
- textLength: len ? parseInt(len, 10) : undefined,
270
- openclawLog: "message_committed",
271
- source: "log-bridge",
272
- },
273
- });
274
- }
275
- // ── Lane task error (diagnostic) ────────────────────────────────────────────
276
- // Safety net: catches lane-level errors from diagnostic logs.
277
- // The agent_end hook already covers agent errors → llm-errors rule.
278
- // This emits as infra.error to avoid double-counting in the llm-errors window
279
- // while still ensuring infra-errors fires if the hook path fails.
280
- // Format: "lane task error: lane=main durationMs=1 error="Error: ...""
281
- function handleLaneTaskError(rec) {
282
- const { lane, error: errorMsg } = rec.kvs;
283
- const dedupeKey = `lane-error:${lane}:${rec.ts}`;
284
- if (dedupeSet.has(dedupeKey))
285
- return;
286
- dedupeSet.add(dedupeKey);
287
- ingest({
288
- type: "infra.error",
289
- ts: rec.ts,
290
- outcome: "error",
291
- error: errorMsg,
292
- meta: {
293
- lane,
294
- source: "log-bridge",
295
- openclawLog: "lane_task_error",
296
- },
297
- });
298
- }
299
- // ── Exec command (exec) ────────────────────────────────────────────────────
300
- function handleExecCommand(rec) {
301
- pendingCommand = rec.message;
302
- }
303
- // ── Main transport function ────────────────────────────────────────────────
304
- function transport(logObj) {
305
- const rec = parseLogRecord(logObj);
306
- if (!rec)
307
- return;
308
- const msg = rec.message;
309
- if (rec.subsystem === "agent/embedded") {
310
- if (msg.startsWith("embedded run tool start:")) {
311
- handleToolStart(rec);
312
- }
313
- else if (msg.startsWith("embedded run tool end:")) {
314
- handleToolEnd(rec);
315
- }
316
- else if (msg.startsWith("embedded run prompt end:")) {
317
- handleRunPromptEnd(rec);
318
- }
319
- else if (msg.startsWith("embedded run agent start:")) {
320
- handleAgentRunStart(rec);
321
- }
322
- else if (msg.startsWith("embedded run agent end:")) {
323
- handleAgentRunEnd(rec);
324
- }
325
- else if (msg.startsWith("embedded run compaction start:")) {
326
- handleCompactionStart(rec);
327
- }
328
- else if (msg.startsWith("embedded run compaction retry:")) {
329
- handleCompactionRetry(rec);
330
- }
331
- else if (msg.startsWith("Committed messaging text:")) {
332
- handleMessageCommitted(rec);
333
- }
334
- }
335
- else if (rec.subsystem === "diagnostic") {
336
- if (msg.startsWith("session state:")) {
337
- handleSessionState(rec);
338
- }
339
- else if (msg.startsWith("lane task error:")) {
340
- handleLaneTaskError(rec);
341
- }
342
- }
343
- else if (rec.subsystem === "exec") {
344
- if (msg.startsWith("elevated command")) {
345
- handleExecCommand(rec);
346
- }
347
- }
348
- // Periodic dedup set pruning (check every 100 log records)
349
- if (++pruneCounter >= 100) {
350
- pruneCounter = 0;
351
- pruneDedupeSet(dedupeSet);
352
- }
353
- }
354
- function cleanup() {
355
- toolFlights.clear();
356
- sessionStates.clear();
357
- dedupeSet.clear();
358
- pendingCommand = null;
359
- lastRunDurationMs = null;
360
- pruneCounter = 0;
361
- }
362
- return { transport, cleanup };
363
- }
@@ -1,87 +0,0 @@
1
- {
2
- "id": "openalerts",
3
- "configSchema": {
4
- "type": "object",
5
- "additionalProperties": false,
6
- "properties": {
7
- "apiKey": { "type": "string" },
8
- "alertChannel": { "type": "string" },
9
- "alertTo": { "type": "string" },
10
- "alertAccountId": { "type": "string" },
11
- "cooldownMinutes": { "type": "number", "minimum": 1, "maximum": 1440 },
12
- "maxLogSizeKb": { "type": "number", "minimum": 64, "maximum": 10240 },
13
- "maxLogAgeDays": { "type": "number", "minimum": 1, "maximum": 90 },
14
- "quiet": { "type": "boolean" },
15
- "rules": {
16
- "type": "object",
17
- "properties": {
18
- "cost-hourly-spike": {
19
- "type": "object",
20
- "properties": {
21
- "enabled": { "type": "boolean" },
22
- "threshold": { "type": "number", "minimum": 0 },
23
- "cooldownMinutes": { "type": "number", "minimum": 1 }
24
- },
25
- "additionalProperties": false
26
- },
27
- "cost-daily-budget": {
28
- "type": "object",
29
- "properties": {
30
- "enabled": { "type": "boolean" },
31
- "threshold": { "type": "number", "minimum": 0 },
32
- "cooldownMinutes": { "type": "number", "minimum": 1 }
33
- },
34
- "additionalProperties": false
35
- }
36
- },
37
- "additionalProperties": {
38
- "type": "object",
39
- "properties": {
40
- "enabled": { "type": "boolean" },
41
- "threshold": { "type": "number" },
42
- "cooldownMinutes": { "type": "number" }
43
- },
44
- "additionalProperties": false
45
- }
46
- }
47
- }
48
- },
49
- "uiHints": {
50
- "apiKey": {
51
- "label": "OpenAlerts API Key",
52
- "sensitive": true,
53
- "help": "Optional. Get from app.openalerts.dev. Enables platform diagnosis & dashboard."
54
- },
55
- "alertChannel": {
56
- "label": "Alert Channel",
57
- "help": "telegram, discord, slack, etc. Auto-detected if blank."
58
- },
59
- "alertTo": {
60
- "label": "Alert To",
61
- "help": "Chat/user ID to send alerts to. Auto-detected from allowFrom if blank."
62
- },
63
- "alertAccountId": {
64
- "label": "Account ID",
65
- "help": "For multi-account setups. Default account used if blank.",
66
- "advanced": true
67
- },
68
- "cooldownMinutes": {
69
- "label": "Alert Cooldown",
70
- "help": "Minimum minutes between repeated alerts of the same type. Default: 15."
71
- },
72
- "quiet": {
73
- "label": "Quiet Mode",
74
- "help": "Log alerts to file only — no messages sent to your channels."
75
- },
76
- "rules.cost-hourly-spike.threshold": {
77
- "label": "Hourly Cost Threshold (USD)",
78
- "help": "Alert when total LLM spend in the last 60 minutes exceeds this amount. Default: 5.0.",
79
- "advanced": true
80
- },
81
- "rules.cost-daily-budget.threshold": {
82
- "label": "Daily Budget Threshold (USD)",
83
- "help": "Alert when total LLM spend in the last 24 hours exceeds this amount. Default: 20.0.",
84
- "advanced": true
85
- }
86
- }
87
- }