openhorizon-cli 1.0.2 → 1.0.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/dist/index.js +118 -31
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
|
-
import
|
|
5
|
+
import chalk3 from "chalk";
|
|
6
6
|
import dotenv from "dotenv";
|
|
7
7
|
|
|
8
8
|
// src/config.ts
|
|
@@ -37,7 +37,7 @@ function saveConfig(updates) {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
// src/chat.ts
|
|
40
|
-
import
|
|
40
|
+
import chalk2 from "chalk";
|
|
41
41
|
import { input } from "@inquirer/prompts";
|
|
42
42
|
import { createHash } from "crypto";
|
|
43
43
|
import { marked } from "marked";
|
|
@@ -65,6 +65,73 @@ function getHistoryPath() {
|
|
|
65
65
|
return HISTORY_FILE;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
// src/update.ts
|
|
69
|
+
import chalk from "chalk";
|
|
70
|
+
async function getLatestVersion() {
|
|
71
|
+
try {
|
|
72
|
+
const response = await fetch("https://registry.npmjs.org/openhorizon-cli/latest", {
|
|
73
|
+
signal: AbortSignal.timeout(2e3)
|
|
74
|
+
});
|
|
75
|
+
if (!response.ok) return null;
|
|
76
|
+
const data = await response.json();
|
|
77
|
+
return data.version;
|
|
78
|
+
} catch {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
async function checkForUpdate(currentVersion) {
|
|
83
|
+
const latest = await getLatestVersion();
|
|
84
|
+
if (!latest) return;
|
|
85
|
+
if (latest !== currentVersion) {
|
|
86
|
+
console.log(
|
|
87
|
+
boxen(
|
|
88
|
+
`${chalk.yellow("Update available!")} ${chalk.dim(currentVersion)} \u2192 ${chalk.green(latest)}
|
|
89
|
+
Run ${chalk.cyan("openhorizon update")} to update.`,
|
|
90
|
+
{ padding: 1, margin: 1, borderColor: "yellow", borderStyle: "round" }
|
|
91
|
+
)
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function boxen(text, options) {
|
|
96
|
+
const lines = text.split("\n");
|
|
97
|
+
const width = Math.max(...lines.map((l) => l.replace(/\u001b\[[0-9;]*m/g, "").length));
|
|
98
|
+
const horizontalLine = "\u2500".repeat(width + 2);
|
|
99
|
+
let res = `
|
|
100
|
+
\u256D${horizontalLine}\u256E
|
|
101
|
+
`;
|
|
102
|
+
for (const line of lines) {
|
|
103
|
+
const padding = " ".repeat(width - line.replace(/\u001b\[[0-9;]*m/g, "").length);
|
|
104
|
+
res += ` \u2502 ${line}${padding} \u2502
|
|
105
|
+
`;
|
|
106
|
+
}
|
|
107
|
+
res += ` \u2570${horizontalLine}\u256F
|
|
108
|
+
`;
|
|
109
|
+
return res;
|
|
110
|
+
}
|
|
111
|
+
async function runUpdate(currentVersion) {
|
|
112
|
+
console.log(chalk.blue(`
|
|
113
|
+
Checking for updates...`));
|
|
114
|
+
const latest = await getLatestVersion();
|
|
115
|
+
if (!latest) {
|
|
116
|
+
console.error(chalk.red(" \u2717 Could not fetch the latest version from npm registry."));
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
if (latest === currentVersion) {
|
|
120
|
+
console.log(chalk.green(` \u2713 You are already on the latest version (${chalk.bold(currentVersion)}).
|
|
121
|
+
`));
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
console.log(chalk.yellow(` ! A new version is available: ${chalk.bold(latest)}`));
|
|
125
|
+
console.log(chalk.gray(` Current version: ${currentVersion}
|
|
126
|
+
`));
|
|
127
|
+
console.log(chalk.white(` To update, run the following command:`));
|
|
128
|
+
console.log(chalk.cyan(`
|
|
129
|
+
npm install -g openhorizon-cli
|
|
130
|
+
`));
|
|
131
|
+
console.log(chalk.gray(` (Or use your preferred package manager like bun or pnpm)
|
|
132
|
+
`));
|
|
133
|
+
}
|
|
134
|
+
|
|
68
135
|
// src/chat.ts
|
|
69
136
|
marked.setOptions({
|
|
70
137
|
renderer: new TerminalRenderer()
|
|
@@ -76,7 +143,7 @@ function createSpinner(label) {
|
|
|
76
143
|
const interval = setInterval(() => {
|
|
77
144
|
if (stopped) return;
|
|
78
145
|
process.stdout.write(
|
|
79
|
-
`\r${
|
|
146
|
+
`\r${chalk2.cyan(SPINNER_FRAMES[i % SPINNER_FRAMES.length])} ${chalk2.dim(label)} `
|
|
80
147
|
);
|
|
81
148
|
i++;
|
|
82
149
|
}, 80);
|
|
@@ -104,7 +171,7 @@ async function* streamCompletion(baseUrl, apiKey, model, messages) {
|
|
|
104
171
|
Authorization: `Bearer ${apiKey}`,
|
|
105
172
|
"x-api-key": apiKey,
|
|
106
173
|
"x-client": "openhorizon-cli",
|
|
107
|
-
"x-client-version": "1.0.
|
|
174
|
+
"x-client-version": "1.0.3"
|
|
108
175
|
},
|
|
109
176
|
body: JSON.stringify({ model, messages, stream: true })
|
|
110
177
|
});
|
|
@@ -148,13 +215,13 @@ async function runChatLoop(options) {
|
|
|
148
215
|
const keyFingerprint = createHash("sha256").update(apiKey).digest("hex").slice(0, 8);
|
|
149
216
|
const session = `${date}_${keyFingerprint}_${Math.random().toString(36).slice(2, 7)}`;
|
|
150
217
|
console.log(
|
|
151
|
-
"\n" +
|
|
218
|
+
"\n" + chalk2.green("\u2713 Connected") + chalk2.gray(" \xB7 model ") + chalk2.bold.white(model)
|
|
152
219
|
);
|
|
153
|
-
console.log(
|
|
220
|
+
console.log(chalk2.dim(` History \u2192 ${getHistoryPath()}`));
|
|
154
221
|
console.log(
|
|
155
|
-
|
|
222
|
+
chalk2.gray(
|
|
156
223
|
`
|
|
157
|
-
Commands: ${
|
|
224
|
+
Commands: ${chalk2.white("/model")} ${chalk2.white("/clear")} ${chalk2.white("/help")} ${chalk2.white("exit")}
|
|
158
225
|
`
|
|
159
226
|
)
|
|
160
227
|
);
|
|
@@ -162,10 +229,10 @@ async function runChatLoop(options) {
|
|
|
162
229
|
while (true) {
|
|
163
230
|
let userInput;
|
|
164
231
|
try {
|
|
165
|
-
userInput = await input({ message:
|
|
232
|
+
userInput = await input({ message: chalk2.cyan("\u276F") });
|
|
166
233
|
} catch (err) {
|
|
167
234
|
if (err?.name === "ExitPromptError") {
|
|
168
|
-
console.log(
|
|
235
|
+
console.log(chalk2.gray("\nGoodbye!\n"));
|
|
169
236
|
process.exit(0);
|
|
170
237
|
}
|
|
171
238
|
throw err;
|
|
@@ -173,24 +240,36 @@ async function runChatLoop(options) {
|
|
|
173
240
|
const trimmed = userInput.trim();
|
|
174
241
|
if (!trimmed) continue;
|
|
175
242
|
if (["exit", "quit", "q"].includes(trimmed.toLowerCase())) {
|
|
176
|
-
console.log(
|
|
243
|
+
console.log(chalk2.gray("\nGoodbye!\n"));
|
|
177
244
|
process.exit(0);
|
|
178
245
|
}
|
|
179
246
|
if (trimmed === "/help") {
|
|
180
|
-
console.log(
|
|
181
|
-
console.log(
|
|
182
|
-
console.log(
|
|
247
|
+
console.log(chalk2.gray("\n /model \u2013 show current model"));
|
|
248
|
+
console.log(chalk2.gray(" /version \u2013 show current CLI version"));
|
|
249
|
+
console.log(chalk2.gray(" /update \u2013 check for and install updates"));
|
|
250
|
+
console.log(chalk2.gray(" /clear \u2013 clear conversation history"));
|
|
251
|
+
console.log(chalk2.gray(" exit \u2013 quit\n"));
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
if (trimmed === "/version") {
|
|
255
|
+
console.log(chalk2.blue(`
|
|
256
|
+
OpenHorizon CLI version: ${chalk2.bold("1.0.3")}
|
|
257
|
+
`));
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
if (trimmed === "/update") {
|
|
261
|
+
await runUpdate("1.0.3");
|
|
183
262
|
continue;
|
|
184
263
|
}
|
|
185
264
|
if (trimmed === "/model") {
|
|
186
|
-
console.log(
|
|
187
|
-
Model: ${
|
|
265
|
+
console.log(chalk2.gray(`
|
|
266
|
+
Model: ${chalk2.bold.white(model)}
|
|
188
267
|
`));
|
|
189
268
|
continue;
|
|
190
269
|
}
|
|
191
270
|
if (trimmed === "/clear") {
|
|
192
271
|
messages.length = 0;
|
|
193
|
-
console.log(
|
|
272
|
+
console.log(chalk2.gray("\n \u2713 Conversation cleared.\n"));
|
|
194
273
|
continue;
|
|
195
274
|
}
|
|
196
275
|
messages.push({ role: "user", content: trimmed });
|
|
@@ -207,13 +286,13 @@ async function runChatLoop(options) {
|
|
|
207
286
|
const { promptTokens, completionTokens } = event.usage;
|
|
208
287
|
spinner.stop();
|
|
209
288
|
if (!fullResponse) {
|
|
210
|
-
console.log(
|
|
289
|
+
console.log(chalk2.red(" \u276F No response received.\n"));
|
|
211
290
|
break;
|
|
212
291
|
}
|
|
213
292
|
const rendered = String(await marked(fullResponse)).trimEnd();
|
|
214
|
-
process.stdout.write(
|
|
293
|
+
process.stdout.write(chalk2.green("\u276F ") + "\n");
|
|
215
294
|
process.stdout.write(rendered + "\n");
|
|
216
|
-
const usageStr = promptTokens || completionTokens ?
|
|
295
|
+
const usageStr = promptTokens || completionTokens ? chalk2.dim(` \u2191${promptTokens} \u2193${completionTokens} tok \xB7 ${elapsed}s`) : chalk2.dim(` ${elapsed}s`);
|
|
217
296
|
process.stdout.write(usageStr + "\n\n");
|
|
218
297
|
appendHistory({
|
|
219
298
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -236,15 +315,15 @@ async function runChatLoop(options) {
|
|
|
236
315
|
spinner.stop();
|
|
237
316
|
const msg = err?.message ?? String(err);
|
|
238
317
|
if (msg.includes("404")) {
|
|
239
|
-
console.log(
|
|
318
|
+
console.log(chalk2.red(`
|
|
240
319
|
\u276F 404 \u2014 check your API URL (${baseUrl})
|
|
241
320
|
`));
|
|
242
321
|
} else if (msg.includes("401") || msg.includes("403")) {
|
|
243
|
-
console.log(
|
|
322
|
+
console.log(chalk2.red("\n \u276F Authentication failed \u2014 check your API key.\n"));
|
|
244
323
|
} else if (msg.includes("429")) {
|
|
245
|
-
console.log(
|
|
324
|
+
console.log(chalk2.red("\n \u276F Rate limit reached \u2014 try again shortly.\n"));
|
|
246
325
|
} else {
|
|
247
|
-
console.log(
|
|
326
|
+
console.log(chalk2.red(`
|
|
248
327
|
\u276F ${msg}
|
|
249
328
|
`));
|
|
250
329
|
}
|
|
@@ -256,15 +335,23 @@ async function runChatLoop(options) {
|
|
|
256
335
|
// src/index.ts
|
|
257
336
|
dotenv.config();
|
|
258
337
|
var program = new Command();
|
|
259
|
-
|
|
338
|
+
var version = "1.0.3";
|
|
339
|
+
program.name("openhorizon").description("CLI to interact with OpenHorizon AI Models").version(version, "-v, --version", "Output the current version");
|
|
340
|
+
program.command("version").description("Show the current CLI version").action(() => {
|
|
341
|
+
console.log(chalk3.blue(`OpenHorizon CLI version: ${chalk3.bold(version)}`));
|
|
342
|
+
});
|
|
343
|
+
program.command("update").description("Check for and perform CLI updates").action(async () => {
|
|
344
|
+
await runUpdate(version);
|
|
345
|
+
});
|
|
260
346
|
program.command("chat", { isDefault: true }).description("Start an interactive chat session with the AI").option("-m, --model <model>", "Specify a model to use for this session").option("-b, --base-url <url>", "Base API URL (e.g. https://api.openhorizon.devwtf.in/v1)").action(async (options) => {
|
|
261
347
|
const config = getConfig();
|
|
348
|
+
await checkForUpdate(version);
|
|
262
349
|
const apiKey = process.env.OPENHORIZON_API_KEY || config.apiKey;
|
|
263
350
|
if (!apiKey) {
|
|
264
|
-
console.error(
|
|
351
|
+
console.error(chalk3.red("\n\u2717 Missing API key."));
|
|
265
352
|
console.log(
|
|
266
|
-
|
|
267
|
-
" Run '" +
|
|
353
|
+
chalk3.gray(
|
|
354
|
+
" Run '" + chalk3.white("openhorizon login <api_key>") + chalk3.gray("' or set ") + chalk3.white("OPENHORIZON_API_KEY") + chalk3.gray(".\n")
|
|
268
355
|
)
|
|
269
356
|
);
|
|
270
357
|
process.exit(1);
|
|
@@ -277,9 +364,9 @@ program.command("model").description("Get or set the default AI model").argument
|
|
|
277
364
|
const config = getConfig();
|
|
278
365
|
if (modelName) {
|
|
279
366
|
saveConfig({ defaultModel: modelName });
|
|
280
|
-
console.log(
|
|
367
|
+
console.log(chalk3.green(`\u2713 Default model set to: ${chalk3.bold(modelName)}`));
|
|
281
368
|
} else {
|
|
282
|
-
console.log(
|
|
369
|
+
console.log(chalk3.blue(`Current default model: ${chalk3.bold(config.defaultModel ?? "(not set)")}`));
|
|
283
370
|
}
|
|
284
371
|
});
|
|
285
372
|
program.command("login").description("Save your OpenHorizon API Key securely to local config").argument("[apiKey]", "Your OpenHorizon API Key").action(async (apiKeyInput) => {
|
|
@@ -292,6 +379,6 @@ program.command("login").description("Save your OpenHorizon API Key securely to
|
|
|
292
379
|
});
|
|
293
380
|
}
|
|
294
381
|
saveConfig({ apiKey: key });
|
|
295
|
-
console.log(
|
|
382
|
+
console.log(chalk3.green("\u2713 API Key saved successfully."));
|
|
296
383
|
});
|
|
297
384
|
program.parse(process.argv);
|