@switchbot/openapi-cli 1.1.0 → 1.3.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 (105) 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/device-meta.d.ts +2 -0
  20. package/dist/commands/device-meta.js +142 -0
  21. package/dist/commands/device-meta.js.map +1 -0
  22. package/dist/commands/devices.js +272 -152
  23. package/dist/commands/devices.js.map +1 -1
  24. package/dist/commands/doctor.d.ts +2 -0
  25. package/dist/commands/doctor.js +147 -0
  26. package/dist/commands/doctor.js.map +1 -0
  27. package/dist/commands/events.d.ts +15 -0
  28. package/dist/commands/events.js +188 -0
  29. package/dist/commands/events.js.map +1 -0
  30. package/dist/commands/expand.d.ts +2 -0
  31. package/dist/commands/expand.js +192 -0
  32. package/dist/commands/expand.js.map +1 -0
  33. package/dist/commands/explain.d.ts +2 -0
  34. package/dist/commands/explain.js +137 -0
  35. package/dist/commands/explain.js.map +1 -0
  36. package/dist/commands/history.d.ts +2 -0
  37. package/dist/commands/history.js +104 -0
  38. package/dist/commands/history.js.map +1 -0
  39. package/dist/commands/mcp.d.ts +4 -0
  40. package/dist/commands/mcp.js +386 -0
  41. package/dist/commands/mcp.js.map +1 -0
  42. package/dist/commands/plan.d.ts +38 -0
  43. package/dist/commands/plan.js +356 -0
  44. package/dist/commands/plan.js.map +1 -0
  45. package/dist/commands/quota.d.ts +2 -0
  46. package/dist/commands/quota.js +77 -0
  47. package/dist/commands/quota.js.map +1 -0
  48. package/dist/commands/scenes.js +19 -13
  49. package/dist/commands/scenes.js.map +1 -1
  50. package/dist/commands/schema.d.ts +2 -0
  51. package/dist/commands/schema.js +77 -0
  52. package/dist/commands/schema.js.map +1 -0
  53. package/dist/commands/watch.d.ts +2 -0
  54. package/dist/commands/watch.js +161 -0
  55. package/dist/commands/watch.js.map +1 -0
  56. package/dist/commands/webhook.js +37 -22
  57. package/dist/commands/webhook.js.map +1 -1
  58. package/dist/config.d.ts +11 -0
  59. package/dist/config.js +32 -6
  60. package/dist/config.js.map +1 -1
  61. package/dist/devices/cache.d.ts +50 -0
  62. package/dist/devices/cache.js +152 -1
  63. package/dist/devices/cache.js.map +1 -1
  64. package/dist/devices/catalog.d.ts +49 -0
  65. package/dist/devices/catalog.js +362 -92
  66. package/dist/devices/catalog.js.map +1 -1
  67. package/dist/devices/device-meta.d.ts +15 -0
  68. package/dist/devices/device-meta.js +52 -0
  69. package/dist/devices/device-meta.js.map +1 -0
  70. package/dist/index.js +31 -1
  71. package/dist/index.js.map +1 -1
  72. package/dist/lib/devices.d.ts +144 -0
  73. package/dist/lib/devices.js +329 -0
  74. package/dist/lib/devices.js.map +1 -0
  75. package/dist/lib/scenes.d.ts +7 -0
  76. package/dist/lib/scenes.js +11 -0
  77. package/dist/lib/scenes.js.map +1 -0
  78. package/dist/utils/audit.d.ts +13 -0
  79. package/dist/utils/audit.js +43 -0
  80. package/dist/utils/audit.js.map +1 -0
  81. package/dist/utils/filter.d.ts +45 -0
  82. package/dist/utils/filter.js +96 -0
  83. package/dist/utils/filter.js.map +1 -0
  84. package/dist/utils/flags.d.ts +42 -0
  85. package/dist/utils/flags.js +108 -0
  86. package/dist/utils/flags.js.map +1 -1
  87. package/dist/utils/format.d.ts +9 -0
  88. package/dist/utils/format.js +109 -0
  89. package/dist/utils/format.js.map +1 -0
  90. package/dist/utils/name-resolver.d.ts +17 -0
  91. package/dist/utils/name-resolver.js +78 -0
  92. package/dist/utils/name-resolver.js.map +1 -0
  93. package/dist/utils/output.d.ts +18 -0
  94. package/dist/utils/output.js +66 -6
  95. package/dist/utils/output.js.map +1 -1
  96. package/dist/utils/quota.d.ts +48 -0
  97. package/dist/utils/quota.js +144 -0
  98. package/dist/utils/quota.js.map +1 -0
  99. package/dist/utils/retry.d.ts +23 -0
  100. package/dist/utils/retry.js +60 -0
  101. package/dist/utils/retry.js.map +1 -0
  102. package/dist/utils/string.d.ts +2 -0
  103. package/dist/utils/string.js +23 -0
  104. package/dist/utils/string.js.map +1 -0
  105. package/package.json +4 -1
@@ -31,4 +31,112 @@ export function getTimeout() {
31
31
  export function getConfigPath() {
32
32
  return getFlagValue('--config');
33
33
  }
34
+ /** Named profile → ~/.switchbot/profiles/<name>.json. */
35
+ export function getProfile() {
36
+ return getFlagValue('--profile');
37
+ }
38
+ /**
39
+ * Audit log path. `--audit-log <path>` enables JSONL append on every mutating
40
+ * command; default path is ~/.switchbot/audit.log when `--audit-log` is given
41
+ * without a value. Returns null when the flag is absent.
42
+ */
43
+ export function getAuditLog() {
44
+ const idx = process.argv.indexOf('--audit-log');
45
+ if (idx === -1)
46
+ return null;
47
+ const next = process.argv[idx + 1];
48
+ if (!next || next.startsWith('-')) {
49
+ // bare --audit-log → default location
50
+ return `${process.env.HOME ?? process.env.USERPROFILE ?? '.'}/.switchbot/audit.log`;
51
+ }
52
+ return next;
53
+ }
54
+ /**
55
+ * Max 429 retries before surfacing the error. Default 3. `--no-retry`
56
+ * disables retries entirely; `--retry-on-429 <n>` overrides the count.
57
+ */
58
+ export function getRetryOn429() {
59
+ if (process.argv.includes('--no-retry'))
60
+ return 0;
61
+ const v = getFlagValue('--retry-on-429');
62
+ if (v === undefined)
63
+ return 3;
64
+ const n = Number(v);
65
+ if (!Number.isFinite(n) || n < 0)
66
+ return 3;
67
+ return Math.floor(n);
68
+ }
69
+ /** Backoff strategy for 429 retries. Default 'exponential'. */
70
+ export function getBackoffStrategy() {
71
+ const v = getFlagValue('--backoff');
72
+ if (v === 'linear')
73
+ return 'linear';
74
+ return 'exponential';
75
+ }
76
+ /**
77
+ * Whether local quota counting is disabled. Quota counting is best-effort
78
+ * (see src/utils/quota.ts) — this lets scripts opt out entirely when even
79
+ * best-effort file I/O is unwelcome.
80
+ */
81
+ export function isQuotaDisabled() {
82
+ return process.argv.includes('--no-quota');
83
+ }
84
+ const DEFAULT_LIST_TTL_MS = 60 * 60 * 1000;
85
+ function parseDurationToMs(v) {
86
+ const m = /^(\d+)(ms|s|m|h)?$/.exec(v.trim().toLowerCase());
87
+ if (!m)
88
+ return null;
89
+ const n = Number(m[1]);
90
+ if (!Number.isFinite(n) || n < 0)
91
+ return null;
92
+ const unit = m[2] ?? 'ms';
93
+ switch (unit) {
94
+ case 'ms': return n;
95
+ case 's': return n * 1000;
96
+ case 'm': return n * 60 * 1000;
97
+ case 'h': return n * 60 * 60 * 1000;
98
+ default: return null;
99
+ }
100
+ }
101
+ export { parseDurationToMs };
102
+ /** The --format flag value, or undefined when absent. */
103
+ export function getFormat() {
104
+ return getFlagValue('--format');
105
+ }
106
+ /** Comma-separated --fields value, split into an array. */
107
+ export function getFields() {
108
+ const v = getFlagValue('--fields');
109
+ if (!v)
110
+ return undefined;
111
+ return v.split(',').map((f) => f.trim()).filter(Boolean);
112
+ }
113
+ export function getCacheMode() {
114
+ if (process.argv.includes('--no-cache')) {
115
+ return { listTtlMs: 0, statusTtlMs: 0 };
116
+ }
117
+ // Individual TTL overrides take precedence over the combined --cache flag.
118
+ const listFlag = getFlagValue('--cache-list');
119
+ const statusFlag = getFlagValue('--cache-status');
120
+ if (listFlag !== undefined || statusFlag !== undefined) {
121
+ const listTtlMs = listFlag !== undefined
122
+ ? (parseDurationToMs(listFlag) ?? DEFAULT_LIST_TTL_MS)
123
+ : DEFAULT_LIST_TTL_MS;
124
+ const statusTtlMs = statusFlag !== undefined
125
+ ? (parseDurationToMs(statusFlag) ?? 0)
126
+ : 0;
127
+ return { listTtlMs, statusTtlMs };
128
+ }
129
+ const v = getFlagValue('--cache');
130
+ if (!v || v === 'auto') {
131
+ return { listTtlMs: DEFAULT_LIST_TTL_MS, statusTtlMs: 0 };
132
+ }
133
+ if (v === 'off') {
134
+ return { listTtlMs: 0, statusTtlMs: 0 };
135
+ }
136
+ const ms = parseDurationToMs(v);
137
+ if (ms === null || ms === 0) {
138
+ return { listTtlMs: DEFAULT_LIST_TTL_MS, statusTtlMs: 0 };
139
+ }
140
+ return { listTtlMs: ms, statusTtlMs: ms };
141
+ }
34
142
  //# sourceMappingURL=flags.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"flags.js","sourceRoot":"","sources":["../../src/utils/flags.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,SAAS,YAAY,CAAC,GAAG,SAAmB;IAC1C,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;QAC7B,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,GAAG,KAAK,CAAC,CAAC,IAAI,GAAG,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YAChD,OAAO,OAAO,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,OAAO,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AAC3E,CAAC;AAED,MAAM,UAAU,QAAQ;IACtB,OAAO,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AAC5C,CAAC;AAED,yDAAyD;AACzD,MAAM,UAAU,UAAU;IACxB,MAAM,CAAC,GAAG,YAAY,CAAC,WAAW,CAAC,CAAC;IACpC,IAAI,CAAC,CAAC;QAAE,OAAO,MAAM,CAAC;IACtB,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACpB,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,MAAM,CAAC;IACjD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,8CAA8C;AAC9C,MAAM,UAAU,aAAa;IAC3B,OAAO,YAAY,CAAC,UAAU,CAAC,CAAC;AAClC,CAAC"}
1
+ {"version":3,"file":"flags.js","sourceRoot":"","sources":["../../src/utils/flags.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export type OutputFormat = 'table' | 'json' | 'jsonl' | 'tsv' | 'yaml' | 'id';
2
+ export declare function parseFormat(flag: string | undefined): OutputFormat;
3
+ export declare function resolveFormat(): OutputFormat;
4
+ export declare function resolveFields(): string[] | undefined;
5
+ export declare function filterFields(headers: string[], rows: unknown[][], fields: string[] | undefined): {
6
+ headers: string[];
7
+ rows: unknown[][];
8
+ };
9
+ export declare function renderRows(headers: string[], rows: unknown[][], format: OutputFormat, fields?: string[]): void;
@@ -0,0 +1,109 @@
1
+ import { printTable, printJson, isJsonMode, UsageError } from './output.js';
2
+ import { getFormat, getFields } from './flags.js';
3
+ import { dump as yamlDump } from 'js-yaml';
4
+ export function parseFormat(flag) {
5
+ if (!flag)
6
+ return 'table';
7
+ const lower = flag.toLowerCase();
8
+ switch (lower) {
9
+ case 'table': return 'table';
10
+ case 'json': return 'json';
11
+ case 'jsonl': return 'jsonl';
12
+ case 'tsv': return 'tsv';
13
+ case 'yaml': return 'yaml';
14
+ case 'id': return 'id';
15
+ default: {
16
+ const msg = `Unknown --format "${flag}". Expected: table, json, jsonl, tsv, yaml, id.`;
17
+ if (isJsonMode()) {
18
+ console.error(JSON.stringify({ error: { code: 2, kind: 'usage', message: msg } }));
19
+ }
20
+ else {
21
+ console.error(msg);
22
+ }
23
+ process.exit(2);
24
+ }
25
+ }
26
+ }
27
+ export function resolveFormat() {
28
+ if (process.argv.includes('--json'))
29
+ return 'json';
30
+ return parseFormat(getFormat());
31
+ }
32
+ export function resolveFields() {
33
+ return getFields();
34
+ }
35
+ export function filterFields(headers, rows, fields) {
36
+ if (!fields || fields.length === 0)
37
+ return { headers, rows };
38
+ const unknown = fields.filter((f) => !headers.includes(f));
39
+ if (unknown.length > 0) {
40
+ throw new UsageError(`Unknown field(s): ${unknown.map((f) => `"${f}"`).join(', ')}. ` +
41
+ `Allowed: ${headers.map((f) => `"${f}"`).join(', ')}.`);
42
+ }
43
+ const indices = fields.map((f) => headers.indexOf(f));
44
+ return {
45
+ headers: indices.map((i) => headers[i]),
46
+ rows: rows.map((row) => indices.map((i) => row[i])),
47
+ };
48
+ }
49
+ function cellToString(cell) {
50
+ if (cell === null || cell === undefined)
51
+ return '';
52
+ if (typeof cell === 'boolean')
53
+ return cell ? 'true' : 'false';
54
+ if (typeof cell === 'object')
55
+ return JSON.stringify(cell);
56
+ return String(cell);
57
+ }
58
+ function rowToObject(headers, row) {
59
+ const obj = {};
60
+ for (let i = 0; i < headers.length; i++) {
61
+ obj[headers[i]] = row[i] ?? null;
62
+ }
63
+ return obj;
64
+ }
65
+ export function renderRows(headers, rows, format, fields) {
66
+ const filtered = filterFields(headers, rows, fields);
67
+ const h = filtered.headers;
68
+ const r = filtered.rows;
69
+ switch (format) {
70
+ case 'table':
71
+ printTable(h, r);
72
+ break;
73
+ case 'json':
74
+ printJson(r.map((row) => rowToObject(h, row)));
75
+ break;
76
+ case 'jsonl':
77
+ for (const row of r) {
78
+ console.log(JSON.stringify(rowToObject(h, row)));
79
+ }
80
+ break;
81
+ case 'tsv':
82
+ console.log(h.join('\t'));
83
+ for (const row of r) {
84
+ console.log(row.map(cellToString).join('\t'));
85
+ }
86
+ break;
87
+ case 'yaml':
88
+ for (const row of r) {
89
+ const obj = rowToObject(h, row);
90
+ console.log('---');
91
+ console.log(yamlDump(obj, { lineWidth: -1 }).trimEnd());
92
+ }
93
+ break;
94
+ case 'id': {
95
+ const idIdx = h.indexOf('deviceId') !== -1 ? h.indexOf('deviceId')
96
+ : h.indexOf('sceneId') !== -1 ? h.indexOf('sceneId')
97
+ : -1;
98
+ if (idIdx === -1) {
99
+ throw new UsageError(`--format=id requires a "deviceId" or "sceneId" column. ` +
100
+ `This command outputs: ${h.map((c) => `"${c}"`).join(', ')}.`);
101
+ }
102
+ for (const row of r) {
103
+ console.log(cellToString(row[idIdx]));
104
+ }
105
+ break;
106
+ }
107
+ }
108
+ }
109
+ //# sourceMappingURL=format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/utils/format.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ export interface NameMatch {
2
+ deviceId: string;
3
+ name: string;
4
+ score: number;
5
+ }
6
+ export type NameResolveResult = {
7
+ ok: true;
8
+ deviceId: string;
9
+ } | {
10
+ ok: false;
11
+ ambiguous: true;
12
+ candidates: NameMatch[];
13
+ } | {
14
+ ok: false;
15
+ ambiguous: false;
16
+ };
17
+ export declare function resolveDeviceId(deviceId: string | undefined, nameQuery: string | undefined): string;
@@ -0,0 +1,78 @@
1
+ import { loadCache } from '../devices/cache.js';
2
+ import { loadDeviceMeta } from '../devices/device-meta.js';
3
+ import { levenshtein, normalizeDeviceName } from './string.js';
4
+ import { UsageError, StructuredUsageError } from './output.js';
5
+ function resolveDeviceByName(query) {
6
+ const cache = loadCache();
7
+ if (!cache || Object.keys(cache.devices).length === 0) {
8
+ return { ok: false, ambiguous: false };
9
+ }
10
+ const meta = loadDeviceMeta();
11
+ const q = normalizeDeviceName(query);
12
+ const threshold = Math.min(3, Math.floor(q.length * 0.3));
13
+ const candidates = [];
14
+ for (const [deviceId, device] of Object.entries(cache.devices)) {
15
+ // alias exact match (highest priority)
16
+ const alias = meta.devices[deviceId]?.alias;
17
+ if (alias && normalizeDeviceName(alias) === q) {
18
+ return { ok: true, deviceId };
19
+ }
20
+ const rawName = normalizeDeviceName(device.name);
21
+ // exact match
22
+ if (rawName === q)
23
+ return { ok: true, deviceId };
24
+ // alias substring/fuzzy
25
+ if (alias) {
26
+ const normAlias = normalizeDeviceName(alias);
27
+ if (normAlias.includes(q) || q.includes(normAlias)) {
28
+ candidates.push({ deviceId, name: device.name, score: 1 });
29
+ continue;
30
+ }
31
+ const dist = levenshtein(normAlias, q);
32
+ if (dist <= threshold) {
33
+ candidates.push({ deviceId, name: device.name, score: dist + 1 });
34
+ continue;
35
+ }
36
+ }
37
+ // name substring
38
+ if (rawName.includes(q) || q.includes(rawName)) {
39
+ candidates.push({ deviceId, name: device.name, score: 1 });
40
+ continue;
41
+ }
42
+ // levenshtein
43
+ const dist = levenshtein(rawName, q);
44
+ if (dist <= threshold) {
45
+ candidates.push({ deviceId, name: device.name, score: dist + 1 });
46
+ }
47
+ }
48
+ if (candidates.length === 0)
49
+ return { ok: false, ambiguous: false };
50
+ candidates.sort((a, b) => a.score - b.score);
51
+ const best = candidates[0].score;
52
+ const top = candidates.filter((c) => c.score <= best + 1);
53
+ if (top.length === 1)
54
+ return { ok: true, deviceId: top[0].deviceId };
55
+ return { ok: false, ambiguous: true, candidates: top.slice(0, 4) };
56
+ }
57
+ export function resolveDeviceId(deviceId, nameQuery) {
58
+ if (deviceId && nameQuery) {
59
+ throw new UsageError('Provide either a deviceId argument or --name, not both.');
60
+ }
61
+ if (deviceId)
62
+ return deviceId;
63
+ if (!nameQuery) {
64
+ throw new UsageError('A deviceId argument or --name flag is required.');
65
+ }
66
+ const cache = loadCache();
67
+ if (!cache) {
68
+ throw new UsageError(`--name requires the device cache. Run 'switchbot devices list' first to populate it.`);
69
+ }
70
+ const result = resolveDeviceByName(nameQuery);
71
+ if (result.ok)
72
+ return result.deviceId;
73
+ if (result.ambiguous) {
74
+ throw new StructuredUsageError(`"${nameQuery}" is ambiguous — be more specific or use the deviceId directly.`, { candidates: result.candidates.map((c) => ({ deviceId: c.deviceId, name: c.name })) });
75
+ }
76
+ throw new UsageError(`No device matches "${nameQuery}". Run 'switchbot devices list' to see device names.`);
77
+ }
78
+ //# sourceMappingURL=name-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"name-resolver.js","sourceRoot":"","sources":["../../src/utils/name-resolver.ts"],"names":[],"mappings":"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"}
@@ -2,4 +2,22 @@ export declare function isJsonMode(): boolean;
2
2
  export declare function printJson(data: unknown): void;
3
3
  export declare function printTable(headers: string[], rows: (string | number | boolean | null | undefined)[][]): void;
4
4
  export declare function printKeyValue(data: Record<string, unknown>): void;
5
+ export declare class UsageError extends Error {
6
+ constructor(message: string);
7
+ }
8
+ export type ErrorSubKind = 'device-offline' | 'device-not-found' | 'command-not-supported' | 'auth-failed' | 'quota-exceeded' | 'device-busy' | 'unknown-api-error';
9
+ export interface ErrorPayload {
10
+ code: number;
11
+ kind: 'usage' | 'api' | 'runtime';
12
+ subKind?: ErrorSubKind;
13
+ message: string;
14
+ hint?: string;
15
+ retryable?: boolean;
16
+ context?: Record<string, unknown>;
17
+ }
18
+ export declare class StructuredUsageError extends Error {
19
+ readonly context?: Record<string, unknown> | undefined;
20
+ constructor(message: string, context?: Record<string, unknown> | undefined);
21
+ }
22
+ export declare function buildErrorPayload(error: unknown): ErrorPayload;
5
23
  export declare function handleError(error: unknown): never;
@@ -1,8 +1,9 @@
1
1
  import Table from 'cli-table3';
2
2
  import chalk from 'chalk';
3
3
  import { ApiError, DryRunSignal } from '../api/client.js';
4
+ import { getFormat } from './flags.js';
4
5
  export function isJsonMode() {
5
- return process.argv.includes('--json');
6
+ return process.argv.includes('--json') || getFormat() === 'json';
6
7
  }
7
8
  export function printJson(data) {
8
9
  console.log(JSON.stringify(data, null, 2));
@@ -37,18 +38,77 @@ export function printKeyValue(data) {
37
38
  }
38
39
  console.log(table.toString());
39
40
  }
41
+ export class UsageError extends Error {
42
+ constructor(message) {
43
+ super(message);
44
+ this.name = 'UsageError';
45
+ }
46
+ }
47
+ export class StructuredUsageError extends Error {
48
+ context;
49
+ constructor(message, context) {
50
+ super(message);
51
+ this.context = context;
52
+ this.name = 'StructuredUsageError';
53
+ }
54
+ }
55
+ function classifyApiError(code) {
56
+ switch (code) {
57
+ case 151:
58
+ case 160: return 'command-not-supported';
59
+ case 152: return 'device-not-found';
60
+ case 161:
61
+ case 171: return 'device-offline';
62
+ case 190: return 'device-busy';
63
+ case 401: return 'auth-failed';
64
+ case 429: return 'quota-exceeded';
65
+ default: return 'unknown-api-error';
66
+ }
67
+ }
68
+ export function buildErrorPayload(error) {
69
+ if (error instanceof StructuredUsageError) {
70
+ const payload = { code: 2, kind: 'usage', message: error.message };
71
+ if (error.context)
72
+ payload.context = error.context;
73
+ return payload;
74
+ }
75
+ if (error instanceof UsageError) {
76
+ return { code: 2, kind: 'usage', message: error.message };
77
+ }
78
+ const code = error instanceof ApiError ? error.code : 1;
79
+ const kind = error instanceof ApiError ? 'api' : 'runtime';
80
+ const message = error instanceof Error ? error.message : 'An unknown error occurred';
81
+ const hint = error instanceof ApiError ? (error.hint ?? errorHint(error.code)) : null;
82
+ const retryable = error instanceof ApiError ? error.retryable : false;
83
+ const payload = { code, kind, message };
84
+ if (error instanceof ApiError)
85
+ payload.subKind = classifyApiError(error.code);
86
+ if (hint)
87
+ payload.hint = hint;
88
+ if (retryable)
89
+ payload.retryable = true;
90
+ return payload;
91
+ }
40
92
  export function handleError(error) {
41
93
  if (error instanceof DryRunSignal) {
42
94
  process.exit(0);
43
95
  }
96
+ const payload = buildErrorPayload(error);
97
+ if (isJsonMode()) {
98
+ console.error(JSON.stringify({ error: payload }));
99
+ process.exit(payload.code === 2 ? 2 : 1);
100
+ }
101
+ if (payload.kind === 'usage') {
102
+ console.error(payload.message);
103
+ process.exit(2);
104
+ }
44
105
  if (error instanceof ApiError) {
45
- console.error(chalk.red(`Error (code ${error.code}): ${error.message}`));
46
- const hint = errorHint(error.code);
47
- if (hint)
48
- console.error(chalk.grey(`Hint: ${hint}`));
106
+ console.error(chalk.red(`Error (code ${error.code}): ${payload.message}`));
107
+ if (payload.hint)
108
+ console.error(chalk.grey(`Hint: ${payload.hint}`));
49
109
  }
50
110
  else if (error instanceof Error) {
51
- console.error(chalk.red(`Error: ${error.message}`));
111
+ console.error(chalk.red(`Error: ${payload.message}`));
52
112
  }
53
113
  else {
54
114
  console.error(chalk.red('An unknown error occurred'));
@@ -1 +1 @@
1
- {"version":3,"file":"output.js","sourceRoot":"","sources":["../../src/utils/output.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"output.js","sourceRoot":"","sources":["../../src/utils/output.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Local quota counter. Tracks the SwitchBot 10k/day request budget so the
3
+ * CLI (and any AI agent) can check "how many calls have I already burned?"
4
+ * without pinging the API.
5
+ *
6
+ * Shape (`~/.switchbot/quota.json`):
7
+ * {
8
+ * "days": {
9
+ * "2026-04-18": {
10
+ * "total": 42,
11
+ * "endpoints": {
12
+ * "GET /v1.1/devices": 3,
13
+ * "GET /v1.1/devices/:id/status": 27,
14
+ * "POST /v1.1/devices/:id/commands": 12
15
+ * }
16
+ * }
17
+ * }
18
+ * }
19
+ *
20
+ * We keep the last 7 days to bound the file size and give a short-term
21
+ * trend. Writes are fire-and-forget — a failed write never breaks the
22
+ * actual API call.
23
+ */
24
+ export declare const DAILY_QUOTA = 10000;
25
+ export interface DayBucket {
26
+ total: number;
27
+ endpoints: Record<string, number>;
28
+ }
29
+ export interface QuotaFile {
30
+ days: Record<string, DayBucket>;
31
+ }
32
+ export declare function loadQuota(): QuotaFile;
33
+ /**
34
+ * Normalise a full URL into a SwitchBot-style endpoint pattern. The segment
35
+ * immediately after `devices` or `scenes` is collapsed to `:id` so we can
36
+ * bucket by endpoint shape rather than by specific deviceId/sceneId.
37
+ */
38
+ export declare function normaliseEndpoint(method: string, url: string): string;
39
+ /** Record a single request. Bucketed by local-date + endpoint pattern. */
40
+ export declare function recordRequest(method: string, url: string, now?: Date): void;
41
+ export declare function resetQuota(): void;
42
+ /** Return today's usage (convenience for `quota status`). */
43
+ export declare function todayUsage(now?: Date): {
44
+ date: string;
45
+ total: number;
46
+ remaining: number;
47
+ endpoints: Record<string, number>;
48
+ };