@chvor/cli 0.1.8 → 0.1.9
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 +1 -1
- package/dist/commands/docker.js +2 -2
- package/dist/commands/init.js +37 -40
- package/dist/commands/instances.js +1 -1
- package/dist/commands/onboard.js +18 -34
- package/dist/commands/open.js +1 -1
- package/dist/lib/config.js +3 -2
- package/dist/lib/validate.js +2 -2
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -107,7 +107,7 @@ program
|
|
|
107
107
|
program
|
|
108
108
|
.command("docker")
|
|
109
109
|
.description("Pull and run chvor as a Docker container")
|
|
110
|
-
.option("-p, --port <port>", "Host port", "
|
|
110
|
+
.option("-p, --port <port>", "Host port", "9147")
|
|
111
111
|
.action(async (opts) => {
|
|
112
112
|
const { docker } = await import("./commands/docker.js");
|
|
113
113
|
await docker(opts);
|
package/dist/commands/docker.js
CHANGED
|
@@ -2,14 +2,14 @@ import { execFileSync } from "node:child_process";
|
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { validatePort } from "../lib/validate.js";
|
|
4
4
|
export async function docker(opts) {
|
|
5
|
-
const port = validatePort(opts.port ?? "
|
|
5
|
+
const port = validatePort(opts.port ?? "9147");
|
|
6
6
|
const image = "ghcr.io/luka-zivkovic/chvor:latest";
|
|
7
7
|
console.log("Pulling latest chvor Docker image...");
|
|
8
8
|
execFileSync("docker", ["pull", image], { stdio: "inherit" });
|
|
9
9
|
console.log("Starting chvor container...");
|
|
10
10
|
execFileSync("docker", [
|
|
11
11
|
"run", "-d", "--name", "chvor",
|
|
12
|
-
"-p", `${port}:
|
|
12
|
+
"-p", `${port}:9147`,
|
|
13
13
|
"-v", `${homedir()}/.chvor:/home/node/.chvor`,
|
|
14
14
|
image,
|
|
15
15
|
], { stdio: "inherit" });
|
package/dist/commands/init.js
CHANGED
|
@@ -128,15 +128,14 @@ export async function init(opts) {
|
|
|
128
128
|
{ name: "Google (Gemini)", value: "google-ai" },
|
|
129
129
|
],
|
|
130
130
|
});
|
|
131
|
-
const
|
|
132
|
-
const defaultPort = instanceName ? "3002" : "3001";
|
|
131
|
+
const defaultPort = instanceName ? "9148" : "9147";
|
|
133
132
|
const port = validatePort(await input({ message: "Port?", default: defaultPort }));
|
|
134
133
|
const token = randomBytes(32).toString("hex");
|
|
135
|
-
// 4. Write config
|
|
134
|
+
// 4. Write config (not yet onboarded — set after download + server start)
|
|
136
135
|
writeConfig({
|
|
137
136
|
port,
|
|
138
137
|
token,
|
|
139
|
-
onboarded:
|
|
138
|
+
onboarded: false,
|
|
140
139
|
llmProvider: provider,
|
|
141
140
|
instanceName: instanceName || undefined,
|
|
142
141
|
templateName: manifest.name,
|
|
@@ -154,42 +153,30 @@ export async function init(opts) {
|
|
|
154
153
|
console.warn(" Server started but health check did not pass within timeout.");
|
|
155
154
|
return;
|
|
156
155
|
}
|
|
157
|
-
// 7. Save
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
},
|
|
182
|
-
body: JSON.stringify({
|
|
183
|
-
name: userName,
|
|
184
|
-
timezone,
|
|
185
|
-
onboarded: true,
|
|
186
|
-
}),
|
|
187
|
-
});
|
|
188
|
-
// 9. Collect template-specific credentials
|
|
189
|
-
const templateCredentials = (manifest.credentials ?? []).filter((c) => c.type !== provider // Skip if already collected as LLM provider
|
|
190
|
-
);
|
|
191
|
-
await collectCredentials(templateCredentials, port, token);
|
|
192
|
-
// 10. Provision template
|
|
156
|
+
// 7. Save persona (user name + timezone + template persona)
|
|
157
|
+
try {
|
|
158
|
+
const personaRes = await fetch(`http://localhost:${port}/api/persona`, {
|
|
159
|
+
method: "PATCH",
|
|
160
|
+
headers: {
|
|
161
|
+
"Content-Type": "application/json",
|
|
162
|
+
Authorization: `Bearer ${token}`,
|
|
163
|
+
},
|
|
164
|
+
body: JSON.stringify({
|
|
165
|
+
name: userName,
|
|
166
|
+
timezone,
|
|
167
|
+
onboarded: true,
|
|
168
|
+
}),
|
|
169
|
+
});
|
|
170
|
+
if (!personaRes.ok) {
|
|
171
|
+
console.warn(` Warning: failed to save persona (${personaRes.status}). You can update it later in Settings.`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
console.warn(" Warning: could not reach server to save persona. You can update it later in Settings.");
|
|
176
|
+
}
|
|
177
|
+
// 8. Collect template-specific credentials
|
|
178
|
+
await collectCredentials(manifest.credentials ?? [], port, token);
|
|
179
|
+
// 9. Provision template
|
|
193
180
|
await provision({
|
|
194
181
|
port,
|
|
195
182
|
token,
|
|
@@ -203,6 +190,16 @@ export async function init(opts) {
|
|
|
203
190
|
}
|
|
204
191
|
catch { /* non-critical */ }
|
|
205
192
|
}
|
|
193
|
+
// 10. Mark as onboarded now that everything succeeded
|
|
194
|
+
writeConfig({
|
|
195
|
+
port,
|
|
196
|
+
token,
|
|
197
|
+
onboarded: true,
|
|
198
|
+
llmProvider: provider,
|
|
199
|
+
instanceName: instanceName || undefined,
|
|
200
|
+
templateName: manifest.name,
|
|
201
|
+
installedVersion: version,
|
|
202
|
+
});
|
|
206
203
|
// 11. Done
|
|
207
204
|
const label = instanceName ? ` (instance: ${instanceName})` : "";
|
|
208
205
|
console.log(`\n chvor is running at http://localhost:${port}${label}`);
|
package/dist/commands/onboard.js
CHANGED
|
@@ -2,7 +2,7 @@ import { randomBytes } from "node:crypto";
|
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { input, select
|
|
5
|
+
import { input, select } from "@inquirer/prompts";
|
|
6
6
|
import { writeConfig } from "../lib/config.js";
|
|
7
7
|
import { validatePort } from "../lib/validate.js";
|
|
8
8
|
import { getDataDir, ensureDir } from "../lib/paths.js";
|
|
@@ -26,48 +26,23 @@ export async function onboard() {
|
|
|
26
26
|
{ name: "Google (Gemini)", value: "google-ai" },
|
|
27
27
|
],
|
|
28
28
|
});
|
|
29
|
-
const
|
|
30
|
-
const port = validatePort(await input({ message: "Port?", default: "3001" }));
|
|
29
|
+
const port = validatePort(await input({ message: "Port?", default: "9147" }));
|
|
31
30
|
const token = randomBytes(32).toString("hex");
|
|
31
|
+
// Write config but do NOT mark as onboarded yet — that happens after
|
|
32
|
+
// download + server start succeed, so a failed install doesn't leave
|
|
33
|
+
// the user in a half-configured state.
|
|
32
34
|
writeConfig({
|
|
33
35
|
port,
|
|
34
36
|
token,
|
|
35
|
-
onboarded:
|
|
37
|
+
onboarded: false,
|
|
36
38
|
llmProvider: provider,
|
|
37
39
|
});
|
|
38
40
|
ensureDir(getDataDir());
|
|
39
41
|
const version = pkg.version;
|
|
40
42
|
await downloadRelease(version);
|
|
41
43
|
await spawnServer({ port });
|
|
42
|
-
|
|
43
|
-
// to decide whether we can configure credentials via API
|
|
44
|
-
const serverReady = await pollHealth(port, token, 5000);
|
|
44
|
+
const serverReady = await pollHealth(port, token, 30000);
|
|
45
45
|
if (serverReady) {
|
|
46
|
-
const providerNames = {
|
|
47
|
-
anthropic: "Anthropic",
|
|
48
|
-
openai: "OpenAI",
|
|
49
|
-
"google-ai": "Google AI",
|
|
50
|
-
};
|
|
51
|
-
try {
|
|
52
|
-
const credRes = await fetch(`http://localhost:${port}/api/credentials`, {
|
|
53
|
-
method: "POST",
|
|
54
|
-
headers: {
|
|
55
|
-
"Content-Type": "application/json",
|
|
56
|
-
Authorization: `Bearer ${token}`,
|
|
57
|
-
},
|
|
58
|
-
body: JSON.stringify({
|
|
59
|
-
name: providerNames[provider],
|
|
60
|
-
type: provider,
|
|
61
|
-
data: { apiKey },
|
|
62
|
-
}),
|
|
63
|
-
});
|
|
64
|
-
if (!credRes.ok) {
|
|
65
|
-
console.warn(`Warning: failed to save credentials (${credRes.status}). You can add them later in the UI.`);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
catch {
|
|
69
|
-
console.warn("Warning: could not reach server to save credentials. You can add them later in the UI.");
|
|
70
|
-
}
|
|
71
46
|
try {
|
|
72
47
|
const personaRes = await fetch(`http://localhost:${port}/api/persona`, {
|
|
73
48
|
method: "PATCH",
|
|
@@ -91,10 +66,19 @@ export async function onboard() {
|
|
|
91
66
|
}
|
|
92
67
|
else {
|
|
93
68
|
console.warn("\n Server is still starting up. Your config has been saved." +
|
|
94
|
-
"\n
|
|
69
|
+
"\n Persona will be configured when you open the UI.");
|
|
95
70
|
}
|
|
71
|
+
// Mark as onboarded only after download + server start succeeded
|
|
72
|
+
writeConfig({
|
|
73
|
+
port,
|
|
74
|
+
token,
|
|
75
|
+
onboarded: true,
|
|
76
|
+
llmProvider: provider,
|
|
77
|
+
installedVersion: version,
|
|
78
|
+
});
|
|
96
79
|
console.log(`\n chvor is running at http://localhost:${port}`);
|
|
97
|
-
console.log(" Open this URL in your browser to get started
|
|
80
|
+
console.log(" Open this URL in your browser to get started.");
|
|
81
|
+
console.log(" You can add your API key in Settings once you're in.\n");
|
|
98
82
|
console.log(" Useful commands:");
|
|
99
83
|
console.log(" chvor stop Stop the server");
|
|
100
84
|
console.log(" chvor start Start the server");
|
package/dist/commands/open.js
CHANGED
|
@@ -2,7 +2,7 @@ import { execFileSync } from "node:child_process";
|
|
|
2
2
|
import { readConfig } from "../lib/config.js";
|
|
3
3
|
export async function open() {
|
|
4
4
|
const config = readConfig();
|
|
5
|
-
const port = config.port ?? "
|
|
5
|
+
const port = config.port ?? "9147";
|
|
6
6
|
const url = `http://localhost:${port}`;
|
|
7
7
|
switch (process.platform) {
|
|
8
8
|
case "darwin":
|
package/dist/lib/config.js
CHANGED
|
@@ -2,7 +2,7 @@ import { readFileSync, writeFileSync } from "node:fs";
|
|
|
2
2
|
import { dirname } from "node:path";
|
|
3
3
|
import { getConfigPath, ensureDir } from "./paths.js";
|
|
4
4
|
const DEFAULTS = {
|
|
5
|
-
port: "
|
|
5
|
+
port: "9147",
|
|
6
6
|
onboarded: false,
|
|
7
7
|
};
|
|
8
8
|
export function readConfig() {
|
|
@@ -22,5 +22,6 @@ export function writeConfig(config) {
|
|
|
22
22
|
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", { encoding: "utf-8", mode: 0o600 });
|
|
23
23
|
}
|
|
24
24
|
export function isOnboarded() {
|
|
25
|
-
|
|
25
|
+
const config = readConfig();
|
|
26
|
+
return config.onboarded && !!config.installedVersion;
|
|
26
27
|
}
|
package/dist/lib/validate.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export function validatePort(value) {
|
|
2
2
|
const num = parseInt(value, 10);
|
|
3
|
-
if (isNaN(num) || num <
|
|
4
|
-
throw new Error(`Invalid port: "${value}". Must be an integer between
|
|
3
|
+
if (isNaN(num) || num < 1024 || num > 65535 || String(num) !== value) {
|
|
4
|
+
throw new Error(`Invalid port: "${value}". Must be an integer between 1024 and 65535.`);
|
|
5
5
|
}
|
|
6
6
|
return value;
|
|
7
7
|
}
|