@switchbot/openapi-cli 3.2.2 → 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.
- package/README.md +1 -1
- package/dist/index.js +111 -41
- package/package.json +1 -1
package/README.md
CHANGED
package/dist/index.js
CHANGED
|
@@ -28586,10 +28586,25 @@ Examples:
|
|
|
28586
28586
|
if (isJsonMode()) {
|
|
28587
28587
|
printJson(result);
|
|
28588
28588
|
} else {
|
|
28589
|
-
|
|
28590
|
-
`
|
|
28591
|
-
|
|
28592
|
-
|
|
28589
|
+
if (dryRunned.length > 0) {
|
|
28590
|
+
console.log(`
|
|
28591
|
+
Planned (dry-run): ${dryRunned.length} device(s)`);
|
|
28592
|
+
for (const d of dryRunned) console.log(` - ${d.deviceId}`);
|
|
28593
|
+
}
|
|
28594
|
+
if (preSkipped.length > 0) {
|
|
28595
|
+
console.log(`
|
|
28596
|
+
Skipped (offline): ${preSkipped.length} device(s)`);
|
|
28597
|
+
for (const d of preSkipped) console.log(` - ${d.deviceId}`);
|
|
28598
|
+
}
|
|
28599
|
+
const parts = [
|
|
28600
|
+
`${result.summary.ok} ok`,
|
|
28601
|
+
`${result.summary.failed} failed`
|
|
28602
|
+
];
|
|
28603
|
+
if (dryRunned.length > 0) parts.push(`${dryRunned.length} planned`);
|
|
28604
|
+
if (preSkipped.length > 0) parts.push(`${preSkipped.length} skipped_offline`);
|
|
28605
|
+
parts.push(`(${result.summary.durationMs}ms)`);
|
|
28606
|
+
console.log(`
|
|
28607
|
+
Summary: ${parts.join(", ")}`);
|
|
28593
28608
|
}
|
|
28594
28609
|
if (failed.length > 0) process.exit(1);
|
|
28595
28610
|
}
|
|
@@ -28751,7 +28766,7 @@ function sleep2(ms, signal) {
|
|
|
28751
28766
|
});
|
|
28752
28767
|
}
|
|
28753
28768
|
function registerWatchCommand(devices) {
|
|
28754
|
-
devices.command("watch").description("Poll device status on an interval and emit field-level changes (JSONL)").argument("[deviceId...]", "One or more deviceIds to watch (or use --name for one device)").option("--name <query>", "Resolve one device by fuzzy name (combined with any positional IDs)", stringArg("--name")).option(
|
|
28769
|
+
devices.command("watch").description("Poll device status on an interval and emit field-level changes (human table by default; JSONL with --json for agents)").argument("[deviceId...]", "One or more deviceIds to watch (or use --name for one device)").option("--name <query>", "Resolve one device by fuzzy name (combined with any positional IDs)", stringArg("--name")).option(
|
|
28755
28770
|
"--interval <dur>",
|
|
28756
28771
|
`Polling interval: "30s", "1m", "500ms", ... (default 30s, min ${MIN_INTERVAL_MS / 1e3}s)`,
|
|
28757
28772
|
durationArg("--interval"),
|
|
@@ -28759,16 +28774,22 @@ function registerWatchCommand(devices) {
|
|
|
28759
28774
|
).option("--max <n>", "Stop after N ticks (default: run until Ctrl-C)", intArg("--max", { min: 1 })).option("--for <dur>", 'Stop after elapsed time (e.g. "5m", "30s"). Combines with --max: first limit wins.', durationArg("--for")).option("--include-unchanged", "Emit a tick even when no field changed").addHelpText(
|
|
28760
28775
|
"after",
|
|
28761
28776
|
`
|
|
28762
|
-
|
|
28777
|
+
Default output is a human-readable table of field changes per tick; add --json
|
|
28778
|
+
to get one JSON-Lines record per deviceId per tick (the agent-friendly form).
|
|
28779
|
+
|
|
28780
|
+
The very first poll emits a seed tick with "from": null for every field, so
|
|
28781
|
+
the initial state is observable. Subsequent ticks only include fields whose
|
|
28782
|
+
value changed (unless --include-unchanged is passed).
|
|
28783
|
+
|
|
28784
|
+
Each --json line has the shape:
|
|
28763
28785
|
{ "t": "<ISO>", "tick": <n>, "deviceId": "ID", "type": "Bot",
|
|
28764
28786
|
"changed": { "power": { "from": "off", "to": "on" } } }
|
|
28765
28787
|
|
|
28766
|
-
The very first poll has "from": null for every field (seed).
|
|
28767
|
-
|
|
28768
28788
|
Examples:
|
|
28769
28789
|
$ switchbot devices watch ABC123 --interval 10s
|
|
28770
28790
|
$ switchbot devices watch ABC123 --fields battery,power --interval 1m
|
|
28771
28791
|
$ switchbot devices watch ABC123 DEF456 --interval 30s --max 10
|
|
28792
|
+
# Agent-friendly: one JSONL record per tick, pipeable to jq
|
|
28772
28793
|
$ switchbot devices watch ABC123 --json | jq 'select(.changed.power)'
|
|
28773
28794
|
$ switchbot devices watch --name "Living Room AC" --interval 10s
|
|
28774
28795
|
`
|
|
@@ -45370,16 +45391,30 @@ import { createRequire as createRequire3 } from "node:module";
|
|
|
45370
45391
|
|
|
45371
45392
|
// src/policy/schema.ts
|
|
45372
45393
|
init_cjs_shim();
|
|
45394
|
+
|
|
45395
|
+
// src/embedded-assets.ts
|
|
45396
|
+
init_cjs_shim();
|
|
45373
45397
|
import { readFileSync as readFileSync2 } from "node:fs";
|
|
45374
45398
|
import { fileURLToPath } from "node:url";
|
|
45399
|
+
function readAsset(relPath) {
|
|
45400
|
+
const resolved = fileURLToPath(new URL(relPath, import.meta.url));
|
|
45401
|
+
return readFileSync2(resolved, "utf-8");
|
|
45402
|
+
}
|
|
45403
|
+
function readPolicySchemaJson(version2) {
|
|
45404
|
+
return readAsset(`./policy/schema/v${version2}.json`);
|
|
45405
|
+
}
|
|
45406
|
+
function readPolicyExampleYaml() {
|
|
45407
|
+
return readAsset(`./policy/examples/policy.example.yaml`);
|
|
45408
|
+
}
|
|
45409
|
+
|
|
45410
|
+
// src/policy/schema.ts
|
|
45375
45411
|
var SUPPORTED_POLICY_SCHEMA_VERSIONS = ["0.2"];
|
|
45376
45412
|
var CURRENT_POLICY_SCHEMA_VERSION = "0.2";
|
|
45377
45413
|
var schemaCache = /* @__PURE__ */ new Map();
|
|
45378
45414
|
function loadPolicySchema(version2 = CURRENT_POLICY_SCHEMA_VERSION) {
|
|
45379
45415
|
const cached2 = schemaCache.get(version2);
|
|
45380
45416
|
if (cached2) return cached2;
|
|
45381
|
-
const
|
|
45382
|
-
const raw = readFileSync2(fileURLToPath(url2), "utf-8");
|
|
45417
|
+
const raw = readPolicySchemaJson(version2);
|
|
45383
45418
|
const parsed = JSON.parse(raw);
|
|
45384
45419
|
schemaCache.set(version2, parsed);
|
|
45385
45420
|
return parsed;
|
|
@@ -46638,7 +46673,6 @@ function diffPolicyValues(leftDoc, rightDoc, leftSource, rightSource, maxChanges
|
|
|
46638
46673
|
}
|
|
46639
46674
|
|
|
46640
46675
|
// src/commands/mcp.ts
|
|
46641
|
-
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
46642
46676
|
import { dirname as pathDirname, join as pathJoin } from "node:path";
|
|
46643
46677
|
import os13 from "node:os";
|
|
46644
46678
|
import fs15 from "node:fs";
|
|
@@ -47581,8 +47615,7 @@ API docs: https://github.com/OpenWonderLabs/SwitchBotAPI`
|
|
|
47581
47615
|
context: { policyPath }
|
|
47582
47616
|
});
|
|
47583
47617
|
}
|
|
47584
|
-
const
|
|
47585
|
-
const template = fs15.readFileSync(fileURLToPath2(templateUrl), "utf-8");
|
|
47618
|
+
const template = readPolicyExampleYaml();
|
|
47586
47619
|
fs15.mkdirSync(pathDirname(policyPath), { recursive: true });
|
|
47587
47620
|
fs15.writeFileSync(policyPath, template, { encoding: "utf-8" });
|
|
47588
47621
|
const structured = {
|
|
@@ -48714,40 +48747,61 @@ Total: ${entries.length} device type(s) (source: ${source})`);
|
|
|
48714
48747
|
handleError(error48);
|
|
48715
48748
|
}
|
|
48716
48749
|
});
|
|
48717
|
-
catalog.command("search").description("Fuzzy search the effective catalog by type name, alias, role, or command name").argument("<keyword>", "Substring to match (case-insensitive) against type, alias, role, or command").action((keyword) => {
|
|
48750
|
+
catalog.command("search").description("Fuzzy search the effective catalog by type name, alias, role, or command name").argument("<keyword>", "Substring to match (case-insensitive) against type, alias, role, or command").option("--strict", "Only return entries whose type name matches (skip alias/role/command fallbacks)").action((keyword, options) => {
|
|
48718
48751
|
try {
|
|
48719
48752
|
const q = keyword.toLowerCase();
|
|
48720
48753
|
const entries = getEffectiveCatalog();
|
|
48721
|
-
const
|
|
48722
|
-
|
|
48723
|
-
|
|
48724
|
-
|
|
48725
|
-
|
|
48726
|
-
|
|
48727
|
-
|
|
48754
|
+
const strict = options.strict === true;
|
|
48755
|
+
const hits = [];
|
|
48756
|
+
for (const e of entries) {
|
|
48757
|
+
const matched = [];
|
|
48758
|
+
const typeHit = e.type.toLowerCase().includes(q);
|
|
48759
|
+
const aliasExact = (e.aliases ?? []).some((a) => a.toLowerCase() === q);
|
|
48760
|
+
const aliasSubstr = (e.aliases ?? []).some((a) => a.toLowerCase().includes(q) && a.toLowerCase() !== q);
|
|
48761
|
+
const roleHit = (e.role ?? "").toLowerCase().includes(q);
|
|
48762
|
+
const cmdMatches = e.commands.filter((c) => c.command.toLowerCase().includes(q)).map((c) => c.command);
|
|
48763
|
+
if (typeHit) matched.push("type");
|
|
48764
|
+
if (aliasExact) matched.push("alias");
|
|
48765
|
+
else if (aliasSubstr) matched.push("alias-only");
|
|
48766
|
+
if (roleHit) matched.push("role");
|
|
48767
|
+
if (cmdMatches.length > 0) matched.push(`commands[${cmdMatches.join(",")}]`);
|
|
48768
|
+
if (strict) {
|
|
48769
|
+
if (!typeHit) continue;
|
|
48770
|
+
} else if (matched.length === 0) {
|
|
48771
|
+
continue;
|
|
48772
|
+
}
|
|
48773
|
+
let tier;
|
|
48774
|
+
if (typeHit || aliasExact) tier = 0;
|
|
48775
|
+
else if (roleHit || cmdMatches.length > 0) tier = 1;
|
|
48776
|
+
else tier = 2;
|
|
48777
|
+
hits.push({ entry: e, tier, matched });
|
|
48778
|
+
}
|
|
48779
|
+
hits.sort((a, b2) => a.tier - b2.tier);
|
|
48728
48780
|
if (isJsonMode()) {
|
|
48729
|
-
printJson({
|
|
48781
|
+
printJson({
|
|
48782
|
+
query: keyword,
|
|
48783
|
+
strict,
|
|
48784
|
+
matches: hits.map((h) => ({ ...h.entry, _matchedOn: h.matched, _tier: h.tier }))
|
|
48785
|
+
});
|
|
48730
48786
|
return;
|
|
48731
48787
|
}
|
|
48732
48788
|
if (hits.length === 0) {
|
|
48733
|
-
|
|
48789
|
+
const suffix = strict ? " (strict mode \u2014 try without --strict)" : "";
|
|
48790
|
+
console.log(`No catalog entries match "${keyword}"${suffix}.`);
|
|
48734
48791
|
return;
|
|
48735
48792
|
}
|
|
48736
48793
|
const fmt = resolveFormat();
|
|
48737
|
-
const headers = ["type", "category", "role", "
|
|
48738
|
-
const rows = hits.map((
|
|
48739
|
-
|
|
48740
|
-
|
|
48741
|
-
|
|
48742
|
-
|
|
48743
|
-
|
|
48744
|
-
if (cmdMatches.length > 0) matched.push(`commands[${cmdMatches.join(",")}]`);
|
|
48745
|
-
return [e.type, e.category, e.role ?? "\u2014", matched.join(", ") || "\u2014"];
|
|
48746
|
-
});
|
|
48794
|
+
const headers = ["type", "category", "role", "matched_on"];
|
|
48795
|
+
const rows = hits.map((h) => [
|
|
48796
|
+
h.entry.type,
|
|
48797
|
+
h.entry.category,
|
|
48798
|
+
h.entry.role ?? "\u2014",
|
|
48799
|
+
h.matched.join(", ") || "\u2014"
|
|
48800
|
+
]);
|
|
48747
48801
|
renderRows(headers, rows, fmt, resolveFields());
|
|
48748
48802
|
if (fmt === "table") {
|
|
48749
48803
|
console.log(`
|
|
48750
|
-
${hits.length} match${hits.length === 1 ? "" : "es"} for "${keyword}"`);
|
|
48804
|
+
${hits.length} match${hits.length === 1 ? "" : "es"} for "${keyword}"${strict ? " (strict)" : ""}`);
|
|
48751
48805
|
}
|
|
48752
48806
|
} catch (error48) {
|
|
48753
48807
|
handleError(error48);
|
|
@@ -51331,7 +51385,6 @@ var import_yaml8 = __toESM(require_dist(), 1);
|
|
|
51331
51385
|
init_output();
|
|
51332
51386
|
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync, mkdirSync, copyFileSync, statSync } from "node:fs";
|
|
51333
51387
|
import { dirname, resolve as resolvePath } from "node:path";
|
|
51334
|
-
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
51335
51388
|
|
|
51336
51389
|
// src/policy/format.ts
|
|
51337
51390
|
init_cjs_shim();
|
|
@@ -51391,8 +51444,7 @@ function formatValidationResult(result, source, opts = {}) {
|
|
|
51391
51444
|
// src/commands/policy.ts
|
|
51392
51445
|
var LATEST_SUPPORTED_VERSION2 = SUPPORTED_POLICY_SCHEMA_VERSIONS[SUPPORTED_POLICY_SCHEMA_VERSIONS.length - 1];
|
|
51393
51446
|
function readEmbeddedTemplate() {
|
|
51394
|
-
|
|
51395
|
-
return readFileSync3(fileURLToPath3(url2), "utf-8");
|
|
51447
|
+
return readPolicyExampleYaml();
|
|
51396
51448
|
}
|
|
51397
51449
|
var PolicyFileExistsError = class extends Error {
|
|
51398
51450
|
constructor(policyPath) {
|
|
@@ -56221,11 +56273,29 @@ function resolveStatusSyncRuntime(options) {
|
|
|
56221
56273
|
}
|
|
56222
56274
|
const openclawToken = options.openclawToken ?? process.env.OPENCLAW_TOKEN;
|
|
56223
56275
|
if (!openclawToken) {
|
|
56224
|
-
throw new UsageError(
|
|
56276
|
+
throw new UsageError(
|
|
56277
|
+
[
|
|
56278
|
+
"OpenClaw token missing. Provide one of:",
|
|
56279
|
+
" 1. --openclaw-token <token>",
|
|
56280
|
+
" 2. OPENCLAW_TOKEN=<token> in the environment",
|
|
56281
|
+
"",
|
|
56282
|
+
"The token is issued by your OpenClaw server admin (same token you use for `events mqtt-tail --sink openclaw`).",
|
|
56283
|
+
"After setting it, re-run the command and verify with `switchbot status-sync status`."
|
|
56284
|
+
].join("\n")
|
|
56285
|
+
);
|
|
56225
56286
|
}
|
|
56226
56287
|
const openclawModel = options.openclawModel ?? process.env.OPENCLAW_MODEL;
|
|
56227
56288
|
if (!openclawModel) {
|
|
56228
|
-
throw new UsageError(
|
|
56289
|
+
throw new UsageError(
|
|
56290
|
+
[
|
|
56291
|
+
"OpenClaw model missing. Provide one of:",
|
|
56292
|
+
" 1. --openclaw-model <model>",
|
|
56293
|
+
" 2. OPENCLAW_MODEL=<model> in the environment",
|
|
56294
|
+
"",
|
|
56295
|
+
"The model name maps this CLI to a registered agent/device on the OpenClaw side.",
|
|
56296
|
+
"After setting it, re-run the command and verify with `switchbot status-sync status`."
|
|
56297
|
+
].join("\n")
|
|
56298
|
+
);
|
|
56229
56299
|
}
|
|
56230
56300
|
return {
|
|
56231
56301
|
openclawUrl: options.openclawUrl ?? process.env.OPENCLAW_URL ?? DEFAULT_OPENCLAW_URL,
|
|
@@ -56860,7 +56930,7 @@ init_output();
|
|
|
56860
56930
|
import { spawn as spawn5 } from "node:child_process";
|
|
56861
56931
|
import fs29 from "node:fs";
|
|
56862
56932
|
import path25 from "node:path";
|
|
56863
|
-
import { fileURLToPath as
|
|
56933
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
56864
56934
|
init_arg_parsers();
|
|
56865
56935
|
init_source();
|
|
56866
56936
|
function readDaemonPid() {
|
|
@@ -57010,7 +57080,7 @@ The daemon reads the same policy file as \`switchbot rules run\`.
|
|
|
57010
57080
|
} catch {
|
|
57011
57081
|
}
|
|
57012
57082
|
}
|
|
57013
|
-
const thisFile =
|
|
57083
|
+
const thisFile = fileURLToPath2(import.meta.url);
|
|
57014
57084
|
const cliEntry = path25.resolve(path25.dirname(thisFile), "..", "index.js");
|
|
57015
57085
|
const args = ["rules", "run"];
|
|
57016
57086
|
if (opts.policy) args.push(opts.policy);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@switchbot/openapi-cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0",
|
|
4
4
|
"description": "SwitchBot smart home CLI — control devices, run scenes, stream real-time events, and integrate AI agents via MCP. Full API v1.1 coverage.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"switchbot",
|