@lousy-agents/cli 4.0.0 → 4.0.2
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/api/copilot-with-fastify/package-lock.json +5202 -0
- package/api/copilot-with-fastify/src/app.integration.ts +45 -0
- package/api/copilot-with-fastify/src/app.test.ts +28 -0
- package/api/copilot-with-fastify/src/app.ts +11 -0
- package/api/copilot-with-fastify/src/index.ts +12 -0
- package/api/copilot-with-fastify/src/parse-port.test.ts +100 -0
- package/api/copilot-with-fastify/src/parse-port.ts +20 -0
- package/dist/index.js +32 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { AddressInfo } from "node:net";
|
|
2
|
+
import type { FastifyInstance } from "fastify";
|
|
3
|
+
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
4
|
+
import { buildApp } from "./app.js";
|
|
5
|
+
|
|
6
|
+
describe("API integration", () => {
|
|
7
|
+
let app: FastifyInstance;
|
|
8
|
+
let baseUrl: string;
|
|
9
|
+
|
|
10
|
+
beforeAll(async () => {
|
|
11
|
+
app = buildApp();
|
|
12
|
+
await app.listen({ port: 0, host: "127.0.0.1" });
|
|
13
|
+
const address = app.server.address() as AddressInfo;
|
|
14
|
+
baseUrl = `http://127.0.0.1:${address.port}`;
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
afterAll(async () => {
|
|
18
|
+
await app.close();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe("GET /health", () => {
|
|
22
|
+
it("should respond with 200 and health status", async () => {
|
|
23
|
+
const response = await fetch(`${baseUrl}/health`);
|
|
24
|
+
|
|
25
|
+
expect(response.status).toBe(200);
|
|
26
|
+
expect(await response.json()).toEqual({ status: "ok" });
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("should return application/json content type", async () => {
|
|
30
|
+
const response = await fetch(`${baseUrl}/health`);
|
|
31
|
+
|
|
32
|
+
expect(response.headers.get("content-type")).toContain(
|
|
33
|
+
"application/json",
|
|
34
|
+
);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe("undefined routes", () => {
|
|
39
|
+
it("should return 404 for non-existent routes", async () => {
|
|
40
|
+
const response = await fetch(`${baseUrl}/nonexistent`);
|
|
41
|
+
|
|
42
|
+
expect(response.status).toBe(404);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { FastifyInstance } from "fastify";
|
|
2
|
+
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
3
|
+
import { buildApp } from "./app.js";
|
|
4
|
+
|
|
5
|
+
describe("Health endpoint", () => {
|
|
6
|
+
let app: FastifyInstance;
|
|
7
|
+
|
|
8
|
+
beforeAll(async () => {
|
|
9
|
+
app = buildApp();
|
|
10
|
+
await app.ready();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
afterAll(async () => {
|
|
14
|
+
await app.close();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
describe("GET /health", () => {
|
|
18
|
+
it("should return 200 with status ok", async () => {
|
|
19
|
+
const response = await app.inject({
|
|
20
|
+
method: "GET",
|
|
21
|
+
url: "/health",
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
expect(response.statusCode).toBe(200);
|
|
25
|
+
expect(response.json()).toEqual({ status: "ok" });
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { buildApp } from "./app.js";
|
|
2
|
+
import { parsePort } from "./parse-port.js";
|
|
3
|
+
|
|
4
|
+
const app = buildApp();
|
|
5
|
+
const port = parsePort(process.env.PORT);
|
|
6
|
+
|
|
7
|
+
try {
|
|
8
|
+
await app.listen({ port, host: "0.0.0.0" });
|
|
9
|
+
} catch (err) {
|
|
10
|
+
app.log.error(err);
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { parsePort } from "./parse-port.js";
|
|
3
|
+
|
|
4
|
+
describe("parsePort", () => {
|
|
5
|
+
describe("when value is undefined", () => {
|
|
6
|
+
it("should return default port 3000", () => {
|
|
7
|
+
const result = parsePort(undefined);
|
|
8
|
+
|
|
9
|
+
expect(result).toBe(3000);
|
|
10
|
+
});
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
describe("when value is empty string", () => {
|
|
14
|
+
it("should return default port 3000", () => {
|
|
15
|
+
const result = parsePort("");
|
|
16
|
+
|
|
17
|
+
expect(result).toBe(3000);
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe("when value is a valid unprivileged port", () => {
|
|
22
|
+
it("should return the parsed port", () => {
|
|
23
|
+
const result = parsePort("8080");
|
|
24
|
+
|
|
25
|
+
expect(result).toBe(8080);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe("when value is invalid", () => {
|
|
30
|
+
let warnSpy: ReturnType<typeof vi.spyOn>;
|
|
31
|
+
|
|
32
|
+
beforeEach(() => {
|
|
33
|
+
warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
afterEach(() => {
|
|
37
|
+
warnSpy.mockRestore();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("should warn and return default for non-numeric value", () => {
|
|
41
|
+
const result = parsePort("abc");
|
|
42
|
+
|
|
43
|
+
expect(result).toBe(3000);
|
|
44
|
+
expect(warnSpy).toHaveBeenCalledWith(
|
|
45
|
+
'Invalid PORT "abc", using default 3000',
|
|
46
|
+
);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("should warn and return default for privileged port", () => {
|
|
50
|
+
const result = parsePort("80");
|
|
51
|
+
|
|
52
|
+
expect(result).toBe(3000);
|
|
53
|
+
expect(warnSpy).toHaveBeenCalledWith(
|
|
54
|
+
'Invalid PORT "80", using default 3000',
|
|
55
|
+
);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("should warn and return default for port above max", () => {
|
|
59
|
+
const result = parsePort("70000");
|
|
60
|
+
|
|
61
|
+
expect(result).toBe(3000);
|
|
62
|
+
expect(warnSpy).toHaveBeenCalledWith(
|
|
63
|
+
'Invalid PORT "70000", using default 3000',
|
|
64
|
+
);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("should warn and return default for non-integer port", () => {
|
|
68
|
+
const result = parsePort("3000.5");
|
|
69
|
+
|
|
70
|
+
expect(result).toBe(3000);
|
|
71
|
+
expect(warnSpy).toHaveBeenCalledWith(
|
|
72
|
+
'Invalid PORT "3000.5", using default 3000',
|
|
73
|
+
);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe("when value is not provided or empty", () => {
|
|
78
|
+
let warnSpy: ReturnType<typeof vi.spyOn>;
|
|
79
|
+
|
|
80
|
+
beforeEach(() => {
|
|
81
|
+
warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
afterEach(() => {
|
|
85
|
+
warnSpy.mockRestore();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("should not warn when value is undefined", () => {
|
|
89
|
+
parsePort(undefined);
|
|
90
|
+
|
|
91
|
+
expect(warnSpy).not.toHaveBeenCalled();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("should not warn when value is empty string", () => {
|
|
95
|
+
parsePort("");
|
|
96
|
+
|
|
97
|
+
expect(warnSpy).not.toHaveBeenCalled();
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const defaultPort = 3000;
|
|
2
|
+
const minUnprivilegedPort = 1024;
|
|
3
|
+
const maxPort = 65535;
|
|
4
|
+
|
|
5
|
+
export function parsePort(value: string | undefined): number {
|
|
6
|
+
if (value === undefined || value === "") {
|
|
7
|
+
return defaultPort;
|
|
8
|
+
}
|
|
9
|
+
const parsed = Number(value);
|
|
10
|
+
if (
|
|
11
|
+
!Number.isInteger(parsed) ||
|
|
12
|
+
parsed < minUnprivilegedPort ||
|
|
13
|
+
parsed > maxPort
|
|
14
|
+
) {
|
|
15
|
+
// biome-ignore lint/suspicious/noConsole: user-facing warning for invalid PORT
|
|
16
|
+
console.warn(`Invalid PORT "${value}", using default ${defaultPort}`);
|
|
17
|
+
return defaultPort;
|
|
18
|
+
}
|
|
19
|
+
return parsed;
|
|
20
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -35198,7 +35198,38 @@ const CLI_TEMPLATE_DIR = (0,external_node_path_.join)(PROJECT_ROOT, "cli", "copi
|
|
|
35198
35198
|
path: "vitest.setup.ts",
|
|
35199
35199
|
content: readRestApiTemplateFile("vitest.setup.ts")
|
|
35200
35200
|
},
|
|
35201
|
-
...buildCommonNodes(readRestApiTemplateFile)
|
|
35201
|
+
...buildCommonNodes(readRestApiTemplateFile),
|
|
35202
|
+
// Source code
|
|
35203
|
+
{
|
|
35204
|
+
type: "file",
|
|
35205
|
+
path: "src/app.ts",
|
|
35206
|
+
content: readRestApiTemplateFile("src/app.ts")
|
|
35207
|
+
},
|
|
35208
|
+
{
|
|
35209
|
+
type: "file",
|
|
35210
|
+
path: "src/app.test.ts",
|
|
35211
|
+
content: readRestApiTemplateFile("src/app.test.ts")
|
|
35212
|
+
},
|
|
35213
|
+
{
|
|
35214
|
+
type: "file",
|
|
35215
|
+
path: "src/app.integration.ts",
|
|
35216
|
+
content: readRestApiTemplateFile("src/app.integration.ts")
|
|
35217
|
+
},
|
|
35218
|
+
{
|
|
35219
|
+
type: "file",
|
|
35220
|
+
path: "src/parse-port.ts",
|
|
35221
|
+
content: readRestApiTemplateFile("src/parse-port.ts")
|
|
35222
|
+
},
|
|
35223
|
+
{
|
|
35224
|
+
type: "file",
|
|
35225
|
+
path: "src/parse-port.test.ts",
|
|
35226
|
+
content: readRestApiTemplateFile("src/parse-port.test.ts")
|
|
35227
|
+
},
|
|
35228
|
+
{
|
|
35229
|
+
type: "file",
|
|
35230
|
+
path: "src/index.ts",
|
|
35231
|
+
content: readRestApiTemplateFile("src/index.ts")
|
|
35232
|
+
}
|
|
35202
35233
|
]
|
|
35203
35234
|
};
|
|
35204
35235
|
return cachedRestApiStructure;
|