openhome-cli 0.1.3 → 0.1.5

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/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  getApiKey,
3
3
  getConfig,
4
- getJwt as getJwt2,
4
+ getJwt,
5
5
  getTrackedAbilities,
6
6
  keychainDelete,
7
7
  registerAbility,
@@ -16,6 +16,9 @@ import { fileURLToPath } from "url";
16
16
  import { dirname, join as join6 } from "path";
17
17
  import { readFileSync as readFileSync5 } from "fs";
18
18
 
19
+ // src/commands/login.ts
20
+ import { execFile } from "child_process";
21
+
19
22
  // src/api/endpoints.ts
20
23
  var API_BASE = "https://app.openhome.com";
21
24
  var WS_BASE = "wss://app.openhome.com";
@@ -272,10 +275,38 @@ function handleCancel(value) {
272
275
 
273
276
  // src/commands/login.ts
274
277
  import chalk2 from "chalk";
278
+ var SETTINGS_URL = "https://app.openhome.com/dashboard/settings";
279
+ function openBrowser(url) {
280
+ try {
281
+ if (process.platform === "darwin") {
282
+ execFile("open", [url]);
283
+ } else if (process.platform === "win32") {
284
+ execFile("cmd", ["/c", "start", url]);
285
+ } else {
286
+ execFile("xdg-open", [url]);
287
+ }
288
+ } catch {
289
+ }
290
+ }
275
291
  async function loginCommand() {
276
292
  p.intro("\u{1F511} OpenHome Login");
293
+ const open = await p.confirm({
294
+ message: `Press Enter to open your browser and navigate to the ${chalk2.bold("API Keys")} tab`,
295
+ initialValue: true,
296
+ active: "Open browser",
297
+ inactive: "Skip"
298
+ });
299
+ handleCancel(open);
300
+ if (open) {
301
+ openBrowser(SETTINGS_URL);
302
+ console.log(
303
+ `
304
+ ${chalk2.dim(`Opened ${chalk2.bold("app.openhome.com/dashboard/settings")} \u2014 click the ${chalk2.bold("API Keys")} tab`)}
305
+ `
306
+ );
307
+ }
277
308
  const apiKey = await p.password({
278
- message: "Enter your OpenHome API key",
309
+ message: "Paste your API key here",
279
310
  validate: (val) => {
280
311
  if (!val || !val.trim()) return "API key is required";
281
312
  }
@@ -286,17 +317,13 @@ async function loginCommand() {
286
317
  let agents;
287
318
  try {
288
319
  const client = new ApiClient(apiKey);
289
- const verification = await client.verifyApiKey(apiKey);
290
- if (!verification.valid) {
291
- s.stop("Verification failed.");
292
- error(verification.message ?? "Invalid API key.");
293
- process.exit(1);
294
- }
295
320
  agents = await client.getPersonalities();
296
321
  s.stop("API key verified.");
297
322
  } catch (err) {
298
323
  s.stop("Verification failed.");
299
- error(err instanceof Error ? err.message : String(err));
324
+ error(
325
+ err instanceof Error && err.message.includes("401") ? "Invalid API key \u2014 check the value and try again." : err instanceof Error ? err.message : String(err)
326
+ );
300
327
  process.exit(1);
301
328
  }
302
329
  saveApiKey(apiKey);
@@ -548,7 +575,7 @@ import archiver from "archiver";
548
575
  import { createWriteStream } from "fs";
549
576
  import { Writable } from "stream";
550
577
  async function createAbilityZip(dirPath) {
551
- return new Promise((resolve5, reject) => {
578
+ return new Promise((resolve6, reject) => {
552
579
  const chunks = [];
553
580
  const writable = new Writable({
554
581
  write(chunk, _encoding, callback) {
@@ -557,7 +584,7 @@ async function createAbilityZip(dirPath) {
557
584
  }
558
585
  });
559
586
  writable.on("finish", () => {
560
- resolve5(Buffer.concat(chunks));
587
+ resolve6(Buffer.concat(chunks));
561
588
  });
562
589
  writable.on("error", reject);
563
590
  const archive = archiver("zip", { zlib: { level: 9 } });
@@ -1825,7 +1852,7 @@ async function deleteCommand(abilityArg, opts = {}) {
1825
1852
  client = new MockApiClient();
1826
1853
  } else {
1827
1854
  const apiKey = getApiKey() ?? "";
1828
- const jwt = getJwt2() ?? void 0;
1855
+ const jwt = getJwt() ?? void 0;
1829
1856
  if (!apiKey && !jwt) {
1830
1857
  error("Not authenticated. Run: openhome login");
1831
1858
  process.exit(1);
@@ -1914,7 +1941,7 @@ async function toggleCommand(abilityArg, opts = {}) {
1914
1941
  client = new MockApiClient();
1915
1942
  } else {
1916
1943
  const apiKey = getApiKey() ?? "";
1917
- const jwt = getJwt2() ?? void 0;
1944
+ const jwt = getJwt() ?? void 0;
1918
1945
  if (!apiKey && !jwt) {
1919
1946
  error("Not authenticated. Run: openhome login");
1920
1947
  process.exit(1);
@@ -2013,7 +2040,7 @@ async function assignCommand(opts = {}) {
2013
2040
  client = new MockApiClient();
2014
2041
  } else {
2015
2042
  const apiKey = getApiKey() ?? "";
2016
- const jwt = getJwt2() ?? void 0;
2043
+ const jwt = getJwt() ?? void 0;
2017
2044
  if (!apiKey && !jwt) {
2018
2045
  error("Not authenticated. Run: openhome login");
2019
2046
  process.exit(1);
@@ -2122,7 +2149,7 @@ async function listCommand(opts = {}) {
2122
2149
  client = new MockApiClient();
2123
2150
  } else {
2124
2151
  const apiKey = getApiKey() ?? "";
2125
- const jwt = getJwt2() ?? void 0;
2152
+ const jwt = getJwt() ?? void 0;
2126
2153
  if (!apiKey && !jwt) {
2127
2154
  error("Not authenticated. Run: openhome login");
2128
2155
  process.exit(1);
@@ -2422,7 +2449,7 @@ async function chatCommand(agentArg, opts = {}) {
2422
2449
  }
2423
2450
  const wsUrl = `${WS_BASE}${ENDPOINTS.voiceStream(apiKey, agentId)}`;
2424
2451
  info(`Connecting to agent ${chalk9.bold(agentId)}...`);
2425
- await new Promise((resolve5) => {
2452
+ await new Promise((resolve6) => {
2426
2453
  const ws = new WebSocket(wsUrl, {
2427
2454
  perMessageDeflate: false,
2428
2455
  headers: {
@@ -2550,7 +2577,7 @@ async function chatCommand(agentArg, opts = {}) {
2550
2577
  console.error("");
2551
2578
  error(`WebSocket error: ${err.message}`);
2552
2579
  rl.close();
2553
- resolve5();
2580
+ resolve6();
2554
2581
  });
2555
2582
  ws.on("close", (code) => {
2556
2583
  if (pingInterval) clearInterval(pingInterval);
@@ -2561,7 +2588,7 @@ async function chatCommand(agentArg, opts = {}) {
2561
2588
  info(`Connection closed (code: ${code})`);
2562
2589
  }
2563
2590
  rl.close();
2564
- resolve5();
2591
+ resolve6();
2565
2592
  });
2566
2593
  rl.on("close", () => {
2567
2594
  if (connected) {
@@ -2628,7 +2655,7 @@ async function triggerCommand(phraseArg, opts = {}) {
2628
2655
  info(`Sending "${chalk10.bold(phrase)}" to agent ${chalk10.bold(agentId)}...`);
2629
2656
  const s = p.spinner();
2630
2657
  s.start("Waiting for response...");
2631
- await new Promise((resolve5) => {
2658
+ await new Promise((resolve6) => {
2632
2659
  const ws = new WebSocket2(wsUrl, {
2633
2660
  perMessageDeflate: false,
2634
2661
  headers: {
@@ -2658,7 +2685,7 @@ async function triggerCommand(phraseArg, opts = {}) {
2658
2685
  ${chalk10.cyan("Agent:")} ${fullResponse}`);
2659
2686
  }
2660
2687
  cleanup();
2661
- resolve5();
2688
+ resolve6();
2662
2689
  }, RESPONSE_TIMEOUT);
2663
2690
  });
2664
2691
  ws.on("message", (raw) => {
@@ -2675,7 +2702,7 @@ ${chalk10.cyan("Agent:")} ${fullResponse}`);
2675
2702
  ${chalk10.cyan("Agent:")} ${fullResponse}
2676
2703
  `);
2677
2704
  cleanup();
2678
- resolve5();
2705
+ resolve6();
2679
2706
  }
2680
2707
  }
2681
2708
  break;
@@ -2692,7 +2719,7 @@ ${chalk10.cyan("Agent:")} ${fullResponse}
2692
2719
  ${chalk10.cyan("Agent:")} ${fullResponse}
2693
2720
  `);
2694
2721
  cleanup();
2695
- resolve5();
2722
+ resolve6();
2696
2723
  }
2697
2724
  }
2698
2725
  break;
@@ -2707,7 +2734,7 @@ ${chalk10.cyan("Agent:")} ${fullResponse}
2707
2734
  `Server error: ${errData?.message || errData?.title || "Unknown"}`
2708
2735
  );
2709
2736
  cleanup();
2710
- resolve5();
2737
+ resolve6();
2711
2738
  break;
2712
2739
  }
2713
2740
  }
@@ -2717,12 +2744,12 @@ ${chalk10.cyan("Agent:")} ${fullResponse}
2717
2744
  ws.on("error", (err) => {
2718
2745
  s.stop("Connection error.");
2719
2746
  error(err.message);
2720
- resolve5();
2747
+ resolve6();
2721
2748
  });
2722
2749
  ws.on("close", () => {
2723
2750
  if (pingInterval) clearInterval(pingInterval);
2724
2751
  if (responseTimer) clearTimeout(responseTimer);
2725
- resolve5();
2752
+ resolve6();
2726
2753
  });
2727
2754
  });
2728
2755
  }
@@ -2947,7 +2974,7 @@ async function logsCommand(opts = {}) {
2947
2974
  info(`Streaming logs from agent ${chalk12.bold(agentId)}...`);
2948
2975
  info(`Press ${chalk12.bold("Ctrl+C")} to stop.
2949
2976
  `);
2950
- await new Promise((resolve5) => {
2977
+ await new Promise((resolve6) => {
2951
2978
  const ws = new WebSocket3(wsUrl, {
2952
2979
  perMessageDeflate: false,
2953
2980
  headers: {
@@ -3027,13 +3054,13 @@ async function logsCommand(opts = {}) {
3027
3054
  });
3028
3055
  ws.on("error", (err) => {
3029
3056
  error(`WebSocket error: ${err.message}`);
3030
- resolve5();
3057
+ resolve6();
3031
3058
  });
3032
3059
  ws.on("close", (code) => {
3033
3060
  if (pingInterval) clearInterval(pingInterval);
3034
3061
  console.log("");
3035
3062
  info(`Connection closed (code: ${code})`);
3036
- resolve5();
3063
+ resolve6();
3037
3064
  });
3038
3065
  process.on("SIGINT", () => {
3039
3066
  console.log("");
@@ -3044,17 +3071,17 @@ async function logsCommand(opts = {}) {
3044
3071
  }
3045
3072
 
3046
3073
  // src/commands/set-jwt.ts
3047
- import { execFile } from "child_process";
3074
+ import { execFile as execFile2 } from "child_process";
3048
3075
  import chalk13 from "chalk";
3049
3076
  var OPENHOME_URL = "https://app.openhome.com";
3050
- function openBrowser(url) {
3077
+ function openBrowser2(url) {
3051
3078
  try {
3052
3079
  if (process.platform === "darwin") {
3053
- execFile("open", [url]);
3080
+ execFile2("open", [url]);
3054
3081
  } else if (process.platform === "win32") {
3055
- execFile("cmd", ["/c", "start", url]);
3082
+ execFile2("cmd", ["/c", "start", url]);
3056
3083
  } else {
3057
- execFile("xdg-open", [url]);
3084
+ execFile2("xdg-open", [url]);
3058
3085
  }
3059
3086
  } catch {
3060
3087
  }
@@ -3091,7 +3118,7 @@ async function setJwtCommand(token) {
3091
3118
  console.log(
3092
3119
  chalk13.dim(` Opening ${chalk13.bold("app.openhome.com")} in your browser...`)
3093
3120
  );
3094
- openBrowser(OPENHOME_URL);
3121
+ openBrowser2(OPENHOME_URL);
3095
3122
  console.log("");
3096
3123
  p.note(
3097
3124
  [
@@ -3144,6 +3171,45 @@ async function setJwtCommand(token) {
3144
3171
  }
3145
3172
  }
3146
3173
 
3174
+ // src/commands/validate.ts
3175
+ import { resolve as resolve5 } from "path";
3176
+ import chalk14 from "chalk";
3177
+ async function validateCommand(pathArg = ".") {
3178
+ const targetDir = resolve5(pathArg);
3179
+ p.intro(`\u{1F50E} Validate ability`);
3180
+ const s = p.spinner();
3181
+ s.start("Running checks...");
3182
+ const result = validateAbility(targetDir);
3183
+ if (result.errors.length === 0 && result.warnings.length === 0) {
3184
+ s.stop("All checks passed.");
3185
+ p.outro("Ability is ready to deploy! \u{1F389}");
3186
+ return;
3187
+ }
3188
+ s.stop("Checks complete.");
3189
+ if (result.errors.length > 0) {
3190
+ p.note(
3191
+ result.errors.map(
3192
+ (issue) => `${chalk14.red("\u2717")} ${issue.file ? chalk14.bold(`[${issue.file}]`) + " " : ""}${issue.message}`
3193
+ ).join("\n"),
3194
+ `${result.errors.length} Error(s)`
3195
+ );
3196
+ }
3197
+ if (result.warnings.length > 0) {
3198
+ p.note(
3199
+ result.warnings.map(
3200
+ (w) => `${chalk14.yellow("\u26A0")} ${w.file ? chalk14.bold(`[${w.file}]`) + " " : ""}${w.message}`
3201
+ ).join("\n"),
3202
+ `${result.warnings.length} Warning(s)`
3203
+ );
3204
+ }
3205
+ if (result.passed) {
3206
+ p.outro("Validation passed (with warnings).");
3207
+ } else {
3208
+ error("Fix errors before deploying.");
3209
+ process.exit(1);
3210
+ }
3211
+ }
3212
+
3147
3213
  // src/cli.ts
3148
3214
  var __filename = fileURLToPath(import.meta.url);
3149
3215
  var __dirname = dirname(__filename);
@@ -3176,16 +3242,6 @@ async function interactiveMenu() {
3176
3242
  label: "\u2728 Create Ability",
3177
3243
  hint: "Scaffold and deploy a new ability"
3178
3244
  },
3179
- {
3180
- value: "chat",
3181
- label: "\u{1F4AC} Chat",
3182
- hint: "Talk to your agent"
3183
- },
3184
- {
3185
- value: "trigger",
3186
- label: "\u26A1 Trigger",
3187
- hint: "Fire an ability remotely with a phrase"
3188
- },
3189
3245
  {
3190
3246
  value: "list",
3191
3247
  label: "\u{1F4CB} My Abilities",
@@ -3212,30 +3268,15 @@ async function interactiveMenu() {
3212
3268
  hint: "View agents and set default"
3213
3269
  },
3214
3270
  {
3215
- value: "status",
3216
- label: "\u{1F50D} Status",
3217
- hint: "Check ability status"
3218
- },
3219
- {
3220
- value: "config",
3221
- label: "\u2699\uFE0F Edit Config",
3222
- hint: "Update trigger words, description, category"
3271
+ value: "chat",
3272
+ label: "\u{1F4AC} Chat",
3273
+ hint: "Talk to your agent"
3223
3274
  },
3224
3275
  {
3225
3276
  value: "logs",
3226
3277
  label: "\u{1F4E1} Logs",
3227
3278
  hint: "Stream live agent messages"
3228
3279
  },
3229
- {
3230
- value: "whoami",
3231
- label: "\u{1F464} Who Am I",
3232
- hint: "Show auth, default agent, tracked abilities"
3233
- },
3234
- {
3235
- value: "set-jwt",
3236
- label: "\u{1F511} Enable Management",
3237
- hint: "Unlock list, delete, toggle, assign"
3238
- },
3239
3280
  {
3240
3281
  value: "logout",
3241
3282
  label: "\u{1F513} Log Out",
@@ -3249,12 +3290,6 @@ async function interactiveMenu() {
3249
3290
  case "init":
3250
3291
  await initCommand();
3251
3292
  break;
3252
- case "chat":
3253
- await chatCommand();
3254
- break;
3255
- case "trigger":
3256
- await triggerCommand();
3257
- break;
3258
3293
  case "list":
3259
3294
  await listCommand();
3260
3295
  break;
@@ -3270,21 +3305,12 @@ async function interactiveMenu() {
3270
3305
  case "agents":
3271
3306
  await agentsCommand();
3272
3307
  break;
3273
- case "status":
3274
- await statusCommand();
3275
- break;
3276
- case "config":
3277
- await configEditCommand();
3308
+ case "chat":
3309
+ await chatCommand();
3278
3310
  break;
3279
3311
  case "logs":
3280
3312
  await logsCommand();
3281
3313
  break;
3282
- case "whoami":
3283
- await whoamiCommand();
3284
- break;
3285
- case "set-jwt":
3286
- await setJwtCommand();
3287
- break;
3288
3314
  case "logout":
3289
3315
  await logoutCommand();
3290
3316
  await ensureLoggedIn();
@@ -3350,6 +3376,9 @@ program.command("logs").description("Stream live agent messages and logs").optio
3350
3376
  program.command("whoami").description("Show auth status, default agent, and tracked abilities").action(async () => {
3351
3377
  await whoamiCommand();
3352
3378
  });
3379
+ program.command("validate [path]").description("Check an ability for errors before deploying").action(async (path) => {
3380
+ await validateCommand(path);
3381
+ });
3353
3382
  program.command("set-jwt [token]").description(
3354
3383
  "Save a session token to enable management commands (list, delete, toggle, assign)"
3355
3384
  ).action(async (token) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openhome-cli",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "CLI for managing OpenHome voice AI abilities",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli.ts CHANGED
@@ -19,6 +19,7 @@ import { whoamiCommand } from "./commands/whoami.js";
19
19
  import { configEditCommand } from "./commands/config-edit.js";
20
20
  import { logsCommand } from "./commands/logs.js";
21
21
  import { setJwtCommand } from "./commands/set-jwt.js";
22
+ import { validateCommand } from "./commands/validate.js";
22
23
  import { p, handleCancel } from "./ui/format.js";
23
24
 
24
25
  // Read version from package.json
@@ -61,16 +62,6 @@ async function interactiveMenu(): Promise<void> {
61
62
  label: "✨ Create Ability",
62
63
  hint: "Scaffold and deploy a new ability",
63
64
  },
64
- {
65
- value: "chat",
66
- label: "💬 Chat",
67
- hint: "Talk to your agent",
68
- },
69
- {
70
- value: "trigger",
71
- label: "⚡ Trigger",
72
- hint: "Fire an ability remotely with a phrase",
73
- },
74
65
  {
75
66
  value: "list",
76
67
  label: "📋 My Abilities",
@@ -97,30 +88,15 @@ async function interactiveMenu(): Promise<void> {
97
88
  hint: "View agents and set default",
98
89
  },
99
90
  {
100
- value: "status",
101
- label: "🔍 Status",
102
- hint: "Check ability status",
103
- },
104
- {
105
- value: "config",
106
- label: "⚙️ Edit Config",
107
- hint: "Update trigger words, description, category",
91
+ value: "chat",
92
+ label: "💬 Chat",
93
+ hint: "Talk to your agent",
108
94
  },
109
95
  {
110
96
  value: "logs",
111
97
  label: "📡 Logs",
112
98
  hint: "Stream live agent messages",
113
99
  },
114
- {
115
- value: "whoami",
116
- label: "👤 Who Am I",
117
- hint: "Show auth, default agent, tracked abilities",
118
- },
119
- {
120
- value: "set-jwt",
121
- label: "🔑 Enable Management",
122
- hint: "Unlock list, delete, toggle, assign",
123
- },
124
100
  {
125
101
  value: "logout",
126
102
  label: "🔓 Log Out",
@@ -135,12 +111,6 @@ async function interactiveMenu(): Promise<void> {
135
111
  case "init":
136
112
  await initCommand();
137
113
  break;
138
- case "chat":
139
- await chatCommand();
140
- break;
141
- case "trigger":
142
- await triggerCommand();
143
- break;
144
114
  case "list":
145
115
  await listCommand();
146
116
  break;
@@ -156,21 +126,12 @@ async function interactiveMenu(): Promise<void> {
156
126
  case "agents":
157
127
  await agentsCommand();
158
128
  break;
159
- case "status":
160
- await statusCommand();
161
- break;
162
- case "config":
163
- await configEditCommand();
129
+ case "chat":
130
+ await chatCommand();
164
131
  break;
165
132
  case "logs":
166
133
  await logsCommand();
167
134
  break;
168
- case "whoami":
169
- await whoamiCommand();
170
- break;
171
- case "set-jwt":
172
- await setJwtCommand();
173
- break;
174
135
  case "logout":
175
136
  await logoutCommand();
176
137
  await ensureLoggedIn();
@@ -325,6 +286,13 @@ program
325
286
  await whoamiCommand();
326
287
  });
327
288
 
289
+ program
290
+ .command("validate [path]")
291
+ .description("Check an ability for errors before deploying")
292
+ .action(async (path?: string) => {
293
+ await validateCommand(path);
294
+ });
295
+
328
296
  program
329
297
  .command("set-jwt [token]")
330
298
  .description(
@@ -1,14 +1,46 @@
1
+ import { execFile } from "node:child_process";
1
2
  import { ApiClient } from "../api/client.js";
2
3
  import type { Personality } from "../api/contracts.js";
3
- import { saveApiKey, saveJwt } from "../config/store.js";
4
+ import { saveApiKey } from "../config/store.js";
4
5
  import { success, error, info, p, handleCancel } from "../ui/format.js";
5
6
  import chalk from "chalk";
6
7
 
8
+ const SETTINGS_URL = "https://app.openhome.com/dashboard/settings";
9
+
10
+ function openBrowser(url: string): void {
11
+ try {
12
+ if (process.platform === "darwin") {
13
+ execFile("open", [url]);
14
+ } else if (process.platform === "win32") {
15
+ execFile("cmd", ["/c", "start", url]);
16
+ } else {
17
+ execFile("xdg-open", [url]);
18
+ }
19
+ } catch {
20
+ // best-effort
21
+ }
22
+ }
23
+
7
24
  export async function loginCommand(): Promise<void> {
8
25
  p.intro("🔑 OpenHome Login");
9
26
 
27
+ const open = await p.confirm({
28
+ message: `Press Enter to open your browser and navigate to the ${chalk.bold("API Keys")} tab`,
29
+ initialValue: true,
30
+ active: "Open browser",
31
+ inactive: "Skip",
32
+ });
33
+ handleCancel(open);
34
+
35
+ if (open) {
36
+ openBrowser(SETTINGS_URL);
37
+ console.log(
38
+ `\n ${chalk.dim(`Opened ${chalk.bold("app.openhome.com/dashboard/settings")} — click the ${chalk.bold("API Keys")} tab`)}\n`,
39
+ );
40
+ }
41
+
10
42
  const apiKey = await p.password({
11
- message: "Enter your OpenHome API key",
43
+ message: "Paste your API key here",
12
44
  validate: (val) => {
13
45
  if (!val || !val.trim()) return "API key is required";
14
46
  },
@@ -21,24 +53,23 @@ export async function loginCommand(): Promise<void> {
21
53
  let agents: Personality[];
22
54
  try {
23
55
  const client = new ApiClient(apiKey as string);
24
- const verification = await client.verifyApiKey(apiKey as string);
25
- if (!verification.valid) {
26
- s.stop("Verification failed.");
27
- error(verification.message ?? "Invalid API key.");
28
- process.exit(1);
29
- }
30
56
  agents = await client.getPersonalities();
31
57
  s.stop("API key verified.");
32
58
  } catch (err) {
33
59
  s.stop("Verification failed.");
34
- error(err instanceof Error ? err.message : String(err));
60
+ error(
61
+ err instanceof Error && err.message.includes("401")
62
+ ? "Invalid API key — check the value and try again."
63
+ : err instanceof Error
64
+ ? err.message
65
+ : String(err),
66
+ );
35
67
  process.exit(1);
36
68
  }
37
69
 
38
70
  saveApiKey(apiKey as string);
39
71
  success("API key saved.");
40
72
 
41
- // Show agents on this account
42
73
  if (agents.length > 0) {
43
74
  p.note(
44
75
  agents
@@ -3,7 +3,12 @@ import { existsSync, readFileSync } from "node:fs";
3
3
  import { homedir } from "node:os";
4
4
  import { ApiClient, NotImplementedError } from "../api/client.js";
5
5
  import { MockApiClient } from "../api/mock-client.js";
6
- import { getApiKey, getConfig, getTrackedAbilities } from "../config/store.js";
6
+ import {
7
+ getApiKey,
8
+ getConfig,
9
+ getJwt,
10
+ getTrackedAbilities,
11
+ } from "../config/store.js";
7
12
  import { error, warn, info, p, handleCancel } from "../ui/format.js";
8
13
  import chalk from "chalk";
9
14