@chvor/cli 0.1.7 → 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 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", "3001")
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);
@@ -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 ?? "3001");
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}:3001`,
12
+ "-p", `${port}:9147`,
13
13
  "-v", `${homedir()}/.chvor:/home/node/.chvor`,
14
14
  image,
15
15
  ], { stdio: "inherit" });
@@ -128,15 +128,14 @@ export async function init(opts) {
128
128
  { name: "Google (Gemini)", value: "google-ai" },
129
129
  ],
130
130
  });
131
- const apiKey = await password({ message: "API key:" });
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: true,
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 LLM provider credential
158
- const providerNames = {
159
- anthropic: "Anthropic",
160
- openai: "OpenAI",
161
- "google-ai": "Google AI",
162
- };
163
- await fetch(`http://localhost:${port}/api/credentials`, {
164
- method: "POST",
165
- headers: {
166
- "Content-Type": "application/json",
167
- Authorization: `Bearer ${token}`,
168
- },
169
- body: JSON.stringify({
170
- name: providerNames[provider],
171
- type: provider,
172
- data: { apiKey },
173
- }),
174
- });
175
- // 8. Save persona (user name + timezone + template persona)
176
- await fetch(`http://localhost:${port}/api/persona`, {
177
- method: "PATCH",
178
- headers: {
179
- "Content-Type": "application/json",
180
- Authorization: `Bearer ${token}`,
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}`);
@@ -31,7 +31,7 @@ function discoverInstances() {
31
31
  }
32
32
  instances.push({
33
33
  name: "(default)",
34
- port: config.port || "3001",
34
+ port: config.port || "9147",
35
35
  running,
36
36
  pid,
37
37
  template: config.templateName,
@@ -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, password } from "@inquirer/prompts";
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 apiKey = await password({ message: "API key:" });
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: true,
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
- // spawnServer already polls health internally — check once more briefly
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 Credentials and persona will be configured when you open the UI.");
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.\n");
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");
@@ -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 ?? "3001";
5
+ const port = config.port ?? "9147";
6
6
  const url = `http://localhost:${port}`;
7
7
  switch (process.platform) {
8
8
  case "darwin":
@@ -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: "3001",
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
- return readConfig().onboarded;
25
+ const config = readConfig();
26
+ return config.onboarded && !!config.installedVersion;
26
27
  }
@@ -1,7 +1,7 @@
1
1
  export function validatePort(value) {
2
2
  const num = parseInt(value, 10);
3
- if (isNaN(num) || num < 1 || num > 65535 || String(num) !== value) {
4
- throw new Error(`Invalid port: "${value}". Must be an integer between 1 and 65535.`);
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chvor/cli",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "Your own AI — install and run chvor.",
5
5
  "license": "SEE LICENSE IN LICENSE.md",
6
6
  "type": "module",