@switchbot/openapi-cli 1.0.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/LICENSE +21 -0
- package/README.md +278 -0
- package/dist/api/client.d.ts +12 -0
- package/dist/api/client.js +95 -0
- package/dist/api/client.js.map +1 -0
- package/dist/auth.d.ts +1 -0
- package/dist/auth.js +21 -0
- package/dist/auth.js.map +1 -0
- package/dist/commands/completion.d.ts +2 -0
- package/dist/commands/completion.js +259 -0
- package/dist/commands/completion.js.map +1 -0
- package/dist/commands/config.d.ts +2 -0
- package/dist/commands/config.js +37 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/devices.d.ts +2 -0
- package/dist/commands/devices.js +402 -0
- package/dist/commands/devices.js.map +1 -0
- package/dist/commands/scenes.d.ts +2 -0
- package/dist/commands/scenes.js +57 -0
- package/dist/commands/scenes.js.map +1 -0
- package/dist/commands/webhook.d.ts +2 -0
- package/dist/commands/webhook.js +167 -0
- package/dist/commands/webhook.js.map +1 -0
- package/dist/config.d.ts +7 -0
- package/dist/config.js +77 -0
- package/dist/config.js.map +1 -0
- package/dist/devices/catalog.d.ts +21 -0
- package/dist/devices/catalog.js +391 -0
- package/dist/devices/catalog.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +85 -0
- package/dist/index.js.map +1 -0
- package/dist/utils/flags.d.ts +10 -0
- package/dist/utils/flags.js +34 -0
- package/dist/utils/flags.js.map +1 -0
- package/dist/utils/output.d.ts +5 -0
- package/dist/utils/output.js +78 -0
- package/dist/utils/output.js.map +1 -0
- package/package.json +62 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 chenliuyun
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
# switchbot-openapi-cli
|
|
2
|
+
|
|
3
|
+
Command-line interface for the [SwitchBot API v1.1](https://github.com/OpenWonderLabs/SwitchBotAPI).
|
|
4
|
+
List devices, query status, send control commands, run scenes, and manage webhooks from your terminal or shell scripts.
|
|
5
|
+
|
|
6
|
+
## Requirements
|
|
7
|
+
|
|
8
|
+
- Node.js ≥ 18
|
|
9
|
+
- A SwitchBot account with Developer Options enabled (see [Credentials](#credentials))
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
git clone <repo-url>
|
|
15
|
+
cd switchbot-openapi-cli
|
|
16
|
+
npm install
|
|
17
|
+
npm run build
|
|
18
|
+
# Optional: make it globally available as `switchbot`
|
|
19
|
+
npm link
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
During development you can run directly from sources without building:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm run dev -- devices list
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Credentials
|
|
29
|
+
|
|
30
|
+
The CLI reads credentials in this order (first match wins):
|
|
31
|
+
|
|
32
|
+
1. **Environment variables** — `SWITCHBOT_TOKEN` and `SWITCHBOT_SECRET`
|
|
33
|
+
2. **Config file** — `~/.switchbot/config.json` (written by `config set-token`, mode `0600`)
|
|
34
|
+
|
|
35
|
+
Obtain the token and secret from the SwitchBot mobile app:
|
|
36
|
+
**Profile → Preferences → Developer Options → Get Token**.
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# One-time setup (writes ~/.switchbot/config.json)
|
|
40
|
+
switchbot config set-token <token> <secret>
|
|
41
|
+
|
|
42
|
+
# Or export environment variables (e.g. in CI)
|
|
43
|
+
export SWITCHBOT_TOKEN=...
|
|
44
|
+
export SWITCHBOT_SECRET=...
|
|
45
|
+
|
|
46
|
+
# Confirm which source is active and see the masked secret
|
|
47
|
+
switchbot config show
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Global options
|
|
51
|
+
|
|
52
|
+
| Option | Description |
|
|
53
|
+
| ------------------- | ------------------------------------------------------------------ |
|
|
54
|
+
| `--json` | Print the raw JSON response instead of a formatted table |
|
|
55
|
+
| `-v`, `--verbose` | Log HTTP request/response details to stderr |
|
|
56
|
+
| `--dry-run` | Print mutating requests (POST/PUT/DELETE) without sending them |
|
|
57
|
+
| `--timeout <ms>` | HTTP request timeout in milliseconds (default: `30000`) |
|
|
58
|
+
| `--config <path>` | Override credential file location (default: `~/.switchbot/config.json`) |
|
|
59
|
+
| `-V`, `--version` | Print the CLI version |
|
|
60
|
+
| `-h`, `--help` | Show help for any command or subcommand |
|
|
61
|
+
|
|
62
|
+
Every subcommand supports `--help`, and most include a parameter-format reference and examples.
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
switchbot --help
|
|
66
|
+
switchbot devices command --help
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### `--dry-run`
|
|
70
|
+
|
|
71
|
+
Intercepts every non-GET request: the CLI prints the URL/body it would have
|
|
72
|
+
sent, then exits `0` without contacting the API. `GET` requests (list, status,
|
|
73
|
+
query) are still executed so you can preview the state involved.
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
switchbot devices command ABC123 turnOn --dry-run
|
|
77
|
+
# [dry-run] Would POST https://api.switch-bot.com/v1.1/devices/ABC123/commands
|
|
78
|
+
# [dry-run] body: {"command":"turnOn","parameter":"default","commandType":"command"}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Commands
|
|
82
|
+
|
|
83
|
+
### `config` — credential management
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
switchbot config set-token <token> <secret> # Save to ~/.switchbot/config.json
|
|
87
|
+
switchbot config show # Print current source + masked secret
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### `devices` — list, status, control
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
# List all physical devices and IR remote devices
|
|
94
|
+
# Columns: deviceId, deviceName, type, controlType, family, roomID, room, hub, cloud
|
|
95
|
+
switchbot devices list
|
|
96
|
+
switchbot devices list --json | jq '.deviceList[].deviceId'
|
|
97
|
+
|
|
98
|
+
# Filter by family / room (family & room info requires the 'src: OpenClaw'
|
|
99
|
+
# header, which this CLI sends on every request)
|
|
100
|
+
switchbot devices list --json | jq '.deviceList[] | select(.familyName == "Home")'
|
|
101
|
+
switchbot devices list --json | jq '[.deviceList[], .infraredRemoteList[]] | group_by(.familyName)'
|
|
102
|
+
|
|
103
|
+
# Query real-time status of a physical device
|
|
104
|
+
switchbot devices status <deviceId>
|
|
105
|
+
switchbot devices status <deviceId> --json
|
|
106
|
+
|
|
107
|
+
# Send a control command
|
|
108
|
+
switchbot devices command <deviceId> <cmd> [parameter] [--type command|customize]
|
|
109
|
+
|
|
110
|
+
# Describe a specific device (1 API call): metadata + supported commands + status fields
|
|
111
|
+
switchbot devices describe <deviceId>
|
|
112
|
+
switchbot devices describe <deviceId> --json
|
|
113
|
+
|
|
114
|
+
# Discover what's supported (offline reference, no API call)
|
|
115
|
+
switchbot devices types # List all device types + IR remote types
|
|
116
|
+
switchbot devices commands <type> # Show commands, parameter formats, and status fields
|
|
117
|
+
switchbot devices commands Bot
|
|
118
|
+
switchbot devices commands "Smart Lock"
|
|
119
|
+
switchbot devices commands curtain # Case-insensitive, substring match
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
#### Parameter formats
|
|
123
|
+
|
|
124
|
+
`parameter` is optional — omit it for commands like `turnOn`/`turnOff` (auto-defaults to `"default"`).
|
|
125
|
+
Numeric-only and JSON-object parameters are auto-parsed; strings with colons / commas / semicolons pass through as-is.
|
|
126
|
+
|
|
127
|
+
For the exact commands and parameter formats a specific device supports, query the built-in catalog:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
switchbot devices commands <type> # e.g. Bot, Curtain, "Smart Lock", "Robot Vacuum Cleaner S10"
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Generic parameter shapes (which one applies is decided by the device — see the catalog):
|
|
134
|
+
|
|
135
|
+
| Shape | Example |
|
|
136
|
+
| ------------------- | -------------------------------------------------------- |
|
|
137
|
+
| _(none)_ | `devices command <id> turnOn` |
|
|
138
|
+
| `<integer>` | `devices command <id> setBrightness 75` |
|
|
139
|
+
| `<R:G:B>` | `devices command <id> setColor "255:0:0"` |
|
|
140
|
+
| `<direction;angle>` | `devices command <id> setPosition "up;60"` |
|
|
141
|
+
| `<a,b,c,…>` | `devices command <id> setAll "26,1,3,on"` |
|
|
142
|
+
| `<json object>` | `'{"action":"sweep","param":{"fanLevel":2,"times":1}}'` |
|
|
143
|
+
| Custom IR button | `devices command <id> MyButton --type customize` |
|
|
144
|
+
|
|
145
|
+
For the complete per-device command reference, see the [SwitchBot API docs](https://github.com/OpenWonderLabs/SwitchBotAPI#send-device-control-commands).
|
|
146
|
+
|
|
147
|
+
### `scenes` — run manual scenes
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
switchbot scenes list # Columns: sceneId, sceneName
|
|
151
|
+
switchbot scenes execute <sceneId>
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### `webhook` — receive device events over HTTP
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
# Register a receiver URL for events from ALL devices
|
|
158
|
+
switchbot webhook setup https://your.host/hook
|
|
159
|
+
|
|
160
|
+
# Query what is currently configured
|
|
161
|
+
switchbot webhook query
|
|
162
|
+
switchbot webhook query --details https://your.host/hook
|
|
163
|
+
|
|
164
|
+
# Enable / disable / re-submit the registered URL
|
|
165
|
+
switchbot webhook update https://your.host/hook --enable
|
|
166
|
+
switchbot webhook update https://your.host/hook --disable
|
|
167
|
+
|
|
168
|
+
# Remove the configuration
|
|
169
|
+
switchbot webhook delete https://your.host/hook
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
The CLI validates that `<url>` is an absolute `http://` or `https://` URL before calling the API. `--enable` and `--disable` are mutually exclusive.
|
|
173
|
+
|
|
174
|
+
### `completion` — shell tab-completion
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
# Bash: load on every new shell
|
|
178
|
+
echo 'source <(switchbot completion bash)' >> ~/.bashrc
|
|
179
|
+
|
|
180
|
+
# Zsh
|
|
181
|
+
echo 'source <(switchbot completion zsh)' >> ~/.zshrc
|
|
182
|
+
|
|
183
|
+
# Fish
|
|
184
|
+
switchbot completion fish > ~/.config/fish/completions/switchbot.fish
|
|
185
|
+
|
|
186
|
+
# PowerShell (profile)
|
|
187
|
+
switchbot completion powershell >> $PROFILE
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Supported shells: `bash`, `zsh`, `fish`, `powershell` (`pwsh` is accepted as an alias).
|
|
191
|
+
|
|
192
|
+
## Output modes
|
|
193
|
+
|
|
194
|
+
- **Default** — ANSI-colored tables for `list`/`status`, key-value tables for details.
|
|
195
|
+
- **`--json`** — raw JSON passthrough, ideal for `jq` and scripting.
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
switchbot devices list --json | jq '.deviceList[] | {id: .deviceId, name: .deviceName}'
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Exit codes
|
|
202
|
+
|
|
203
|
+
| Code | Meaning |
|
|
204
|
+
| ---- | ----------------------------------------------------------------------------------- |
|
|
205
|
+
| `0` | Success (including `--dry-run` intercept) |
|
|
206
|
+
| `1` | Runtime error — API error, network failure, missing credentials |
|
|
207
|
+
| `2` | Usage error — bad flag, missing/invalid argument, unknown subcommand, unknown device type, invalid URL, conflicting flags |
|
|
208
|
+
|
|
209
|
+
Typical errors bubble up in the form `Error: <message>` on stderr. The SwitchBot-specific error codes that get mapped to readable English messages:
|
|
210
|
+
|
|
211
|
+
| Code | Meaning |
|
|
212
|
+
| ---- | ------------------------------------------- |
|
|
213
|
+
| 151 | Device type error |
|
|
214
|
+
| 152 | Device not found |
|
|
215
|
+
| 160 | Command not supported by this device |
|
|
216
|
+
| 161 | Device offline (BLE devices need a Hub) |
|
|
217
|
+
| 171 | Hub offline |
|
|
218
|
+
| 190 | Device internal error / server busy |
|
|
219
|
+
| 401 | Authentication failed (check token/secret) |
|
|
220
|
+
| 429 | Request rate too high (10,000 req/day cap) |
|
|
221
|
+
|
|
222
|
+
## Environment variables
|
|
223
|
+
|
|
224
|
+
| Variable | Description |
|
|
225
|
+
| ------------------- | ------------------------------------------------------------------ |
|
|
226
|
+
| `SWITCHBOT_TOKEN` | API token — takes priority over the config file |
|
|
227
|
+
| `SWITCHBOT_SECRET` | API secret — takes priority over the config file |
|
|
228
|
+
| `NO_COLOR` | Disable ANSI colors in all output (automatically respected) |
|
|
229
|
+
|
|
230
|
+
## Scripting examples
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
# Turn off every Bot device
|
|
234
|
+
switchbot devices list --json \
|
|
235
|
+
| jq -r '.deviceList[] | select(.deviceType == "Bot") | .deviceId' \
|
|
236
|
+
| while read id; do switchbot devices command "$id" turnOff; done
|
|
237
|
+
|
|
238
|
+
# Dump each scene as `<id> <name>`
|
|
239
|
+
switchbot scenes list --json | jq -r '.[] | "\(.sceneId) \(.sceneName)"'
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## Development
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
npm run dev -- <args> # Run from TypeScript sources via tsx
|
|
246
|
+
npm run build # Compile to dist/
|
|
247
|
+
npm test # Run the Vitest suite (282 tests)
|
|
248
|
+
npm run test:watch # Watch mode
|
|
249
|
+
npm run test:coverage # Coverage report (v8, HTML + text)
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### Project layout
|
|
253
|
+
|
|
254
|
+
```
|
|
255
|
+
src/
|
|
256
|
+
├── index.ts # Commander entry; mounts all subcommands; global flags
|
|
257
|
+
├── auth.ts # HMAC-SHA256 signature (token + t + nonce → sign)
|
|
258
|
+
├── config.ts # Credential load/save; env > file priority; --config override
|
|
259
|
+
├── api/client.ts # axios instance + request/response interceptors;
|
|
260
|
+
│ # --verbose / --dry-run / --timeout wiring
|
|
261
|
+
├── devices/catalog.ts # Static catalog powering `devices types`/`devices commands`
|
|
262
|
+
├── commands/
|
|
263
|
+
│ ├── config.ts
|
|
264
|
+
│ ├── devices.ts
|
|
265
|
+
│ ├── scenes.ts
|
|
266
|
+
│ ├── webhook.ts
|
|
267
|
+
│ └── completion.ts # `switchbot completion bash|zsh|fish|powershell`
|
|
268
|
+
└── utils/
|
|
269
|
+
├── flags.ts # Global flag readers (isVerbose / isDryRun / getTimeout / getConfigPath)
|
|
270
|
+
└── output.ts # printTable / printKeyValue / printJson / handleError
|
|
271
|
+
tests/ # Vitest suite (282 tests, mocked axios, no network)
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
## References
|
|
275
|
+
|
|
276
|
+
- [SwitchBot API v1.1 documentation](https://github.com/OpenWonderLabs/SwitchBotAPI)
|
|
277
|
+
- Base URL: `https://api.switch-bot.com`
|
|
278
|
+
- Rate limit: 10,000 requests/day per account
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type AxiosInstance } from 'axios';
|
|
2
|
+
/** Thrown by the request interceptor when --dry-run intercepts a mutating call. */
|
|
3
|
+
export declare class DryRunSignal extends Error {
|
|
4
|
+
readonly method: string;
|
|
5
|
+
readonly url: string;
|
|
6
|
+
constructor(method: string, url: string);
|
|
7
|
+
}
|
|
8
|
+
export declare function createClient(): AxiosInstance;
|
|
9
|
+
export declare class ApiError extends Error {
|
|
10
|
+
readonly code: number;
|
|
11
|
+
constructor(message: string, code: number);
|
|
12
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { buildAuthHeaders } from '../auth.js';
|
|
4
|
+
import { loadConfig } from '../config.js';
|
|
5
|
+
import { isVerbose, isDryRun, getTimeout } from '../utils/flags.js';
|
|
6
|
+
const API_ERROR_MESSAGES = {
|
|
7
|
+
151: 'Device type does not support this command',
|
|
8
|
+
152: 'Device ID does not exist',
|
|
9
|
+
160: 'This device does not support this command',
|
|
10
|
+
161: 'Device offline (check Wi-Fi / Bluetooth connection)',
|
|
11
|
+
171: 'Hub device offline (BLE devices require a Hub to communicate)',
|
|
12
|
+
190: 'Device internal error — often an invalid deviceId, unsupported parameter, or device busy',
|
|
13
|
+
};
|
|
14
|
+
/** Thrown by the request interceptor when --dry-run intercepts a mutating call. */
|
|
15
|
+
export class DryRunSignal extends Error {
|
|
16
|
+
method;
|
|
17
|
+
url;
|
|
18
|
+
constructor(method, url) {
|
|
19
|
+
super('dry-run');
|
|
20
|
+
this.method = method;
|
|
21
|
+
this.url = url;
|
|
22
|
+
this.name = 'DryRunSignal';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export function createClient() {
|
|
26
|
+
const { token, secret } = loadConfig();
|
|
27
|
+
const verbose = isVerbose();
|
|
28
|
+
const dryRun = isDryRun();
|
|
29
|
+
const client = axios.create({
|
|
30
|
+
baseURL: 'https://api.switch-bot.com',
|
|
31
|
+
timeout: getTimeout(),
|
|
32
|
+
});
|
|
33
|
+
// Inject auth headers; optionally log the request; short-circuit on --dry-run.
|
|
34
|
+
client.interceptors.request.use((config) => {
|
|
35
|
+
const authHeaders = buildAuthHeaders(token, secret);
|
|
36
|
+
Object.assign(config.headers, authHeaders);
|
|
37
|
+
const method = (config.method ?? 'get').toUpperCase();
|
|
38
|
+
const url = `${config.baseURL ?? ''}${config.url ?? ''}`;
|
|
39
|
+
if (verbose) {
|
|
40
|
+
process.stderr.write(chalk.grey(`[verbose] ${method} ${url}\n`));
|
|
41
|
+
if (config.data !== undefined) {
|
|
42
|
+
process.stderr.write(chalk.grey(`[verbose] body: ${JSON.stringify(config.data)}\n`));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (dryRun && method !== 'GET') {
|
|
46
|
+
console.log(chalk.yellow(`[dry-run] Would ${method} ${url}`));
|
|
47
|
+
if (config.data !== undefined) {
|
|
48
|
+
console.log(chalk.yellow(`[dry-run] body: ${JSON.stringify(config.data)}`));
|
|
49
|
+
}
|
|
50
|
+
throw new DryRunSignal(method, url);
|
|
51
|
+
}
|
|
52
|
+
return config;
|
|
53
|
+
});
|
|
54
|
+
// Handle API-level errors (HTTP 200 but statusCode !== 100)
|
|
55
|
+
client.interceptors.response.use((response) => {
|
|
56
|
+
if (verbose) {
|
|
57
|
+
process.stderr.write(chalk.grey(`[verbose] ${response.status} ${response.statusText}\n`));
|
|
58
|
+
}
|
|
59
|
+
const data = response.data;
|
|
60
|
+
if (data.statusCode !== undefined && data.statusCode !== 100) {
|
|
61
|
+
const msg = API_ERROR_MESSAGES[data.statusCode] ??
|
|
62
|
+
data.message ??
|
|
63
|
+
`API error code: ${data.statusCode}`;
|
|
64
|
+
throw new ApiError(msg, data.statusCode);
|
|
65
|
+
}
|
|
66
|
+
return response;
|
|
67
|
+
}, (error) => {
|
|
68
|
+
if (error instanceof DryRunSignal)
|
|
69
|
+
throw error;
|
|
70
|
+
if (axios.isAxiosError(error)) {
|
|
71
|
+
if (error.code === 'ECONNABORTED' || error.code === 'ETIMEDOUT') {
|
|
72
|
+
throw new ApiError(`Request timed out after ${getTimeout()}ms (override with --timeout <ms>)`, 0);
|
|
73
|
+
}
|
|
74
|
+
const status = error.response?.status;
|
|
75
|
+
if (status === 401) {
|
|
76
|
+
throw new ApiError('Authentication failed: invalid token or daily 10,000-request quota exceeded', 401);
|
|
77
|
+
}
|
|
78
|
+
if (status === 429) {
|
|
79
|
+
throw new ApiError('Request rate too high: daily 10,000-request quota exceeded', 429);
|
|
80
|
+
}
|
|
81
|
+
throw new ApiError(`HTTP ${status ?? '?'}: ${error.message}`, status ?? 0);
|
|
82
|
+
}
|
|
83
|
+
throw error;
|
|
84
|
+
});
|
|
85
|
+
return client;
|
|
86
|
+
}
|
|
87
|
+
export class ApiError extends Error {
|
|
88
|
+
code;
|
|
89
|
+
constructor(message, code) {
|
|
90
|
+
super(message);
|
|
91
|
+
this.code = code;
|
|
92
|
+
this.name = 'ApiError';
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"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"}
|
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function buildAuthHeaders(token: string, secret: string): Record<string, string>;
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
3
|
+
export function buildAuthHeaders(token, secret) {
|
|
4
|
+
const t = String(Date.now()); // 13-digit millisecond timestamp
|
|
5
|
+
const nonce = uuidv4(); // unique per request
|
|
6
|
+
const data = token + t + nonce;
|
|
7
|
+
const sign = crypto
|
|
8
|
+
.createHmac('sha256', secret)
|
|
9
|
+
.update(data)
|
|
10
|
+
.digest('base64')
|
|
11
|
+
.toUpperCase(); // API requires uppercase
|
|
12
|
+
return {
|
|
13
|
+
Authorization: token,
|
|
14
|
+
t,
|
|
15
|
+
sign,
|
|
16
|
+
nonce,
|
|
17
|
+
src: 'OpenClaw',
|
|
18
|
+
'Content-Type': 'application/json',
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=auth.js.map
|
package/dist/auth.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,EAAE,IAAI,MAAM,EAAE,MAAM,MAAM,CAAC;AAEpC,MAAM,UAAU,gBAAgB,CAAC,KAAa,EAAE,MAAc;IAC5D,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAE,iCAAiC;IAChE,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,CAAQ,qBAAqB;IACpD,MAAM,IAAI,GAAG,KAAK,GAAG,CAAC,GAAG,KAAK,CAAC;IAC/B,MAAM,IAAI,GAAG,MAAM;SAChB,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC;SAC5B,MAAM,CAAC,IAAI,CAAC;SACZ,MAAM,CAAC,QAAQ,CAAC;SAChB,WAAW,EAAE,CAAC,CAAc,yBAAyB;IAExD,OAAO;QACL,aAAa,EAAE,KAAK;QACpB,CAAC;QACD,IAAI;QACJ,KAAK;QACL,GAAG,EAAE,UAAU;QACf,cAAc,EAAE,kBAAkB;KACnC,CAAC;AACJ,CAAC"}
|