@switchbot/openapi-cli 2.5.0 → 2.5.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.
@@ -9,6 +9,25 @@ export function isJsonMode() {
9
9
  export function printJson(data) {
10
10
  console.log(JSON.stringify({ schemaVersion: SCHEMA_VERSION, data }, null, 2));
11
11
  }
12
+ /**
13
+ * Emit a structured JSON error envelope on stdout.
14
+ *
15
+ * Bug #SYS-1: Under `--json`, both success and error payloads must share
16
+ * the same output channel (stdout) so a single `cli --json ... | jq` pipe
17
+ * can decode either shape. Use this helper everywhere that previously
18
+ * called `console.error(JSON.stringify({ error: ... }))` in --json mode.
19
+ *
20
+ * The envelope is always `{ schemaVersion, error }` — callers pass only the
21
+ * error payload. Also emits a brief human-readable line on stderr when a
22
+ * TTY is attached, so interactive runs still see the failure.
23
+ */
24
+ export function emitJsonError(errorPayload) {
25
+ console.log(JSON.stringify({ schemaVersion: SCHEMA_VERSION, error: errorPayload }));
26
+ if (process.stderr.isTTY) {
27
+ const msg = typeof errorPayload.message === 'string' ? errorPayload.message : 'Error';
28
+ console.error(chalk.red(msg));
29
+ }
30
+ }
12
31
  function escapeMarkdownCell(s) {
13
32
  // Pipes break markdown table layout; backslash-escape them. Collapse
14
33
  // newlines into <br> so each row stays on one line.
@@ -119,11 +138,12 @@ export class StructuredUsageError extends Error {
119
138
  function classifyApiError(code) {
120
139
  switch (code) {
121
140
  case 151:
122
- case 160: return 'command-not-supported';
141
+ case 160:
142
+ case 3005: return 'command-not-supported';
123
143
  case 152: return 'device-not-found';
124
144
  case 161:
125
145
  case 171: return 'device-offline';
126
- case 190: return 'device-busy';
146
+ case 190: return 'device-internal-error';
127
147
  case 401: return 'auth-failed';
128
148
  case 429: return 'quota-exceeded';
129
149
  default: return 'unknown-api-error';
@@ -212,7 +232,16 @@ export function handleError(error) {
212
232
  }
213
233
  const payload = buildErrorPayload(error);
214
234
  if (isJsonMode()) {
215
- console.error(JSON.stringify({ schemaVersion: SCHEMA_VERSION, error: payload }));
235
+ // Bug #SYS-1: Under --json, route the structured envelope to stdout so
236
+ // `cli --json ... | jq` pipelines can decode the error shape exactly
237
+ // the same way they decode success. Previously it went to stderr, which
238
+ // silently broke every error-path pipeline. TTY users still get a
239
+ // terse human-readable line on stderr so interactive runs don't look
240
+ // like the process simply exited.
241
+ console.log(JSON.stringify({ schemaVersion: SCHEMA_VERSION, error: payload }));
242
+ if (process.stderr.isTTY) {
243
+ console.error(chalk.red(payload.message));
244
+ }
216
245
  process.exit(payload.code === 2 ? 2 : 1);
217
246
  }
218
247
  if (payload.kind === 'usage') {
@@ -247,11 +276,13 @@ function errorHint(code) {
247
276
  case 171:
248
277
  return 'The Hub itself is offline — check its power and Wi-Fi.';
249
278
  case 190:
250
- return "Often means the deviceId is wrong or the command/parameter is invalid for this device. Double-check with 'switchbot devices list' and 'switchbot devices describe <deviceId>'. Use --verbose to see the raw API response.";
279
+ return 'SwitchBot API code 190 is a generic internal error. Common causes: invalid deviceId, unsupported command/parameter, or the endpoint does not apply (e.g., "webhook query" with no webhook configured). Verify with --verbose.';
251
280
  case 401:
252
281
  return "Re-run 'switchbot config set-token <token> <secret>', or verify SWITCHBOT_TOKEN / SWITCHBOT_SECRET.";
253
282
  case 429:
254
283
  return 'Daily quota is 10,000 requests/account — retry after midnight UTC.';
284
+ case 3005:
285
+ return "SwitchBot rejected the command as invalid for this specific device model. For IR remotes, this often means the command works only on --type customize (user-learned buttons). Try 'switchbot devices commands <type>' or check the device's capabilities.";
255
286
  default:
256
287
  return null;
257
288
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@switchbot/openapi-cli",
3
- "version": "2.5.0",
3
+ "version": "2.5.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",