@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.
- package/README.md +34 -42
- package/dist/index.js +56945 -169
- package/dist/policy/schema/v0.2.json +1 -1
- package/package.json +3 -2
- package/dist/api/client.js +0 -235
- package/dist/auth.js +0 -20
- package/dist/commands/agent-bootstrap.js +0 -182
- package/dist/commands/auth.js +0 -354
- package/dist/commands/batch.js +0 -413
- package/dist/commands/cache.js +0 -126
- package/dist/commands/capabilities.js +0 -385
- package/dist/commands/catalog.js +0 -359
- package/dist/commands/completion.js +0 -385
- package/dist/commands/config.js +0 -376
- package/dist/commands/daemon.js +0 -367
- package/dist/commands/device-meta.js +0 -159
- package/dist/commands/devices.js +0 -948
- package/dist/commands/doctor.js +0 -1015
- package/dist/commands/events.js +0 -563
- package/dist/commands/expand.js +0 -130
- package/dist/commands/explain.js +0 -139
- package/dist/commands/health.js +0 -113
- package/dist/commands/history.js +0 -320
- package/dist/commands/identity.js +0 -59
- package/dist/commands/install.js +0 -246
- package/dist/commands/mcp.js +0 -2017
- package/dist/commands/plan.js +0 -653
- package/dist/commands/policy.js +0 -586
- package/dist/commands/quota.js +0 -78
- package/dist/commands/rules.js +0 -875
- package/dist/commands/scenes.js +0 -264
- package/dist/commands/schema.js +0 -177
- package/dist/commands/status-sync.js +0 -131
- package/dist/commands/uninstall.js +0 -237
- package/dist/commands/upgrade-check.js +0 -88
- package/dist/commands/watch.js +0 -194
- package/dist/commands/webhook.js +0 -182
- package/dist/config.js +0 -258
- package/dist/credentials/backends/file.js +0 -101
- package/dist/credentials/backends/linux.js +0 -129
- package/dist/credentials/backends/macos.js +0 -129
- package/dist/credentials/backends/windows.js +0 -215
- package/dist/credentials/keychain.js +0 -88
- package/dist/credentials/prime.js +0 -52
- package/dist/devices/cache.js +0 -293
- package/dist/devices/catalog.js +0 -767
- package/dist/devices/device-meta.js +0 -56
- package/dist/devices/history-agg.js +0 -138
- package/dist/devices/history-query.js +0 -181
- package/dist/devices/param-validator.js +0 -433
- package/dist/devices/resources.js +0 -270
- package/dist/install/default-steps.js +0 -257
- package/dist/install/preflight.js +0 -212
- package/dist/install/steps.js +0 -67
- package/dist/lib/command-keywords.js +0 -17
- package/dist/lib/daemon-state.js +0 -46
- package/dist/lib/destructive-mode.js +0 -12
- package/dist/lib/devices.js +0 -382
- package/dist/lib/idempotency.js +0 -106
- package/dist/lib/plan-store.js +0 -68
- package/dist/lib/request-context.js +0 -12
- package/dist/lib/scenes.js +0 -10
- package/dist/logger.js +0 -16
- package/dist/mcp/device-history.js +0 -145
- package/dist/mcp/events-subscription.js +0 -213
- package/dist/mqtt/client.js +0 -180
- package/dist/mqtt/credential.js +0 -30
- package/dist/policy/add-rule.js +0 -124
- package/dist/policy/diff.js +0 -91
- package/dist/policy/format.js +0 -57
- package/dist/policy/load.js +0 -61
- package/dist/policy/migrate.js +0 -67
- package/dist/policy/schema.js +0 -18
- package/dist/policy/validate.js +0 -262
- package/dist/rules/action.js +0 -205
- package/dist/rules/audit-query.js +0 -89
- package/dist/rules/conflict-analyzer.js +0 -203
- package/dist/rules/cron-scheduler.js +0 -186
- package/dist/rules/destructive.js +0 -52
- package/dist/rules/engine.js +0 -757
- package/dist/rules/matcher.js +0 -230
- package/dist/rules/pid-file.js +0 -95
- package/dist/rules/quiet-hours.js +0 -45
- package/dist/rules/suggest.js +0 -95
- package/dist/rules/throttle.js +0 -116
- package/dist/rules/types.js +0 -34
- package/dist/rules/webhook-listener.js +0 -223
- package/dist/rules/webhook-token.js +0 -90
- package/dist/schema/field-aliases.js +0 -131
- package/dist/sinks/dispatcher.js +0 -12
- package/dist/sinks/file.js +0 -19
- package/dist/sinks/format.js +0 -56
- package/dist/sinks/homeassistant.js +0 -44
- package/dist/sinks/openclaw.js +0 -33
- package/dist/sinks/stdout.js +0 -5
- package/dist/sinks/telegram.js +0 -28
- package/dist/sinks/types.js +0 -1
- package/dist/sinks/webhook.js +0 -22
- package/dist/status-sync/manager.js +0 -268
- package/dist/utils/arg-parsers.js +0 -66
- package/dist/utils/audit.js +0 -117
- package/dist/utils/filter.js +0 -189
- package/dist/utils/flags.js +0 -186
- package/dist/utils/format.js +0 -117
- package/dist/utils/health.js +0 -101
- package/dist/utils/help-json.js +0 -54
- package/dist/utils/name-resolver.js +0 -137
- package/dist/utils/output.js +0 -404
- package/dist/utils/quota.js +0 -227
- package/dist/utils/redact.js +0 -68
- package/dist/utils/retry.js +0 -140
- package/dist/utils/string.js +0 -22
- package/dist/version.js +0 -4
package/dist/commands/catalog.js
DELETED
|
@@ -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
|
-
}
|