openhome-cli 0.1.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 (45) hide show
  1. package/README.md +470 -0
  2. package/bin/openhome.js +2 -0
  3. package/dist/chunk-Q4UKUXDB.js +164 -0
  4. package/dist/cli.d.ts +2 -0
  5. package/dist/cli.js +3184 -0
  6. package/dist/store-DR7EKQ5T.js +16 -0
  7. package/package.json +44 -0
  8. package/src/api/client.ts +231 -0
  9. package/src/api/contracts.ts +103 -0
  10. package/src/api/endpoints.ts +19 -0
  11. package/src/api/mock-client.ts +145 -0
  12. package/src/cli.ts +339 -0
  13. package/src/commands/agents.ts +88 -0
  14. package/src/commands/assign.ts +123 -0
  15. package/src/commands/chat.ts +265 -0
  16. package/src/commands/config-edit.ts +163 -0
  17. package/src/commands/delete.ts +107 -0
  18. package/src/commands/deploy.ts +430 -0
  19. package/src/commands/init.ts +895 -0
  20. package/src/commands/list.ts +78 -0
  21. package/src/commands/login.ts +54 -0
  22. package/src/commands/logout.ts +14 -0
  23. package/src/commands/logs.ts +174 -0
  24. package/src/commands/status.ts +174 -0
  25. package/src/commands/toggle.ts +118 -0
  26. package/src/commands/trigger.ts +193 -0
  27. package/src/commands/validate.ts +53 -0
  28. package/src/commands/whoami.ts +54 -0
  29. package/src/config/keychain.ts +62 -0
  30. package/src/config/store.ts +137 -0
  31. package/src/ui/format.ts +95 -0
  32. package/src/util/zip.ts +74 -0
  33. package/src/validation/rules.ts +71 -0
  34. package/src/validation/validator.ts +204 -0
  35. package/tasks/feature-request-sdk-api.md +246 -0
  36. package/tasks/prd-openhome-cli.md +605 -0
  37. package/templates/api/README.md.tmpl +11 -0
  38. package/templates/api/__init__.py.tmpl +0 -0
  39. package/templates/api/config.json.tmpl +4 -0
  40. package/templates/api/main.py.tmpl +30 -0
  41. package/templates/basic/README.md.tmpl +7 -0
  42. package/templates/basic/__init__.py.tmpl +0 -0
  43. package/templates/basic/config.json.tmpl +4 -0
  44. package/templates/basic/main.py.tmpl +22 -0
  45. package/tsconfig.json +19 -0
package/README.md ADDED
@@ -0,0 +1,470 @@
1
+ # OpenHome CLI
2
+
3
+ Command-line tool for managing OpenHome voice AI abilities. Create and deploy abilities without leaving your terminal.
4
+
5
+ **Status:** v0.1.0 (MVP)
6
+ **Node:** 18+
7
+ **Platform:** macOS (primary), Linux/Windows (config-file fallback for keychain)
8
+
9
+ ---
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ # Clone and link globally
15
+ git clone https://github.com/Bradymck/openhome-cli.git
16
+ cd openhome-cli
17
+ npm install
18
+ npm run build
19
+ npm link
20
+
21
+ # Now available everywhere
22
+ openhome
23
+ ```
24
+
25
+ ---
26
+
27
+ ## Quick Start
28
+
29
+ ```bash
30
+ # 1. Log in with your API key
31
+ openhome login
32
+
33
+ # 2. Create a new ability
34
+ openhome init my-ability
35
+
36
+ # 3. Edit main.py in your editor
37
+
38
+ # 4. Deploy
39
+ openhome deploy ./my-ability
40
+ ```
41
+
42
+ Or just run `openhome` with no arguments for the interactive menu.
43
+
44
+ ---
45
+
46
+ ## Commands
47
+
48
+ ### `openhome` (no arguments)
49
+
50
+ Opens an interactive menu. Use arrow keys to navigate, Enter to select. The menu loops after each command — pick another or choose Exit.
51
+
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
+ If you are not logged in, the CLI prompts for login before showing the menu.
68
+
69
+ All commands below also work directly from the terminal.
70
+
71
+ ---
72
+
73
+ ### `openhome login`
74
+
75
+ Authenticate with your OpenHome API key.
76
+
77
+ 1. Prompts for your API key (masked input)
78
+ 2. Verifies the key against the OpenHome API
79
+ 3. Stores the key securely (macOS Keychain, or `~/.openhome/config.json` fallback)
80
+
81
+ ```bash
82
+ openhome login
83
+ ```
84
+
85
+ ---
86
+
87
+ ### `openhome init [name]`
88
+
89
+ Scaffold a new ability with all required files.
90
+
91
+ ```bash
92
+ # Interactive (prompts for name)
93
+ openhome init
94
+
95
+ # Direct
96
+ openhome init my-weather-bot
97
+ ```
98
+
99
+ **Prompts:**
100
+ 1. **Name** — lowercase, numbers, hyphens only (e.g. `my-ability`)
101
+ 2. **Ability type** — Skill (user-triggered), Brain Skill (auto-triggered), or Background Daemon
102
+ 3. **Template** — Basic (speak + response) or API (external API with secrets)
103
+ 4. **Trigger words** — comma-separated phrases that activate the ability
104
+
105
+ **Generated files:**
106
+
107
+ | File | Purpose |
108
+ |------|---------|
109
+ | `main.py` | Your ability code (Python) |
110
+ | `config.json` | Name + trigger words |
111
+ | `__init__.py` | Required by OpenHome (empty) |
112
+ | `README.md` | Description of your ability |
113
+
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.
147
+
148
+ ---
149
+
150
+ ### `openhome deploy [path]`
151
+
152
+ Validate, zip, and upload an ability to OpenHome.
153
+
154
+ ```bash
155
+ # Deploy from current directory
156
+ openhome deploy
157
+
158
+ # Deploy specific ability
159
+ openhome deploy ./my-ability
160
+
161
+ # Preview without uploading
162
+ openhome deploy ./my-ability --dry-run
163
+
164
+ # Test with fake API
165
+ openhome deploy ./my-ability --mock
166
+
167
+ # Attach to specific agent
168
+ openhome deploy ./my-ability --personality pers_alice
169
+ ```
170
+
171
+ | Flag | What it does |
172
+ |------|-------------|
173
+ | `--dry-run` | Show what would deploy. No zip, no upload. |
174
+ | `--mock` | Use fake API responses for testing |
175
+ | `--personality <id>` | Override default agent for this deploy |
176
+
177
+ **What happens on deploy:**
178
+ 1. Validates ability (blocks if errors)
179
+ 2. Creates ZIP (excludes `__pycache__`, `.pyc`, `.git`)
180
+ 3. Asks for confirmation
181
+ 4. Uploads to OpenHome
182
+
183
+ > **Note:** The upload endpoint is not yet live on the server. When it returns "Not Implemented", the CLI saves your zip to `~/.openhome/last-deploy.zip` for manual upload at [app.openhome.com](https://app.openhome.com).
184
+
185
+ ---
186
+
187
+ ### `openhome list`
188
+
189
+ List all your deployed abilities.
190
+
191
+ ```bash
192
+ openhome list
193
+
194
+ # Test with fake data
195
+ openhome list --mock
196
+ ```
197
+
198
+ Shows a table with name, version, status, and last update date.
199
+
200
+ Status colors: green = active, yellow = processing, red = failed, gray = disabled.
201
+
202
+ > **Note:** This endpoint is not yet live. Use `--mock` to preview the output format.
203
+
204
+ ---
205
+
206
+ ### `openhome agents`
207
+
208
+ View your agents and set a default for deploys.
209
+
210
+ ```bash
211
+ openhome agents
212
+
213
+ # Test with fake data
214
+ openhome agents --mock
215
+ ```
216
+
217
+ Shows all agents on your account with names and descriptions. Optionally set or change your default agent (used by `deploy` when `--personality` is not specified).
218
+
219
+ ---
220
+
221
+ ### `openhome status [ability]`
222
+
223
+ Show detailed info for one ability.
224
+
225
+ ```bash
226
+ # By name
227
+ openhome status my-weather-bot
228
+
229
+ # Read name from local config.json
230
+ openhome status
231
+
232
+ # Test with fake data
233
+ openhome status my-weather-bot --mock
234
+ ```
235
+
236
+ Shows: name, display name, status, version, timestamps, linked agents, validation errors, and deploy history.
237
+
238
+ > **Note:** This endpoint is not yet live. Use `--mock` to preview the output format.
239
+
240
+ ---
241
+
242
+ ## Validation Rules
243
+
244
+ Deploy automatically checks these rules before uploading. Errors block deployment. Warnings do not.
245
+
246
+ ### Required Files
247
+
248
+ Every ability must have:
249
+ - `main.py`
250
+ - `__init__.py`
251
+ - `config.json`
252
+ - `README.md`
253
+
254
+ ### config.json
255
+
256
+ Must contain:
257
+ - `unique_name` — non-empty string
258
+ - `matching_hotwords` — array of strings
259
+
260
+ ### main.py Required Patterns
261
+
262
+ Your main Python file must include:
263
+
264
+ | What | Why |
265
+ |------|-----|
266
+ | Class extending `MatchingCapability` | OpenHome ability base class |
267
+ | `call(self, ...)` method | Entry point OpenHome calls |
268
+ | `worker: AgentWorker = None` | Required field declaration |
269
+ | `capability_worker: CapabilityWorker = None` | Required field declaration |
270
+ | `resume_normal_flow()` call | Returns control to user after ability runs |
271
+ | `# {{register_capability}}` comment | Template marker used by OpenHome |
272
+
273
+ ### Blocked Patterns (Errors)
274
+
275
+ These are not allowed in any `.py` file:
276
+
277
+ | Pattern | Use Instead |
278
+ |---------|-------------|
279
+ | `print()` | `self.worker.editor_logging_handler` |
280
+ | `asyncio.sleep()` | `self.worker.session_tasks.sleep()` |
281
+ | `asyncio.create_task()` | `self.worker.session_tasks.create()` |
282
+ | `open()` | `capability_worker` file helpers |
283
+ | `exec()` | Not allowed |
284
+ | `eval()` | Not allowed |
285
+ | `pickle` / `dill` / `shelve` / `marshal` | Not allowed (security) |
286
+ | `assert` | Not allowed |
287
+ | `hashlib.md5()` | Not allowed |
288
+
289
+ ### Blocked Imports (Errors)
290
+
291
+ | Import | Why |
292
+ |--------|-----|
293
+ | `redis` | Not available in sandbox |
294
+ | `from src.utils.db_handler` | Internal, not for abilities |
295
+ | `connection_manager` | Internal, not for abilities |
296
+ | `user_config` | Internal, not for abilities |
297
+
298
+ ### Warnings (Do Not Block)
299
+
300
+ | Check | Message |
301
+ |-------|---------|
302
+ | Hardcoded API keys (`sk_...`, `key_...`) | Use `capability_worker.get_single_key()` instead |
303
+ | Multiple class definitions | Only one `MatchingCapability` class expected per ability |
304
+
305
+ ---
306
+
307
+ ## Configuration
308
+
309
+ ### Storage Location
310
+
311
+ ```
312
+ ~/.openhome/
313
+ config.json # Settings + fallback API key
314
+ last-deploy.zip # Saved when upload endpoint unavailable
315
+ ```
316
+
317
+ ### Config Fields
318
+
319
+ | Field | Purpose | Default |
320
+ |-------|---------|---------|
321
+ | `api_base_url` | Override API endpoint | `https://app.openhome.com` |
322
+ | `default_personality_id` | Default agent for deploys | (none) |
323
+ | `api_key` | Fallback key storage | (none — prefers Keychain) |
324
+
325
+ ### API Key Storage
326
+
327
+ On macOS, your API key is stored in the system Keychain (service: `openhome-cli`, account: `api-key`). On other platforms, it falls back to `~/.openhome/config.json`.
328
+
329
+ ---
330
+
331
+ ## Project Structure
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
389
+
390
+ | Package | Version | Purpose |
391
+ |---------|---------|---------|
392
+ | commander | 12.x | CLI argument parsing |
393
+ | @clack/prompts | 1.x | Interactive menus, spinners, prompts |
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 |
400
+
401
+ ---
402
+
403
+ ## API Status
404
+
405
+ | Endpoint | Method | Status |
406
+ |----------|--------|--------|
407
+ | `/api/sdk/get_personalities` | POST | Live |
408
+ | `/websocket/voice-stream/{key}/{agent}` | WebSocket | Live |
409
+ | `/api/sdk/abilities` | POST (upload) | Not yet implemented |
410
+ | `/api/sdk/abilities` | GET (list) | Not yet implemented |
411
+ | `/api/sdk/abilities/:id` | GET (detail) | Not yet implemented |
412
+
413
+ The CLI handles "Not Implemented" responses gracefully. When an endpoint is unavailable:
414
+ - **Deploy**: Saves zip to `~/.openhome/last-deploy.zip` and shows manual upload instructions
415
+ - **List/Status**: Suggests using `--mock` flag to preview the output format
416
+
417
+ Use `--mock` on any command to test with fake data while endpoints are being built.
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.
429
+
430
+ ---
431
+
432
+ ## Roadmap
433
+
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
+ - [ ] `openhome watch` — Auto-deploy on file changes
440
+ - [ ] Background Daemon and Brain Skill templates
441
+ - [ ] Cross-platform secure key storage (Windows Credential Manager, Linux Secret Service)
442
+
443
+ ### Needs Server-Side Work
444
+
445
+ - [ ] Upload endpoint (`POST /api/sdk/abilities`)
446
+ - [ ] List endpoint (`GET /api/sdk/abilities`)
447
+ - [ ] Detail endpoint (`GET /api/sdk/abilities/:id`)
448
+ - [ ] Log streaming endpoint (WebSocket)
449
+ - [ ] Delete endpoint (`DELETE /api/sdk/abilities/:id`)
450
+
451
+ ---
452
+
453
+ ## Terminology
454
+
455
+ | Term | Meaning |
456
+ |------|---------|
457
+ | **Ability** | A Python plugin that adds a feature to an OpenHome agent |
458
+ | **Agent** | A voice AI personality that can have multiple abilities (called "personality" in the API) |
459
+ | **Trigger words** | Spoken phrases that activate an ability (called `matching_hotwords` in config.json) |
460
+ | **Skill** | An ability type that runs when the user triggers it |
461
+ | **Brain Skill** | An ability type that the agent triggers automatically |
462
+ | **Background Daemon** | An ability type that runs continuously from session start |
463
+ | **CapabilityWorker** | The runtime helper object for speaking, listening, file I/O, and secrets |
464
+ | **AgentWorker** | The runtime object for logging and session management |
465
+
466
+ ---
467
+
468
+ ## License
469
+
470
+ MIT
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import '../dist/cli.js';
@@ -0,0 +1,164 @@
1
+ // src/config/store.ts
2
+ import {
3
+ readFileSync,
4
+ writeFileSync,
5
+ mkdirSync,
6
+ existsSync,
7
+ readdirSync
8
+ } from "fs";
9
+ import { homedir } from "os";
10
+ import { join } from "path";
11
+
12
+ // src/config/keychain.ts
13
+ import { execFileSync } from "child_process";
14
+ var SERVICE = "openhome-cli";
15
+ var ACCOUNT = "api-key";
16
+ function keychainGet(service = SERVICE, account = ACCOUNT) {
17
+ try {
18
+ const result = execFileSync(
19
+ "security",
20
+ ["find-generic-password", "-a", account, "-s", service, "-w"],
21
+ { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }
22
+ );
23
+ return result.trim() || null;
24
+ } catch {
25
+ return null;
26
+ }
27
+ }
28
+ function keychainSet(password, service = SERVICE, account = ACCOUNT) {
29
+ try {
30
+ execFileSync(
31
+ "security",
32
+ [
33
+ "add-generic-password",
34
+ "-a",
35
+ account,
36
+ "-s",
37
+ service,
38
+ "-w",
39
+ password,
40
+ "-U"
41
+ ],
42
+ { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }
43
+ );
44
+ return true;
45
+ } catch {
46
+ return false;
47
+ }
48
+ }
49
+ function keychainDelete(service = SERVICE, account = ACCOUNT) {
50
+ try {
51
+ execFileSync(
52
+ "security",
53
+ ["delete-generic-password", "-a", account, "-s", service],
54
+ { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }
55
+ );
56
+ return true;
57
+ } catch {
58
+ return false;
59
+ }
60
+ }
61
+
62
+ // src/config/store.ts
63
+ var CONFIG_DIR = join(homedir(), ".openhome");
64
+ var CONFIG_FILE = join(CONFIG_DIR, "config.json");
65
+ function ensureConfigDir() {
66
+ if (!existsSync(CONFIG_DIR)) {
67
+ mkdirSync(CONFIG_DIR, { recursive: true, mode: 448 });
68
+ }
69
+ }
70
+ function getConfig() {
71
+ ensureConfigDir();
72
+ if (!existsSync(CONFIG_FILE)) {
73
+ return {};
74
+ }
75
+ try {
76
+ const raw = readFileSync(CONFIG_FILE, "utf8");
77
+ return JSON.parse(raw);
78
+ } catch {
79
+ return {};
80
+ }
81
+ }
82
+ function saveConfig(config) {
83
+ ensureConfigDir();
84
+ writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), {
85
+ encoding: "utf8",
86
+ mode: 384
87
+ });
88
+ }
89
+ function getApiKey() {
90
+ const fromKeychain = keychainGet();
91
+ if (fromKeychain) return fromKeychain;
92
+ const config = getConfig();
93
+ return config.api_key ?? null;
94
+ }
95
+ function registerAbility(name, absPath) {
96
+ const config = getConfig();
97
+ const abilities = config.abilities ?? [];
98
+ const idx = abilities.findIndex((a) => a.path === absPath);
99
+ if (idx >= 0) {
100
+ abilities[idx].name = name;
101
+ } else {
102
+ abilities.push({
103
+ name,
104
+ path: absPath,
105
+ created_at: (/* @__PURE__ */ new Date()).toISOString()
106
+ });
107
+ }
108
+ config.abilities = abilities;
109
+ saveConfig(config);
110
+ }
111
+ function getTrackedAbilities() {
112
+ const config = getConfig();
113
+ const tracked = (config.abilities ?? []).filter((a) => {
114
+ try {
115
+ return existsSync(join(a.path, "config.json"));
116
+ } catch {
117
+ return false;
118
+ }
119
+ });
120
+ const abilitiesDir = join(process.cwd(), "abilities");
121
+ if (existsSync(abilitiesDir)) {
122
+ try {
123
+ const dirs = readdirSync(abilitiesDir, { withFileTypes: true });
124
+ for (const d of dirs) {
125
+ if (!d.isDirectory()) continue;
126
+ const dirPath = join(abilitiesDir, d.name);
127
+ const configPath = join(dirPath, "config.json");
128
+ if (!existsSync(configPath)) continue;
129
+ if (tracked.some((a) => a.path === dirPath)) continue;
130
+ try {
131
+ const abilityConfig = JSON.parse(
132
+ readFileSync(configPath, "utf8")
133
+ );
134
+ tracked.push({
135
+ name: abilityConfig.unique_name ?? d.name,
136
+ path: dirPath,
137
+ created_at: (/* @__PURE__ */ new Date()).toISOString()
138
+ });
139
+ } catch {
140
+ }
141
+ }
142
+ } catch {
143
+ }
144
+ }
145
+ return tracked;
146
+ }
147
+ function saveApiKey(key) {
148
+ const saved = keychainSet(key);
149
+ if (!saved) {
150
+ const config = getConfig();
151
+ config.api_key = key;
152
+ saveConfig(config);
153
+ }
154
+ }
155
+
156
+ export {
157
+ keychainDelete,
158
+ getConfig,
159
+ saveConfig,
160
+ getApiKey,
161
+ registerAbility,
162
+ getTrackedAbilities,
163
+ saveApiKey
164
+ };
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+
2
+ export { }