openhorizon-cli 1.0.2 → 1.0.4
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 +119 -33
- 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.4"
|
|
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("/version")} ${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.4")}
|
|
257
|
+
`));
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
if (trimmed === "/update") {
|
|
261
|
+
await runUpdate("1.0.4");
|
|
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,12 @@ 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
|
-
const rendered = String(await marked(fullResponse)).
|
|
214
|
-
process.stdout.write(
|
|
215
|
-
|
|
216
|
-
const usageStr = promptTokens || completionTokens ? chalk.dim(` \u2191${promptTokens} \u2193${completionTokens} tok \xB7 ${elapsed}s`) : chalk.dim(` ${elapsed}s`);
|
|
292
|
+
const rendered = String(await marked(fullResponse)).trim();
|
|
293
|
+
process.stdout.write(chalk2.green("\u276F ") + rendered + "\n");
|
|
294
|
+
const usageStr = promptTokens || completionTokens ? chalk2.dim(` \u2191${promptTokens} \u2193${completionTokens} tok \xB7 ${elapsed}s`) : chalk2.dim(` ${elapsed}s`);
|
|
217
295
|
process.stdout.write(usageStr + "\n\n");
|
|
218
296
|
appendHistory({
|
|
219
297
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -236,15 +314,15 @@ async function runChatLoop(options) {
|
|
|
236
314
|
spinner.stop();
|
|
237
315
|
const msg = err?.message ?? String(err);
|
|
238
316
|
if (msg.includes("404")) {
|
|
239
|
-
console.log(
|
|
317
|
+
console.log(chalk2.red(`
|
|
240
318
|
\u276F 404 \u2014 check your API URL (${baseUrl})
|
|
241
319
|
`));
|
|
242
320
|
} else if (msg.includes("401") || msg.includes("403")) {
|
|
243
|
-
console.log(
|
|
321
|
+
console.log(chalk2.red("\n \u276F Authentication failed \u2014 check your API key.\n"));
|
|
244
322
|
} else if (msg.includes("429")) {
|
|
245
|
-
console.log(
|
|
323
|
+
console.log(chalk2.red("\n \u276F Rate limit reached \u2014 try again shortly.\n"));
|
|
246
324
|
} else {
|
|
247
|
-
console.log(
|
|
325
|
+
console.log(chalk2.red(`
|
|
248
326
|
\u276F ${msg}
|
|
249
327
|
`));
|
|
250
328
|
}
|
|
@@ -256,15 +334,23 @@ async function runChatLoop(options) {
|
|
|
256
334
|
// src/index.ts
|
|
257
335
|
dotenv.config();
|
|
258
336
|
var program = new Command();
|
|
259
|
-
|
|
337
|
+
var version = "1.0.4";
|
|
338
|
+
program.name("openhorizon").description("CLI to interact with OpenHorizon AI Models").version(version, "-v, --version", "Output the current version");
|
|
339
|
+
program.command("version").description("Show the current CLI version").action(() => {
|
|
340
|
+
console.log(chalk3.blue(`OpenHorizon CLI version: ${chalk3.bold(version)}`));
|
|
341
|
+
});
|
|
342
|
+
program.command("update").description("Check for and perform CLI updates").action(async () => {
|
|
343
|
+
await runUpdate(version);
|
|
344
|
+
});
|
|
260
345
|
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
346
|
const config = getConfig();
|
|
347
|
+
await checkForUpdate(version);
|
|
262
348
|
const apiKey = process.env.OPENHORIZON_API_KEY || config.apiKey;
|
|
263
349
|
if (!apiKey) {
|
|
264
|
-
console.error(
|
|
350
|
+
console.error(chalk3.red("\n\u2717 Missing API key."));
|
|
265
351
|
console.log(
|
|
266
|
-
|
|
267
|
-
" Run '" +
|
|
352
|
+
chalk3.gray(
|
|
353
|
+
" Run '" + chalk3.white("openhorizon login <api_key>") + chalk3.gray("' or set ") + chalk3.white("OPENHORIZON_API_KEY") + chalk3.gray(".\n")
|
|
268
354
|
)
|
|
269
355
|
);
|
|
270
356
|
process.exit(1);
|
|
@@ -277,9 +363,9 @@ program.command("model").description("Get or set the default AI model").argument
|
|
|
277
363
|
const config = getConfig();
|
|
278
364
|
if (modelName) {
|
|
279
365
|
saveConfig({ defaultModel: modelName });
|
|
280
|
-
console.log(
|
|
366
|
+
console.log(chalk3.green(`\u2713 Default model set to: ${chalk3.bold(modelName)}`));
|
|
281
367
|
} else {
|
|
282
|
-
console.log(
|
|
368
|
+
console.log(chalk3.blue(`Current default model: ${chalk3.bold(config.defaultModel ?? "(not set)")}`));
|
|
283
369
|
}
|
|
284
370
|
});
|
|
285
371
|
program.command("login").description("Save your OpenHorizon API Key securely to local config").argument("[apiKey]", "Your OpenHorizon API Key").action(async (apiKeyInput) => {
|
|
@@ -292,6 +378,6 @@ program.command("login").description("Save your OpenHorizon API Key securely to
|
|
|
292
378
|
});
|
|
293
379
|
}
|
|
294
380
|
saveConfig({ apiKey: key });
|
|
295
|
-
console.log(
|
|
381
|
+
console.log(chalk3.green("\u2713 API Key saved successfully."));
|
|
296
382
|
});
|
|
297
383
|
program.parse(process.argv);
|