@chvor/cli 0.1.2 → 0.1.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/lib/process.js +3 -3
- package/dist/lib/process.test.js +71 -0
- package/package.json +7 -2
package/dist/lib/process.js
CHANGED
|
@@ -97,7 +97,7 @@ export async function spawnServer(opts = {}) {
|
|
|
97
97
|
}
|
|
98
98
|
if (opts.foreground) {
|
|
99
99
|
console.log(`Starting Chvor on port ${port} (foreground)...`);
|
|
100
|
-
const child = spawn("node", [serverEntry], {
|
|
100
|
+
const child = spawn("node", ["--import", "tsx", serverEntry], {
|
|
101
101
|
env,
|
|
102
102
|
stdio: "inherit",
|
|
103
103
|
});
|
|
@@ -149,7 +149,7 @@ export async function spawnServer(opts = {}) {
|
|
|
149
149
|
ensureDir(logsDir);
|
|
150
150
|
const logPath = join(logsDir, "server.log");
|
|
151
151
|
const logFd = openSync(logPath, "a");
|
|
152
|
-
const child = spawn("node", [serverEntry], {
|
|
152
|
+
const child = spawn("node", ["--import", "tsx", serverEntry], {
|
|
153
153
|
env,
|
|
154
154
|
stdio: ["ignore", logFd, logFd],
|
|
155
155
|
detached: true,
|
|
@@ -232,7 +232,7 @@ export async function pollHealth(port, token, timeoutMs = 30000, intervalMs = 50
|
|
|
232
232
|
function sleep(ms) {
|
|
233
233
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
234
234
|
}
|
|
235
|
-
function filterEnv(env) {
|
|
235
|
+
export function filterEnv(env) {
|
|
236
236
|
const result = {};
|
|
237
237
|
for (const [key, value] of Object.entries(env)) {
|
|
238
238
|
if (value !== undefined) {
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { filterEnv, pollHealth } from "./process.js";
|
|
3
|
+
describe("filterEnv", () => {
|
|
4
|
+
it("removes entries with undefined values", () => {
|
|
5
|
+
const input = { A: "1", B: undefined, C: "3" };
|
|
6
|
+
const result = filterEnv(input);
|
|
7
|
+
expect(result).toEqual({ A: "1", C: "3" });
|
|
8
|
+
});
|
|
9
|
+
it("returns empty object for empty input", () => {
|
|
10
|
+
expect(filterEnv({})).toEqual({});
|
|
11
|
+
});
|
|
12
|
+
it("keeps all entries when none are undefined", () => {
|
|
13
|
+
const input = { X: "x", Y: "y" };
|
|
14
|
+
expect(filterEnv(input)).toEqual({ X: "x", Y: "y" });
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
describe("pollHealth", () => {
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
vi.useFakeTimers({ shouldAdvanceTime: true });
|
|
20
|
+
vi.stubGlobal("fetch", vi.fn());
|
|
21
|
+
});
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
vi.useRealTimers();
|
|
24
|
+
vi.restoreAllMocks();
|
|
25
|
+
});
|
|
26
|
+
it("returns true when server responds with ok: true", async () => {
|
|
27
|
+
const mockFetch = vi.mocked(fetch);
|
|
28
|
+
mockFetch.mockResolvedValueOnce(new Response(JSON.stringify({ ok: true }), { status: 200 }));
|
|
29
|
+
const result = await pollHealth("3001");
|
|
30
|
+
expect(result).toBe(true);
|
|
31
|
+
});
|
|
32
|
+
it("sends Authorization header when token is provided", async () => {
|
|
33
|
+
const mockFetch = vi.mocked(fetch);
|
|
34
|
+
mockFetch.mockResolvedValueOnce(new Response(JSON.stringify({ ok: true }), { status: 200 }));
|
|
35
|
+
await pollHealth("3001", "my-secret-token");
|
|
36
|
+
expect(mockFetch).toHaveBeenCalledWith("http://localhost:3001/api/health", { headers: { Authorization: "Bearer my-secret-token" } });
|
|
37
|
+
});
|
|
38
|
+
it("retries on fetch failure and succeeds eventually", async () => {
|
|
39
|
+
const mockFetch = vi.mocked(fetch);
|
|
40
|
+
mockFetch
|
|
41
|
+
.mockRejectedValueOnce(new Error("ECONNREFUSED"))
|
|
42
|
+
.mockRejectedValueOnce(new Error("ECONNREFUSED"))
|
|
43
|
+
.mockResolvedValueOnce(new Response(JSON.stringify({ ok: true }), { status: 200 }));
|
|
44
|
+
const result = await pollHealth("3001", undefined, 30000, 100);
|
|
45
|
+
expect(result).toBe(true);
|
|
46
|
+
expect(mockFetch).toHaveBeenCalledTimes(3);
|
|
47
|
+
});
|
|
48
|
+
it("returns false when timeout expires", async () => {
|
|
49
|
+
const mockFetch = vi.mocked(fetch);
|
|
50
|
+
mockFetch.mockRejectedValue(new Error("ECONNREFUSED"));
|
|
51
|
+
const result = await pollHealth("3001", undefined, 1000, 200);
|
|
52
|
+
expect(result).toBe(false);
|
|
53
|
+
});
|
|
54
|
+
it("retries when response is not ok", async () => {
|
|
55
|
+
const mockFetch = vi.mocked(fetch);
|
|
56
|
+
mockFetch
|
|
57
|
+
.mockResolvedValueOnce(new Response("Internal Server Error", { status: 500 }))
|
|
58
|
+
.mockResolvedValueOnce(new Response(JSON.stringify({ ok: true }), { status: 200 }));
|
|
59
|
+
const result = await pollHealth("3001", undefined, 30000, 100);
|
|
60
|
+
expect(result).toBe(true);
|
|
61
|
+
expect(mockFetch).toHaveBeenCalledTimes(2);
|
|
62
|
+
});
|
|
63
|
+
it("retries when body.ok is not true", async () => {
|
|
64
|
+
const mockFetch = vi.mocked(fetch);
|
|
65
|
+
mockFetch
|
|
66
|
+
.mockResolvedValueOnce(new Response(JSON.stringify({ ok: false }), { status: 200 }))
|
|
67
|
+
.mockResolvedValueOnce(new Response(JSON.stringify({ ok: true }), { status: 200 }));
|
|
68
|
+
const result = await pollHealth("3001", undefined, 30000, 100);
|
|
69
|
+
expect(result).toBe(true);
|
|
70
|
+
});
|
|
71
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chvor/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Your own AI — install and run chvor.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
6
6
|
"type": "module",
|
|
@@ -17,16 +17,21 @@
|
|
|
17
17
|
"scripts": {
|
|
18
18
|
"build": "tsc",
|
|
19
19
|
"prepublishOnly": "tsc",
|
|
20
|
-
"typecheck": "tsc --noEmit"
|
|
20
|
+
"typecheck": "tsc --noEmit",
|
|
21
|
+
"test": "vitest run"
|
|
21
22
|
},
|
|
22
23
|
"dependencies": {
|
|
23
24
|
"commander": "^13",
|
|
24
25
|
"@inquirer/prompts": "^7",
|
|
26
|
+
"tsx": "^4",
|
|
25
27
|
"yaml": "^2"
|
|
26
28
|
},
|
|
27
29
|
"optionalDependencies": {
|
|
28
30
|
"better-sqlite3": "^11"
|
|
29
31
|
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"vitest": "^4.1.0"
|
|
34
|
+
},
|
|
30
35
|
"repository": {
|
|
31
36
|
"type": "git",
|
|
32
37
|
"url": "https://github.com/luka-zivkovic/chvor.git",
|