kubeagent 0.1.8 → 0.1.10
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/auth.js +21 -14
- package/dist/cli.js +3 -0
- package/dist/update-notifier.d.ts +1 -0
- package/dist/update-notifier.js +38 -0
- package/dist/update-notifier.test.d.ts +1 -0
- package/dist/update-notifier.test.js +50 -0
- package/package.json +1 -1
package/dist/auth.js
CHANGED
|
@@ -53,13 +53,19 @@ export async function loginBrowser(serverUrl, appUrl) {
|
|
|
53
53
|
res.end();
|
|
54
54
|
return;
|
|
55
55
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
56
|
+
// Parse credentials from POST body (hidden form submit) to avoid
|
|
57
|
+
// leaking the JWT in URL query params, browser history, and Referer headers.
|
|
58
|
+
const chunks = [];
|
|
59
|
+
req.on("data", (chunk) => chunks.push(chunk));
|
|
60
|
+
req.on("end", () => {
|
|
61
|
+
const body = Buffer.concat(chunks).toString();
|
|
62
|
+
const params = new URLSearchParams(body);
|
|
63
|
+
const receivedState = params.get("state");
|
|
64
|
+
const token = params.get("token");
|
|
65
|
+
const email = params.get("email") ?? "";
|
|
66
|
+
const name = params.get("name") ?? "";
|
|
67
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
68
|
+
res.end(`<!DOCTYPE html>
|
|
63
69
|
<html lang="en">
|
|
64
70
|
<head>
|
|
65
71
|
<meta charset="UTF-8" />
|
|
@@ -89,13 +95,14 @@ export async function loginBrowser(serverUrl, appUrl) {
|
|
|
89
95
|
</div>
|
|
90
96
|
</body>
|
|
91
97
|
</html>`);
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
98
|
+
server.close();
|
|
99
|
+
if (receivedState !== state || !token) {
|
|
100
|
+
reject(new Error("Invalid callback — state mismatch or missing token"));
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
resolve({ token, email, name });
|
|
104
|
+
}
|
|
105
|
+
});
|
|
99
106
|
});
|
|
100
107
|
server.listen(0, "127.0.0.1", async () => {
|
|
101
108
|
const port = server.address().port;
|
package/dist/cli.js
CHANGED
|
@@ -25,6 +25,7 @@ import { scanProjectDirectory, matchProjectsToWorkloads, bestMatches } from "./o
|
|
|
25
25
|
import { scanCluster } from "./onboard/cluster-scan.js";
|
|
26
26
|
import { formatProjectMarkdown } from "./onboard/code-scan.js";
|
|
27
27
|
import { writeProjectKb, ensureKbDir } from "./kb/writer.js";
|
|
28
|
+
import { checkForUpdate } from "./update-notifier.js";
|
|
28
29
|
const program = new Command();
|
|
29
30
|
program
|
|
30
31
|
.name("kubeagent")
|
|
@@ -462,3 +463,5 @@ program
|
|
|
462
463
|
}
|
|
463
464
|
});
|
|
464
465
|
program.parse();
|
|
466
|
+
// Non-blocking update check — runs after command completes
|
|
467
|
+
checkForUpdate();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function checkForUpdate(): Promise<void>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
const require = createRequire(import.meta.url);
|
|
4
|
+
const { version: currentVersion } = require("../package.json");
|
|
5
|
+
const REGISTRY_URL = "https://registry.npmjs.org/kubeagent/latest";
|
|
6
|
+
function isNewer(latest, current) {
|
|
7
|
+
const parse = (v) => v.replace(/^v/, "").split(".").map(Number);
|
|
8
|
+
const [lMaj, lMin, lPatch] = parse(latest);
|
|
9
|
+
const [cMaj, cMin, cPatch] = parse(current);
|
|
10
|
+
if (lMaj !== cMaj)
|
|
11
|
+
return lMaj > cMaj;
|
|
12
|
+
if (lMin !== cMin)
|
|
13
|
+
return lMin > cMin;
|
|
14
|
+
return lPatch > cPatch;
|
|
15
|
+
}
|
|
16
|
+
export async function checkForUpdate() {
|
|
17
|
+
try {
|
|
18
|
+
const controller = new AbortController();
|
|
19
|
+
const timeout = setTimeout(() => controller.abort(), 3000);
|
|
20
|
+
const res = await fetch(REGISTRY_URL, { signal: controller.signal });
|
|
21
|
+
clearTimeout(timeout);
|
|
22
|
+
if (!res.ok)
|
|
23
|
+
return;
|
|
24
|
+
const data = (await res.json());
|
|
25
|
+
const latest = data.version;
|
|
26
|
+
if (!latest || !isNewer(latest, currentVersion))
|
|
27
|
+
return;
|
|
28
|
+
console.log("\n" +
|
|
29
|
+
chalk.yellow(` Update available: ${currentVersion} → ${latest}`) +
|
|
30
|
+
"\n" +
|
|
31
|
+
chalk.dim(" Run ") +
|
|
32
|
+
chalk.cyan("npm update -g kubeagent") +
|
|
33
|
+
chalk.dim(" to update\n"));
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// Network errors, timeouts — silently ignore
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
+
// Mock chalk to return plain strings
|
|
3
|
+
vi.mock("chalk", () => {
|
|
4
|
+
const identity = (s) => s;
|
|
5
|
+
const handler = {
|
|
6
|
+
get: () => new Proxy(identity, handler),
|
|
7
|
+
apply: (_t, _this, args) => args[0],
|
|
8
|
+
};
|
|
9
|
+
return { default: new Proxy(identity, handler) };
|
|
10
|
+
});
|
|
11
|
+
describe("update-notifier", () => {
|
|
12
|
+
let originalFetch;
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
originalFetch = globalThis.fetch;
|
|
15
|
+
});
|
|
16
|
+
afterEach(() => {
|
|
17
|
+
globalThis.fetch = originalFetch;
|
|
18
|
+
vi.restoreAllMocks();
|
|
19
|
+
});
|
|
20
|
+
it("prints update message when newer version exists", async () => {
|
|
21
|
+
globalThis.fetch = vi.fn().mockResolvedValue({
|
|
22
|
+
ok: true,
|
|
23
|
+
json: () => Promise.resolve({ version: "99.0.0" }),
|
|
24
|
+
});
|
|
25
|
+
const spy = vi.spyOn(console, "log").mockImplementation(() => { });
|
|
26
|
+
const { checkForUpdate } = await import("./update-notifier.js");
|
|
27
|
+
await checkForUpdate();
|
|
28
|
+
expect(spy).toHaveBeenCalled();
|
|
29
|
+
const output = spy.mock.calls.map((c) => c.join(" ")).join("\n");
|
|
30
|
+
expect(output).toContain("Update available");
|
|
31
|
+
expect(output).toContain("99.0.0");
|
|
32
|
+
});
|
|
33
|
+
it("prints nothing when already up to date", async () => {
|
|
34
|
+
globalThis.fetch = vi.fn().mockResolvedValue({
|
|
35
|
+
ok: true,
|
|
36
|
+
json: () => Promise.resolve({ version: "0.0.1" }),
|
|
37
|
+
});
|
|
38
|
+
const spy = vi.spyOn(console, "log").mockImplementation(() => { });
|
|
39
|
+
const { checkForUpdate } = await import("./update-notifier.js");
|
|
40
|
+
await checkForUpdate();
|
|
41
|
+
expect(spy).not.toHaveBeenCalled();
|
|
42
|
+
});
|
|
43
|
+
it("prints nothing on network error", async () => {
|
|
44
|
+
globalThis.fetch = vi.fn().mockRejectedValue(new Error("offline"));
|
|
45
|
+
const spy = vi.spyOn(console, "log").mockImplementation(() => { });
|
|
46
|
+
const { checkForUpdate } = await import("./update-notifier.js");
|
|
47
|
+
await checkForUpdate();
|
|
48
|
+
expect(spy).not.toHaveBeenCalled();
|
|
49
|
+
});
|
|
50
|
+
});
|