@switchbot/openapi-cli 3.3.2 โ†’ 3.4.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.
package/README.md CHANGED
@@ -93,7 +93,7 @@ Under the hood every surface shares the same catalog, cache, and HMAC client โ€”
93
93
  - ๐ŸŽจ **Dual output modes** โ€” colorized tables by default; `--json` passthrough for `jq` and scripting
94
94
  - ๐Ÿ” **Secure credentials** โ€” HMAC-SHA256 signed requests; config file written with `0600`; env-var override for CI
95
95
  - ๐Ÿ” **Dry-run mode** โ€” preview every mutating request before it hits the API
96
- - ๐Ÿงช **Fully tested** โ€” 1959 Vitest tests, mocked axios, zero network in CI
96
+ - ๐Ÿงช **Fully tested** โ€” 2204 Vitest tests, mocked axios, zero network in CI
97
97
  - โšก **Shell completion** โ€” Bash / Zsh / Fish / PowerShell
98
98
 
99
99
  ## Requirements
@@ -273,11 +273,58 @@ Five annotated starter files covering common setups live in
273
273
  With a policy.yaml (v0.2) you can declare automations that the CLI
274
274
  executes for you. Supported triggers: **MQTT** (device events),
275
275
  **cron** (schedule-driven), and **webhook** (local HTTP POST).
276
- Supported conditions: `time_between` (quiet hours) and `device_state`
277
- (live API check with per-tick dedup). Every fire is recorded in
278
- `~/.switchbot/audit.log`. `rules run` is long-running; use
276
+ Supported conditions: `time_between` (quiet hours), `device_state`
277
+ (live API check with per-tick dedup), and `llm` (AI decision โ€” see
278
+ below). Every fire is recorded in `~/.switchbot/audit.log`. `rules run` is long-running; use
279
279
  `daemon start` / `daemon reload` for the managed background mode.
280
280
 
281
+ **Actions** โ€” each rule's `then` array accepts two action types:
282
+
283
+ - `type: command` (default, no `type` field required) โ€” sends a device command, e.g. `devices command <id> turnOn`
284
+ - `type: notify` โ€” delivers a payload to an external channel after the rule fires:
285
+ - `channel: webhook` โ€” HTTP POST to a URL (only `http://` and `https://` schemes are accepted; `rules lint` rejects others)
286
+ - `channel: file` โ€” appends a JSONL line to a local file. `to` must be an absolute path; relative or `~`-prefixed paths are rejected by `rules lint` (code `notify-relative-path`) and at runtime
287
+ - `channel: openclaw` โ€” HTTP POST to an OpenClaw endpoint (same protocol restriction)
288
+ - Optional `template` field supports `{{ rule.name }}`, `{{ event.* }}`, `{{ device.id }}` placeholders. Nested fields use dot paths, e.g. `{{ event.context.deviceMac }}`; arrays index numerically, e.g. `{{ event.list.0 }}`
289
+
290
+ ```yaml
291
+ then:
292
+ - command: devices command AC_001 turnOn
293
+ - type: notify
294
+ channel: webhook
295
+ to: https://your.host/hook
296
+ template: '{"rule":"{{ rule.name }}","fired":"{{ rule.fired_at }}"}'
297
+ ```
298
+
299
+ **LLM condition** โ€” add an AI judgement step before actions fire. The engine calls the
300
+ configured LLM provider, passes the prompt plus recent event context, and gates execution
301
+ on the model's yes/no answer:
302
+
303
+ ```yaml
304
+ conditions:
305
+ - llm:
306
+ prompt: "Is the temperature above normal comfort range?"
307
+ provider: auto # auto | openai | anthropic
308
+ cache_ttl: 5m # skip redundant calls for identical context
309
+ budget:
310
+ max_calls_per_hour: 20
311
+ on_error: pass # fail | pass | skip
312
+ ```
313
+
314
+ Set `OPENAI_API_KEY` or `ANTHROPIC_API_KEY` (provider `auto` tries Anthropic first).
315
+ `rules lint` flags misconfigured LLM conditions (no provider key, cache TTL too high for
316
+ the trigger frequency, budget zero). Evaluation decisions are recorded in the trace log.
317
+
318
+ **Decision trace** โ€” enable `automation.audit.evaluate_trace` in `policy.yaml` to record
319
+ every evaluation decision (why a rule fired or was blocked):
320
+
321
+ ```yaml
322
+ automation:
323
+ audit:
324
+ evaluate_trace: sampled # full | sampled | off (default: sampled)
325
+ evaluate_retention_days: 7
326
+ ```
327
+
281
328
  ```bash
282
329
  # 1. Author rules under `automation.rules`. See examples/policies/automation.yaml
283
330
  # for a walkthrough covering the three trigger sources.
@@ -308,8 +355,39 @@ switchbot rules last-fired -n 20 # 20 most recent fire entries
308
355
  switchbot rules conflicts # opposing actions, high-frequency MQTT,
309
356
  # destructive commands, quiet-hours gaps
310
357
  switchbot rules doctor --json # lint + conflicts combined; exit 0 when clean
358
+
359
+ # 8. Scaffold a new rule from natural language (heuristic or LLM-backed).
360
+ switchbot rules suggest --intent "turn off AC at 11pm"
361
+ switchbot rules suggest --intent "if door opens and temp below 20 turn on heater" \
362
+ --llm auto # routes complex intents to LLM automatically
363
+ switchbot rules suggest --intent "..." --llm openai # explicit backend
364
+ # Set OPENAI_API_KEY or ANTHROPIC_API_KEY; auto mode falls back to heuristic on failure
365
+
366
+ # 9. Explain why a specific evaluation fired or was blocked (requires evaluate_trace).
367
+ switchbot rules trace-explain --rule "motion on" --last
368
+ switchbot rules trace-explain --rule "motion on" --since 1h --json
369
+ switchbot rules trace-explain <fireId> # single evaluation by ID
370
+
371
+ # 10. Simulate a rule against historical events without running the engine.
372
+ switchbot rules simulate "motion on" # replay last 24h from audit log
373
+ switchbot rules simulate "motion on" --since 7d --json
374
+ switchbot rules simulate policy.yaml --rule "night AC" --against events.jsonl
311
375
  ```
312
376
 
377
+ `rules suggest` enforces several guardrails on LLM output so a model can't quietly arm
378
+ something unsafe:
379
+
380
+ - **`dry_run` is forced to `true`** on every LLM-generated rule. Review the output and
381
+ flip it yourself before running the engine without `--dry-run`.
382
+ - **Explicit overrides always win.** If you pass `--trigger`, the LLM's answer must match;
383
+ a mismatch fails fast. Within the same trigger, mismatched `--event` / `--schedule` /
384
+ `--days` / `--webhook-path` are rewritten to your value with a warning.
385
+ - **`--llm` is enum-validated at the CLI** (`auto | openai | anthropic`) โ€” junk values
386
+ exit non-zero instead of falling through.
387
+ - **Notify URLs must be `http://` or `https://`.** `rules lint` and the runtime both
388
+ reject `file://`, `ftp://`, etc., so a generated webhook can't smuggle in a non-HTTP
389
+ scheme.
390
+
313
391
  When `quiet_hours` is configured in `policy.yaml`, `rules conflicts` additionally flags event-driven (MQTT / webhook) rules that lack a `time_between` condition โ€” they would fire uninhibited during the quiet window. The hint in each finding includes a ready-to-paste `time_between` condition to add.
314
392
 
315
393
  Webhook trigger token management:
@@ -842,8 +920,16 @@ Exposes MCP tools (`list_devices`, `describe_device`, `get_device_status`,
842
920
  `send_command`, `list_scenes`, `run_scene`, `search_catalog`,
843
921
  `account_overview`, `plan_suggest`, `plan_run`, `audit_query`,
844
922
  `audit_stats`, `policy_diff`, `policy_validate`, `policy_new`,
845
- `policy_migrate`) plus a `switchbot://events` resource for real-time
846
- shadow updates.
923
+ `policy_migrate`, `rules_suggest`, `rule_notifications`,
924
+ `rules_explain`, `rules_simulate`) plus a
925
+ `switchbot://events` resource for real-time shadow updates.
926
+ `rules_suggest` accepts an optional `llm` parameter (`openai | anthropic | auto`)
927
+ to generate YAML for complex intents via an LLM backend.
928
+ `rule_notifications` returns `rule-notify` audit entries, filterable by rule
929
+ name, time range, channel, and result.
930
+ `rules_explain` returns the decision trace for a specific evaluation (why a rule
931
+ fired or was blocked); `rules_simulate` replays historical events against a rule
932
+ and reports would-fire / blocked / throttled outcomes.
847
933
  See [`docs/agent-guide.md`](./docs/agent-guide.md) for the full tool reference and safety rules (destructive-command guard).
848
934
 
849
935
  ### `doctor` โ€” self-check
@@ -853,7 +939,7 @@ switchbot doctor
853
939
  switchbot doctor --json
854
940
  ```
855
941
 
856
- Runs local checks (Node version, credentials, profiles, catalog, cache, quota, clock, MQTT, policy, MCP) and exits 1 if any check fails. `warn` results exit 0. The MQTT check reports `ok` when REST credentials are configured (auto-provisioned on first use). Use this to diagnose connectivity or config issues before running automation.
942
+ Runs local checks (Node version, credentials, profiles, catalog, cache, quota, clock, MQTT, policy, MCP, notify-connectivity) and exits 1 if any check fails. `warn` results exit 0. The MQTT check reports `ok` when REST credentials are configured (auto-provisioned on first use). The `notify-connectivity` check probes webhook URLs declared in `type: notify` actions. Use this to diagnose connectivity or config issues before running automation.
857
943
 
858
944
  `--json` output includes `maturityScore` (0โ€“100) and `maturityLabel` (`production-ready` / `mostly-ready` / `needs-work` / `not-ready`) to give an at-a-glance readiness rating:
859
945
 
@@ -1123,7 +1209,7 @@ npm install
1123
1209
 
1124
1210
  npm run dev -- <args> # Run from TypeScript sources via tsx
1125
1211
  npm run build # Compile to dist/
1126
- npm test # Run the Vitest suite (1959 tests)
1212
+ npm test # Run the Vitest suite (2204 tests)
1127
1213
  npm run test:watch # Watch mode
1128
1214
  npm run test:coverage # Coverage report (v8, HTML + text)
1129
1215
  ```
@@ -1165,8 +1251,16 @@ src/
1165
1251
  โ”‚ โ”œโ”€โ”€ audit-query.ts # Audit log filtering + aggregation
1166
1252
  โ”‚ โ”œโ”€โ”€ conflict-analyzer.ts # Static conflict detection (opposing actions,
1167
1253
  โ”‚ โ”‚ # high-freq MQTT, destructive cmds, quiet-hours gaps)
1168
- โ”‚ โ”œโ”€โ”€ suggest.ts # Heuristic-based rule YAML generation
1169
- โ”‚ โ””โ”€โ”€ types.ts # Shared rule/trigger/condition/action types
1254
+ โ”‚ โ”œโ”€โ”€ suggest.ts # Heuristic + LLM-backed rule YAML generation
1255
+ โ”‚ โ”œโ”€โ”€ notify.ts # notify action executor (webhook / file / openclaw)
1256
+ โ”‚ โ””โ”€โ”€ types.ts # Shared rule/trigger/condition/action types (CommandAction | NotifyAction)
1257
+ โ”œโ”€โ”€ llm/
1258
+ โ”‚ โ”œโ”€โ”€ index.ts # createLLMProvider factory + LLM_AUTO_THRESHOLD
1259
+ โ”‚ โ”œโ”€โ”€ complexity.ts # Intent complexity scorer (0โ€“10) for auto-routing
1260
+ โ”‚ โ”œโ”€โ”€ rule-prompt.ts # System prompt builder (embeds v0.2 schema snippet)
1261
+ โ”‚ โ””โ”€โ”€ providers/
1262
+ โ”‚ โ”œโ”€โ”€ openai.ts # OpenAI-compatible provider (uses Node.js https)
1263
+ โ”‚ โ””โ”€โ”€ anthropic.ts # Anthropic provider
1170
1264
  โ”œโ”€โ”€ status-sync/
1171
1265
  โ”‚ โ””โ”€โ”€ manager.ts # Spawn/stop logic, state file, OpenClaw bridge
1172
1266
  โ”œโ”€โ”€ lib/
@@ -1181,7 +1275,8 @@ src/
1181
1275
  โ”‚ โ”œโ”€โ”€ install.ts # `switchbot install` / `uninstall`
1182
1276
  โ”‚ โ”œโ”€โ”€ policy.ts # `policy validate/new/migrate/diff/add-rule/backup/restore`
1183
1277
  โ”‚ โ”œโ”€โ”€ rules.ts # `rules suggest/lint/list/explain/run/reload/tail/replay/
1184
- โ”‚ โ”‚ # conflicts/doctor/summary/last-fired/webhook-*`
1278
+ โ”‚ โ”‚ # conflicts/doctor/summary/last-fired/webhook-*/
1279
+ โ”‚ โ”‚ # trace-explain/simulate`
1185
1280
  โ”‚ โ”œโ”€โ”€ scenes.ts
1186
1281
  โ”‚ โ”œโ”€โ”€ health.ts # `health check/serve` โ€” report + HTTP endpoints
1187
1282
  โ”‚ โ”œโ”€โ”€ upgrade-check.ts # `upgrade-check` โ€” npm registry version check
@@ -1205,7 +1300,7 @@ src/
1205
1300
  โ”œโ”€โ”€ format.ts # renderRows / filterFields / output-format dispatch
1206
1301
  โ”œโ”€โ”€ audit.ts # JSONL audit log writer
1207
1302
  โ””โ”€โ”€ quota.ts # Local daily-quota counter
1208
- tests/ # Vitest suite (1959 tests, mocked axios, no network)
1303
+ tests/ # Vitest suite (2204 tests, mocked axios, no network)
1209
1304
  ```
1210
1305
 
1211
1306
  ### Release flow