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 CHANGED
@@ -53,13 +53,19 @@ export async function loginBrowser(serverUrl, appUrl) {
53
53
  res.end();
54
54
  return;
55
55
  }
56
- const url = new URL(req.url, "http://localhost");
57
- const receivedState = url.searchParams.get("state");
58
- const token = url.searchParams.get("token");
59
- const email = url.searchParams.get("email") ?? "";
60
- const name = url.searchParams.get("name") ?? "";
61
- res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
62
- res.end(`<!DOCTYPE html>
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
- server.close();
93
- if (receivedState !== state || !token) {
94
- reject(new Error("Invalid callback — state mismatch or missing token"));
95
- }
96
- else {
97
- resolve({ token, email, name });
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
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kubeagent",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "AI-powered Kubernetes management CLI",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "type": "module",