@switchbot/openapi-cli 3.4.0 → 3.4.1
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 +110 -602
- package/dist/index.js +177 -40
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7221,7 +7221,7 @@ function emitJsonError(errorPayload) {
|
|
|
7221
7221
|
function emitStreamHeader(opts) {
|
|
7222
7222
|
console.log(
|
|
7223
7223
|
JSON.stringify({
|
|
7224
|
-
schemaVersion: SCHEMA_VERSION,
|
|
7224
|
+
schemaVersion: opts.schemaVersion ?? SCHEMA_VERSION,
|
|
7225
7225
|
stream: true,
|
|
7226
7226
|
eventKind: opts.eventKind,
|
|
7227
7227
|
cadence: opts.cadence
|
|
@@ -7531,7 +7531,7 @@ var init_output = __esm({
|
|
|
7531
7531
|
init_source();
|
|
7532
7532
|
init_client();
|
|
7533
7533
|
init_flags();
|
|
7534
|
-
SCHEMA_VERSION = "1.
|
|
7534
|
+
SCHEMA_VERSION = "1.2";
|
|
7535
7535
|
ASCII_BORDER_CHARS = {
|
|
7536
7536
|
top: "-",
|
|
7537
7537
|
"top-mid": "+",
|
|
@@ -28296,8 +28296,21 @@ function commandToJson(cmd, opts = {}) {
|
|
|
28296
28296
|
}
|
|
28297
28297
|
function resolveTargetCommand(root, argv) {
|
|
28298
28298
|
let cmd = root;
|
|
28299
|
+
const rootOptions = root.options;
|
|
28300
|
+
let consumeNext = false;
|
|
28299
28301
|
for (const token of argv) {
|
|
28300
|
-
if (
|
|
28302
|
+
if (consumeNext) {
|
|
28303
|
+
consumeNext = false;
|
|
28304
|
+
continue;
|
|
28305
|
+
}
|
|
28306
|
+
if (token.startsWith("-")) {
|
|
28307
|
+
if (!token.includes("=")) {
|
|
28308
|
+
const localOpts = cmd.options;
|
|
28309
|
+
const opt = localOpts.find((o) => o.short === token || o.long === token) || rootOptions.find((o) => o.short === token || o.long === token);
|
|
28310
|
+
if (opt && (opt.required || opt.optional)) consumeNext = true;
|
|
28311
|
+
}
|
|
28312
|
+
continue;
|
|
28313
|
+
}
|
|
28301
28314
|
const sub = cmd.commands.find(
|
|
28302
28315
|
(c) => c.name() === token || c.aliases().includes(token)
|
|
28303
28316
|
);
|
|
@@ -31763,6 +31776,26 @@ function buildRelaySetMode(opts) {
|
|
|
31763
31776
|
}
|
|
31764
31777
|
return `${ch};${modeInt}`;
|
|
31765
31778
|
}
|
|
31779
|
+
function buildBrightnessSet(opts) {
|
|
31780
|
+
if (!opts.brightness) throw new UsageError("--brightness is required (1-100)");
|
|
31781
|
+
const b2 = parseInt(opts.brightness, 10);
|
|
31782
|
+
if (!Number.isFinite(b2) || b2 < 1 || b2 > 100) {
|
|
31783
|
+
throw new UsageError(`--brightness must be an integer between 1 and 100 (got "${opts.brightness}")`);
|
|
31784
|
+
}
|
|
31785
|
+
return String(b2);
|
|
31786
|
+
}
|
|
31787
|
+
function buildColorSet(opts) {
|
|
31788
|
+
if (!opts.color) throw new UsageError('--color is required (e.g. "255:0:0", "#FF0000", "red")');
|
|
31789
|
+
const result = validateSetColor(opts.color);
|
|
31790
|
+
if (!result.ok) throw new UsageError(result.error);
|
|
31791
|
+
return result.normalized ?? opts.color;
|
|
31792
|
+
}
|
|
31793
|
+
function buildColorTemperatureSet(opts) {
|
|
31794
|
+
if (!opts.colorTemp) throw new UsageError("--color-temp is required (2700-6500)");
|
|
31795
|
+
const result = validateSetColorTemperature(opts.colorTemp);
|
|
31796
|
+
if (!result.ok) throw new UsageError(result.error);
|
|
31797
|
+
return result.normalized ?? opts.colorTemp;
|
|
31798
|
+
}
|
|
31766
31799
|
function validateParameter(deviceType, command, raw) {
|
|
31767
31800
|
if (!deviceType) return { ok: true };
|
|
31768
31801
|
if (deviceType === "Air Conditioner" && command === "setAll") {
|
|
@@ -31783,7 +31816,7 @@ function validateParameter(deviceType, command, raw) {
|
|
|
31783
31816
|
if (command === "setColor" && isColorDevice(deviceType)) {
|
|
31784
31817
|
return validateSetColor(raw);
|
|
31785
31818
|
}
|
|
31786
|
-
if (command === "setColorTemperature" &&
|
|
31819
|
+
if (command === "setColorTemperature" && isBrightnessDevice(deviceType)) {
|
|
31787
31820
|
return validateSetColorTemperature(raw);
|
|
31788
31821
|
}
|
|
31789
31822
|
return { ok: true };
|
|
@@ -31792,7 +31825,12 @@ function isBrightnessDevice(deviceType) {
|
|
|
31792
31825
|
return deviceType === "Color Bulb" || deviceType === "Strip Light" || deviceType === "Strip Light 3" || deviceType === "Ceiling Light" || deviceType === "Ceiling Light Pro" || deviceType === "Floor Lamp" || deviceType === "Light Strip" || deviceType === "Dimmer" || deviceType === "Fill Light";
|
|
31793
31826
|
}
|
|
31794
31827
|
function isColorDevice(deviceType) {
|
|
31795
|
-
return deviceType === "Color Bulb" || deviceType === "Strip Light" || deviceType === "Strip Light 3" || deviceType === "
|
|
31828
|
+
return deviceType === "Color Bulb" || deviceType === "Strip Light" || deviceType === "Strip Light 3" || deviceType === "Floor Lamp" || deviceType === "Light Strip" || deviceType === "Fill Light";
|
|
31829
|
+
}
|
|
31830
|
+
function isLightingCommandSupported(deviceType, command) {
|
|
31831
|
+
if (command === "setBrightness" || command === "setColorTemperature") return isBrightnessDevice(deviceType);
|
|
31832
|
+
if (command === "setColor") return isColorDevice(deviceType);
|
|
31833
|
+
return false;
|
|
31796
31834
|
}
|
|
31797
31835
|
function validateSetBrightness(raw) {
|
|
31798
31836
|
if (raw === void 0 || raw === "" || raw === "default") {
|
|
@@ -32923,10 +32961,11 @@ init_arg_parsers();
|
|
|
32923
32961
|
init_output();
|
|
32924
32962
|
init_cache();
|
|
32925
32963
|
init_devices();
|
|
32964
|
+
init_catalog();
|
|
32926
32965
|
init_flags();
|
|
32927
32966
|
init_client();
|
|
32928
32967
|
function registerExpandCommand(devices) {
|
|
32929
|
-
devices.command("expand").description("Send a command with semantic flags instead of raw positional parameters").argument("[deviceId]", 'Target device ID from "devices list" (or use --name)').argument("[command]", "Command name: setAll (AC), setPosition (Curtain/Blind Tilt), setMode (Relay Switch 2)").option("--name <query>", "Resolve device by fuzzy name instead of deviceId", stringArg("--name")).option("--name-strategy <s>", `Name match strategy: ${ALL_STRATEGIES.join("|")} (default: require-unique)`, stringArg("--name-strategy")).option("--name-type <type>", 'Narrow --name by device type (e.g. "Curtain", "Air Conditioner")', stringArg("--name-type")).option("--name-category <cat>", "Narrow --name by category: physical|ir", enumArg("--name-category", ["physical", "ir"])).option("--name-room <room>", "Narrow --name by room name (substring match)", stringArg("--name-room")).option("--temp <celsius>", "AC setAll: temperature in Celsius (16-30)", intArg("--temp", { min: 16, max: 30 })).option("--mode <mode>", "AC: auto|cool|dry|fan|heat Curtain: default|performance|silent Relay: toggle|edge|detached|momentary", stringArg("--mode")).option("--fan <speed>", "AC setAll: fan speed auto|low|mid|high", stringArg("--fan")).option("--power <state>", "AC setAll: on|off", stringArg("--power")).option("--position <percent>", "Curtain setPosition: 0-100 (0=open, 100=closed)", intArg("--position", { min: 0, max: 100 })).option("--direction <dir>", "Blind Tilt setPosition: up|down", stringArg("--direction")).option("--angle <percent>", "Blind Tilt setPosition: 0-100 (0=closed, 100=open)", intArg("--angle", { min: 0, max: 100 })).option("--channel <n>", "Relay Switch 2 setMode: channel 1 or 2", intArg("--channel", { min: 1, max: 2 })).option("--yes", "Confirm destructive commands").addHelpText("after", `
|
|
32968
|
+
devices.command("expand").description("Send a command with semantic flags instead of raw positional parameters").argument("[deviceId]", 'Target device ID from "devices list" (or use --name)').argument("[command]", "Command name: setAll (AC), setPosition (Curtain/Blind Tilt), setMode (Relay Switch 2), setBrightness/setColor/setColorTemperature (lighting)").option("--name <query>", "Resolve device by fuzzy name instead of deviceId", stringArg("--name")).option("--name-strategy <s>", `Name match strategy: ${ALL_STRATEGIES.join("|")} (default: require-unique)`, stringArg("--name-strategy")).option("--name-type <type>", 'Narrow --name by device type (e.g. "Curtain", "Air Conditioner")', stringArg("--name-type")).option("--name-category <cat>", "Narrow --name by category: physical|ir", enumArg("--name-category", ["physical", "ir"])).option("--name-room <room>", "Narrow --name by room name (substring match)", stringArg("--name-room")).option("--temp <celsius>", "AC setAll: temperature in Celsius (16-30)", intArg("--temp", { min: 16, max: 30 })).option("--mode <mode>", "AC: auto|cool|dry|fan|heat Curtain: default|performance|silent Relay: toggle|edge|detached|momentary", stringArg("--mode")).option("--fan <speed>", "AC setAll: fan speed auto|low|mid|high", stringArg("--fan")).option("--power <state>", "AC setAll: on|off", stringArg("--power")).option("--position <percent>", "Curtain setPosition: 0-100 (0=open, 100=closed)", intArg("--position", { min: 0, max: 100 })).option("--direction <dir>", "Blind Tilt setPosition: up|down", stringArg("--direction")).option("--angle <percent>", "Blind Tilt setPosition: 0-100 (0=closed, 100=open)", intArg("--angle", { min: 0, max: 100 })).option("--channel <n>", "Relay Switch 2 setMode: channel 1 or 2", intArg("--channel", { min: 1, max: 2 })).option("--brightness <percent>", "setBrightness: 1-100 percent", intArg("--brightness", { min: 1, max: 100 })).option("--color <value>", "setColor: R:G:B, #RRGGBB, or named color (red, blue, etc.)", stringArg("--color")).option("--color-temp <kelvin>", "setColorTemperature: 2700-6500 Kelvin", intArg("--color-temp", { min: 2700, max: 6500 })).option("--yes", "Confirm destructive commands").addHelpText("after", `
|
|
32930
32969
|
Translates semantic flags into the wire parameter format, then sends the command.
|
|
32931
32970
|
|
|
32932
32971
|
Supported expansions:
|
|
@@ -32947,12 +32986,25 @@ Supported expansions:
|
|
|
32947
32986
|
--channel 1 --mode edge \u2192 "1;1"
|
|
32948
32987
|
--mode values: toggle (0) | edge (1) | detached (2) | momentary (3)
|
|
32949
32988
|
|
|
32989
|
+
Color Bulb / Strip Light / Ceiling Light \u2014 setBrightness
|
|
32990
|
+
--brightness 80 \u2192 "80"
|
|
32991
|
+
|
|
32992
|
+
Color Bulb / Strip Light / Floor Lamp \u2014 setColor
|
|
32993
|
+
--color "255:0:0" \u2192 "255:0:0"
|
|
32994
|
+
--color "#FF0000" \u2192 "255:0:0"
|
|
32995
|
+
--color red \u2192 "255:0:0"
|
|
32996
|
+
|
|
32997
|
+
Color Bulb / Strip Light / Ceiling Light \u2014 setColorTemperature
|
|
32998
|
+
--color-temp 4000 \u2192 "4000"
|
|
32999
|
+
|
|
32950
33000
|
Examples:
|
|
32951
|
-
$ switchbot devices expand <acId> setAll
|
|
32952
|
-
$ switchbot devices expand <curtainId> setPosition
|
|
32953
|
-
$ switchbot devices expand <blindId> setPosition
|
|
32954
|
-
$ switchbot devices expand <relayId> setMode
|
|
32955
|
-
$ switchbot devices expand <
|
|
33001
|
+
$ switchbot devices expand <acId> setAll --temp 26 --mode cool --fan low --power on
|
|
33002
|
+
$ switchbot devices expand <curtainId> setPosition --position 50 --mode silent
|
|
33003
|
+
$ switchbot devices expand <blindId> setPosition --direction up --angle 50
|
|
33004
|
+
$ switchbot devices expand <relayId> setMode --channel 1 --mode edge
|
|
33005
|
+
$ switchbot devices expand <stripId> setBrightness --brightness 80
|
|
33006
|
+
$ switchbot devices expand <bulbId> setColor --color "#FF0000"
|
|
33007
|
+
$ switchbot devices expand <bulbId> setColorTemperature --color-temp 4000
|
|
32956
33008
|
$ switchbot devices expand --name "Living Room AC" setAll --temp 26 --mode cool --fan low --power on
|
|
32957
33009
|
`).action(async (deviceIdArg, commandArg, options) => {
|
|
32958
33010
|
let deviceId = "";
|
|
@@ -32970,12 +33022,22 @@ Examples:
|
|
|
32970
33022
|
category: options.nameCategory,
|
|
32971
33023
|
room: options.nameRoom
|
|
32972
33024
|
});
|
|
32973
|
-
if (!effectiveCommand) throw new UsageError("A command argument is required (setAll, setPosition, setMode).");
|
|
33025
|
+
if (!effectiveCommand) throw new UsageError("A command argument is required (setAll, setPosition, setMode, setBrightness, setColor, setColorTemperature).");
|
|
32974
33026
|
command = effectiveCommand;
|
|
32975
33027
|
const cached2 = getCachedDevice(deviceId);
|
|
32976
33028
|
const deviceType = cached2?.type ?? "";
|
|
32977
33029
|
let parameter;
|
|
32978
33030
|
if (command === "setAll") {
|
|
33031
|
+
if (!cached2) {
|
|
33032
|
+
throw new UsageError(
|
|
33033
|
+
`Device ${deviceId} is not in the local cache \u2014 run 'switchbot devices list' first so 'expand' can verify this is an Air Conditioner.`
|
|
33034
|
+
);
|
|
33035
|
+
}
|
|
33036
|
+
if (deviceType !== "Air Conditioner") {
|
|
33037
|
+
throw new UsageError(
|
|
33038
|
+
`"setAll" is only supported on Air Conditioner devices, but "${cached2.type}" was found.`
|
|
33039
|
+
);
|
|
33040
|
+
}
|
|
32979
33041
|
parameter = buildAcSetAll(options);
|
|
32980
33042
|
} else if (command === "setPosition") {
|
|
32981
33043
|
if (!cached2) {
|
|
@@ -32983,13 +33045,56 @@ Examples:
|
|
|
32983
33045
|
`Device ${deviceId} is not in the local cache \u2014 run 'switchbot devices list' first so 'expand' knows whether this is a Curtain or a Blind Tilt.`
|
|
32984
33046
|
);
|
|
32985
33047
|
}
|
|
33048
|
+
const positionTypes = ["Curtain", "Curtain 3", "Roller Shade", "Blind Tilt"];
|
|
33049
|
+
if (!positionTypes.some((t) => deviceType.startsWith(t))) {
|
|
33050
|
+
throw new UsageError(
|
|
33051
|
+
`"setPosition" is only supported on Curtain, Roller Shade, and Blind Tilt devices, but "${cached2.type}" was found.`
|
|
33052
|
+
);
|
|
33053
|
+
}
|
|
32986
33054
|
const isBlind = deviceType.startsWith("Blind Tilt");
|
|
32987
|
-
|
|
33055
|
+
const isRollerShade = deviceType.startsWith("Roller Shade");
|
|
33056
|
+
if (isBlind) {
|
|
33057
|
+
parameter = buildBlindTiltSetPosition(options);
|
|
33058
|
+
} else if (isRollerShade) {
|
|
33059
|
+
if (!options.position) throw new UsageError("--position is required (0-100)");
|
|
33060
|
+
parameter = options.position;
|
|
33061
|
+
} else {
|
|
33062
|
+
parameter = buildCurtainSetPosition(options);
|
|
33063
|
+
}
|
|
32988
33064
|
} else if (command === "setMode" && deviceType.startsWith("Relay Switch")) {
|
|
32989
33065
|
parameter = buildRelaySetMode(options);
|
|
33066
|
+
} else if (command === "setBrightness" || command === "setColor" || command === "setColorTemperature") {
|
|
33067
|
+
if (!cached2) {
|
|
33068
|
+
throw new UsageError(
|
|
33069
|
+
`Device "${deviceId}" is not in the local cache \u2014 run 'switchbot devices list' first so 'expand' can verify this device supports ${command}.`
|
|
33070
|
+
);
|
|
33071
|
+
}
|
|
33072
|
+
const catalogResult = findCatalogEntry(cached2.type);
|
|
33073
|
+
const catalogEntry = Array.isArray(catalogResult) ? catalogResult[0] : catalogResult;
|
|
33074
|
+
const supportedHint = command === "setColor" ? "Color Bulb, Strip Light, Floor Lamp, and similar RGB lighting devices" : "Color Bulb, Strip Light, Ceiling Light, Floor Lamp, and similar lighting devices";
|
|
33075
|
+
if (catalogEntry !== null) {
|
|
33076
|
+
if (!catalogEntry.commands.some((c) => c.command === command)) {
|
|
33077
|
+
throw new UsageError(
|
|
33078
|
+
`Device type "${cached2.type}" does not support ${command}. Supported on: ${supportedHint}.`
|
|
33079
|
+
);
|
|
33080
|
+
}
|
|
33081
|
+
} else {
|
|
33082
|
+
if (!isLightingCommandSupported(cached2.type, command)) {
|
|
33083
|
+
throw new UsageError(
|
|
33084
|
+
`Device type "${cached2.type}" does not support ${command}. Supported on: ${supportedHint}.`
|
|
33085
|
+
);
|
|
33086
|
+
}
|
|
33087
|
+
}
|
|
33088
|
+
if (command === "setBrightness") {
|
|
33089
|
+
parameter = buildBrightnessSet(options);
|
|
33090
|
+
} else if (command === "setColor") {
|
|
33091
|
+
parameter = buildColorSet(options);
|
|
33092
|
+
} else {
|
|
33093
|
+
parameter = buildColorTemperatureSet(options);
|
|
33094
|
+
}
|
|
32990
33095
|
} else {
|
|
32991
33096
|
throw new UsageError(
|
|
32992
|
-
`'expand' does not support "${command}" for device type "${deviceType || "unknown"}". Use 'switchbot devices command' to send raw parameters instead.`
|
|
33097
|
+
`'expand' does not support "${command}" for device type "${deviceType || "unknown"}". Supported: setAll (AC), setPosition (Curtain/Blind Tilt), setMode (Relay Switch), setBrightness/setColor/setColorTemperature (lighting). Use 'switchbot devices command' to send raw parameters instead.`
|
|
32993
33098
|
);
|
|
32994
33099
|
}
|
|
32995
33100
|
if (!options.yes && !isDryRun() && isDestructiveCommand(deviceType, command, "command")) {
|
|
@@ -33402,7 +33507,7 @@ Examples:
|
|
|
33402
33507
|
const results = await Promise.allSettled(ids.map((id) => fetchDeviceStatus(id)));
|
|
33403
33508
|
const fetchedAt2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
33404
33509
|
const batch = results.map(
|
|
33405
|
-
(r, i) => r.status === "fulfilled" ? { deviceId: ids[i], ok: true,
|
|
33510
|
+
(r, i) => r.status === "fulfilled" ? { deviceId: ids[i], ok: true, fetchedAt: fetchedAt2, ...annotateStatusPayload(ids[i], r.value) } : { deviceId: ids[i], ok: false, error: r.reason?.message ?? String(r.reason) }
|
|
33406
33511
|
);
|
|
33407
33512
|
const batchFmt = resolveFormat();
|
|
33408
33513
|
if (isJsonMode() || batchFmt === "json") {
|
|
@@ -33414,7 +33519,7 @@ Examples:
|
|
|
33414
33519
|
} else {
|
|
33415
33520
|
const rawFields = resolveFields();
|
|
33416
33521
|
for (const entry of batch) {
|
|
33417
|
-
const { deviceId: deviceId2, ok, error: error48,
|
|
33522
|
+
const { deviceId: deviceId2, ok, error: error48, fetchedAt: ts, ...status } = entry;
|
|
33418
33523
|
console.log(`
|
|
33419
33524
|
\u2500\u2500\u2500 ${String(deviceId2)} \u2500\u2500\u2500`);
|
|
33420
33525
|
if (!ok) {
|
|
@@ -33440,11 +33545,11 @@ Examples:
|
|
|
33440
33545
|
const fetchedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
33441
33546
|
const fmt = resolveFormat();
|
|
33442
33547
|
if (fmt === "json" && process.argv.includes("--json")) {
|
|
33443
|
-
printJson({ ...body,
|
|
33548
|
+
printJson({ ...body, fetchedAt });
|
|
33444
33549
|
return;
|
|
33445
33550
|
}
|
|
33446
33551
|
if (fmt !== "table") {
|
|
33447
|
-
const statusWithTs = { ...body,
|
|
33552
|
+
const statusWithTs = { ...body, fetchedAt };
|
|
33448
33553
|
const allHeaders = Object.keys(statusWithTs);
|
|
33449
33554
|
const allRows = [Object.values(statusWithTs)];
|
|
33450
33555
|
const rawFields = resolveFields();
|
|
@@ -33773,7 +33878,7 @@ Examples:
|
|
|
33773
33878
|
const joinedMatch = findCatalogEntry(joined);
|
|
33774
33879
|
if (joinedMatch && !Array.isArray(joinedMatch)) {
|
|
33775
33880
|
if (isJsonMode()) {
|
|
33776
|
-
printJson(normalizeCatalogForJson(joinedMatch));
|
|
33881
|
+
printJson([normalizeCatalogForJson(joinedMatch)]);
|
|
33777
33882
|
} else {
|
|
33778
33883
|
renderCatalogEntry(joinedMatch);
|
|
33779
33884
|
}
|
|
@@ -48672,9 +48777,9 @@ var logLevel = process.env.LOG_LEVEL || "warn";
|
|
|
48672
48777
|
var logFormat = process.env.LOG_FORMAT || "json";
|
|
48673
48778
|
var pinoConfig = {
|
|
48674
48779
|
level: logLevel,
|
|
48675
|
-
transport: logFormat === "pretty" ? { target: "pino-pretty" } : void 0
|
|
48780
|
+
transport: logFormat === "pretty" ? { target: "pino-pretty", options: { destination: 2 } } : void 0
|
|
48676
48781
|
};
|
|
48677
|
-
var log = pino(pinoConfig);
|
|
48782
|
+
var log = logFormat === "pretty" ? pino(pinoConfig) : pino(pinoConfig, pino.destination(2));
|
|
48678
48783
|
|
|
48679
48784
|
// src/mcp/device-history.ts
|
|
48680
48785
|
init_cjs_shim();
|
|
@@ -52513,12 +52618,27 @@ function listRegisteredTools(server) {
|
|
|
52513
52618
|
if (!internal._registeredTools) return [];
|
|
52514
52619
|
return Object.keys(internal._registeredTools).sort();
|
|
52515
52620
|
}
|
|
52621
|
+
function listRegisteredToolsWithMeta(server) {
|
|
52622
|
+
const internal = server;
|
|
52623
|
+
if (!internal._registeredTools) return [];
|
|
52624
|
+
return Object.entries(internal._registeredTools).sort(([a], [b2]) => a.localeCompare(b2)).map(([name, reg]) => {
|
|
52625
|
+
const entry = { name };
|
|
52626
|
+
if (reg.description) entry.description = reg.description;
|
|
52627
|
+
if (reg.inputSchema) {
|
|
52628
|
+
try {
|
|
52629
|
+
entry.inputSchema = external_exports.toJSONSchema(reg.inputSchema);
|
|
52630
|
+
} catch {
|
|
52631
|
+
}
|
|
52632
|
+
}
|
|
52633
|
+
return entry;
|
|
52634
|
+
});
|
|
52635
|
+
}
|
|
52516
52636
|
function listRegisteredResources() {
|
|
52517
52637
|
return ["switchbot://events"];
|
|
52518
52638
|
}
|
|
52519
52639
|
function printMcpToolDirectory() {
|
|
52520
52640
|
const server = createSwitchBotMcpServer();
|
|
52521
|
-
const tools =
|
|
52641
|
+
const tools = listRegisteredToolsWithMeta(server);
|
|
52522
52642
|
const resources = listRegisteredResources().map((uri) => ({ uri }));
|
|
52523
52643
|
if (isJsonMode()) {
|
|
52524
52644
|
printJson({ tools, resources });
|
|
@@ -52526,7 +52646,8 @@ function printMcpToolDirectory() {
|
|
|
52526
52646
|
}
|
|
52527
52647
|
console.log("Tools:");
|
|
52528
52648
|
for (const tool of tools) {
|
|
52529
|
-
|
|
52649
|
+
const desc = tool.description ? ` \u2014 ${tool.description.slice(0, 80)}` : "";
|
|
52650
|
+
console.log(` ${tool.name}${desc}`);
|
|
52530
52651
|
}
|
|
52531
52652
|
console.log("");
|
|
52532
52653
|
console.log("Resources:");
|
|
@@ -52538,7 +52659,7 @@ Total: ${tools.length} tool(s), ${resources.length} resource(s)`);
|
|
|
52538
52659
|
}
|
|
52539
52660
|
function registerMcpCommand(program3) {
|
|
52540
52661
|
const mcp = program3.command("mcp").description("Run as a Model Context Protocol server so AI agents can call SwitchBot tools").addHelpText("after", `
|
|
52541
|
-
The MCP server exposes twenty-
|
|
52662
|
+
The MCP server exposes twenty-four tools:
|
|
52542
52663
|
- list_devices fetch all physical + IR devices
|
|
52543
52664
|
- get_device_status live status for a physical device
|
|
52544
52665
|
- send_command control a device (destructive commands need confirm:true)
|
|
@@ -52561,6 +52682,8 @@ function registerMcpCommand(program3) {
|
|
|
52561
52682
|
- audit_stats aggregate audit counts by kind/result/device/rule
|
|
52562
52683
|
- rule_notifications query rule notify action delivery history
|
|
52563
52684
|
- rules_suggest draft an automation rule YAML from intent (heuristic, no LLM)
|
|
52685
|
+
- rules_explain show why a rule evaluation fired or was blocked
|
|
52686
|
+
- rules_simulate simulate a rule against historical events
|
|
52564
52687
|
- policy_add_rule append a rule into automation.rules[] in policy.yaml
|
|
52565
52688
|
|
|
52566
52689
|
Resource (read-only):
|
|
@@ -53003,7 +53126,7 @@ Examples:
|
|
|
53003
53126
|
throw new UsageError(`"${match.type}" exists in the effective catalog but not in source "${source}".`);
|
|
53004
53127
|
}
|
|
53005
53128
|
if (isJsonMode()) {
|
|
53006
|
-
printJson(picked);
|
|
53129
|
+
printJson([picked]);
|
|
53007
53130
|
return;
|
|
53008
53131
|
}
|
|
53009
53132
|
renderEntry(picked);
|
|
@@ -53745,7 +53868,7 @@ Examples:
|
|
|
53745
53868
|
let matchedCount = 0;
|
|
53746
53869
|
const ac = new AbortController();
|
|
53747
53870
|
const forTimer = forMs !== null && forMs > 0 ? setTimeout(() => ac.abort(), forMs) : null;
|
|
53748
|
-
if (isJsonMode()) emitStreamHeader({ eventKind: "event", cadence: "push" });
|
|
53871
|
+
if (isJsonMode()) emitStreamHeader({ eventKind: "event", cadence: "push", schemaVersion: EVENTS_SCHEMA_VERSION });
|
|
53749
53872
|
await new Promise((resolve2, reject) => {
|
|
53750
53873
|
let server = null;
|
|
53751
53874
|
try {
|
|
@@ -53897,7 +54020,7 @@ Examples:
|
|
|
53897
54020
|
if (!isJsonMode()) {
|
|
53898
54021
|
console.error("Fetching MQTT credentials from SwitchBot service\u2026");
|
|
53899
54022
|
}
|
|
53900
|
-
if (isJsonMode()) emitStreamHeader({ eventKind: "event", cadence: "push" });
|
|
54023
|
+
if (isJsonMode()) emitStreamHeader({ eventKind: "event", cadence: "push", schemaVersion: EVENTS_SCHEMA_VERSION });
|
|
53901
54024
|
if (isJsonMode()) {
|
|
53902
54025
|
const sessionStartAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
53903
54026
|
emitJsonStreamRecord({
|
|
@@ -55467,7 +55590,7 @@ function runSchemaExport(options) {
|
|
|
55467
55590
|
payload.resources = RESOURCE_CATALOG;
|
|
55468
55591
|
payload.cliAddedFields = [
|
|
55469
55592
|
{
|
|
55470
|
-
field: "
|
|
55593
|
+
field: "fetchedAt",
|
|
55471
55594
|
appliesTo: ["devices status", "devices describe"],
|
|
55472
55595
|
type: "string (ISO-8601)",
|
|
55473
55596
|
description: "CLI-synthesized timestamp indicating when this status response was fetched or served from the cache. Not part of the upstream SwitchBot API."
|
|
@@ -55530,7 +55653,7 @@ Common top-level fields:
|
|
|
55530
55653
|
schemaVersion CLI schema version (stable for agent contracts)
|
|
55531
55654
|
data.version Catalog schema version
|
|
55532
55655
|
data.types Array of SchemaEntry (or CompactSchemaEntry with --compact)
|
|
55533
|
-
data.
|
|
55656
|
+
data.fetchedAt CLI-added; present on live-query responses ('devices status'),
|
|
55534
55657
|
not on this offline export.
|
|
55535
55658
|
|
|
55536
55659
|
Examples:
|
|
@@ -56841,13 +56964,12 @@ function registerRun(rules) {
|
|
|
56841
56964
|
const loaded = loadAutomation(pathArg);
|
|
56842
56965
|
if (!loaded) return;
|
|
56843
56966
|
if (loaded.automation?.enabled !== true) {
|
|
56844
|
-
|
|
56845
|
-
|
|
56846
|
-
|
|
56847
|
-
|
|
56848
|
-
|
|
56849
|
-
}
|
|
56850
|
-
process.exit(0);
|
|
56967
|
+
exitWithError({
|
|
56968
|
+
code: 1,
|
|
56969
|
+
kind: "runtime",
|
|
56970
|
+
message: "automation.enabled is not true \u2014 set it to true in your policy file to start the daemon.",
|
|
56971
|
+
hint: "Set automation.enabled: true in your policy file, then re-run."
|
|
56972
|
+
});
|
|
56851
56973
|
}
|
|
56852
56974
|
const lint = lintRules(loaded.automation);
|
|
56853
56975
|
if (!lint.valid) {
|
|
@@ -59658,7 +59780,7 @@ The daemon reads the same policy file as \`switchbot rules run\`.
|
|
|
59658
59780
|
}
|
|
59659
59781
|
}
|
|
59660
59782
|
const thisFile = fileURLToPath3(import.meta.url);
|
|
59661
|
-
const cliEntry = path28.resolve(path28.dirname(thisFile), "..", "index.js");
|
|
59783
|
+
const cliEntry = path28.basename(thisFile) === "index.js" ? thisFile : path28.resolve(path28.dirname(thisFile), "..", "index.js");
|
|
59662
59784
|
const args = ["rules", "run"];
|
|
59663
59785
|
if (opts.policy) args.push(opts.policy);
|
|
59664
59786
|
fs32.mkdirSync(path28.dirname(DAEMON_PID_FILE), { recursive: true, mode: 448 });
|
|
@@ -60015,17 +60137,32 @@ try {
|
|
|
60015
60137
|
} catch (err) {
|
|
60016
60138
|
if (err instanceof CommanderError) {
|
|
60017
60139
|
if (err.code === "commander.helpDisplayed") {
|
|
60140
|
+
const helpRequested = process.argv.includes("--help") || process.argv.includes("-h") || process.argv.includes("help");
|
|
60141
|
+
if (helpRequested) {
|
|
60142
|
+
if (isJsonMode()) {
|
|
60143
|
+
const target = resolveTargetCommand(program2, process.argv.slice(2));
|
|
60144
|
+
printJson(commandToJson(target, { includeIdentity: target === program2 }));
|
|
60145
|
+
}
|
|
60146
|
+
process.exit(0);
|
|
60147
|
+
}
|
|
60018
60148
|
if (isJsonMode()) {
|
|
60019
60149
|
const target = resolveTargetCommand(program2, process.argv.slice(2));
|
|
60020
|
-
|
|
60150
|
+
const subNames = target.commands.map((c) => c.name()).join(", ");
|
|
60151
|
+
const usefulMessage = subNames ? `${target.name()}: a subcommand is required. Available: ${subNames}` : err.message;
|
|
60152
|
+
emitJsonError({ code: 2, kind: "usage", message: usefulMessage });
|
|
60021
60153
|
}
|
|
60022
|
-
process.exit(
|
|
60154
|
+
process.exit(2);
|
|
60023
60155
|
}
|
|
60024
60156
|
if (err.code === "commander.version") {
|
|
60025
60157
|
process.exit(0);
|
|
60026
60158
|
}
|
|
60027
60159
|
if (isJsonMode()) {
|
|
60028
|
-
|
|
60160
|
+
const errorMessage = err.code === "commander.help" ? (() => {
|
|
60161
|
+
const target = resolveTargetCommand(program2, process.argv.slice(2));
|
|
60162
|
+
const subNames = target.commands.map((c) => c.name()).join(", ");
|
|
60163
|
+
return subNames ? `${target.name()}: a subcommand is required. Available: ${subNames}` : err.message;
|
|
60164
|
+
})() : err.message;
|
|
60165
|
+
emitJsonError({ code: 2, kind: "usage", message: errorMessage });
|
|
60029
60166
|
}
|
|
60030
60167
|
process.exit(2);
|
|
60031
60168
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@switchbot/openapi-cli",
|
|
3
|
-
"version": "3.4.
|
|
3
|
+
"version": "3.4.1",
|
|
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",
|