@switchbot/openapi-cli 3.1.0 → 3.2.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.
Files changed (113) hide show
  1. package/README.md +34 -42
  2. package/dist/index.js +56945 -169
  3. package/dist/policy/schema/v0.2.json +1 -1
  4. package/package.json +3 -2
  5. package/dist/api/client.js +0 -235
  6. package/dist/auth.js +0 -20
  7. package/dist/commands/agent-bootstrap.js +0 -182
  8. package/dist/commands/auth.js +0 -354
  9. package/dist/commands/batch.js +0 -413
  10. package/dist/commands/cache.js +0 -126
  11. package/dist/commands/capabilities.js +0 -385
  12. package/dist/commands/catalog.js +0 -359
  13. package/dist/commands/completion.js +0 -385
  14. package/dist/commands/config.js +0 -376
  15. package/dist/commands/daemon.js +0 -367
  16. package/dist/commands/device-meta.js +0 -159
  17. package/dist/commands/devices.js +0 -948
  18. package/dist/commands/doctor.js +0 -1015
  19. package/dist/commands/events.js +0 -563
  20. package/dist/commands/expand.js +0 -130
  21. package/dist/commands/explain.js +0 -139
  22. package/dist/commands/health.js +0 -113
  23. package/dist/commands/history.js +0 -320
  24. package/dist/commands/identity.js +0 -59
  25. package/dist/commands/install.js +0 -246
  26. package/dist/commands/mcp.js +0 -2017
  27. package/dist/commands/plan.js +0 -653
  28. package/dist/commands/policy.js +0 -586
  29. package/dist/commands/quota.js +0 -78
  30. package/dist/commands/rules.js +0 -875
  31. package/dist/commands/scenes.js +0 -264
  32. package/dist/commands/schema.js +0 -177
  33. package/dist/commands/status-sync.js +0 -131
  34. package/dist/commands/uninstall.js +0 -237
  35. package/dist/commands/upgrade-check.js +0 -88
  36. package/dist/commands/watch.js +0 -194
  37. package/dist/commands/webhook.js +0 -182
  38. package/dist/config.js +0 -258
  39. package/dist/credentials/backends/file.js +0 -101
  40. package/dist/credentials/backends/linux.js +0 -129
  41. package/dist/credentials/backends/macos.js +0 -129
  42. package/dist/credentials/backends/windows.js +0 -215
  43. package/dist/credentials/keychain.js +0 -88
  44. package/dist/credentials/prime.js +0 -52
  45. package/dist/devices/cache.js +0 -293
  46. package/dist/devices/catalog.js +0 -767
  47. package/dist/devices/device-meta.js +0 -56
  48. package/dist/devices/history-agg.js +0 -138
  49. package/dist/devices/history-query.js +0 -181
  50. package/dist/devices/param-validator.js +0 -433
  51. package/dist/devices/resources.js +0 -270
  52. package/dist/install/default-steps.js +0 -257
  53. package/dist/install/preflight.js +0 -212
  54. package/dist/install/steps.js +0 -67
  55. package/dist/lib/command-keywords.js +0 -17
  56. package/dist/lib/daemon-state.js +0 -46
  57. package/dist/lib/destructive-mode.js +0 -12
  58. package/dist/lib/devices.js +0 -382
  59. package/dist/lib/idempotency.js +0 -106
  60. package/dist/lib/plan-store.js +0 -68
  61. package/dist/lib/request-context.js +0 -12
  62. package/dist/lib/scenes.js +0 -10
  63. package/dist/logger.js +0 -16
  64. package/dist/mcp/device-history.js +0 -145
  65. package/dist/mcp/events-subscription.js +0 -213
  66. package/dist/mqtt/client.js +0 -180
  67. package/dist/mqtt/credential.js +0 -30
  68. package/dist/policy/add-rule.js +0 -124
  69. package/dist/policy/diff.js +0 -91
  70. package/dist/policy/format.js +0 -57
  71. package/dist/policy/load.js +0 -61
  72. package/dist/policy/migrate.js +0 -67
  73. package/dist/policy/schema.js +0 -18
  74. package/dist/policy/validate.js +0 -262
  75. package/dist/rules/action.js +0 -205
  76. package/dist/rules/audit-query.js +0 -89
  77. package/dist/rules/conflict-analyzer.js +0 -203
  78. package/dist/rules/cron-scheduler.js +0 -186
  79. package/dist/rules/destructive.js +0 -52
  80. package/dist/rules/engine.js +0 -757
  81. package/dist/rules/matcher.js +0 -230
  82. package/dist/rules/pid-file.js +0 -95
  83. package/dist/rules/quiet-hours.js +0 -45
  84. package/dist/rules/suggest.js +0 -95
  85. package/dist/rules/throttle.js +0 -116
  86. package/dist/rules/types.js +0 -34
  87. package/dist/rules/webhook-listener.js +0 -223
  88. package/dist/rules/webhook-token.js +0 -90
  89. package/dist/schema/field-aliases.js +0 -131
  90. package/dist/sinks/dispatcher.js +0 -12
  91. package/dist/sinks/file.js +0 -19
  92. package/dist/sinks/format.js +0 -56
  93. package/dist/sinks/homeassistant.js +0 -44
  94. package/dist/sinks/openclaw.js +0 -33
  95. package/dist/sinks/stdout.js +0 -5
  96. package/dist/sinks/telegram.js +0 -28
  97. package/dist/sinks/types.js +0 -1
  98. package/dist/sinks/webhook.js +0 -22
  99. package/dist/status-sync/manager.js +0 -268
  100. package/dist/utils/arg-parsers.js +0 -66
  101. package/dist/utils/audit.js +0 -117
  102. package/dist/utils/filter.js +0 -189
  103. package/dist/utils/flags.js +0 -186
  104. package/dist/utils/format.js +0 -117
  105. package/dist/utils/health.js +0 -101
  106. package/dist/utils/help-json.js +0 -54
  107. package/dist/utils/name-resolver.js +0 -137
  108. package/dist/utils/output.js +0 -404
  109. package/dist/utils/quota.js +0 -227
  110. package/dist/utils/redact.js +0 -68
  111. package/dist/utils/retry.js +0 -140
  112. package/dist/utils/string.js +0 -22
  113. package/dist/version.js +0 -4
@@ -1,359 +0,0 @@
1
- import { enumArg } from '../utils/arg-parsers.js';
2
- import { printTable, printJson, isJsonMode, handleError, UsageError } from '../utils/output.js';
3
- import { resolveFormat, resolveFields, renderRows } from '../utils/format.js';
4
- import { DEVICE_CATALOG, findCatalogEntry, getCatalogOverlayPath, getEffectiveCatalog, loadCatalogOverlay, resetCatalogOverlayCache, deriveSafetyTier, } from '../devices/catalog.js';
5
- export function registerCatalogCommand(program) {
6
- const SOURCES = ['built-in', 'overlay', 'effective'];
7
- const catalog = program
8
- .command('catalog')
9
- .description('Inspect the SwitchBot device catalog (supported device types + any local overlay)')
10
- .addHelpText('after', `
11
- This CLI ships with a static catalog of known SwitchBot device types and
12
- their commands (see 'switchbot devices types'). You can extend or override
13
- it locally by dropping a JSON array at:
14
-
15
- ~/.switchbot/catalog.json
16
-
17
- Overlay rules (applied in order):
18
- • Entry whose "type" matches a built-in replaces that entry's fields
19
- (partial merge — overlay keys win, missing keys fall back to built-in).
20
- • Entry with a new "type" is appended. New entries MUST supply both
21
- "category" and "commands"; otherwise they are ignored silently.
22
- • Entry like { "type": "X", "remove": true } deletes the built-in "X".
23
-
24
- Subcommands:
25
- path Print the overlay file path and whether it exists
26
- show Show the effective catalog (or one entry). Alias: list
27
- list Alias of show (matches the muscle-memory spelling)
28
- search Fuzzy search types/aliases/roles/commands for a keyword
29
- diff Show what the overlay changes vs the built-in catalog
30
- refresh Re-read the overlay file (clears in-process cache)
31
-
32
- Examples:
33
- $ switchbot catalog path
34
- $ switchbot catalog list
35
- $ switchbot catalog show
36
- $ switchbot catalog show "Smart Lock"
37
- $ switchbot catalog search Hub
38
- $ switchbot catalog show --source built-in
39
- $ switchbot catalog diff
40
- $ switchbot catalog refresh
41
- `);
42
- catalog
43
- .command('path')
44
- .description('Print the overlay file path and whether it exists')
45
- .action(() => {
46
- const overlay = loadCatalogOverlay();
47
- if (isJsonMode()) {
48
- printJson({
49
- path: overlay.path,
50
- exists: overlay.exists,
51
- valid: overlay.error === undefined,
52
- error: overlay.error,
53
- entryCount: overlay.entries.length,
54
- });
55
- return;
56
- }
57
- console.log(`Overlay path: ${overlay.path}`);
58
- console.log(`Exists: ${overlay.exists ? 'yes' : 'no'}`);
59
- if (overlay.exists) {
60
- if (overlay.error) {
61
- console.log(`Status: invalid — ${overlay.error}`);
62
- }
63
- else {
64
- console.log(`Status: valid (${overlay.entries.length} entr${overlay.entries.length === 1 ? 'y' : 'ies'})`);
65
- }
66
- }
67
- else {
68
- console.log(`(Create the file to extend the built-in catalog — see 'switchbot catalog --help'.)`);
69
- }
70
- });
71
- catalog
72
- .command('show')
73
- .alias('list')
74
- .description("Show the effective catalog (or one entry). Alias: 'list'. Defaults to 'effective' source.")
75
- .argument('[type...]', 'Optional device type/alias (case-insensitive, partial match)')
76
- .option('--source <source>', 'Which catalog to show: built-in | overlay | effective (default)', enumArg('--source', SOURCES), 'effective')
77
- .addHelpText('after', `
78
- Examples:
79
- $ switchbot catalog show
80
- $ switchbot catalog show Bot
81
- $ switchbot catalog show Robot Vacuum
82
- $ switchbot catalog show --source built-in
83
- $ switchbot catalog show --json
84
- `)
85
- .action((typeParts, options) => {
86
- try {
87
- const source = options.source;
88
- if (!['built-in', 'overlay', 'effective'].includes(source)) {
89
- throw new UsageError(`Unknown --source "${source}". Expected: built-in, overlay, effective.`);
90
- }
91
- let entries;
92
- if (source === 'built-in') {
93
- entries = DEVICE_CATALOG;
94
- }
95
- else if (source === 'overlay') {
96
- const overlay = loadCatalogOverlay();
97
- if (overlay.error) {
98
- throw new Error(`Overlay file is invalid: ${overlay.error}`);
99
- }
100
- // Only entries that are full catalog entries (have category + commands)
101
- // or that explicitly remove a built-in are rendered here. Partial
102
- // overrides are hidden because they're not self-contained entries;
103
- // use `catalog diff` to inspect them.
104
- entries = overlay.entries.filter((e) => e.category !== undefined && e.commands !== undefined && !e.remove);
105
- }
106
- else {
107
- entries = getEffectiveCatalog();
108
- }
109
- const typeQuery = typeParts.join(' ').trim();
110
- if (typeQuery) {
111
- const match = findCatalogEntry(typeQuery);
112
- if (!match) {
113
- throw new UsageError(`No device type matches "${typeQuery}".`);
114
- }
115
- if (Array.isArray(match)) {
116
- const types = match.map((m) => m.type).join(', ');
117
- throw new UsageError(`"${typeQuery}" matches multiple types: ${types}. Be more specific.`);
118
- }
119
- // Restrict the match to the requested source if needed.
120
- const picked = entries.find((e) => e.type === match.type);
121
- if (!picked) {
122
- throw new UsageError(`"${match.type}" exists in the effective catalog but not in source "${source}".`);
123
- }
124
- if (isJsonMode()) {
125
- printJson(picked);
126
- return;
127
- }
128
- renderEntry(picked);
129
- return;
130
- }
131
- if (isJsonMode()) {
132
- printJson(entries);
133
- return;
134
- }
135
- const fmt = resolveFormat();
136
- const headers = ['type', 'category', 'commands', 'aliases'];
137
- const rows = entries.map((e) => [
138
- e.type,
139
- e.category,
140
- String(e.commands.length),
141
- (e.aliases ?? []).join(', ') || '—',
142
- ]);
143
- if (fmt !== 'table') {
144
- renderRows(headers, rows, fmt, resolveFields());
145
- }
146
- else {
147
- renderRows(headers, rows, 'table', resolveFields());
148
- console.log(`\nTotal: ${entries.length} device type(s) (source: ${source})`);
149
- }
150
- }
151
- catch (error) {
152
- handleError(error);
153
- }
154
- });
155
- catalog
156
- .command('search')
157
- .description('Fuzzy search the effective catalog by type name, alias, role, or command name')
158
- .argument('<keyword>', 'Substring to match (case-insensitive) against type, alias, role, or command')
159
- .action((keyword) => {
160
- try {
161
- const q = keyword.toLowerCase();
162
- const entries = getEffectiveCatalog();
163
- const hits = entries.filter((e) => {
164
- if (e.type.toLowerCase().includes(q))
165
- return true;
166
- if ((e.role ?? '').toLowerCase().includes(q))
167
- return true;
168
- if ((e.aliases ?? []).some((a) => a.toLowerCase().includes(q)))
169
- return true;
170
- if (e.commands.some((c) => c.command.toLowerCase().includes(q)))
171
- return true;
172
- return false;
173
- });
174
- if (isJsonMode()) {
175
- printJson({ query: keyword, matches: hits });
176
- return;
177
- }
178
- if (hits.length === 0) {
179
- console.log(`No catalog entries match "${keyword}".`);
180
- return;
181
- }
182
- const fmt = resolveFormat();
183
- const headers = ['type', 'category', 'role', 'matched'];
184
- const rows = hits.map((e) => {
185
- const matched = [];
186
- if (e.type.toLowerCase().includes(q))
187
- matched.push('type');
188
- if ((e.aliases ?? []).some((a) => a.toLowerCase().includes(q)))
189
- matched.push('alias');
190
- if ((e.role ?? '').toLowerCase().includes(q))
191
- matched.push('role');
192
- const cmdMatches = e.commands
193
- .filter((c) => c.command.toLowerCase().includes(q))
194
- .map((c) => c.command);
195
- if (cmdMatches.length > 0)
196
- matched.push(`commands[${cmdMatches.join(',')}]`);
197
- return [e.type, e.category, e.role ?? '—', matched.join(', ') || '—'];
198
- });
199
- renderRows(headers, rows, fmt, resolveFields());
200
- if (fmt === 'table') {
201
- console.log(`\n${hits.length} match${hits.length === 1 ? '' : 'es'} for "${keyword}"`);
202
- }
203
- }
204
- catch (error) {
205
- handleError(error);
206
- }
207
- });
208
- catalog
209
- .command('diff')
210
- .description('Show what the overlay replaces, adds, or removes vs the built-in catalog')
211
- .action(() => {
212
- const overlay = loadCatalogOverlay();
213
- const builtInByType = new Map(DEVICE_CATALOG.map((e) => [e.type, e]));
214
- const replaced = [];
215
- const added = [];
216
- const removed = [];
217
- const ignored = [];
218
- for (const e of overlay.entries) {
219
- if (e.remove) {
220
- if (builtInByType.has(e.type))
221
- removed.push(e.type);
222
- else
223
- ignored.push({ type: e.type, reason: 'remove: type not in built-in catalog' });
224
- continue;
225
- }
226
- const existing = builtInByType.get(e.type);
227
- if (existing) {
228
- const changed = [];
229
- const overlayRec = e;
230
- const builtinRec = existing;
231
- for (const k of Object.keys(e)) {
232
- if (k === 'type')
233
- continue;
234
- if (JSON.stringify(overlayRec[k]) !== JSON.stringify(builtinRec[k])) {
235
- changed.push(k);
236
- }
237
- }
238
- replaced.push({ type: e.type, changedKeys: changed });
239
- }
240
- else if (e.category && e.commands) {
241
- added.push(e.type);
242
- }
243
- else {
244
- ignored.push({ type: e.type, reason: 'new entry missing required fields (category and/or commands)' });
245
- }
246
- }
247
- if (isJsonMode()) {
248
- printJson({
249
- overlayPath: overlay.path,
250
- overlayExists: overlay.exists,
251
- overlayValid: overlay.error === undefined,
252
- overlayError: overlay.error,
253
- replaced,
254
- added,
255
- removed,
256
- ignored,
257
- });
258
- return;
259
- }
260
- if (!overlay.exists) {
261
- console.log(`No overlay at ${overlay.path} — effective catalog matches built-in.`);
262
- return;
263
- }
264
- if (overlay.error) {
265
- console.log(`Overlay at ${overlay.path} is invalid: ${overlay.error}`);
266
- console.log('Effective catalog falls back to built-in.');
267
- return;
268
- }
269
- console.log(`Overlay: ${overlay.path}`);
270
- if (replaced.length === 0 && added.length === 0 && removed.length === 0 && ignored.length === 0) {
271
- console.log('(overlay file is empty — effective catalog matches built-in)');
272
- return;
273
- }
274
- if (replaced.length > 0) {
275
- console.log('\nReplaced:');
276
- for (const r of replaced) {
277
- console.log(` ~ ${r.type} (keys: ${r.changedKeys.join(', ') || '—'})`);
278
- }
279
- }
280
- if (added.length > 0) {
281
- console.log('\nAdded:');
282
- for (const t of added)
283
- console.log(` + ${t}`);
284
- }
285
- if (removed.length > 0) {
286
- console.log('\nRemoved:');
287
- for (const t of removed)
288
- console.log(` - ${t}`);
289
- }
290
- if (ignored.length > 0) {
291
- console.log('\nIgnored:');
292
- for (const i of ignored)
293
- console.log(` ! ${i.type} — ${i.reason}`);
294
- }
295
- });
296
- catalog
297
- .command('refresh')
298
- .description('Clear the in-process overlay cache (re-read on next use)')
299
- .action(() => {
300
- resetCatalogOverlayCache();
301
- const overlay = loadCatalogOverlay();
302
- if (isJsonMode()) {
303
- printJson({
304
- refreshed: true,
305
- path: overlay.path,
306
- exists: overlay.exists,
307
- valid: overlay.error === undefined,
308
- error: overlay.error,
309
- entryCount: overlay.entries.length,
310
- });
311
- return;
312
- }
313
- if (!overlay.exists) {
314
- console.log(`Overlay cache cleared. No overlay file at ${overlay.path} yet.`);
315
- return;
316
- }
317
- if (overlay.error) {
318
- console.log(`Overlay cache cleared, but the file is invalid: ${overlay.error}`);
319
- return;
320
- }
321
- console.log(`Overlay cache cleared. Loaded ${overlay.entries.length} entr${overlay.entries.length === 1 ? 'y' : 'ies'} from ${overlay.path}.`);
322
- });
323
- // Note: getCatalogOverlayPath is imported so future subcommands can surface
324
- // the path cheaply without a full overlay read; `path` currently uses the
325
- // richer loadCatalogOverlay() result instead.
326
- void getCatalogOverlayPath;
327
- }
328
- function renderEntry(entry) {
329
- console.log(`Type: ${entry.type}`);
330
- console.log(`Category: ${entry.category === 'ir' ? 'IR remote' : 'Physical device'}`);
331
- if (entry.role)
332
- console.log(`Role: ${entry.role}`);
333
- if (entry.readOnly)
334
- console.log(`ReadOnly: yes (status-only device, no control commands)`);
335
- if (entry.aliases && entry.aliases.length > 0) {
336
- console.log(`Aliases: ${entry.aliases.join(', ')}`);
337
- }
338
- if (entry.commands.length === 0) {
339
- console.log('\nCommands: (none — status-only device)');
340
- }
341
- else {
342
- console.log('\nCommands:');
343
- const rows = entry.commands.map((c) => {
344
- const tier = deriveSafetyTier(c, entry);
345
- const flags = [];
346
- if (c.commandType === 'customize')
347
- flags.push('customize');
348
- if (tier === 'destructive')
349
- flags.push('!destructive');
350
- const label = flags.length > 0 ? `${c.command} [${flags.join(', ')}]` : c.command;
351
- return [label, c.parameter, c.description];
352
- });
353
- printTable(['command', 'parameter', 'description'], rows);
354
- }
355
- if (entry.statusFields && entry.statusFields.length > 0) {
356
- console.log('\nStatus fields (from "devices status"):');
357
- console.log(' ' + entry.statusFields.join(', '));
358
- }
359
- }