openhome-cli 0.1.1 → 0.1.3
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 +184 -171
- package/dist/cli.js +138 -18
- package/package.json +1 -1
- package/src/cli.ts +18 -0
- package/src/commands/assign.ts +6 -0
- package/src/commands/delete.ts +6 -0
- package/src/commands/list.ts +6 -0
- package/src/commands/login.ts +0 -22
- package/src/commands/set-jwt.ts +114 -0
- package/src/commands/toggle.ts +6 -0
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Command-line tool for managing OpenHome voice AI abilities. Create and deploy abilities without leaving your terminal.
|
|
4
4
|
|
|
5
|
-
**
|
|
5
|
+
**Version:** v0.1.2
|
|
6
6
|
**Node:** 18+
|
|
7
7
|
**Platform:** macOS (primary), Linux/Windows (config-file fallback for keychain)
|
|
8
8
|
|
|
@@ -11,14 +11,11 @@ Command-line tool for managing OpenHome voice AI abilities. Create and deploy ab
|
|
|
11
11
|
## Install
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
#
|
|
15
|
-
|
|
16
|
-
cd openhome-cli
|
|
17
|
-
npm install
|
|
18
|
-
npm run build
|
|
19
|
-
npm link
|
|
14
|
+
# Use directly without installing
|
|
15
|
+
npx openhome-cli
|
|
20
16
|
|
|
21
|
-
#
|
|
17
|
+
# Or install globally
|
|
18
|
+
npm install -g openhome-cli
|
|
22
19
|
openhome
|
|
23
20
|
```
|
|
24
21
|
|
|
@@ -49,21 +46,6 @@ Or just run `openhome` with no arguments for the interactive menu.
|
|
|
49
46
|
|
|
50
47
|
Opens an interactive menu. Use arrow keys to navigate, Enter to select. The menu loops after each command — pick another or choose Exit.
|
|
51
48
|
|
|
52
|
-
```
|
|
53
|
-
┌ OpenHome CLI v0.1.0
|
|
54
|
-
│
|
|
55
|
-
◆ What would you like to do?
|
|
56
|
-
│ ● Log Out
|
|
57
|
-
│ ○ Create Ability
|
|
58
|
-
│ ○ Deploy
|
|
59
|
-
│ ○ Chat
|
|
60
|
-
│ ○ My Abilities
|
|
61
|
-
│ ○ My Agents
|
|
62
|
-
│ ○ Status
|
|
63
|
-
│ ○ Exit
|
|
64
|
-
└
|
|
65
|
-
```
|
|
66
|
-
|
|
67
49
|
If you are not logged in, the CLI prompts for login before showing the menu.
|
|
68
50
|
|
|
69
51
|
All commands below also work directly from the terminal.
|
|
@@ -84,6 +66,20 @@ openhome login
|
|
|
84
66
|
|
|
85
67
|
---
|
|
86
68
|
|
|
69
|
+
### `openhome set-jwt [token]`
|
|
70
|
+
|
|
71
|
+
Save a session token to unlock management commands (`list`, `delete`, `toggle`, `assign`).
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
openhome set-jwt eyJ...
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
These management commands use OpenHome's web session API, which requires a JWT rather than the SDK API key. To get your token: open [app.openhome.com](https://app.openhome.com), open DevTools then Application then Local Storage then `token`, copy the value, and run `openhome set-jwt <token>`.
|
|
78
|
+
|
|
79
|
+
The token is saved to `~/.openhome/config.json`. You only need to do this once (until your session expires).
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
87
83
|
### `openhome init [name]`
|
|
88
84
|
|
|
89
85
|
Scaffold a new ability with all required files.
|
|
@@ -111,39 +107,7 @@ openhome init my-weather-bot
|
|
|
111
107
|
| `__init__.py` | Required by OpenHome (empty) |
|
|
112
108
|
| `README.md` | Description of your ability |
|
|
113
109
|
|
|
114
|
-
The generated code auto-validates after creation.
|
|
115
|
-
|
|
116
|
-
---
|
|
117
|
-
|
|
118
|
-
### `openhome logout`
|
|
119
|
-
|
|
120
|
-
Clear stored credentials and log out.
|
|
121
|
-
|
|
122
|
-
```bash
|
|
123
|
-
openhome logout
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
Removes the API key from macOS Keychain and clears the default agent from config. In the interactive menu, selecting Log Out immediately prompts you to log in again.
|
|
127
|
-
|
|
128
|
-
---
|
|
129
|
-
|
|
130
|
-
### `openhome chat [agent]`
|
|
131
|
-
|
|
132
|
-
Chat with an agent via WebSocket. Send text messages and trigger abilities with keywords.
|
|
133
|
-
|
|
134
|
-
```bash
|
|
135
|
-
# Pick an agent interactively
|
|
136
|
-
openhome chat
|
|
137
|
-
|
|
138
|
-
# Chat with a specific agent
|
|
139
|
-
openhome chat pers_abc123
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
Once connected, type messages and press Enter. The agent responds in real-time. Send trigger words (e.g. "play aquaprime") to activate abilities remotely.
|
|
143
|
-
|
|
144
|
-
Commands inside chat: `/quit`, `/exit`, or `/q` to disconnect. Ctrl+C also works.
|
|
145
|
-
|
|
146
|
-
> **Note:** Audio responses from the agent are not playable in the terminal. Text responses display normally.
|
|
110
|
+
The generated code auto-validates after creation. You're prompted to deploy immediately.
|
|
147
111
|
|
|
148
112
|
---
|
|
149
113
|
|
|
@@ -180,7 +144,7 @@ openhome deploy ./my-ability --personality pers_alice
|
|
|
180
144
|
3. Asks for confirmation
|
|
181
145
|
4. Uploads to OpenHome
|
|
182
146
|
|
|
183
|
-
> **Note:**
|
|
147
|
+
> **Note:** There is no update/overwrite endpoint yet. Re-deploying with the same name will fail with a naming conflict. Delete the old version first with `openhome delete`.
|
|
184
148
|
|
|
185
149
|
---
|
|
186
150
|
|
|
@@ -199,7 +163,64 @@ Shows a table with name, version, status, and last update date.
|
|
|
199
163
|
|
|
200
164
|
Status colors: green = active, yellow = processing, red = failed, gray = disabled.
|
|
201
165
|
|
|
202
|
-
> **
|
|
166
|
+
> **Requires session token.** Run `openhome set-jwt <token>` first. See [set-jwt](#openhome-set-jwt-token) above.
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
### `openhome delete [ability]`
|
|
171
|
+
|
|
172
|
+
Delete a deployed ability.
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
# Pick from a list interactively
|
|
176
|
+
openhome delete
|
|
177
|
+
|
|
178
|
+
# Delete by name directly
|
|
179
|
+
openhome delete my-weather-bot
|
|
180
|
+
|
|
181
|
+
# Test with fake data
|
|
182
|
+
openhome delete --mock
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Prompts for confirmation before deleting.
|
|
186
|
+
|
|
187
|
+
> **Requires session token.** Run `openhome set-jwt <token>` first.
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
### `openhome toggle [ability]`
|
|
192
|
+
|
|
193
|
+
Enable or disable a deployed ability.
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
# Interactive
|
|
197
|
+
openhome toggle
|
|
198
|
+
|
|
199
|
+
# By name with flag
|
|
200
|
+
openhome toggle my-weather-bot --enable
|
|
201
|
+
openhome toggle my-weather-bot --disable
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
| Flag | What it does |
|
|
205
|
+
|------|-------------|
|
|
206
|
+
| `--enable` | Enable the ability |
|
|
207
|
+
| `--disable` | Disable the ability |
|
|
208
|
+
|
|
209
|
+
> **Requires session token.** Run `openhome set-jwt <token>` first.
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
### `openhome assign`
|
|
214
|
+
|
|
215
|
+
Assign abilities to an agent (multiselect).
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
openhome assign
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Fetches your agents and abilities, lets you pick an agent, then multiselect which abilities to assign to it.
|
|
222
|
+
|
|
223
|
+
> **Requires session token.** Run `openhome set-jwt <token>` first.
|
|
203
224
|
|
|
204
225
|
---
|
|
205
226
|
|
|
@@ -209,12 +230,40 @@ View your agents and set a default for deploys.
|
|
|
209
230
|
|
|
210
231
|
```bash
|
|
211
232
|
openhome agents
|
|
233
|
+
```
|
|
212
234
|
|
|
213
|
-
|
|
214
|
-
|
|
235
|
+
Shows all agents on your account with names and IDs. Optionally set or change your default agent (used by `deploy` when `--personality` is not specified).
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
### `openhome chat [agent]`
|
|
240
|
+
|
|
241
|
+
Chat with an agent via WebSocket. Send text messages and trigger abilities with keywords.
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
# Pick an agent interactively
|
|
245
|
+
openhome chat
|
|
246
|
+
|
|
247
|
+
# Chat with a specific agent
|
|
248
|
+
openhome chat pers_abc123
|
|
215
249
|
```
|
|
216
250
|
|
|
217
|
-
|
|
251
|
+
Once connected, type messages and press Enter. The agent responds in real-time.
|
|
252
|
+
|
|
253
|
+
Commands inside chat: `/quit`, `/exit`, or `/q` to disconnect. Ctrl+C also works.
|
|
254
|
+
|
|
255
|
+
> **Note:** Audio responses from the agent are not playable in the terminal. Text responses display normally.
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
### `openhome trigger [phrase]`
|
|
260
|
+
|
|
261
|
+
Send a trigger phrase to fire an ability remotely.
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
openhome trigger "play aquaprime"
|
|
265
|
+
openhome trigger --agent pers_abc123 "check weather"
|
|
266
|
+
```
|
|
218
267
|
|
|
219
268
|
---
|
|
220
269
|
|
|
@@ -233,9 +282,51 @@ openhome status
|
|
|
233
282
|
openhome status my-weather-bot --mock
|
|
234
283
|
```
|
|
235
284
|
|
|
236
|
-
|
|
285
|
+
> **Requires session token** (uses the same list endpoint internally). Run `openhome set-jwt <token>` first.
|
|
237
286
|
|
|
238
|
-
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
### `openhome logs`
|
|
290
|
+
|
|
291
|
+
Stream live agent messages and logs.
|
|
292
|
+
|
|
293
|
+
```bash
|
|
294
|
+
openhome logs
|
|
295
|
+
openhome logs --agent pers_abc123
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
### `openhome whoami`
|
|
301
|
+
|
|
302
|
+
Show auth status, default agent, and tracked abilities.
|
|
303
|
+
|
|
304
|
+
```bash
|
|
305
|
+
openhome whoami
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
### `openhome config [path]`
|
|
311
|
+
|
|
312
|
+
Edit trigger words, description, or category in a local `config.json`.
|
|
313
|
+
|
|
314
|
+
```bash
|
|
315
|
+
openhome config
|
|
316
|
+
openhome config ./my-ability
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
---
|
|
320
|
+
|
|
321
|
+
### `openhome logout`
|
|
322
|
+
|
|
323
|
+
Clear stored credentials and log out.
|
|
324
|
+
|
|
325
|
+
```bash
|
|
326
|
+
openhome logout
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
Removes the API key from macOS Keychain and clears the default agent from config.
|
|
239
330
|
|
|
240
331
|
---
|
|
241
332
|
|
|
@@ -259,8 +350,6 @@ Must contain:
|
|
|
259
350
|
|
|
260
351
|
### main.py Required Patterns
|
|
261
352
|
|
|
262
|
-
Your main Python file must include:
|
|
263
|
-
|
|
264
353
|
| What | Why |
|
|
265
354
|
|------|-----|
|
|
266
355
|
| Class extending `MatchingCapability` | OpenHome ability base class |
|
|
@@ -272,8 +361,6 @@ Your main Python file must include:
|
|
|
272
361
|
|
|
273
362
|
### Blocked Patterns (Errors)
|
|
274
363
|
|
|
275
|
-
These are not allowed in any `.py` file:
|
|
276
|
-
|
|
277
364
|
| Pattern | Use Instead |
|
|
278
365
|
|---------|-------------|
|
|
279
366
|
| `print()` | `self.worker.editor_logging_handler` |
|
|
@@ -310,8 +397,7 @@ These are not allowed in any `.py` file:
|
|
|
310
397
|
|
|
311
398
|
```
|
|
312
399
|
~/.openhome/
|
|
313
|
-
config.json # Settings
|
|
314
|
-
last-deploy.zip # Saved when upload endpoint unavailable
|
|
400
|
+
config.json # Settings, fallback API key, session token
|
|
315
401
|
```
|
|
316
402
|
|
|
317
403
|
### Config Fields
|
|
@@ -321,6 +407,7 @@ These are not allowed in any `.py` file:
|
|
|
321
407
|
| `api_base_url` | Override API endpoint | `https://app.openhome.com` |
|
|
322
408
|
| `default_personality_id` | Default agent for deploys | (none) |
|
|
323
409
|
| `api_key` | Fallback key storage | (none — prefers Keychain) |
|
|
410
|
+
| `jwt` | Session token for management commands | (none — set via `set-jwt`) |
|
|
324
411
|
|
|
325
412
|
### API Key Storage
|
|
326
413
|
|
|
@@ -328,125 +415,51 @@ On macOS, your API key is stored in the system Keychain (service: `openhome-cli`
|
|
|
328
415
|
|
|
329
416
|
---
|
|
330
417
|
|
|
331
|
-
##
|
|
332
|
-
|
|
333
|
-
```
|
|
334
|
-
openhome-cli/
|
|
335
|
-
├── bin/openhome.js # Entry point shim
|
|
336
|
-
├── src/
|
|
337
|
-
│ ├── cli.ts # Menu + Commander setup
|
|
338
|
-
│ ├── commands/
|
|
339
|
-
│ │ ├── login.ts # API key auth
|
|
340
|
-
│ │ ├── logout.ts # Clear credentials
|
|
341
|
-
│ │ ├── init.ts # Scaffold new ability
|
|
342
|
-
│ │ ├── deploy.ts # Validate + zip + upload
|
|
343
|
-
│ │ ├── chat.ts # WebSocket chat with agent
|
|
344
|
-
│ │ ├── list.ts # List abilities table
|
|
345
|
-
│ │ ├── agents.ts # View agents + set default
|
|
346
|
-
│ │ └── status.ts # Ability detail view
|
|
347
|
-
│ ├── api/
|
|
348
|
-
│ │ ├── client.ts # HTTP client + error handling
|
|
349
|
-
│ │ ├── mock-client.ts # Fake responses for testing
|
|
350
|
-
│ │ ├── contracts.ts # TypeScript interfaces
|
|
351
|
-
│ │ └── endpoints.ts # URL constants
|
|
352
|
-
│ ├── validation/
|
|
353
|
-
│ │ ├── rules.ts # All validation rules
|
|
354
|
-
│ │ └── validator.ts # Rule runner
|
|
355
|
-
│ ├── config/
|
|
356
|
-
│ │ ├── store.ts # Config file + Keychain
|
|
357
|
-
│ │ └── keychain.ts # macOS Keychain helpers
|
|
358
|
-
│ ├── ui/
|
|
359
|
-
│ │ └── format.ts # Colors, tables, prompts
|
|
360
|
-
│ └── util/
|
|
361
|
-
│ └── zip.ts # ZIP creation (archiver)
|
|
362
|
-
└── templates/
|
|
363
|
-
├── basic/ # Simple ability template
|
|
364
|
-
└── api/ # API-calling ability template
|
|
365
|
-
```
|
|
366
|
-
|
|
367
|
-
---
|
|
368
|
-
|
|
369
|
-
## Development
|
|
370
|
-
|
|
371
|
-
```bash
|
|
372
|
-
# Install dependencies
|
|
373
|
-
npm install
|
|
374
|
-
|
|
375
|
-
# Build
|
|
376
|
-
npm run build
|
|
377
|
-
|
|
378
|
-
# Run without building (dev mode)
|
|
379
|
-
npm run dev
|
|
380
|
-
|
|
381
|
-
# Type check
|
|
382
|
-
npm run lint
|
|
383
|
-
|
|
384
|
-
# Run tests
|
|
385
|
-
npm run test
|
|
386
|
-
```
|
|
387
|
-
|
|
388
|
-
### Tech Stack
|
|
418
|
+
## What This Tool Does NOT Do
|
|
389
419
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
| chalk | 5.x | Terminal colors |
|
|
395
|
-
| ws | 8.x | WebSocket client for agent chat |
|
|
396
|
-
| archiver | 7.x | ZIP file creation |
|
|
397
|
-
| typescript | 5.x | Type safety |
|
|
398
|
-
| tsup | 8.x | Build tool |
|
|
399
|
-
| vitest | 2.x | Testing |
|
|
420
|
+
- **No local ability testing** — Abilities run on the OpenHome platform. Deploy and use "Start Live Test" in the web editor to test.
|
|
421
|
+
- **No ability editing** — The CLI does not modify deployed abilities. Edit locally, then re-deploy.
|
|
422
|
+
- **No update/redeploy** — There is no endpoint to overwrite an existing ability version. Deploy creates a new entry; delete the old one via `openhome delete`.
|
|
423
|
+
- **No Windows Keychain** — API key stored in plaintext config on non-macOS platforms.
|
|
400
424
|
|
|
401
425
|
---
|
|
402
426
|
|
|
403
427
|
## API Status
|
|
404
428
|
|
|
405
|
-
| Endpoint |
|
|
406
|
-
|
|
407
|
-
|
|
|
408
|
-
|
|
|
409
|
-
|
|
|
410
|
-
|
|
|
411
|
-
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
- **List/Status**: Suggests using `--mock` flag to preview the output format
|
|
429
|
+
| Command | Endpoint | Auth | Status |
|
|
430
|
+
|---------|----------|------|--------|
|
|
431
|
+
| `login` | `POST /api/sdk/verify_apikey/` | API key | Live |
|
|
432
|
+
| `agents` | `POST /api/sdk/get_personalities/` | API key | Live |
|
|
433
|
+
| `chat` | WebSocket `/websocket/voice-stream/` | API key | Live |
|
|
434
|
+
| `deploy` | `POST /api/capabilities/add-capability/` | API key | Live |
|
|
435
|
+
| `list` | `GET /api/capabilities/get-installed-capabilities/` | JWT | Live |
|
|
436
|
+
| `delete` | `POST /api/capabilities/delete-capability/` | JWT | Live |
|
|
437
|
+
| `toggle` | `PUT /api/capabilities/edit-installed-capability/:id/` | JWT | Live |
|
|
438
|
+
| `assign` | `PUT /api/personalities/edit-personality/` | JWT | Live |
|
|
416
439
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
---
|
|
420
|
-
|
|
421
|
-
## What This Tool Does NOT Do
|
|
422
|
-
|
|
423
|
-
- **No local ability testing** — Abilities run on the OpenHome platform. Deploy and use "Start Live Test" in the web editor to test.
|
|
424
|
-
- **No log streaming** — `openhome logs` is not yet implemented.
|
|
425
|
-
- **No ability deletion** — Must be done through the web dashboard.
|
|
426
|
-
- **No ability editing** — The CLI does not modify deployed abilities. Edit locally, then re-deploy.
|
|
427
|
-
- **No multi-agent deploy** — One ability deploys to one agent at a time.
|
|
428
|
-
- **No Windows Keychain** — API key stored in plaintext config on non-macOS platforms.
|
|
440
|
+
Commands marked **JWT** require `openhome set-jwt <token>` first. OpenHome currently uses separate auth for SDK operations (API key) vs. account management (web session JWT). Once OpenHome adds API key support to management endpoints, the `set-jwt` step will no longer be needed.
|
|
429
441
|
|
|
430
442
|
---
|
|
431
443
|
|
|
432
444
|
## Roadmap
|
|
433
445
|
|
|
434
|
-
### Planned
|
|
435
|
-
|
|
436
|
-
- [ ] `openhome logs [ability]` — Stream ability logs in real-time
|
|
437
|
-
- [ ] `openhome delete [ability]` — Remove a deployed ability
|
|
438
|
-
- [ ] `openhome update` — Re-deploy an existing ability (shortcut for deploy)
|
|
439
446
|
- [ ] `openhome watch` — Auto-deploy on file changes
|
|
447
|
+
- [ ] `openhome update` — Re-deploy/overwrite an existing ability (pending server-side update endpoint)
|
|
440
448
|
- [ ] Background Daemon and Brain Skill templates
|
|
441
449
|
- [ ] Cross-platform secure key storage (Windows Credential Manager, Linux Secret Service)
|
|
450
|
+
- [ ] Management commands without JWT (pending OpenHome API update)
|
|
451
|
+
|
|
452
|
+
---
|
|
442
453
|
|
|
443
|
-
|
|
454
|
+
## Development
|
|
444
455
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
456
|
+
```bash
|
|
457
|
+
npm install
|
|
458
|
+
npm run build # Build
|
|
459
|
+
npm run dev # Run without building
|
|
460
|
+
npm run lint # Type check
|
|
461
|
+
npm run test # Run tests
|
|
462
|
+
```
|
|
450
463
|
|
|
451
464
|
---
|
|
452
465
|
|
package/dist/cli.js
CHANGED
|
@@ -301,24 +301,6 @@ async function loginCommand() {
|
|
|
301
301
|
}
|
|
302
302
|
saveApiKey(apiKey);
|
|
303
303
|
success("API key saved.");
|
|
304
|
-
p.note(
|
|
305
|
-
[
|
|
306
|
-
"Some features (list, toggle, delete, assign) require a session token.",
|
|
307
|
-
"To get it: go to app.openhome.com \u2192 open DevTools Console \u2192 run:",
|
|
308
|
-
" localStorage.getItem('access_token')"
|
|
309
|
-
].join("\n"),
|
|
310
|
-
"Optional: Session Token"
|
|
311
|
-
);
|
|
312
|
-
const jwtInput = await p.text({
|
|
313
|
-
message: "Paste your session token (or press Enter to skip)",
|
|
314
|
-
placeholder: "eyJhbGci..."
|
|
315
|
-
});
|
|
316
|
-
handleCancel(jwtInput);
|
|
317
|
-
const jwt = jwtInput?.trim();
|
|
318
|
-
if (jwt) {
|
|
319
|
-
saveJwt(jwt);
|
|
320
|
-
success("Session token saved.");
|
|
321
|
-
}
|
|
322
304
|
if (agents.length > 0) {
|
|
323
305
|
p.note(
|
|
324
306
|
agents.map((a) => `${chalk2.bold(a.name)} ${chalk2.gray(a.id)}`).join("\n"),
|
|
@@ -1848,6 +1830,12 @@ async function deleteCommand(abilityArg, opts = {}) {
|
|
|
1848
1830
|
error("Not authenticated. Run: openhome login");
|
|
1849
1831
|
process.exit(1);
|
|
1850
1832
|
}
|
|
1833
|
+
if (!jwt) {
|
|
1834
|
+
error(
|
|
1835
|
+
"This command requires a session token.\nGet it from app.openhome.com \u2192 DevTools \u2192 Application \u2192 Local Storage \u2192 token\nThen run: openhome set-jwt <token>"
|
|
1836
|
+
);
|
|
1837
|
+
process.exit(1);
|
|
1838
|
+
}
|
|
1851
1839
|
client = new ApiClient(apiKey, getConfig().api_base_url, jwt);
|
|
1852
1840
|
}
|
|
1853
1841
|
const s = p.spinner();
|
|
@@ -1931,6 +1919,12 @@ async function toggleCommand(abilityArg, opts = {}) {
|
|
|
1931
1919
|
error("Not authenticated. Run: openhome login");
|
|
1932
1920
|
process.exit(1);
|
|
1933
1921
|
}
|
|
1922
|
+
if (!jwt) {
|
|
1923
|
+
error(
|
|
1924
|
+
"This command requires a session token.\nGet it from app.openhome.com \u2192 DevTools \u2192 Application \u2192 Local Storage \u2192 token\nThen run: openhome set-jwt <token>"
|
|
1925
|
+
);
|
|
1926
|
+
process.exit(1);
|
|
1927
|
+
}
|
|
1934
1928
|
client = new ApiClient(apiKey, getConfig().api_base_url, jwt);
|
|
1935
1929
|
}
|
|
1936
1930
|
const s = p.spinner();
|
|
@@ -2024,6 +2018,12 @@ async function assignCommand(opts = {}) {
|
|
|
2024
2018
|
error("Not authenticated. Run: openhome login");
|
|
2025
2019
|
process.exit(1);
|
|
2026
2020
|
}
|
|
2021
|
+
if (!jwt) {
|
|
2022
|
+
error(
|
|
2023
|
+
"This command requires a session token.\nGet it from app.openhome.com \u2192 DevTools \u2192 Application \u2192 Local Storage \u2192 token\nThen run: openhome set-jwt <token>"
|
|
2024
|
+
);
|
|
2025
|
+
process.exit(1);
|
|
2026
|
+
}
|
|
2027
2027
|
client = new ApiClient(apiKey, getConfig().api_base_url, jwt);
|
|
2028
2028
|
}
|
|
2029
2029
|
const s = p.spinner();
|
|
@@ -2127,6 +2127,12 @@ async function listCommand(opts = {}) {
|
|
|
2127
2127
|
error("Not authenticated. Run: openhome login");
|
|
2128
2128
|
process.exit(1);
|
|
2129
2129
|
}
|
|
2130
|
+
if (!jwt) {
|
|
2131
|
+
error(
|
|
2132
|
+
"This command requires a session token.\nGet it from app.openhome.com \u2192 DevTools \u2192 Application \u2192 Local Storage \u2192 token\nThen run: openhome set-jwt <token>"
|
|
2133
|
+
);
|
|
2134
|
+
process.exit(1);
|
|
2135
|
+
}
|
|
2130
2136
|
client = new ApiClient(apiKey, getConfig().api_base_url, jwt);
|
|
2131
2137
|
}
|
|
2132
2138
|
const s = p.spinner();
|
|
@@ -3037,6 +3043,107 @@ async function logsCommand(opts = {}) {
|
|
|
3037
3043
|
});
|
|
3038
3044
|
}
|
|
3039
3045
|
|
|
3046
|
+
// src/commands/set-jwt.ts
|
|
3047
|
+
import { execFile } from "child_process";
|
|
3048
|
+
import chalk13 from "chalk";
|
|
3049
|
+
var OPENHOME_URL = "https://app.openhome.com";
|
|
3050
|
+
function openBrowser(url) {
|
|
3051
|
+
try {
|
|
3052
|
+
if (process.platform === "darwin") {
|
|
3053
|
+
execFile("open", [url]);
|
|
3054
|
+
} else if (process.platform === "win32") {
|
|
3055
|
+
execFile("cmd", ["/c", "start", url]);
|
|
3056
|
+
} else {
|
|
3057
|
+
execFile("xdg-open", [url]);
|
|
3058
|
+
}
|
|
3059
|
+
} catch {
|
|
3060
|
+
}
|
|
3061
|
+
}
|
|
3062
|
+
async function setJwtCommand(token) {
|
|
3063
|
+
if (token) {
|
|
3064
|
+
p.intro("\u{1F511} Enable Management Features");
|
|
3065
|
+
try {
|
|
3066
|
+
saveJwt(token.trim());
|
|
3067
|
+
success("Session token saved.");
|
|
3068
|
+
p.outro(
|
|
3069
|
+
"Management commands (list, delete, toggle, assign) are now unlocked."
|
|
3070
|
+
);
|
|
3071
|
+
} catch (err) {
|
|
3072
|
+
error(
|
|
3073
|
+
`Failed to save token: ${err instanceof Error ? err.message : String(err)}`
|
|
3074
|
+
);
|
|
3075
|
+
process.exit(1);
|
|
3076
|
+
}
|
|
3077
|
+
return;
|
|
3078
|
+
}
|
|
3079
|
+
p.intro("\u{1F511} Enable Management Features");
|
|
3080
|
+
p.note(
|
|
3081
|
+
[
|
|
3082
|
+
"Some commands (list, delete, enable/disable abilities) need your",
|
|
3083
|
+
"OpenHome session token to work.",
|
|
3084
|
+
"",
|
|
3085
|
+
"Think of it as a temporary key that proves you're logged in to",
|
|
3086
|
+
"your account. You only need to do this once."
|
|
3087
|
+
].join("\n"),
|
|
3088
|
+
"What's this?"
|
|
3089
|
+
);
|
|
3090
|
+
console.log("");
|
|
3091
|
+
console.log(
|
|
3092
|
+
chalk13.dim(` Opening ${chalk13.bold("app.openhome.com")} in your browser...`)
|
|
3093
|
+
);
|
|
3094
|
+
openBrowser(OPENHOME_URL);
|
|
3095
|
+
console.log("");
|
|
3096
|
+
p.note(
|
|
3097
|
+
[
|
|
3098
|
+
`${chalk13.bold("Step 1")} Make sure you're logged in to app.openhome.com`,
|
|
3099
|
+
"",
|
|
3100
|
+
`${chalk13.bold("Step 2")} Open the browser console:`,
|
|
3101
|
+
` Mac \u2192 ${chalk13.cyan("Cmd + Option + J")}`,
|
|
3102
|
+
` Windows / Linux \u2192 ${chalk13.cyan("F12")}, then click the Console tab`,
|
|
3103
|
+
"",
|
|
3104
|
+
`${chalk13.bold("Step 3")} Paste this command into the console and press Enter:`,
|
|
3105
|
+
"",
|
|
3106
|
+
` ${chalk13.green("copy(localStorage.getItem('token'))")}`,
|
|
3107
|
+
"",
|
|
3108
|
+
`${chalk13.bold("Step 4")} Your token is now in your clipboard.`,
|
|
3109
|
+
` Come back here and paste it below.`
|
|
3110
|
+
].join("\n"),
|
|
3111
|
+
"How to get your token"
|
|
3112
|
+
);
|
|
3113
|
+
const input = await p.password({
|
|
3114
|
+
message: "Paste your token here",
|
|
3115
|
+
validate: (val) => {
|
|
3116
|
+
if (!val || !val.trim()) return "Token is required";
|
|
3117
|
+
if (val.trim().length < 20)
|
|
3118
|
+
return "That doesn't look right \u2014 the token should be much longer";
|
|
3119
|
+
}
|
|
3120
|
+
});
|
|
3121
|
+
if (typeof input === "symbol") {
|
|
3122
|
+
p.cancel("Cancelled.");
|
|
3123
|
+
return;
|
|
3124
|
+
}
|
|
3125
|
+
try {
|
|
3126
|
+
saveJwt(input.trim());
|
|
3127
|
+
success("Session token saved.");
|
|
3128
|
+
p.note(
|
|
3129
|
+
[
|
|
3130
|
+
"These commands are now unlocked:",
|
|
3131
|
+
` ${chalk13.bold("openhome list")} \u2014 see all your deployed abilities`,
|
|
3132
|
+
` ${chalk13.bold("openhome delete")} \u2014 remove an ability`,
|
|
3133
|
+
` ${chalk13.bold("openhome toggle")} \u2014 enable or disable an ability`,
|
|
3134
|
+
` ${chalk13.bold("openhome assign")} \u2014 link abilities to an agent`
|
|
3135
|
+
].join("\n"),
|
|
3136
|
+
"You're all set"
|
|
3137
|
+
);
|
|
3138
|
+
p.outro("Done!");
|
|
3139
|
+
} catch (err) {
|
|
3140
|
+
error(
|
|
3141
|
+
`Failed to save token: ${err instanceof Error ? err.message : String(err)}`
|
|
3142
|
+
);
|
|
3143
|
+
process.exit(1);
|
|
3144
|
+
}
|
|
3145
|
+
}
|
|
3146
|
+
|
|
3040
3147
|
// src/cli.ts
|
|
3041
3148
|
var __filename = fileURLToPath(import.meta.url);
|
|
3042
3149
|
var __dirname = dirname(__filename);
|
|
@@ -3124,6 +3231,11 @@ async function interactiveMenu() {
|
|
|
3124
3231
|
label: "\u{1F464} Who Am I",
|
|
3125
3232
|
hint: "Show auth, default agent, tracked abilities"
|
|
3126
3233
|
},
|
|
3234
|
+
{
|
|
3235
|
+
value: "set-jwt",
|
|
3236
|
+
label: "\u{1F511} Enable Management",
|
|
3237
|
+
hint: "Unlock list, delete, toggle, assign"
|
|
3238
|
+
},
|
|
3127
3239
|
{
|
|
3128
3240
|
value: "logout",
|
|
3129
3241
|
label: "\u{1F513} Log Out",
|
|
@@ -3170,6 +3282,9 @@ async function interactiveMenu() {
|
|
|
3170
3282
|
case "whoami":
|
|
3171
3283
|
await whoamiCommand();
|
|
3172
3284
|
break;
|
|
3285
|
+
case "set-jwt":
|
|
3286
|
+
await setJwtCommand();
|
|
3287
|
+
break;
|
|
3173
3288
|
case "logout":
|
|
3174
3289
|
await logoutCommand();
|
|
3175
3290
|
await ensureLoggedIn();
|
|
@@ -3235,6 +3350,11 @@ program.command("logs").description("Stream live agent messages and logs").optio
|
|
|
3235
3350
|
program.command("whoami").description("Show auth status, default agent, and tracked abilities").action(async () => {
|
|
3236
3351
|
await whoamiCommand();
|
|
3237
3352
|
});
|
|
3353
|
+
program.command("set-jwt [token]").description(
|
|
3354
|
+
"Save a session token to enable management commands (list, delete, toggle, assign)"
|
|
3355
|
+
).action(async (token) => {
|
|
3356
|
+
await setJwtCommand(token);
|
|
3357
|
+
});
|
|
3238
3358
|
if (process.argv.length <= 2) {
|
|
3239
3359
|
interactiveMenu().catch((err) => {
|
|
3240
3360
|
console.error(err instanceof Error ? err.message : String(err));
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -18,6 +18,7 @@ import { triggerCommand } from "./commands/trigger.js";
|
|
|
18
18
|
import { whoamiCommand } from "./commands/whoami.js";
|
|
19
19
|
import { configEditCommand } from "./commands/config-edit.js";
|
|
20
20
|
import { logsCommand } from "./commands/logs.js";
|
|
21
|
+
import { setJwtCommand } from "./commands/set-jwt.js";
|
|
21
22
|
import { p, handleCancel } from "./ui/format.js";
|
|
22
23
|
|
|
23
24
|
// Read version from package.json
|
|
@@ -115,6 +116,11 @@ async function interactiveMenu(): Promise<void> {
|
|
|
115
116
|
label: "👤 Who Am I",
|
|
116
117
|
hint: "Show auth, default agent, tracked abilities",
|
|
117
118
|
},
|
|
119
|
+
{
|
|
120
|
+
value: "set-jwt",
|
|
121
|
+
label: "🔑 Enable Management",
|
|
122
|
+
hint: "Unlock list, delete, toggle, assign",
|
|
123
|
+
},
|
|
118
124
|
{
|
|
119
125
|
value: "logout",
|
|
120
126
|
label: "🔓 Log Out",
|
|
@@ -162,6 +168,9 @@ async function interactiveMenu(): Promise<void> {
|
|
|
162
168
|
case "whoami":
|
|
163
169
|
await whoamiCommand();
|
|
164
170
|
break;
|
|
171
|
+
case "set-jwt":
|
|
172
|
+
await setJwtCommand();
|
|
173
|
+
break;
|
|
165
174
|
case "logout":
|
|
166
175
|
await logoutCommand();
|
|
167
176
|
await ensureLoggedIn();
|
|
@@ -316,6 +325,15 @@ program
|
|
|
316
325
|
await whoamiCommand();
|
|
317
326
|
});
|
|
318
327
|
|
|
328
|
+
program
|
|
329
|
+
.command("set-jwt [token]")
|
|
330
|
+
.description(
|
|
331
|
+
"Save a session token to enable management commands (list, delete, toggle, assign)",
|
|
332
|
+
)
|
|
333
|
+
.action(async (token?: string) => {
|
|
334
|
+
await setJwtCommand(token);
|
|
335
|
+
});
|
|
336
|
+
|
|
319
337
|
// ── Entry point: menu if no args, subcommand otherwise ───────────
|
|
320
338
|
|
|
321
339
|
if (process.argv.length <= 2) {
|
package/src/commands/assign.ts
CHANGED
|
@@ -20,6 +20,12 @@ export async function assignCommand(
|
|
|
20
20
|
error("Not authenticated. Run: openhome login");
|
|
21
21
|
process.exit(1);
|
|
22
22
|
}
|
|
23
|
+
if (!jwt) {
|
|
24
|
+
error(
|
|
25
|
+
"This command requires a session token.\nGet it from app.openhome.com → DevTools → Application → Local Storage → token\nThen run: openhome set-jwt <token>",
|
|
26
|
+
);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
23
29
|
client = new ApiClient(apiKey, getConfig().api_base_url, jwt);
|
|
24
30
|
}
|
|
25
31
|
|
package/src/commands/delete.ts
CHANGED
|
@@ -21,6 +21,12 @@ export async function deleteCommand(
|
|
|
21
21
|
error("Not authenticated. Run: openhome login");
|
|
22
22
|
process.exit(1);
|
|
23
23
|
}
|
|
24
|
+
if (!jwt) {
|
|
25
|
+
error(
|
|
26
|
+
"This command requires a session token.\nGet it from app.openhome.com → DevTools → Application → Local Storage → token\nThen run: openhome set-jwt <token>",
|
|
27
|
+
);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
24
30
|
client = new ApiClient(apiKey, getConfig().api_base_url, jwt);
|
|
25
31
|
}
|
|
26
32
|
|
package/src/commands/list.ts
CHANGED
|
@@ -36,6 +36,12 @@ export async function listCommand(
|
|
|
36
36
|
error("Not authenticated. Run: openhome login");
|
|
37
37
|
process.exit(1);
|
|
38
38
|
}
|
|
39
|
+
if (!jwt) {
|
|
40
|
+
error(
|
|
41
|
+
"This command requires a session token.\nGet it from app.openhome.com → DevTools → Application → Local Storage → token\nThen run: openhome set-jwt <token>",
|
|
42
|
+
);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
39
45
|
client = new ApiClient(apiKey, getConfig().api_base_url, jwt);
|
|
40
46
|
}
|
|
41
47
|
|
package/src/commands/login.ts
CHANGED
|
@@ -38,28 +38,6 @@ export async function loginCommand(): Promise<void> {
|
|
|
38
38
|
saveApiKey(apiKey as string);
|
|
39
39
|
success("API key saved.");
|
|
40
40
|
|
|
41
|
-
// Ask for JWT for full management access
|
|
42
|
-
p.note(
|
|
43
|
-
[
|
|
44
|
-
"Some features (list, toggle, delete, assign) require a session token.",
|
|
45
|
-
"To get it: go to app.openhome.com → open DevTools Console → run:",
|
|
46
|
-
" localStorage.getItem('access_token')",
|
|
47
|
-
].join("\n"),
|
|
48
|
-
"Optional: Session Token",
|
|
49
|
-
);
|
|
50
|
-
|
|
51
|
-
const jwtInput = await p.text({
|
|
52
|
-
message: "Paste your session token (or press Enter to skip)",
|
|
53
|
-
placeholder: "eyJhbGci...",
|
|
54
|
-
});
|
|
55
|
-
handleCancel(jwtInput);
|
|
56
|
-
|
|
57
|
-
const jwt = (jwtInput as string | undefined)?.trim();
|
|
58
|
-
if (jwt) {
|
|
59
|
-
saveJwt(jwt);
|
|
60
|
-
success("Session token saved.");
|
|
61
|
-
}
|
|
62
|
-
|
|
63
41
|
// Show agents on this account
|
|
64
42
|
if (agents.length > 0) {
|
|
65
43
|
p.note(
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { saveJwt } from "../config/store.js";
|
|
3
|
+
import { success, error, p } from "../ui/format.js";
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
|
|
6
|
+
const OPENHOME_URL = "https://app.openhome.com";
|
|
7
|
+
|
|
8
|
+
function openBrowser(url: string): void {
|
|
9
|
+
try {
|
|
10
|
+
if (process.platform === "darwin") {
|
|
11
|
+
execFile("open", [url]);
|
|
12
|
+
} else if (process.platform === "win32") {
|
|
13
|
+
execFile("cmd", ["/c", "start", url]);
|
|
14
|
+
} else {
|
|
15
|
+
execFile("xdg-open", [url]);
|
|
16
|
+
}
|
|
17
|
+
} catch {
|
|
18
|
+
// best-effort — user can open manually
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export async function setJwtCommand(token?: string): Promise<void> {
|
|
23
|
+
// Direct usage: openhome set-jwt eyJ...
|
|
24
|
+
if (token) {
|
|
25
|
+
p.intro("🔑 Enable Management Features");
|
|
26
|
+
try {
|
|
27
|
+
saveJwt(token.trim());
|
|
28
|
+
success("Session token saved.");
|
|
29
|
+
p.outro(
|
|
30
|
+
"Management commands (list, delete, toggle, assign) are now unlocked.",
|
|
31
|
+
);
|
|
32
|
+
} catch (err) {
|
|
33
|
+
error(
|
|
34
|
+
`Failed to save token: ${err instanceof Error ? err.message : String(err)}`,
|
|
35
|
+
);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Guided interactive flow
|
|
42
|
+
p.intro("🔑 Enable Management Features");
|
|
43
|
+
|
|
44
|
+
p.note(
|
|
45
|
+
[
|
|
46
|
+
"Some commands (list, delete, enable/disable abilities) need your",
|
|
47
|
+
"OpenHome session token to work.",
|
|
48
|
+
"",
|
|
49
|
+
"Think of it as a temporary key that proves you're logged in to",
|
|
50
|
+
"your account. You only need to do this once.",
|
|
51
|
+
].join("\n"),
|
|
52
|
+
"What's this?",
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
console.log("");
|
|
56
|
+
console.log(
|
|
57
|
+
chalk.dim(` Opening ${chalk.bold("app.openhome.com")} in your browser...`),
|
|
58
|
+
);
|
|
59
|
+
openBrowser(OPENHOME_URL);
|
|
60
|
+
console.log("");
|
|
61
|
+
|
|
62
|
+
p.note(
|
|
63
|
+
[
|
|
64
|
+
`${chalk.bold("Step 1")} Make sure you're logged in to app.openhome.com`,
|
|
65
|
+
"",
|
|
66
|
+
`${chalk.bold("Step 2")} Open the browser console:`,
|
|
67
|
+
` Mac → ${chalk.cyan("Cmd + Option + J")}`,
|
|
68
|
+
` Windows / Linux → ${chalk.cyan("F12")}, then click the Console tab`,
|
|
69
|
+
"",
|
|
70
|
+
`${chalk.bold("Step 3")} Paste this command into the console and press Enter:`,
|
|
71
|
+
"",
|
|
72
|
+
` ${chalk.green("copy(localStorage.getItem('token'))")}`,
|
|
73
|
+
"",
|
|
74
|
+
`${chalk.bold("Step 4")} Your token is now in your clipboard.`,
|
|
75
|
+
` Come back here and paste it below.`,
|
|
76
|
+
].join("\n"),
|
|
77
|
+
"How to get your token",
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
const input = await p.password({
|
|
81
|
+
message: "Paste your token here",
|
|
82
|
+
validate: (val) => {
|
|
83
|
+
if (!val || !val.trim()) return "Token is required";
|
|
84
|
+
if (val.trim().length < 20)
|
|
85
|
+
return "That doesn't look right — the token should be much longer";
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
if (typeof input === "symbol") {
|
|
90
|
+
p.cancel("Cancelled.");
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
saveJwt((input as string).trim());
|
|
96
|
+
success("Session token saved.");
|
|
97
|
+
p.note(
|
|
98
|
+
[
|
|
99
|
+
"These commands are now unlocked:",
|
|
100
|
+
` ${chalk.bold("openhome list")} — see all your deployed abilities`,
|
|
101
|
+
` ${chalk.bold("openhome delete")} — remove an ability`,
|
|
102
|
+
` ${chalk.bold("openhome toggle")} — enable or disable an ability`,
|
|
103
|
+
` ${chalk.bold("openhome assign")} — link abilities to an agent`,
|
|
104
|
+
].join("\n"),
|
|
105
|
+
"You're all set",
|
|
106
|
+
);
|
|
107
|
+
p.outro("Done!");
|
|
108
|
+
} catch (err) {
|
|
109
|
+
error(
|
|
110
|
+
`Failed to save token: ${err instanceof Error ? err.message : String(err)}`,
|
|
111
|
+
);
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
}
|
package/src/commands/toggle.ts
CHANGED
|
@@ -21,6 +21,12 @@ export async function toggleCommand(
|
|
|
21
21
|
error("Not authenticated. Run: openhome login");
|
|
22
22
|
process.exit(1);
|
|
23
23
|
}
|
|
24
|
+
if (!jwt) {
|
|
25
|
+
error(
|
|
26
|
+
"This command requires a session token.\nGet it from app.openhome.com → DevTools → Application → Local Storage → token\nThen run: openhome set-jwt <token>",
|
|
27
|
+
);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
24
30
|
client = new ApiClient(apiKey, getConfig().api_base_url, jwt);
|
|
25
31
|
}
|
|
26
32
|
|