feat: 2.5.0 — history aggregate + v2.4.0 report bug fixes#19
Merged
chenliuyun merged 25 commits intomainfrom Apr 20, 2026
Merged
feat: 2.5.0 — history aggregate + v2.4.0 report bug fixes#19chenliuyun merged 25 commits intomainfrom
chenliuyun merged 25 commits intomainfrom
Conversation
added 5 commits
April 20, 2026 14:58
Design for 2.5.0 on-demand bucketed aggregation over existing JSONL storage. Additive only: new `history aggregate` CLI subcommand, new `aggregate_device_history` MCP tool, backed by a pure async function that reuses history-query.ts streaming primitives. Scope: count/min/max/avg/sum/p50/p95 per (bucket × metric), per-device only. Explicit non-goals: trend helpers, cross-device aggregation, storage migration, streaming subscriptions.
added 9 commits
April 20, 2026 16:14
The test now uses a record with a timestamp inside the 5m query window but file mtime outside it. Before the fix, the record was 3 days old, so it failed the per-record timestamp filter even without mtime pruning. The test now correctly verifies the mtime prune is necessary. Verified by temporarily disabling mtime check in history-agg.ts; test fails with count===2 (both files' records included) when prune is disabled.
Register aggregate_device_history MCP tool delegating to aggregateDeviceHistory from history-agg.ts; uses z.object().strict() to reject unknown input keys; annotated with _meta.agentSafetyTier='read'. Bumps tool count test to 11.
…story Add 'history aggregate' to COMMAND_META as a read-tier leaf (80ms latency). Append 'aggregate_device_history' to MCP_TOOLS array. Register history.aggregate subcommand in test program.
added 11 commits
April 20, 2026 17:14
… fix for bug #4) Convert all 10 plain-object inputSchemas to z.object({...}).strict() so unknown keys are rejected with JSON-RPC -32602, closing the silent-passthrough hole that allowed dryRun:true (or any unknown key) to reach the Smart Lock / Garage Door live API calls. Add dryRun:boolean?.optional() to send_command and run_scene; when true, returns { ok:true, dryRun:true, wouldSend:{...} } without any API call.
MCP initialize response now reports accurate serverInfo.version from package.json instead of hardcoded '2.0.0'. This fixes misleading version to MCP clients that gate capabilities on reported version. Updated four references in mcp.ts (initialize response, account_overview, /healthz, /ready endpoints) to use centralized VERSION constant imported from version.ts.
The v2.4.0 release notes claimed "MCP tools mirror the tier in
meta.agentSafetyTier" but only aggregate_device_history (added in 2.5.0
work) actually exposed it. This fix adds _meta: { agentSafetyTier: <tier> }
to all other 10 MCP tool registrations, matching their CLI safety tiers
from COMMAND_META:
- list_devices, get_device_status, get_device_history, query_device_history,
list_scenes, search_catalog, describe_device, account_overview: read
- send_command, run_scene: action
Also adds tests/mcp/tool-meta.test.ts to verify every tool has _meta and
spot-check key tiers match expected values.
Fixes bug #6.
…tch (bug #1) Under require-unique, the exact-name short-circuit at line 73 returned immediately on the first exact hit, bypassing the ambiguity check entirely. This meant a query like '空调' on an account with both '空调' (exact) and '卧室空调' (substring) silently resolved to the exact device instead of raising ambiguous_name_match (exit 2). Fix: gate the short-circuit by strategy. For require-unique, the exact hit is pushed as a score-0 candidate and the full candidate list is assembled before the ambiguity check runs. Fuzzy/first/exact/prefix/substring behaviour is unchanged. Tests added (4 new): - exact + substring under require-unique → ambiguous (reporter scenario) - two exact-same-name devices under require-unique → ambiguous - single exact match, no other matches under require-unique → resolves OK - fuzzy still short-circuits on exact match (regression pin)
Adds .alias('status') to the 'cache show' subcommand registration so that
'switchbot cache status' works alongside 'switchbot cache show', matching
the parallel quota command's UX.
…yle (bug #8) v2.4.0 release note claimed '--format markdown' was supported but actual behavior was 'Unknown --format markdown' exit 2. --table-style markdown was already wired; --format markdown now routes through printTable with an explicit style override, independent of --table-style.
) Add fileMissing flag to VerifyReport. When audit.log does not exist (fresh install), return fileMissing=true, exit with 0, and set status to 'warn' in JSON output. Existing behavior preserved for malformed/unversioned content (still exits 1). Tests: 6 new tests added to verify the behavior: fresh account, empty file, malformed entries, and JSON output variants.
Add --no-color as a root-level option to suppress ANSI colors in output. Also respect the NO_COLOR environment variable per https://no-color.org/. Early initialization sets chalk.level=0 when either flag or env is set, ensuring colors are disabled globally before any commands execute.
v2.4.0 release note claimed every mqtt-tail event is appended to device history, but control events (__connect, __reconnect, __disconnect, __heartbeat) have no deviceId and were only emitted to stdout. Route them into a dedicated ~/.switchbot/device-history/__control.jsonl alongside the per-device files, sharing the same 50MB rotation logic. Errors are swallowed — history writes never block the event stream.
C1 (#14): update --idempotency-key / --idempotency-key-prefix help text in devices.ts and batch.ts to mention process-local scope, per-process cache semantics, and that independent CLI invocations do not share cache. C2 (#15): mcp --help "eight tools" → "eleven tools"; list all 11 by name including get_device_history, query_device_history, aggregate_device_history. C3 (#17): add `scenes describe <sceneId>` subcommand. Returns sceneId, sceneName, stepCount:null, and a note explaining v1.1 API limitation. Exits 2 with scene_not_found + candidate list on unknown sceneId. Adds 'scenes describe' to COMMAND_META in capabilities.ts. Adds 2 tests (known + unknown scene). C4 (#13): add JSDoc comment on hints field in agent-bootstrap.ts clarifying empty array semantics. Add 'hints' field to cliAddedFields in schema.ts. C5 (#16): create docs/verbose-redaction.md documenting masked headers (authorization, token, sign, nonce, x-api-key, cookie, set-cookie, x-auth-token, t) and the --trace-unsafe opt-out flag. C6 (#18): plan schema output now includes agentNotes.deviceNameStrategy documenting that deviceName uses require-unique resolution and plans should pin deviceId for determinism.
04419fb to
90f07be
Compare
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR ships switchbot-cli 2.5.0. Two logical halves bundled into one release:
Half 1 — History aggregation feature (design + implementation)
history aggregate <deviceId>CLI subcommand and MCPaggregate_device_historytoolcount / min / max / avg / sum / p50 / p95) streamed over the existing JSONL rotation files — zero storage format change--since/--from/--to, repeatable--metric,--agg <csv>,--bucket <dur>,--max-bucket-samples <n>maxBucketSamples × 8 bytesper(bucket × metric)cell for quantile computationdocs/superpowers/specs/2026-04-20-device-history-aggregation-design.mddocs/superpowers/plans/2026-04-20-device-history-aggregation.mdHalf 2 — v2.4.0 smoke-test report bug fixes
Closes ~15 of the 18 items in the OpenClaw v2.4.0 bug report. Security, correctness, and drift between release-note claims and shipped behavior.
Security & correctness:
send_command/run_scenesilently accepteddryRun:trueand fired anyway. All 11 MCP tools now use Zod.strict()— unknown keys reject with-32602.dryRunis a first-class arg on mutating tools; when set, no API call is made and the response is{ok:true, dryRun:true, wouldSend:{...}}.require-uniquename-resolver strategy was short-circuiting on exact-name match even when substring matches existed. Exact hits now enter the candidate list underrequire-uniqueand go through the ambiguity check.serverInfo.versionwas hardcoded2.0.0; now wired topackage.json#version._meta.agentSafetyTier(release notes already claimed this but no tool actually did).history verifyexits 0 withstatus:warnon fresh installs missingaudit.log(previously exit 1 — user-hostile).events mqtt-tailcontrol events (__connect/__reconnect/__disconnect/__heartbeat) now append to~/.switchbot/device-history/__control.jsonl.Release-note drift fixed:
--format markdownnow accepted as alias for--format table --table-style markdowncache statusnow works as alias forcache show--no-colorflag +NO_COLORenv var honored globallyPolish:
agent-bootstrapdocumentshints: []semantics--idempotency-keyhelp text clarifies process-local scopemcp --helplists all 11 tools (was stale at 8)docs/verbose-redaction.mddocuments 9 masked headersscenes describe <sceneId>subcommandplan schemanow documentsdeviceNameStrategydefaultExplicitly deferred (documented in CHANGELOG):
capabilities --types/--fields/--used; covered byschema exportTest plan
npm run buildcleannpm test— 895 / 895 passing (including the previously-flakyevents.test.ts > rejects oversized bodies with 413)tests/devices/history-agg.test.ts,tests/commands/strict-schemas.test.ts,tests/commands/dry-run.test.ts,tests/mcp/server-version.test.ts,tests/mcp/tool-meta.test.ts,tests/utils/color-flag.test.tshistory.test.ts,name-resolver.test.ts,format.test.ts,cache.test.ts,events.test.ts,scenes.test.tsWhy bundle into one release
External v2.4.0 assessment scored the project 7.8/10, with release-note credibility the weakest dimension. Shipping a separate 2.4.1 would have delayed the security fix (MCP
dryRun) behind 2.5.0 feature work. All bug-fix items are additive or bug-for-bug compatible; zero breaking changes.