@switchbot/openapi-cli 1.0.1 → 1.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 (90) hide show
  1. package/README.md +174 -18
  2. package/dist/api/client.d.ts +7 -1
  3. package/dist/api/client.js +44 -8
  4. package/dist/api/client.js.map +1 -1
  5. package/dist/commands/batch.d.ts +2 -0
  6. package/dist/commands/batch.js +252 -0
  7. package/dist/commands/batch.js.map +1 -0
  8. package/dist/commands/cache.d.ts +2 -0
  9. package/dist/commands/cache.js +108 -0
  10. package/dist/commands/cache.js.map +1 -0
  11. package/dist/commands/capabilities.d.ts +2 -0
  12. package/dist/commands/capabilities.js +91 -0
  13. package/dist/commands/capabilities.js.map +1 -0
  14. package/dist/commands/catalog.d.ts +2 -0
  15. package/dist/commands/catalog.js +291 -0
  16. package/dist/commands/catalog.js.map +1 -0
  17. package/dist/commands/config.js +123 -10
  18. package/dist/commands/config.js.map +1 -1
  19. package/dist/commands/devices.js +234 -112
  20. package/dist/commands/devices.js.map +1 -1
  21. package/dist/commands/doctor.d.ts +2 -0
  22. package/dist/commands/doctor.js +147 -0
  23. package/dist/commands/doctor.js.map +1 -0
  24. package/dist/commands/events.d.ts +15 -0
  25. package/dist/commands/events.js +188 -0
  26. package/dist/commands/events.js.map +1 -0
  27. package/dist/commands/explain.d.ts +2 -0
  28. package/dist/commands/explain.js +137 -0
  29. package/dist/commands/explain.js.map +1 -0
  30. package/dist/commands/history.d.ts +2 -0
  31. package/dist/commands/history.js +104 -0
  32. package/dist/commands/history.js.map +1 -0
  33. package/dist/commands/mcp.d.ts +4 -0
  34. package/dist/commands/mcp.js +386 -0
  35. package/dist/commands/mcp.js.map +1 -0
  36. package/dist/commands/plan.d.ts +37 -0
  37. package/dist/commands/plan.js +344 -0
  38. package/dist/commands/plan.js.map +1 -0
  39. package/dist/commands/quota.d.ts +2 -0
  40. package/dist/commands/quota.js +77 -0
  41. package/dist/commands/quota.js.map +1 -0
  42. package/dist/commands/scenes.js +19 -13
  43. package/dist/commands/scenes.js.map +1 -1
  44. package/dist/commands/schema.d.ts +2 -0
  45. package/dist/commands/schema.js +77 -0
  46. package/dist/commands/schema.js.map +1 -0
  47. package/dist/commands/watch.d.ts +2 -0
  48. package/dist/commands/watch.js +161 -0
  49. package/dist/commands/watch.js.map +1 -0
  50. package/dist/commands/webhook.js +37 -22
  51. package/dist/commands/webhook.js.map +1 -1
  52. package/dist/config.d.ts +11 -0
  53. package/dist/config.js +32 -6
  54. package/dist/config.js.map +1 -1
  55. package/dist/devices/cache.d.ts +75 -0
  56. package/dist/devices/cache.js +225 -0
  57. package/dist/devices/cache.js.map +1 -0
  58. package/dist/devices/catalog.d.ts +49 -0
  59. package/dist/devices/catalog.js +362 -92
  60. package/dist/devices/catalog.js.map +1 -1
  61. package/dist/index.js +31 -1
  62. package/dist/index.js.map +1 -1
  63. package/dist/lib/devices.d.ts +144 -0
  64. package/dist/lib/devices.js +329 -0
  65. package/dist/lib/devices.js.map +1 -0
  66. package/dist/lib/scenes.d.ts +7 -0
  67. package/dist/lib/scenes.js +11 -0
  68. package/dist/lib/scenes.js.map +1 -0
  69. package/dist/utils/audit.d.ts +13 -0
  70. package/dist/utils/audit.js +43 -0
  71. package/dist/utils/audit.js.map +1 -0
  72. package/dist/utils/filter.d.ts +45 -0
  73. package/dist/utils/filter.js +96 -0
  74. package/dist/utils/filter.js.map +1 -0
  75. package/dist/utils/flags.d.ts +42 -0
  76. package/dist/utils/flags.js +108 -0
  77. package/dist/utils/flags.js.map +1 -1
  78. package/dist/utils/format.d.ts +9 -0
  79. package/dist/utils/format.js +109 -0
  80. package/dist/utils/format.js.map +1 -0
  81. package/dist/utils/output.d.ts +11 -0
  82. package/dist/utils/output.js +37 -6
  83. package/dist/utils/output.js.map +1 -1
  84. package/dist/utils/quota.d.ts +48 -0
  85. package/dist/utils/quota.js +144 -0
  86. package/dist/utils/quota.js.map +1 -0
  87. package/dist/utils/retry.d.ts +23 -0
  88. package/dist/utils/retry.js +60 -0
  89. package/dist/utils/retry.js.map +1 -0
  90. package/package.json +4 -1
@@ -0,0 +1,147 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { printJson, isJsonMode } from '../utils/output.js';
5
+ import { getEffectiveCatalog } from '../devices/catalog.js';
6
+ import { configFilePath, listProfiles } from '../config.js';
7
+ import { describeCache } from '../devices/cache.js';
8
+ async function checkCredentials() {
9
+ const envOk = Boolean(process.env.SWITCHBOT_TOKEN && process.env.SWITCHBOT_SECRET);
10
+ if (envOk)
11
+ return { name: 'credentials', status: 'ok', detail: 'env: SWITCHBOT_TOKEN + SWITCHBOT_SECRET' };
12
+ const file = configFilePath();
13
+ if (!fs.existsSync(file)) {
14
+ return {
15
+ name: 'credentials',
16
+ status: 'fail',
17
+ detail: `No env vars and no config at ${file}. Run 'switchbot config set-token'.`,
18
+ };
19
+ }
20
+ try {
21
+ const raw = fs.readFileSync(file, 'utf-8');
22
+ const cfg = JSON.parse(raw);
23
+ if (!cfg.token || !cfg.secret) {
24
+ return { name: 'credentials', status: 'fail', detail: `Config ${file} missing token/secret.` };
25
+ }
26
+ return { name: 'credentials', status: 'ok', detail: `file: ${file}` };
27
+ }
28
+ catch (err) {
29
+ return {
30
+ name: 'credentials',
31
+ status: 'fail',
32
+ detail: `Unreadable config ${file}: ${err instanceof Error ? err.message : String(err)}`,
33
+ };
34
+ }
35
+ }
36
+ function checkProfiles() {
37
+ const dir = path.join(os.homedir(), '.switchbot', 'profiles');
38
+ if (!fs.existsSync(dir)) {
39
+ return { name: 'profiles', status: 'ok', detail: 'no profile dir (default profile only)' };
40
+ }
41
+ const profiles = listProfiles();
42
+ return {
43
+ name: 'profiles',
44
+ status: 'ok',
45
+ detail: profiles.length ? `found ${profiles.length}: ${profiles.join(', ')}` : 'profile dir empty',
46
+ };
47
+ }
48
+ function checkClockSkew() {
49
+ const now = Date.now();
50
+ const drift = now - Math.floor(now / 1000) * 1000;
51
+ // HMAC signing uses ms timestamps — we can't detect remote skew without a
52
+ // round-trip, but we can flag if the local clock has NTP issues via the
53
+ // classic "jumps back" pattern. Best-effort: just report local time.
54
+ const iso = new Date().toISOString();
55
+ return { name: 'clock', status: 'ok', detail: `local time ${iso} (drift check needs API round-trip)` };
56
+ void drift;
57
+ }
58
+ function checkCatalog() {
59
+ const catalog = getEffectiveCatalog();
60
+ const missingRole = catalog.filter((e) => !e.role).length;
61
+ if (catalog.length === 0) {
62
+ return { name: 'catalog', status: 'fail', detail: 'catalog empty — package corrupt?' };
63
+ }
64
+ const status = missingRole > 0 ? 'warn' : 'ok';
65
+ return {
66
+ name: 'catalog',
67
+ status,
68
+ detail: `${catalog.length} types loaded${missingRole > 0 ? `, ${missingRole} missing role` : ''}`,
69
+ };
70
+ }
71
+ function checkCache() {
72
+ try {
73
+ const info = describeCache();
74
+ const parts = [];
75
+ parts.push(info.list.exists ? `list: ${info.list.path}` : 'list: (none)');
76
+ parts.push(info.status.exists ? `status: ${info.status.entryCount} entries` : 'status: (none)');
77
+ return { name: 'cache', status: 'ok', detail: parts.join(' | ') };
78
+ }
79
+ catch (err) {
80
+ return { name: 'cache', status: 'warn', detail: `cache inspect failed: ${err instanceof Error ? err.message : String(err)}` };
81
+ }
82
+ }
83
+ function checkQuotaFile() {
84
+ const p = path.join(os.homedir(), '.switchbot', 'quota.json');
85
+ if (!fs.existsSync(p)) {
86
+ return { name: 'quota', status: 'ok', detail: 'no quota file yet (will be created on first call)' };
87
+ }
88
+ try {
89
+ const raw = fs.readFileSync(p, 'utf-8');
90
+ JSON.parse(raw);
91
+ return { name: 'quota', status: 'ok', detail: p };
92
+ }
93
+ catch {
94
+ return { name: 'quota', status: 'warn', detail: `${p} unreadable/malformed — run 'switchbot quota reset'` };
95
+ }
96
+ }
97
+ function checkNodeVersion() {
98
+ const major = Number(process.versions.node.split('.')[0]);
99
+ if (Number.isFinite(major) && major < 18) {
100
+ return { name: 'node', status: 'fail', detail: `Node ${process.versions.node} — minimum is 18` };
101
+ }
102
+ return { name: 'node', status: 'ok', detail: `Node ${process.versions.node}` };
103
+ }
104
+ export function registerDoctorCommand(program) {
105
+ program
106
+ .command('doctor')
107
+ .description('Self-check: credentials, catalog, cache, quota, profiles, Node version')
108
+ .addHelpText('after', `
109
+ Runs a battery of local sanity checks and exits with code 0 only when every
110
+ check is 'ok'. 'warn' → exit 0 (informational); 'fail' → exit 1.
111
+
112
+ Examples:
113
+ $ switchbot doctor
114
+ $ switchbot --json doctor | jq '.checks[] | select(.status != "ok")'
115
+ `)
116
+ .action(async () => {
117
+ const checks = [
118
+ checkNodeVersion(),
119
+ await checkCredentials(),
120
+ checkProfiles(),
121
+ checkCatalog(),
122
+ checkCache(),
123
+ checkQuotaFile(),
124
+ checkClockSkew(),
125
+ ];
126
+ const summary = {
127
+ ok: checks.filter((c) => c.status === 'ok').length,
128
+ warn: checks.filter((c) => c.status === 'warn').length,
129
+ fail: checks.filter((c) => c.status === 'fail').length,
130
+ };
131
+ const overallFail = summary.fail > 0;
132
+ if (isJsonMode()) {
133
+ printJson({ overall: overallFail ? 'fail' : summary.warn > 0 ? 'warn' : 'ok', summary, checks });
134
+ }
135
+ else {
136
+ for (const c of checks) {
137
+ const icon = c.status === 'ok' ? '✓' : c.status === 'warn' ? '!' : '✗';
138
+ console.log(`${icon} ${c.name.padEnd(12)} ${c.detail}`);
139
+ }
140
+ console.log('');
141
+ console.log(`${summary.ok} ok, ${summary.warn} warn, ${summary.fail} fail`);
142
+ }
143
+ if (overallFail)
144
+ process.exit(1);
145
+ });
146
+ }
147
+ //# sourceMappingURL=doctor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import { Command } from 'commander';
2
+ import http from 'node:http';
3
+ interface EventRecord {
4
+ t: string;
5
+ remote: string;
6
+ path: string;
7
+ body: unknown;
8
+ matched: boolean;
9
+ }
10
+ export declare function startReceiver(port: number, pathMatch: string, filter: {
11
+ deviceId?: string;
12
+ type?: string;
13
+ } | null, onEvent: (ev: EventRecord) => void): http.Server;
14
+ export declare function registerEventsCommand(program: Command): void;
15
+ export {};
@@ -0,0 +1,188 @@
1
+ import http from 'node:http';
2
+ import { printJson, isJsonMode, handleError, UsageError } from '../utils/output.js';
3
+ const DEFAULT_PORT = 3000;
4
+ const DEFAULT_PATH = '/';
5
+ const MAX_BODY_BYTES = 1_000_000;
6
+ function matchFilter(body, filter) {
7
+ if (!filter)
8
+ return true;
9
+ if (!body || typeof body !== 'object')
10
+ return false;
11
+ const b = body;
12
+ const ctx = (b.context ?? b);
13
+ if (filter.deviceId && ctx.deviceMac !== filter.deviceId && ctx.deviceId !== filter.deviceId) {
14
+ return false;
15
+ }
16
+ if (filter.type && ctx.deviceType !== filter.type) {
17
+ return false;
18
+ }
19
+ return true;
20
+ }
21
+ function parseFilter(flag) {
22
+ if (!flag)
23
+ return null;
24
+ const allowed = new Set(['deviceId', 'type']);
25
+ const out = {};
26
+ for (const pair of flag.split(',')) {
27
+ const eq = pair.indexOf('=');
28
+ if (eq === -1 || eq === 0) {
29
+ throw new UsageError(`Invalid --filter pair "${pair.trim()}". Expected "key=value". Supported keys: deviceId, type.`);
30
+ }
31
+ const k = pair.slice(0, eq).trim();
32
+ const v = pair.slice(eq + 1).trim();
33
+ if (!v) {
34
+ throw new UsageError(`Empty value for --filter key "${k}". Expected "key=value". Supported keys: deviceId, type.`);
35
+ }
36
+ if (!allowed.has(k)) {
37
+ throw new UsageError(`Unknown --filter key "${k}". Supported keys: deviceId, type.`);
38
+ }
39
+ if (k === 'deviceId')
40
+ out.deviceId = v;
41
+ else if (k === 'type')
42
+ out.type = v;
43
+ }
44
+ return out;
45
+ }
46
+ export function startReceiver(port, pathMatch, filter, onEvent) {
47
+ const server = http.createServer((req, res) => {
48
+ if (req.method !== 'POST') {
49
+ res.statusCode = 405;
50
+ res.end('method not allowed');
51
+ return;
52
+ }
53
+ if (req.url !== pathMatch && pathMatch !== '*') {
54
+ res.statusCode = 404;
55
+ res.end('not found');
56
+ return;
57
+ }
58
+ const chunks = [];
59
+ let size = 0;
60
+ let bailed = false;
61
+ req.on('data', (c) => {
62
+ if (bailed)
63
+ return;
64
+ size += c.length;
65
+ if (size > MAX_BODY_BYTES) {
66
+ bailed = true;
67
+ res.statusCode = 413;
68
+ res.setHeader('connection', 'close');
69
+ res.end('payload too large');
70
+ // Drop remaining upload without destroying the socket mid-flush.
71
+ req.on('data', () => { });
72
+ return;
73
+ }
74
+ chunks.push(c);
75
+ });
76
+ req.on('end', () => {
77
+ if (bailed)
78
+ return;
79
+ const raw = Buffer.concat(chunks).toString('utf-8');
80
+ let body = raw;
81
+ try {
82
+ body = JSON.parse(raw);
83
+ }
84
+ catch {
85
+ // keep raw
86
+ }
87
+ const matched = matchFilter(body, filter);
88
+ onEvent({
89
+ t: new Date().toISOString(),
90
+ remote: `${req.socket.remoteAddress ?? ''}:${req.socket.remotePort ?? ''}`,
91
+ path: req.url ?? '/',
92
+ body,
93
+ matched,
94
+ });
95
+ res.statusCode = 204;
96
+ res.end();
97
+ });
98
+ });
99
+ server.listen(port);
100
+ return server;
101
+ }
102
+ export function registerEventsCommand(program) {
103
+ const events = program
104
+ .command('events')
105
+ .description('Subscribe to local webhook events forwarded by SwitchBot');
106
+ events
107
+ .command('tail')
108
+ .description('Run a local HTTP receiver and print incoming webhook events as JSONL')
109
+ .option('--port <n>', `Local port to listen on (default ${DEFAULT_PORT})`, String(DEFAULT_PORT))
110
+ .option('--path <p>', `HTTP path to match (default "${DEFAULT_PATH}"; use "*" for all paths)`, DEFAULT_PATH)
111
+ .option('--filter <expr>', 'Filter events, e.g. "deviceId=ABC123" or "type=Bot" (comma-separated)')
112
+ .option('--max <n>', 'Stop after N matching events (default: run until Ctrl-C)')
113
+ .addHelpText('after', `
114
+ SwitchBot posts events to a single webhook URL configured via:
115
+ $ switchbot webhook setup https://<your-public-host>/<path>
116
+
117
+ 'events tail' only runs the LOCAL receiver — it does not tunnel. Expose
118
+ the port to the internet yourself (ngrok/cloudflared/reverse proxy) and
119
+ point the SwitchBot webhook at that public URL.
120
+
121
+ Output (JSONL, one event per line):
122
+ { "t": "<ISO>", "remote": "<ip:port>", "path": "/",
123
+ "body": <parsed JSON or raw string>, "matched": true }
124
+
125
+ Filter grammar: comma-separated "key=value" pairs. Supported keys:
126
+ deviceId=<id> match by context.deviceMac / context.deviceId
127
+ type=<type> match by context.deviceType (e.g. "Bot", "WoMeter")
128
+
129
+ Examples:
130
+ $ switchbot events tail --port 3000
131
+ $ switchbot events tail --port 3000 --filter deviceId=ABC123
132
+ $ switchbot events tail --filter 'type=WoMeter' --max 5 --json
133
+ `)
134
+ .action(async (options) => {
135
+ try {
136
+ const port = Number(options.port);
137
+ if (!Number.isInteger(port) || port <= 0 || port > 65535) {
138
+ throw new UsageError(`Invalid --port "${options.port}". Must be 1..65535.`);
139
+ }
140
+ const maxMatched = options.max !== undefined ? Number(options.max) : null;
141
+ if (maxMatched !== null && (!Number.isFinite(maxMatched) || maxMatched < 1)) {
142
+ throw new UsageError(`Invalid --max "${options.max}". Must be a positive integer.`);
143
+ }
144
+ const filter = parseFilter(options.filter);
145
+ let matchedCount = 0;
146
+ const ac = new AbortController();
147
+ await new Promise((resolve, reject) => {
148
+ let server = null;
149
+ try {
150
+ server = startReceiver(port, options.path, filter, (ev) => {
151
+ if (!ev.matched)
152
+ return;
153
+ matchedCount++;
154
+ if (isJsonMode()) {
155
+ printJson(ev);
156
+ }
157
+ else {
158
+ const when = new Date(ev.t).toLocaleTimeString();
159
+ console.log(`[${when}] ${ev.remote} ${ev.path} ${JSON.stringify(ev.body)}`);
160
+ }
161
+ if (maxMatched !== null && matchedCount >= maxMatched) {
162
+ ac.abort();
163
+ }
164
+ });
165
+ server.on('error', (err) => reject(err));
166
+ }
167
+ catch (err) {
168
+ reject(err);
169
+ return;
170
+ }
171
+ const startMsg = `Listening on http://127.0.0.1:${port}${options.path} (Ctrl-C to stop)`;
172
+ if (!isJsonMode())
173
+ console.error(startMsg);
174
+ const cleanup = () => {
175
+ server?.close();
176
+ resolve();
177
+ };
178
+ process.once('SIGINT', cleanup);
179
+ process.once('SIGTERM', cleanup);
180
+ ac.signal.addEventListener('abort', cleanup, { once: true });
181
+ });
182
+ }
183
+ catch (error) {
184
+ handleError(error);
185
+ }
186
+ });
187
+ }
188
+ //# sourceMappingURL=events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/commands/events.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerExplainCommand(devices: Command): void;
@@ -0,0 +1,137 @@
1
+ import { printJson, isJsonMode, handleError } from '../utils/output.js';
2
+ import { describeDevice, fetchDeviceList, } from '../lib/devices.js';
3
+ function deviceName(d) {
4
+ return d.deviceName;
5
+ }
6
+ export function registerExplainCommand(devices) {
7
+ devices
8
+ .command('explain')
9
+ .description('One-shot device summary: metadata + capabilities + live status + children (for Hubs)')
10
+ .argument('<deviceId>', 'Device ID to explain')
11
+ .option('--no-live', 'Skip the live status API call (catalog-only output)')
12
+ .addHelpText('after', `
13
+ 'explain' is the agent-friendly sibling of 'describe'. It combines:
14
+ - metadata (id, name, type, category, role)
15
+ - live status (unless --no-live)
16
+ - commands with idempotent/destructive flags
17
+ - children (for Hub devices: IR remotes bound to this hub)
18
+ - suggested actions (pre-baked common usages)
19
+ - warnings (deprecated types, missing cloud service, etc.)
20
+
21
+ Examples:
22
+ $ switchbot devices explain <id>
23
+ $ switchbot --json devices explain <id> | jq '.commands[] | select(.destructive)'
24
+ $ switchbot devices explain <id> --no-live
25
+ `)
26
+ .action(async (deviceId, options) => {
27
+ try {
28
+ const wantLive = options.live !== false;
29
+ const desc = await describeDevice(deviceId, { live: wantLive });
30
+ const warnings = [];
31
+ if (desc.isPhysical && !desc.device.enableCloudService) {
32
+ warnings.push('Cloud service disabled on this device — commands will fail.');
33
+ }
34
+ if (!desc.catalog) {
35
+ warnings.push(`No catalog entry for type "${desc.typeName}". Commands cannot be validated offline.`);
36
+ }
37
+ let children = [];
38
+ if (desc.catalog?.role === 'hub') {
39
+ const body = await fetchDeviceList();
40
+ children = body.infraredRemoteList
41
+ .filter((ir) => ir.hubDeviceId === deviceId)
42
+ .map((ir) => ({ deviceId: ir.deviceId, name: ir.deviceName, type: ir.remoteType }));
43
+ }
44
+ const caps = desc.capabilities;
45
+ const commands = caps && 'commands' in caps
46
+ ? caps.commands.map((c) => ({
47
+ command: c.command,
48
+ parameter: c.parameter,
49
+ idempotent: c.idempotent,
50
+ destructive: c.destructive,
51
+ }))
52
+ : [];
53
+ const statusFields = caps && 'statusFields' in caps ? caps.statusFields : [];
54
+ const liveStatus = caps && 'liveStatus' in caps ? caps.liveStatus : undefined;
55
+ const location = desc.isPhysical
56
+ ? {
57
+ family: desc.device.familyName,
58
+ room: desc.device.roomName ?? undefined,
59
+ }
60
+ : desc.inheritedLocation
61
+ ? { family: desc.inheritedLocation.family, room: desc.inheritedLocation.room }
62
+ : undefined;
63
+ const result = {
64
+ deviceId,
65
+ type: desc.typeName,
66
+ category: desc.isPhysical ? 'physical' : 'ir',
67
+ name: deviceName(desc.device),
68
+ role: desc.catalog?.role ?? null,
69
+ readOnly: desc.catalog?.readOnly ?? false,
70
+ location,
71
+ liveStatus,
72
+ commands,
73
+ statusFields,
74
+ children,
75
+ suggestedActions: desc.suggestedActions,
76
+ warnings,
77
+ };
78
+ if (isJsonMode()) {
79
+ printJson(result);
80
+ return;
81
+ }
82
+ printHuman(result);
83
+ }
84
+ catch (err) {
85
+ handleError(err);
86
+ }
87
+ });
88
+ }
89
+ function printHuman(r) {
90
+ console.log(`# ${r.name} (${r.deviceId})`);
91
+ console.log(`type: ${r.type} [${r.category}${r.role ? ', ' + r.role : ''}${r.readOnly ? ', read-only' : ''}]`);
92
+ if (r.location?.family || r.location?.room) {
93
+ const loc = [r.location?.family, r.location?.room].filter(Boolean).join(' / ');
94
+ console.log(`location: ${loc}`);
95
+ }
96
+ if (r.warnings.length) {
97
+ console.log('warnings:');
98
+ for (const w of r.warnings)
99
+ console.log(` ! ${w}`);
100
+ }
101
+ if (r.liveStatus && !('error' in r.liveStatus)) {
102
+ console.log('live status:');
103
+ for (const [k, v] of Object.entries(r.liveStatus)) {
104
+ console.log(` ${k}: ${JSON.stringify(v)}`);
105
+ }
106
+ }
107
+ else if (r.liveStatus && 'error' in r.liveStatus) {
108
+ console.log(`live status: error — ${r.liveStatus.error}`);
109
+ }
110
+ if (r.commands.length) {
111
+ console.log('commands:');
112
+ for (const c of r.commands) {
113
+ const flags = [c.idempotent && 'idempotent', c.destructive && 'destructive']
114
+ .filter(Boolean)
115
+ .join(', ');
116
+ const suffix = flags ? ` [${flags}]` : '';
117
+ console.log(` ${c.command}${c.parameter !== '—' ? ` <${c.parameter}>` : ''}${suffix}`);
118
+ }
119
+ }
120
+ if (r.statusFields.length) {
121
+ console.log(`status fields: ${r.statusFields.join(', ')}`);
122
+ }
123
+ if (r.children.length) {
124
+ console.log(`children (${r.children.length}):`);
125
+ for (const c of r.children) {
126
+ console.log(` ${c.deviceId} ${c.name} [${c.type}]`);
127
+ }
128
+ }
129
+ if (r.suggestedActions.length) {
130
+ console.log('suggested:');
131
+ for (const s of r.suggestedActions) {
132
+ const param = s.parameter ? ` ${s.parameter}` : '';
133
+ console.log(` ${s.description}: ${s.command}${param}`);
134
+ }
135
+ }
136
+ }
137
+ //# sourceMappingURL=explain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"explain.js","sourceRoot":"","sources":["../../src/commands/explain.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerHistoryCommand(program: Command): void;
@@ -0,0 +1,104 @@
1
+ import path from 'node:path';
2
+ import os from 'node:os';
3
+ import { printJson, isJsonMode, handleError } from '../utils/output.js';
4
+ import { readAudit } from '../utils/audit.js';
5
+ import { executeCommand } from '../lib/devices.js';
6
+ const DEFAULT_AUDIT = path.join(os.homedir(), '.switchbot', 'audit.log');
7
+ export function registerHistoryCommand(program) {
8
+ const history = program
9
+ .command('history')
10
+ .description('View and replay commands recorded via --audit-log')
11
+ .addHelpText('after', `
12
+ Every 'devices command' run with --audit-log is appended as JSONL to the
13
+ audit file (default ~/.switchbot/audit.log). 'history show' prints the file,
14
+ 'history replay <n>' re-runs the Nth entry (1-indexed, most-recent last).
15
+
16
+ Examples:
17
+ $ switchbot --audit-log devices command <id> turnOff
18
+ $ switchbot history show --limit 10
19
+ $ switchbot history replay 3
20
+ `);
21
+ history
22
+ .command('show')
23
+ .description('Print recent audit entries')
24
+ .option('--file <path>', `Path to the audit log (default ${DEFAULT_AUDIT})`)
25
+ .option('--limit <n>', 'Show only the last N entries')
26
+ .action((options) => {
27
+ const file = options.file ?? DEFAULT_AUDIT;
28
+ const entries = readAudit(file);
29
+ const limited = options.limit !== undefined
30
+ ? entries.slice(-Math.max(1, Number(options.limit) || 1))
31
+ : entries;
32
+ if (isJsonMode()) {
33
+ printJson({ file, total: entries.length, entries: limited });
34
+ return;
35
+ }
36
+ if (entries.length === 0) {
37
+ console.log(`(no entries in ${file})`);
38
+ return;
39
+ }
40
+ const startIdx = entries.length - limited.length;
41
+ limited.forEach((e, i) => {
42
+ const idx = startIdx + i + 1;
43
+ const mark = e.result === 'error' ? '✗' : e.dryRun ? '◦' : '✓';
44
+ const param = e.parameter !== undefined && e.parameter !== 'default'
45
+ ? ` ${JSON.stringify(e.parameter)}`
46
+ : '';
47
+ const err = e.error ? ` [err: ${e.error}]` : '';
48
+ console.log(`${String(idx).padStart(4)} ${mark} ${e.t} ${e.deviceId} ${e.command}${param}${err}`);
49
+ });
50
+ });
51
+ history
52
+ .command('replay')
53
+ .description('Re-run a recorded command by its 1-indexed position')
54
+ .argument('<index>', 'Entry index (1 = oldest; as shown by "history show")')
55
+ .option('--file <path>', `Path to the audit log (default ${DEFAULT_AUDIT})`)
56
+ .addHelpText('after', `
57
+ Dry-run-honouring: pass --dry-run on the parent command to preview without
58
+ sending the actual call. Errors from the recorded entry are NOT replayed —
59
+ replay always attempts the command fresh.
60
+
61
+ Examples:
62
+ $ switchbot history replay 3
63
+ $ switchbot --dry-run history replay 3
64
+ `)
65
+ .action(async (indexArg, options) => {
66
+ const file = options.file ?? DEFAULT_AUDIT;
67
+ const entries = readAudit(file);
68
+ const idx = Number(indexArg);
69
+ if (!Number.isInteger(idx) || idx < 1 || idx > entries.length) {
70
+ const msg = `Invalid index ${indexArg}. Log has ${entries.length} entries.`;
71
+ if (isJsonMode()) {
72
+ console.error(JSON.stringify({ error: { code: 2, kind: 'usage', message: msg } }));
73
+ }
74
+ else {
75
+ console.error(msg);
76
+ }
77
+ process.exit(2);
78
+ }
79
+ const entry = entries[idx - 1];
80
+ if (entry.kind !== 'command') {
81
+ const msg = `Entry ${idx} is not a command (kind=${entry.kind}).`;
82
+ if (isJsonMode()) {
83
+ console.error(JSON.stringify({ error: { code: 2, kind: 'usage', message: msg } }));
84
+ }
85
+ else {
86
+ console.error(msg);
87
+ }
88
+ process.exit(2);
89
+ }
90
+ try {
91
+ const result = await executeCommand(entry.deviceId, entry.command, entry.parameter, entry.commandType);
92
+ if (isJsonMode()) {
93
+ printJson({ replayed: entry, result });
94
+ }
95
+ else {
96
+ console.log(`✓ replayed ${entry.command} on ${entry.deviceId}`);
97
+ }
98
+ }
99
+ catch (err) {
100
+ handleError(err);
101
+ }
102
+ });
103
+ }
104
+ //# sourceMappingURL=history.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"history.js","sourceRoot":"","sources":["../../src/commands/history.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { Command } from 'commander';
2
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
+ export declare function createSwitchBotMcpServer(): McpServer;
4
+ export declare function registerMcpCommand(program: Command): void;