@switchbot/openapi-cli 3.2.1 → 3.3.0

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 (332) hide show
  1. package/README.md +3 -1
  2. package/dist/index.js +57419 -170
  3. package/package.json +9 -5
  4. package/dist/api/client.d.ts +0 -31
  5. package/dist/api/client.js +0 -236
  6. package/dist/api/client.js.map +0 -1
  7. package/dist/auth.d.ts +0 -1
  8. package/dist/auth.js +0 -21
  9. package/dist/auth.js.map +0 -1
  10. package/dist/commands/agent-bootstrap.d.ts +0 -10
  11. package/dist/commands/agent-bootstrap.js +0 -200
  12. package/dist/commands/agent-bootstrap.js.map +0 -1
  13. package/dist/commands/auth.d.ts +0 -18
  14. package/dist/commands/auth.js +0 -355
  15. package/dist/commands/auth.js.map +0 -1
  16. package/dist/commands/batch.d.ts +0 -2
  17. package/dist/commands/batch.js +0 -414
  18. package/dist/commands/batch.js.map +0 -1
  19. package/dist/commands/cache.d.ts +0 -2
  20. package/dist/commands/cache.js +0 -127
  21. package/dist/commands/cache.js.map +0 -1
  22. package/dist/commands/capabilities.d.ts +0 -31
  23. package/dist/commands/capabilities.js +0 -383
  24. package/dist/commands/capabilities.js.map +0 -1
  25. package/dist/commands/catalog.d.ts +0 -2
  26. package/dist/commands/catalog.js +0 -360
  27. package/dist/commands/catalog.js.map +0 -1
  28. package/dist/commands/completion.d.ts +0 -2
  29. package/dist/commands/completion.js +0 -386
  30. package/dist/commands/completion.js.map +0 -1
  31. package/dist/commands/config.d.ts +0 -21
  32. package/dist/commands/config.js +0 -377
  33. package/dist/commands/config.js.map +0 -1
  34. package/dist/commands/daemon.d.ts +0 -2
  35. package/dist/commands/daemon.js +0 -411
  36. package/dist/commands/daemon.js.map +0 -1
  37. package/dist/commands/device-meta.d.ts +0 -2
  38. package/dist/commands/device-meta.js +0 -160
  39. package/dist/commands/device-meta.js.map +0 -1
  40. package/dist/commands/devices.d.ts +0 -2
  41. package/dist/commands/devices.js +0 -949
  42. package/dist/commands/devices.js.map +0 -1
  43. package/dist/commands/doctor.d.ts +0 -3
  44. package/dist/commands/doctor.js +0 -1016
  45. package/dist/commands/doctor.js.map +0 -1
  46. package/dist/commands/events.d.ts +0 -31
  47. package/dist/commands/events.js +0 -564
  48. package/dist/commands/events.js.map +0 -1
  49. package/dist/commands/expand.d.ts +0 -2
  50. package/dist/commands/expand.js +0 -131
  51. package/dist/commands/expand.js.map +0 -1
  52. package/dist/commands/explain.d.ts +0 -2
  53. package/dist/commands/explain.js +0 -140
  54. package/dist/commands/explain.js.map +0 -1
  55. package/dist/commands/health.d.ts +0 -8
  56. package/dist/commands/health.js +0 -114
  57. package/dist/commands/health.js.map +0 -1
  58. package/dist/commands/history.d.ts +0 -2
  59. package/dist/commands/history.js +0 -321
  60. package/dist/commands/history.js.map +0 -1
  61. package/dist/commands/identity.d.ts +0 -45
  62. package/dist/commands/identity.js +0 -60
  63. package/dist/commands/identity.js.map +0 -1
  64. package/dist/commands/install.d.ts +0 -20
  65. package/dist/commands/install.js +0 -247
  66. package/dist/commands/install.js.map +0 -1
  67. package/dist/commands/mcp.d.ts +0 -14
  68. package/dist/commands/mcp.js +0 -2018
  69. package/dist/commands/mcp.js.map +0 -1
  70. package/dist/commands/plan.d.ts +0 -51
  71. package/dist/commands/plan.js +0 -654
  72. package/dist/commands/plan.js.map +0 -1
  73. package/dist/commands/policy.d.ts +0 -24
  74. package/dist/commands/policy.js +0 -587
  75. package/dist/commands/policy.js.map +0 -1
  76. package/dist/commands/quota.d.ts +0 -2
  77. package/dist/commands/quota.js +0 -79
  78. package/dist/commands/quota.js.map +0 -1
  79. package/dist/commands/rules.d.ts +0 -2
  80. package/dist/commands/rules.js +0 -876
  81. package/dist/commands/rules.js.map +0 -1
  82. package/dist/commands/scenes.d.ts +0 -2
  83. package/dist/commands/scenes.js +0 -265
  84. package/dist/commands/scenes.js.map +0 -1
  85. package/dist/commands/schema.d.ts +0 -2
  86. package/dist/commands/schema.js +0 -185
  87. package/dist/commands/schema.js.map +0 -1
  88. package/dist/commands/status-sync.d.ts +0 -2
  89. package/dist/commands/status-sync.js +0 -132
  90. package/dist/commands/status-sync.js.map +0 -1
  91. package/dist/commands/uninstall.d.ts +0 -20
  92. package/dist/commands/uninstall.js +0 -238
  93. package/dist/commands/uninstall.js.map +0 -1
  94. package/dist/commands/upgrade-check.d.ts +0 -2
  95. package/dist/commands/upgrade-check.js +0 -107
  96. package/dist/commands/upgrade-check.js.map +0 -1
  97. package/dist/commands/watch.d.ts +0 -2
  98. package/dist/commands/watch.js +0 -195
  99. package/dist/commands/watch.js.map +0 -1
  100. package/dist/commands/webhook.d.ts +0 -2
  101. package/dist/commands/webhook.js +0 -183
  102. package/dist/commands/webhook.js.map +0 -1
  103. package/dist/config.d.ts +0 -57
  104. package/dist/config.js +0 -259
  105. package/dist/config.js.map +0 -1
  106. package/dist/credentials/backends/file.d.ts +0 -18
  107. package/dist/credentials/backends/file.js +0 -102
  108. package/dist/credentials/backends/file.js.map +0 -1
  109. package/dist/credentials/backends/linux.d.ts +0 -16
  110. package/dist/credentials/backends/linux.js +0 -130
  111. package/dist/credentials/backends/linux.js.map +0 -1
  112. package/dist/credentials/backends/macos.d.ts +0 -18
  113. package/dist/credentials/backends/macos.js +0 -130
  114. package/dist/credentials/backends/macos.js.map +0 -1
  115. package/dist/credentials/backends/windows.d.ts +0 -23
  116. package/dist/credentials/backends/windows.js +0 -216
  117. package/dist/credentials/backends/windows.js.map +0 -1
  118. package/dist/credentials/keychain.d.ts +0 -83
  119. package/dist/credentials/keychain.js +0 -89
  120. package/dist/credentials/keychain.js.map +0 -1
  121. package/dist/credentials/prime.d.ts +0 -32
  122. package/dist/credentials/prime.js +0 -53
  123. package/dist/credentials/prime.js.map +0 -1
  124. package/dist/devices/cache.d.ts +0 -79
  125. package/dist/devices/cache.js +0 -294
  126. package/dist/devices/cache.js.map +0 -1
  127. package/dist/devices/catalog.d.ts +0 -138
  128. package/dist/devices/catalog.js +0 -768
  129. package/dist/devices/catalog.js.map +0 -1
  130. package/dist/devices/device-meta.d.ts +0 -15
  131. package/dist/devices/device-meta.js +0 -57
  132. package/dist/devices/device-meta.js.map +0 -1
  133. package/dist/devices/history-agg.d.ts +0 -37
  134. package/dist/devices/history-agg.js +0 -139
  135. package/dist/devices/history-agg.js.map +0 -1
  136. package/dist/devices/history-query.d.ts +0 -45
  137. package/dist/devices/history-query.js +0 -182
  138. package/dist/devices/history-query.js.map +0 -1
  139. package/dist/devices/param-validator.d.ts +0 -40
  140. package/dist/devices/param-validator.js +0 -434
  141. package/dist/devices/param-validator.js.map +0 -1
  142. package/dist/devices/resources.d.ts +0 -74
  143. package/dist/devices/resources.js +0 -271
  144. package/dist/devices/resources.js.map +0 -1
  145. package/dist/index.d.ts +0 -1
  146. package/dist/index.js.map +0 -1
  147. package/dist/install/default-steps.d.ts +0 -66
  148. package/dist/install/default-steps.js +0 -258
  149. package/dist/install/default-steps.js.map +0 -1
  150. package/dist/install/preflight.d.ts +0 -60
  151. package/dist/install/preflight.js +0 -213
  152. package/dist/install/preflight.js.map +0 -1
  153. package/dist/install/steps.d.ts +0 -61
  154. package/dist/install/steps.js +0 -68
  155. package/dist/install/steps.js.map +0 -1
  156. package/dist/lib/command-keywords.d.ts +0 -5
  157. package/dist/lib/command-keywords.js +0 -18
  158. package/dist/lib/command-keywords.js.map +0 -1
  159. package/dist/lib/daemon-state.d.ts +0 -24
  160. package/dist/lib/daemon-state.js +0 -47
  161. package/dist/lib/daemon-state.js.map +0 -1
  162. package/dist/lib/destructive-mode.d.ts +0 -2
  163. package/dist/lib/destructive-mode.js +0 -13
  164. package/dist/lib/destructive-mode.js.map +0 -1
  165. package/dist/lib/devices.d.ts +0 -151
  166. package/dist/lib/devices.js +0 -383
  167. package/dist/lib/devices.js.map +0 -1
  168. package/dist/lib/idempotency.d.ts +0 -46
  169. package/dist/lib/idempotency.js +0 -107
  170. package/dist/lib/idempotency.js.map +0 -1
  171. package/dist/lib/plan-store.d.ts +0 -19
  172. package/dist/lib/plan-store.js +0 -69
  173. package/dist/lib/plan-store.js.map +0 -1
  174. package/dist/lib/request-context.d.ts +0 -7
  175. package/dist/lib/request-context.js +0 -13
  176. package/dist/lib/request-context.js.map +0 -1
  177. package/dist/lib/scenes.d.ts +0 -7
  178. package/dist/lib/scenes.js +0 -11
  179. package/dist/lib/scenes.js.map +0 -1
  180. package/dist/logger.d.ts +0 -4
  181. package/dist/logger.js +0 -17
  182. package/dist/logger.js.map +0 -1
  183. package/dist/mcp/device-history.d.ts +0 -36
  184. package/dist/mcp/device-history.js +0 -146
  185. package/dist/mcp/device-history.js.map +0 -1
  186. package/dist/mcp/events-subscription.d.ts +0 -45
  187. package/dist/mcp/events-subscription.js +0 -214
  188. package/dist/mcp/events-subscription.js.map +0 -1
  189. package/dist/mqtt/client.d.ts +0 -25
  190. package/dist/mqtt/client.js +0 -181
  191. package/dist/mqtt/client.js.map +0 -1
  192. package/dist/mqtt/credential.d.ts +0 -16
  193. package/dist/mqtt/credential.js +0 -31
  194. package/dist/mqtt/credential.js.map +0 -1
  195. package/dist/policy/add-rule.d.ts +0 -21
  196. package/dist/policy/add-rule.js +0 -125
  197. package/dist/policy/add-rule.js.map +0 -1
  198. package/dist/policy/diff.d.ts +0 -21
  199. package/dist/policy/diff.js +0 -92
  200. package/dist/policy/diff.js.map +0 -1
  201. package/dist/policy/format.d.ts +0 -6
  202. package/dist/policy/format.js +0 -58
  203. package/dist/policy/format.js.map +0 -1
  204. package/dist/policy/load.d.ts +0 -32
  205. package/dist/policy/load.js +0 -62
  206. package/dist/policy/load.js.map +0 -1
  207. package/dist/policy/migrate.d.ts +0 -21
  208. package/dist/policy/migrate.js +0 -68
  209. package/dist/policy/migrate.js.map +0 -1
  210. package/dist/policy/schema.d.ts +0 -5
  211. package/dist/policy/schema.js +0 -19
  212. package/dist/policy/schema.js.map +0 -1
  213. package/dist/policy/validate.d.ts +0 -19
  214. package/dist/policy/validate.js +0 -263
  215. package/dist/policy/validate.js.map +0 -1
  216. package/dist/rules/action.d.ts +0 -65
  217. package/dist/rules/action.js +0 -217
  218. package/dist/rules/action.js.map +0 -1
  219. package/dist/rules/audit-query.d.ts +0 -51
  220. package/dist/rules/audit-query.js +0 -90
  221. package/dist/rules/audit-query.js.map +0 -1
  222. package/dist/rules/conflict-analyzer.d.ts +0 -57
  223. package/dist/rules/conflict-analyzer.js +0 -215
  224. package/dist/rules/conflict-analyzer.js.map +0 -1
  225. package/dist/rules/cron-scheduler.d.ts +0 -62
  226. package/dist/rules/cron-scheduler.js +0 -187
  227. package/dist/rules/cron-scheduler.js.map +0 -1
  228. package/dist/rules/destructive.d.ts +0 -20
  229. package/dist/rules/destructive.js +0 -53
  230. package/dist/rules/destructive.js.map +0 -1
  231. package/dist/rules/engine.d.ts +0 -193
  232. package/dist/rules/engine.js +0 -758
  233. package/dist/rules/engine.js.map +0 -1
  234. package/dist/rules/matcher.d.ts +0 -56
  235. package/dist/rules/matcher.js +0 -231
  236. package/dist/rules/matcher.js.map +0 -1
  237. package/dist/rules/pid-file.d.ts +0 -43
  238. package/dist/rules/pid-file.js +0 -96
  239. package/dist/rules/pid-file.js.map +0 -1
  240. package/dist/rules/quiet-hours.d.ts +0 -26
  241. package/dist/rules/quiet-hours.js +0 -46
  242. package/dist/rules/quiet-hours.js.map +0 -1
  243. package/dist/rules/suggest.d.ts +0 -20
  244. package/dist/rules/suggest.js +0 -96
  245. package/dist/rules/suggest.js.map +0 -1
  246. package/dist/rules/throttle.d.ts +0 -61
  247. package/dist/rules/throttle.js +0 -117
  248. package/dist/rules/throttle.js.map +0 -1
  249. package/dist/rules/types.d.ts +0 -117
  250. package/dist/rules/types.js +0 -35
  251. package/dist/rules/types.js.map +0 -1
  252. package/dist/rules/webhook-listener.d.ts +0 -63
  253. package/dist/rules/webhook-listener.js +0 -224
  254. package/dist/rules/webhook-listener.js.map +0 -1
  255. package/dist/rules/webhook-token.d.ts +0 -50
  256. package/dist/rules/webhook-token.js +0 -91
  257. package/dist/rules/webhook-token.js.map +0 -1
  258. package/dist/schema/field-aliases.d.ts +0 -34
  259. package/dist/schema/field-aliases.js +0 -132
  260. package/dist/schema/field-aliases.js.map +0 -1
  261. package/dist/sinks/dispatcher.d.ts +0 -7
  262. package/dist/sinks/dispatcher.js +0 -13
  263. package/dist/sinks/dispatcher.js.map +0 -1
  264. package/dist/sinks/file.d.ts +0 -6
  265. package/dist/sinks/file.js +0 -20
  266. package/dist/sinks/file.js.map +0 -1
  267. package/dist/sinks/format.d.ts +0 -20
  268. package/dist/sinks/format.js +0 -57
  269. package/dist/sinks/format.js.map +0 -1
  270. package/dist/sinks/homeassistant.d.ts +0 -18
  271. package/dist/sinks/homeassistant.js +0 -45
  272. package/dist/sinks/homeassistant.js.map +0 -1
  273. package/dist/sinks/openclaw.d.ts +0 -13
  274. package/dist/sinks/openclaw.js +0 -34
  275. package/dist/sinks/openclaw.js.map +0 -1
  276. package/dist/sinks/stdout.d.ts +0 -4
  277. package/dist/sinks/stdout.js +0 -6
  278. package/dist/sinks/stdout.js.map +0 -1
  279. package/dist/sinks/telegram.d.ts +0 -11
  280. package/dist/sinks/telegram.js +0 -29
  281. package/dist/sinks/telegram.js.map +0 -1
  282. package/dist/sinks/types.d.ts +0 -13
  283. package/dist/sinks/types.js +0 -2
  284. package/dist/sinks/types.js.map +0 -1
  285. package/dist/sinks/webhook.d.ts +0 -6
  286. package/dist/sinks/webhook.js +0 -23
  287. package/dist/sinks/webhook.js.map +0 -1
  288. package/dist/status-sync/manager.d.ts +0 -48
  289. package/dist/status-sync/manager.js +0 -269
  290. package/dist/status-sync/manager.js.map +0 -1
  291. package/dist/utils/arg-parsers.d.ts +0 -16
  292. package/dist/utils/arg-parsers.js +0 -67
  293. package/dist/utils/arg-parsers.js.map +0 -1
  294. package/dist/utils/audit.d.ts +0 -69
  295. package/dist/utils/audit.js +0 -122
  296. package/dist/utils/audit.js.map +0 -1
  297. package/dist/utils/filter.d.ts +0 -81
  298. package/dist/utils/filter.js +0 -190
  299. package/dist/utils/filter.js.map +0 -1
  300. package/dist/utils/flags.d.ts +0 -72
  301. package/dist/utils/flags.js +0 -187
  302. package/dist/utils/flags.js.map +0 -1
  303. package/dist/utils/format.d.ts +0 -9
  304. package/dist/utils/format.js +0 -118
  305. package/dist/utils/format.js.map +0 -1
  306. package/dist/utils/health.d.ts +0 -48
  307. package/dist/utils/health.js +0 -102
  308. package/dist/utils/health.js.map +0 -1
  309. package/dist/utils/help-json.d.ts +0 -39
  310. package/dist/utils/help-json.js +0 -55
  311. package/dist/utils/help-json.js.map +0 -1
  312. package/dist/utils/name-resolver.d.ts +0 -26
  313. package/dist/utils/name-resolver.js +0 -138
  314. package/dist/utils/name-resolver.js.map +0 -1
  315. package/dist/utils/output.d.ts +0 -73
  316. package/dist/utils/output.js +0 -405
  317. package/dist/utils/output.js.map +0 -1
  318. package/dist/utils/quota.d.ts +0 -61
  319. package/dist/utils/quota.js +0 -228
  320. package/dist/utils/quota.js.map +0 -1
  321. package/dist/utils/redact.d.ts +0 -23
  322. package/dist/utils/redact.js +0 -69
  323. package/dist/utils/redact.js.map +0 -1
  324. package/dist/utils/retry.d.ts +0 -72
  325. package/dist/utils/retry.js +0 -141
  326. package/dist/utils/retry.js.map +0 -1
  327. package/dist/utils/string.d.ts +0 -2
  328. package/dist/utils/string.js +0 -23
  329. package/dist/utils/string.js.map +0 -1
  330. package/dist/version.d.ts +0 -2
  331. package/dist/version.js +0 -5
  332. package/dist/version.js.map +0 -1
@@ -1,90 +0,0 @@
1
- /**
2
- * Shared filters + aggregations over the audit log for
3
- * `switchbot rules tail` and `switchbot rules replay`.
4
- *
5
- * All functions are pure — no I/O, no clock reads — so they can be
6
- * unit-tested with fixture arrays. The CLI entry points handle file
7
- * reading, `--follow` tailing, and human vs JSON rendering.
8
- */
9
- /** The subset of audit kinds the rules engine emits. */
10
- export const RULE_AUDIT_KINDS = [
11
- 'rule-fire',
12
- 'rule-fire-dry',
13
- 'rule-throttled',
14
- 'rule-webhook-rejected',
15
- ];
16
- /** Keep entries that are rule-engine emitted and match the filter. */
17
- export function filterRuleAudits(entries, filter = {}) {
18
- const kinds = new Set(filter.kinds ?? RULE_AUDIT_KINDS);
19
- const out = [];
20
- for (const e of entries) {
21
- if (!kinds.has(e.kind))
22
- continue;
23
- if (filter.sinceMs !== undefined) {
24
- const ms = Date.parse(e.t);
25
- if (!Number.isFinite(ms) || ms < filter.sinceMs)
26
- continue;
27
- }
28
- if (filter.ruleName !== undefined) {
29
- if (e.rule?.name !== filter.ruleName)
30
- continue;
31
- }
32
- out.push(e);
33
- }
34
- return out;
35
- }
36
- /** Aggregate a filtered stream into per-rule counters. */
37
- export function aggregateRuleAudits(entries) {
38
- const byRule = new Map();
39
- let webhookRejectedCount = 0;
40
- for (const e of entries) {
41
- if (e.kind === 'rule-webhook-rejected' && !e.rule) {
42
- webhookRejectedCount++;
43
- continue;
44
- }
45
- const name = e.rule?.name;
46
- if (!name)
47
- continue;
48
- let s = byRule.get(name);
49
- if (!s) {
50
- s = {
51
- rule: name,
52
- fires: 0,
53
- driesFires: 0,
54
- throttled: 0,
55
- errors: 0,
56
- errorRate: 0,
57
- firstAt: null,
58
- lastAt: null,
59
- triggerSource: null,
60
- };
61
- byRule.set(name, s);
62
- }
63
- if (e.kind === 'rule-fire')
64
- s.fires++;
65
- else if (e.kind === 'rule-fire-dry')
66
- s.driesFires++;
67
- else if (e.kind === 'rule-throttled')
68
- s.throttled++;
69
- if (e.result === 'error')
70
- s.errors++;
71
- if (!s.firstAt || e.t < s.firstAt)
72
- s.firstAt = e.t;
73
- if (!s.lastAt || e.t > s.lastAt)
74
- s.lastAt = e.t;
75
- const source = e.rule?.triggerSource;
76
- if (source) {
77
- if (s.triggerSource === null)
78
- s.triggerSource = source;
79
- else if (s.triggerSource !== source)
80
- s.triggerSource = 'mixed';
81
- }
82
- }
83
- for (const s of byRule.values()) {
84
- const denom = s.fires + s.driesFires;
85
- s.errorRate = denom === 0 ? 0 : s.errors / denom;
86
- }
87
- const summaries = [...byRule.values()].sort((a, b) => b.fires + b.driesFires - (a.fires + a.driesFires));
88
- return { total: entries.length, summaries, webhookRejectedCount };
89
- }
90
- //# sourceMappingURL=audit-query.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"audit-query.js","sourceRoot":"","sources":["../../src/rules/audit-query.ts"],"names":[],"mappings":"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"}
@@ -1,57 +0,0 @@
1
- /**
2
- * Static conflict analysis for automation rules.
3
- *
4
- * Detects patterns that are technically valid but likely to cause
5
- * operational problems:
6
- *
7
- * 1. Opposing-action pairs — same device, opposite commands (e.g.
8
- * turnOn / turnOff), triggered by the same source within a short
9
- * window and with no throttle on either rule.
10
- *
11
- * 2. High-frequency MQTT rules without throttle — rules that listen
12
- * on `device.shadow` (catch-all) with no throttle can fire on
13
- * every shadow push (up to once per second) and exhaust the daily
14
- * API quota quickly.
15
- *
16
- * 3. Potentially-destructive action without quiet-hours protection —
17
- * a rule that targets a destructive verb is technically blocked by
18
- * the engine, but we can still flag it early so users don't get a
19
- * surprise at runtime.
20
- *
21
- * Results are designed to be consumed by `rules doctor --json` and
22
- * by CI pipelines. Each finding carries a `severity` so callers can
23
- * decide how to gate on them.
24
- */
25
- import type { Rule } from './types.js';
26
- export type ConflictSeverity = 'error' | 'warning' | 'info';
27
- export interface ConflictFinding {
28
- severity: ConflictSeverity;
29
- code: string;
30
- message: string;
31
- /** Rule names involved in this finding. */
32
- rules: string[];
33
- hint?: string;
34
- }
35
- export interface ConflictReport {
36
- findings: ConflictFinding[];
37
- /** Count per severity level. */
38
- counts: Record<ConflictSeverity, number>;
39
- /** True when there are no error-severity findings. */
40
- clean: boolean;
41
- }
42
- /**
43
- * MQTT events that fire on every device state push and can rapidly exhaust
44
- * the daily API quota when a rule has no throttle.
45
- *
46
- * Conditional events like `motion.detected` are intentionally excluded —
47
- * they fire discretely, not at continuous high frequency. Extend this set
48
- * when new catch-all or near-continuous event types are added to the classifier.
49
- */
50
- export declare const HIGH_FREQ_EVENTS: readonly string[];
51
- /** Returns true when an MQTT event is known to fire at high frequency. */
52
- export declare function isHighFreqEvent(event: string): boolean;
53
- export interface QuietHours {
54
- start: string;
55
- end: string;
56
- }
57
- export declare function analyzeConflicts(rules: Rule[], quietHours?: QuietHours | null): ConflictReport;
@@ -1,215 +0,0 @@
1
- /**
2
- * Static conflict analysis for automation rules.
3
- *
4
- * Detects patterns that are technically valid but likely to cause
5
- * operational problems:
6
- *
7
- * 1. Opposing-action pairs — same device, opposite commands (e.g.
8
- * turnOn / turnOff), triggered by the same source within a short
9
- * window and with no throttle on either rule.
10
- *
11
- * 2. High-frequency MQTT rules without throttle — rules that listen
12
- * on `device.shadow` (catch-all) with no throttle can fire on
13
- * every shadow push (up to once per second) and exhaust the daily
14
- * API quota quickly.
15
- *
16
- * 3. Potentially-destructive action without quiet-hours protection —
17
- * a rule that targets a destructive verb is technically blocked by
18
- * the engine, but we can still flag it early so users don't get a
19
- * surprise at runtime.
20
- *
21
- * Results are designed to be consumed by `rules doctor --json` and
22
- * by CI pipelines. Each finding carries a `severity` so callers can
23
- * decide how to gate on them.
24
- */
25
- import { isTimeBetween, isAllCondition, isAnyCondition, isNotCondition } from './types.js';
26
- import { parseMaxPerMs } from './throttle.js';
27
- import { isDestructiveCommand } from './destructive.js';
28
- import { extractDeviceIdFromAction } from './action.js';
29
- /** Known opposing command pairs (order-independent). */
30
- const OPPOSING_PAIRS = [
31
- ['turnOn', 'turnOff'],
32
- ['lock', 'unlock'],
33
- ['open', 'close'],
34
- ['openDoor', 'closeDoor'],
35
- ['openCurtain', 'closeCurtain'],
36
- ['turnOn', 'standby'],
37
- ['brightnessUp', 'brightnessDown'],
38
- ['volumeUp', 'volumeDown'],
39
- ['fanSpeedUp', 'fanSpeedDown'],
40
- ];
41
- /**
42
- * MQTT events that fire on every device state push and can rapidly exhaust
43
- * the daily API quota when a rule has no throttle.
44
- *
45
- * Conditional events like `motion.detected` are intentionally excluded —
46
- * they fire discretely, not at continuous high frequency. Extend this set
47
- * when new catch-all or near-continuous event types are added to the classifier.
48
- */
49
- export const HIGH_FREQ_EVENTS = ['device.shadow', '*'];
50
- /** Returns true when an MQTT event is known to fire at high frequency. */
51
- export function isHighFreqEvent(event) {
52
- return HIGH_FREQ_EVENTS.includes(event);
53
- }
54
- function commandsAreOpposing(a, b) {
55
- for (const [x, y] of OPPOSING_PAIRS) {
56
- if ((a === x && b === y) || (a === y && b === x))
57
- return true;
58
- }
59
- return false;
60
- }
61
- function extractCommandVerb(command) {
62
- // command strings are like "devices command <id> turnOn" — extract last token
63
- const parts = command.trim().split(/\s+/);
64
- return parts[parts.length - 1] ?? command;
65
- }
66
- function effectiveCooldownMs(rule) {
67
- if (rule.cooldown) {
68
- try {
69
- return parseMaxPerMs(rule.cooldown);
70
- }
71
- catch {
72
- return null;
73
- }
74
- }
75
- if (rule.throttle?.max_per) {
76
- try {
77
- return parseMaxPerMs(rule.throttle.max_per);
78
- }
79
- catch {
80
- return null;
81
- }
82
- }
83
- return null;
84
- }
85
- function hasTimeBetweenGuard(conditions) {
86
- if (!conditions)
87
- return false;
88
- for (const c of conditions) {
89
- if (isTimeBetween(c))
90
- return true;
91
- if (isAllCondition(c) && hasTimeBetweenGuard(c.all))
92
- return true;
93
- if (isAnyCondition(c) && hasTimeBetweenGuard(c.any))
94
- return true;
95
- if (isNotCondition(c) && hasTimeBetweenGuard([c.not]))
96
- return true;
97
- }
98
- return false;
99
- }
100
- export function analyzeConflicts(rules, quietHours) {
101
- const findings = [];
102
- const active = rules.filter((r) => r.enabled !== false);
103
- // 1. Opposing-action pairs on the same device
104
- for (let i = 0; i < active.length; i++) {
105
- for (let j = i + 1; j < active.length; j++) {
106
- const a = active[i];
107
- const b = active[j];
108
- // Only flag when they share the same trigger source (otherwise they
109
- // can't race each other in normal operation).
110
- if (a.when.source !== b.when.source)
111
- continue;
112
- const cooldownA = effectiveCooldownMs(a);
113
- const cooldownB = effectiveCooldownMs(b);
114
- // If both rules have meaningful cooldowns (≥ 5 minutes), the risk is
115
- // low — skip.
116
- const bothThrottled = cooldownA !== null && cooldownA >= 5 * 60_000 &&
117
- cooldownB !== null && cooldownB >= 5 * 60_000;
118
- if (bothThrottled)
119
- continue;
120
- for (const actionA of a.then) {
121
- for (const actionB of b.then) {
122
- const deviceA = extractDeviceIdFromAction(actionA);
123
- const deviceB = extractDeviceIdFromAction(actionB);
124
- // Skip if devices can't be compared.
125
- if (!deviceA || !deviceB || deviceA !== deviceB)
126
- continue;
127
- const verbA = extractCommandVerb(actionA.command);
128
- const verbB = extractCommandVerb(actionB.command);
129
- if (commandsAreOpposing(verbA, verbB)) {
130
- const noThrottle = cooldownA === null || cooldownB === null;
131
- findings.push({
132
- severity: noThrottle ? 'warning' : 'info',
133
- code: 'opposing-actions',
134
- message: `Rules "${a.name}" and "${b.name}" issue opposing commands (${verbA} / ${verbB}) on device "${deviceA}" via the same trigger source.`,
135
- rules: [a.name, b.name],
136
- hint: noThrottle
137
- ? 'Add a "cooldown" or "throttle.max_per" to both rules to prevent rapid state oscillation.'
138
- : undefined,
139
- });
140
- }
141
- }
142
- }
143
- }
144
- }
145
- // 2. High-frequency MQTT catch-all rules without throttle
146
- for (const rule of active) {
147
- if (rule.when.source !== 'mqtt')
148
- continue;
149
- const event = rule.when.event;
150
- const isHighFreq = isHighFreqEvent(event);
151
- if (!isHighFreq)
152
- continue;
153
- const cooldown = effectiveCooldownMs(rule);
154
- if (cooldown === null) {
155
- findings.push({
156
- severity: 'warning',
157
- code: 'high-frequency-no-throttle',
158
- message: `Rule "${rule.name}" listens on "${event}" (high-frequency catch-all) with no throttle/cooldown. This can rapidly exhaust the daily API quota.`,
159
- rules: [rule.name],
160
- hint: 'Add "cooldown: 1m" or "throttle: { max_per: 1m }" to rate-limit this rule.',
161
- });
162
- }
163
- else if (cooldown < 30_000) {
164
- findings.push({
165
- severity: 'info',
166
- code: 'high-frequency-low-throttle',
167
- message: `Rule "${rule.name}" listens on "${event}" with a throttle under 30 s. Consider increasing to at least 1 m to protect API quota.`,
168
- rules: [rule.name],
169
- });
170
- }
171
- }
172
- // 3. Destructive actions in rules (engine blocks these at runtime, but
173
- // surface early with clear guidance).
174
- for (const rule of active) {
175
- for (let i = 0; i < rule.then.length; i++) {
176
- const verb = extractCommandVerb(rule.then[i].command);
177
- if (isDestructiveCommand(verb)) {
178
- findings.push({
179
- severity: 'error',
180
- code: 'destructive-action-in-rule',
181
- message: `Rule "${rule.name}" then[${i}] contains destructive command "${verb}". The engine blocks this at runtime.`,
182
- rules: [rule.name],
183
- hint: 'Remove the destructive command or replace it with a non-destructive alternative.',
184
- });
185
- }
186
- }
187
- }
188
- // 4. Event-driven rules with no time_between guard when quiet_hours is defined.
189
- // Cron rules fire on an explicit schedule so their overlap with quiet hours
190
- // requires schedule analysis — flag those separately when needed.
191
- if (quietHours?.start && quietHours?.end) {
192
- for (const rule of active) {
193
- if (rule.when.source === 'cron')
194
- continue;
195
- if (!hasTimeBetweenGuard(rule.conditions)) {
196
- findings.push({
197
- severity: 'warning',
198
- code: 'no-quiet-hours-guard',
199
- message: `Rule "${rule.name}" (${rule.when.source} trigger) has no time_between condition and may fire during quiet hours (${quietHours.start}–${quietHours.end}).`,
200
- rules: [rule.name],
201
- hint: `Add a conditions entry "{ time_between: ['${quietHours.end}', '${quietHours.start}'] }" to block this rule during quiet hours.`,
202
- });
203
- }
204
- }
205
- }
206
- const counts = { error: 0, warning: 0, info: 0 };
207
- for (const f of findings)
208
- counts[f.severity]++;
209
- return {
210
- findings,
211
- counts,
212
- clean: counts.error === 0,
213
- };
214
- }
215
- //# sourceMappingURL=conflict-analyzer.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"conflict-analyzer.js","sourceRoot":"","sources":["../../src/rules/conflict-analyzer.ts"],"names":[],"mappings":"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"}
@@ -1,62 +0,0 @@
1
- /**
2
- * Cron trigger scheduler for the rules engine.
3
- *
4
- * Each cron rule gets its own scheduler entry. On every tick the
5
- * scheduler synthesises an `EngineEvent` with `source: 'cron'` and hands
6
- * it to the same dispatch path the MQTT pipeline uses, so conditions,
7
- * throttle, and action execution behave identically regardless of
8
- * trigger source.
9
- *
10
- * Tests can drive the scheduler deterministically via `fireNowForTest()`
11
- * — the scheduler's internal timer still uses `setTimeout`, which means
12
- * `vi.useFakeTimers()` plus `vi.advanceTimersByTime()` also work. Croner
13
- * is used only for `nextRun(fromDate)` calculations; we own the
14
- * timer/dispatch loop so the engine can drain events through a single
15
- * serialised queue.
16
- */
17
- import type { EngineEvent, Rule, DayOfWeek } from './types.js';
18
- /** Return true if `t` falls on one of the listed days (or days is absent/empty). */
19
- export declare function matchesDayFilter(days: DayOfWeek[] | undefined, t: Date): boolean;
20
- export interface CronDispatch {
21
- (rule: Rule, event: EngineEvent): Promise<void>;
22
- }
23
- export interface CronSchedulerOptions {
24
- /** Dispatch callback — the engine's queue wrapper that runs the rule. */
25
- dispatch: CronDispatch;
26
- /** Clock injection for tests; defaults to Date.now. */
27
- now?: () => Date;
28
- }
29
- export declare class CronScheduler {
30
- private readonly opts;
31
- private readonly entries;
32
- private started;
33
- private stopped;
34
- constructor(opts: CronSchedulerOptions);
35
- getScheduledFor(ruleName: string): {
36
- schedule: string;
37
- nextAt: Date | null;
38
- } | null;
39
- hasRegistered(ruleName: string): boolean;
40
- /**
41
- * Register a cron rule. Validates the pattern eagerly — an invalid
42
- * schedule throws synchronously so engine start can surface the error.
43
- */
44
- register(rule: Rule): void;
45
- unregister(ruleName: string): void;
46
- start(): void;
47
- stop(): void;
48
- /**
49
- * Test helper — compute the pattern's next run after a reference
50
- * timestamp without actually scheduling it. Handy for regression tests.
51
- */
52
- nextRunAfter(ruleName: string, after: Date): Date | null;
53
- /**
54
- * Test helper — fire a rule immediately, bypassing the timer. Used by
55
- * unit tests to skip vi.advanceTimersByTime logic when the focus is on
56
- * dispatch behaviour, not scheduling accuracy.
57
- */
58
- fireNowForTest(ruleName: string): Promise<void>;
59
- private nowDate;
60
- private arm;
61
- private fire;
62
- }
@@ -1,187 +0,0 @@
1
- /**
2
- * Cron trigger scheduler for the rules engine.
3
- *
4
- * Each cron rule gets its own scheduler entry. On every tick the
5
- * scheduler synthesises an `EngineEvent` with `source: 'cron'` and hands
6
- * it to the same dispatch path the MQTT pipeline uses, so conditions,
7
- * throttle, and action execution behave identically regardless of
8
- * trigger source.
9
- *
10
- * Tests can drive the scheduler deterministically via `fireNowForTest()`
11
- * — the scheduler's internal timer still uses `setTimeout`, which means
12
- * `vi.useFakeTimers()` plus `vi.advanceTimersByTime()` also work. Croner
13
- * is used only for `nextRun(fromDate)` calculations; we own the
14
- * timer/dispatch loop so the engine can drain events through a single
15
- * serialised queue.
16
- */
17
- import { Cron } from 'croner';
18
- /** Maps JS getDay() (0=Sun) to 3-letter abbreviation. */
19
- const JS_DAY_TO_ABBR = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'];
20
- /** Expand a days[] entry to its canonical 3-letter abbr so comparisons are O(1). */
21
- function normaliseDay(d) {
22
- return d.toLowerCase().slice(0, 3);
23
- }
24
- /** Return true if `t` falls on one of the listed days (or days is absent/empty). */
25
- export function matchesDayFilter(days, t) {
26
- if (!days || days.length === 0)
27
- return true;
28
- const todayAbbr = JS_DAY_TO_ABBR[t.getDay()];
29
- return days.some((d) => normaliseDay(d) === todayAbbr);
30
- }
31
- export class CronScheduler {
32
- opts;
33
- entries = new Map();
34
- started = false;
35
- stopped = false;
36
- constructor(opts) {
37
- this.opts = opts;
38
- }
39
- getScheduledFor(ruleName) {
40
- const s = this.entries.get(ruleName);
41
- if (!s)
42
- return null;
43
- return { schedule: s.schedule, nextAt: s.nextAt };
44
- }
45
- hasRegistered(ruleName) {
46
- return this.entries.has(ruleName);
47
- }
48
- /**
49
- * Register a cron rule. Validates the pattern eagerly — an invalid
50
- * schedule throws synchronously so engine start can surface the error.
51
- */
52
- register(rule) {
53
- if (rule.when.source !== 'cron') {
54
- throw new Error(`CronScheduler.register called for non-cron rule "${rule.name}"`);
55
- }
56
- if (this.entries.has(rule.name)) {
57
- throw new Error(`CronScheduler: duplicate rule name "${rule.name}"`);
58
- }
59
- const schedule = rule.when.schedule;
60
- let pattern;
61
- try {
62
- pattern = new Cron(schedule, { paused: true });
63
- }
64
- catch (err) {
65
- throw new Error(`CronScheduler: invalid cron expression for rule "${rule.name}": ${schedule} (${err instanceof Error ? err.message : String(err)})`);
66
- }
67
- const entry = {
68
- rule,
69
- schedule,
70
- pattern,
71
- timer: null,
72
- nextAt: null,
73
- };
74
- this.entries.set(rule.name, entry);
75
- if (this.started && !this.stopped)
76
- this.arm(entry);
77
- }
78
- unregister(ruleName) {
79
- const e = this.entries.get(ruleName);
80
- if (!e)
81
- return;
82
- if (e.timer)
83
- clearTimeout(e.timer);
84
- try {
85
- e.pattern.stop();
86
- }
87
- catch {
88
- // croner throws when already stopped — ignore.
89
- }
90
- this.entries.delete(ruleName);
91
- }
92
- start() {
93
- if (this.stopped) {
94
- throw new Error('CronScheduler: cannot start after stop().');
95
- }
96
- if (this.started)
97
- return;
98
- this.started = true;
99
- for (const entry of this.entries.values())
100
- this.arm(entry);
101
- }
102
- stop() {
103
- if (this.stopped)
104
- return;
105
- this.stopped = true;
106
- this.started = false;
107
- for (const e of this.entries.values()) {
108
- if (e.timer)
109
- clearTimeout(e.timer);
110
- e.timer = null;
111
- try {
112
- e.pattern.stop();
113
- }
114
- catch {
115
- // ignore
116
- }
117
- }
118
- }
119
- /**
120
- * Test helper — compute the pattern's next run after a reference
121
- * timestamp without actually scheduling it. Handy for regression tests.
122
- */
123
- nextRunAfter(ruleName, after) {
124
- const e = this.entries.get(ruleName);
125
- if (!e)
126
- return null;
127
- return e.pattern.nextRun(after) ?? null;
128
- }
129
- /**
130
- * Test helper — fire a rule immediately, bypassing the timer. Used by
131
- * unit tests to skip vi.advanceTimersByTime logic when the focus is on
132
- * dispatch behaviour, not scheduling accuracy.
133
- */
134
- async fireNowForTest(ruleName) {
135
- const e = this.entries.get(ruleName);
136
- if (!e)
137
- throw new Error(`CronScheduler.fireNowForTest: no rule "${ruleName}"`);
138
- await this.fire(e);
139
- }
140
- nowDate() {
141
- return this.opts.now ? this.opts.now() : new Date();
142
- }
143
- arm(entry) {
144
- if (this.stopped)
145
- return;
146
- const now = this.nowDate();
147
- const next = entry.pattern.nextRun(now);
148
- if (!next) {
149
- entry.nextAt = null;
150
- return;
151
- }
152
- entry.nextAt = next;
153
- const delayMs = Math.max(0, next.getTime() - now.getTime());
154
- entry.timer = setTimeout(() => {
155
- entry.timer = null;
156
- // Fire and then re-arm, regardless of outcome — we never want one
157
- // misbehaving rule to kill its own future ticks.
158
- this.fire(entry)
159
- .catch(() => undefined)
160
- .finally(() => {
161
- if (!this.stopped && this.entries.has(entry.rule.name))
162
- this.arm(entry);
163
- });
164
- }, delayMs);
165
- // Unref so a process with only cron rules still exits on SIGINT when
166
- // the user expects (e.g. in integration tests).
167
- if (typeof entry.timer.unref === 'function') {
168
- entry.timer.unref();
169
- }
170
- }
171
- async fire(entry) {
172
- const when = this.nowDate();
173
- // Apply the optional day-of-week filter before dispatching.
174
- const trigger = entry.rule.when;
175
- if (trigger.source === 'cron' && !matchesDayFilter(trigger.days, when)) {
176
- return;
177
- }
178
- const event = {
179
- source: 'cron',
180
- event: entry.schedule,
181
- t: when,
182
- payload: { schedule: entry.schedule },
183
- };
184
- await this.opts.dispatch(entry.rule, event);
185
- }
186
- }
187
- //# sourceMappingURL=cron-scheduler.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"cron-scheduler.js","sourceRoot":"","sources":["../../src/rules/cron-scheduler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAG9B,yDAAyD;AACzD,MAAM,cAAc,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAU,CAAC;AAElF,oFAAoF;AACpF,SAAS,YAAY,CAAC,CAAY;IAChC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AACrC,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,gBAAgB,CAAC,IAA6B,EAAE,CAAO;IACrE,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAC5C,MAAM,SAAS,GAAG,cAAc,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IAC7C,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC;AACzD,CAAC;AAqBD,MAAM,OAAO,aAAa;IACP,IAAI,CAAuB;IAC3B,OAAO,GAAG,IAAI,GAAG,EAAqB,CAAC;IAChD,OAAO,GAAG,KAAK,CAAC;IAChB,OAAO,GAAG,KAAK,CAAC;IAExB,YAAY,IAA0B;QACpC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAED,eAAe,CAAC,QAAgB;QAC9B,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACrC,IAAI,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;QACpB,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC;IACpD,CAAC;IAED,aAAa,CAAC,QAAgB;QAC5B,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACpC,CAAC;IAED;;;OAGG;IACH,QAAQ,CAAC,IAAU;QACjB,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;YAChC,MAAM,IAAI,KAAK,CAAC,oDAAoD,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC;QACpF,CAAC;QACD,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAChC,MAAM,IAAI,KAAK,CAAC,uCAAuC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC;QACvE,CAAC;QACD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC;QACpC,IAAI,OAAa,CAAC;QAClB,IAAI,CAAC;YACH,OAAO,GAAG,IAAI,IAAI,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;QACjD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CACb,oDAAoD,IAAI,CAAC,IAAI,MAAM,QAAQ,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CACpI,CAAC;QACJ,CAAC;QACD,MAAM,KAAK,GAAc;YACvB,IAAI;YACJ,QAAQ;YACR,OAAO;YACP,KAAK,EAAE,IAAI;YACX,MAAM,EAAE,IAAI;SACb,CAAC;QACF,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACnC,IAAI,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACrD,CAAC;IAED,UAAU,CAAC,QAAgB;QACzB,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACrC,IAAI,CAAC,CAAC;YAAE,OAAO;QACf,IAAI,CAAC,CAAC,KAAK;YAAE,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QACnC,IAAI,CAAC;YACH,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;QACnB,CAAC;QAAC,MAAM,CAAC;YACP,+CAA+C;QACjD,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAChC,CAAC;IAED,KAAK;QACH,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;QAC/D,CAAC;QACD,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO;QACzB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE;YAAE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC7D,CAAC;IAED,IAAI;QACF,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO;QACzB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;QACrB,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;YACtC,IAAI,CAAC,CAAC,KAAK;gBAAE,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;YACnC,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC;YACf,IAAI,CAAC;gBACH,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;YACnB,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,YAAY,CAAC,QAAgB,EAAE,KAAW;QACxC,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACrC,IAAI,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;QACpB,OAAO,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC;IAC1C,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,cAAc,CAAC,QAAgB;QACnC,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACrC,IAAI,CAAC,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,0CAA0C,QAAQ,GAAG,CAAC,CAAC;QAC/E,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACrB,CAAC;IAEO,OAAO;QACb,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;IACtD,CAAC;IAEO,GAAG,CAAC,KAAgB;QAC1B,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO;QACzB,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;QAC3B,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACxC,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC;YACpB,OAAO;QACT,CAAC;QACD,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC;QACpB,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QAC5D,KAAK,CAAC,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC;YACnB,kEAAkE;YAClE,iDAAiD;YACjD,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;iBACb,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC;iBACtB,OAAO,CAAC,GAAG,EAAE;gBACZ,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;oBAAE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YAC1E,CAAC,CAAC,CAAC;QACP,CAAC,EAAE,OAAO,CAAC,CAAC;QACZ,qEAAqE;QACrE,gDAAgD;QAChD,IAAI,OAAQ,KAAK,CAAC,KAA2C,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;YAClF,KAAK,CAAC,KAA0C,CAAC,KAAK,EAAE,CAAC;QAC5D,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,IAAI,CAAC,KAAgB;QACjC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;QAC5B,4DAA4D;QAC5D,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;QAChC,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;YACvE,OAAO;QACT,CAAC;QACD,MAAM,KAAK,GAAgB;YACzB,MAAM,EAAE,MAAM;YACd,KAAK,EAAE,KAAK,CAAC,QAAQ;YACrB,CAAC,EAAE,IAAI;YACP,OAAO,EAAE,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE;SACtC,CAAC;QACF,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC9C,CAAC;CACF"}
@@ -1,20 +0,0 @@
1
- /**
2
- * Destructive command parsing — single source of truth shared between the
3
- * policy validator post-hook (rejects destructive commands inside
4
- * `automation.rules[].then[].command`) and the runtime executor (second-
5
- * line guard that refuses to shell out even if validation was bypassed).
6
- */
7
- export declare const DESTRUCTIVE_COMMANDS: readonly ["lock", "unlock", "deleteWebhook", "deleteScene", "factoryReset"];
8
- export type DestructiveCommand = (typeof DESTRUCTIVE_COMMANDS)[number];
9
- /**
10
- * Parse the verb out of a rule action command string. The expected form
11
- * mirrors what the engine will eventually build: `devices command <id> <verb> [args...]`.
12
- * We also accept scene shorthands (`scenes run <id>`, `webhooks delete <id>`).
13
- *
14
- * Returns null for anything we cannot confidently attribute to a known verb
15
- * slot — the validator treats null as "probably fine, let the engine's own
16
- * guard handle it if it's not."
17
- */
18
- export declare function extractVerb(cmd: string): string | null;
19
- export declare function isDestructiveCommand(cmd: string): boolean;
20
- export declare function destructiveVerbOf(cmd: string): DestructiveCommand | null;