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 +108 -79
- package/package.json +1 -1
- package/src/cli.ts +13 -45
- package/src/commands/login.ts +41 -10
- package/src/commands/status.ts +6 -1
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getApiKey,
|
|
3
3
|
getConfig,
|
|
4
|
-
getJwt
|
|
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: "
|
|
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(
|
|
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((
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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((
|
|
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
|
-
|
|
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
|
-
|
|
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((
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2747
|
+
resolve6();
|
|
2721
2748
|
});
|
|
2722
2749
|
ws.on("close", () => {
|
|
2723
2750
|
if (pingInterval) clearInterval(pingInterval);
|
|
2724
2751
|
if (responseTimer) clearTimeout(responseTimer);
|
|
2725
|
-
|
|
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((
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
3077
|
+
function openBrowser2(url) {
|
|
3051
3078
|
try {
|
|
3052
3079
|
if (process.platform === "darwin") {
|
|
3053
|
-
|
|
3080
|
+
execFile2("open", [url]);
|
|
3054
3081
|
} else if (process.platform === "win32") {
|
|
3055
|
-
|
|
3082
|
+
execFile2("cmd", ["/c", "start", url]);
|
|
3056
3083
|
} else {
|
|
3057
|
-
|
|
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
|
-
|
|
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: "
|
|
3216
|
-
label: "\u{
|
|
3217
|
-
hint: "
|
|
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 "
|
|
3274
|
-
await
|
|
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
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: "
|
|
101
|
-
label: "
|
|
102
|
-
hint: "
|
|
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 "
|
|
160
|
-
await
|
|
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(
|
package/src/commands/login.ts
CHANGED
|
@@ -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
|
|
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: "
|
|
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(
|
|
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
|
package/src/commands/status.ts
CHANGED
|
@@ -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 {
|
|
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
|
|