kubeagent 0.1.29 → 0.1.31
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 +2 -1
- package/dist/cli.js +6 -3
- package/dist/monitor/index.d.ts +7 -0
- package/dist/monitor/index.js +13 -2
- package/dist/update-notifier.js +50 -8
- package/package.json +9 -1
package/dist/auth.js
CHANGED
|
@@ -133,7 +133,8 @@ export async function loginBrowser(serverUrl, appUrl) {
|
|
|
133
133
|
}
|
|
134
134
|
console.log(`\nOpening browser for authentication...`);
|
|
135
135
|
console.log(`If the browser didn't open, visit:\n ${authUrl}`);
|
|
136
|
-
console.log(`Or on a headless/remote machine, run: kubeagent login --device
|
|
136
|
+
console.log(`Or on a headless/remote machine, run: kubeagent login --device`);
|
|
137
|
+
console.log(`\nTip: if you signed up with email/password, check your inbox and verify your email first, then approve here.\n`);
|
|
137
138
|
});
|
|
138
139
|
// Timeout after 5 minutes
|
|
139
140
|
const timeoutId = setTimeout(() => {
|
package/dist/cli.js
CHANGED
|
@@ -37,14 +37,17 @@ program
|
|
|
37
37
|
.option("-c, --context <context>", "Kubernetes context")
|
|
38
38
|
.action(async (opts) => {
|
|
39
39
|
try {
|
|
40
|
-
const issues = await runChecks({ context: opts.context });
|
|
40
|
+
const { issues, podCount, nodeCount, namespaceCount } = await runChecks({ context: opts.context }, true);
|
|
41
|
+
const clusterName = opts.context ?? "current context";
|
|
42
|
+
const summary = chalk.dim(`Checked ${podCount} pod${podCount !== 1 ? "s" : ""}, ${nodeCount} node${nodeCount !== 1 ? "s" : ""}, ${namespaceCount} namespace${namespaceCount !== 1 ? "s" : ""} on ${chalk.bold(clusterName)}`);
|
|
41
43
|
if (issues.length === 0) {
|
|
42
|
-
console.log(chalk.green("All clear.
|
|
44
|
+
console.log(summary + " — " + chalk.green("All clear."));
|
|
43
45
|
}
|
|
44
46
|
else {
|
|
47
|
+
console.log(summary + "\n");
|
|
45
48
|
for (const issue of issues) {
|
|
46
49
|
const color = issue.severity === "critical" ? chalk.red : chalk.yellow;
|
|
47
|
-
console.log(color(`[${issue.severity}] ${issue.message}`));
|
|
50
|
+
console.log(color(`[${issue.severity}] [${clusterName}] ${issue.message}`));
|
|
48
51
|
}
|
|
49
52
|
}
|
|
50
53
|
}
|
package/dist/monitor/index.d.ts
CHANGED
|
@@ -2,7 +2,14 @@ import { type KubectlOptions } from "../kubectl.js";
|
|
|
2
2
|
import type { Issue } from "./types.js";
|
|
3
3
|
export type IssueCallback = (issues: Issue[]) => void | Promise<void>;
|
|
4
4
|
export type ResolveCallback = (resolvedKeys: string[]) => void | Promise<void>;
|
|
5
|
+
export interface CheckSummary {
|
|
6
|
+
issues: Issue[];
|
|
7
|
+
podCount: number;
|
|
8
|
+
nodeCount: number;
|
|
9
|
+
namespaceCount: number;
|
|
10
|
+
}
|
|
5
11
|
export declare function runChecks(options: KubectlOptions): Promise<Issue[]>;
|
|
12
|
+
export declare function runChecks(options: KubectlOptions, withSummary: true): Promise<CheckSummary>;
|
|
6
13
|
export declare function computeResolvedKeys(activeKeys: Set<string>, currentKeys: Set<string>): string[];
|
|
7
14
|
export declare function updateActiveKeys(activeKeys: Set<string>, resolvedKeys: string[], currentKeys: Set<string>): void;
|
|
8
15
|
export declare function startMonitor(options: KubectlOptions, intervalMs: number, onIssues: IssueCallback, onResolved?: ResolveCallback): {
|
package/dist/monitor/index.js
CHANGED
|
@@ -33,7 +33,7 @@ function deduplicateIssues(issues) {
|
|
|
33
33
|
return true;
|
|
34
34
|
});
|
|
35
35
|
}
|
|
36
|
-
export async function runChecks(options) {
|
|
36
|
+
export async function runChecks(options, withSummary) {
|
|
37
37
|
const allIssues = [];
|
|
38
38
|
// Get all pods across namespaces
|
|
39
39
|
const podList = (await kubectlJson(["get", "pods", "--all-namespaces"], options));
|
|
@@ -44,7 +44,18 @@ export async function runChecks(options) {
|
|
|
44
44
|
// Get failed jobs
|
|
45
45
|
const jobList = (await kubectlJson(["get", "jobs", "--all-namespaces", "--field-selector=status.failed>0"], options).catch(() => ({ items: [] })));
|
|
46
46
|
allIssues.push(...findJobIssues(jobList));
|
|
47
|
-
|
|
47
|
+
const issues = deduplicateIssues(allIssues);
|
|
48
|
+
if (!withSummary)
|
|
49
|
+
return issues;
|
|
50
|
+
// Fetch namespace count (non-fatal)
|
|
51
|
+
const nsList = await kubectlJson(["get", "namespaces"], options)
|
|
52
|
+
.catch(() => ({ items: [] }));
|
|
53
|
+
return {
|
|
54
|
+
issues,
|
|
55
|
+
podCount: podList.items.length,
|
|
56
|
+
nodeCount: nodeList.items.length,
|
|
57
|
+
namespaceCount: nsList.items.length,
|
|
58
|
+
};
|
|
48
59
|
}
|
|
49
60
|
// How long a pod must be Pending before it's reported as an issue.
|
|
50
61
|
const PENDING_GRACE_MS = 60_000;
|
package/dist/update-notifier.js
CHANGED
|
@@ -1,8 +1,32 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
|
+
import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { homedir } from "node:os";
|
|
3
6
|
const require = createRequire(import.meta.url);
|
|
4
7
|
const { version: currentVersion } = require("../package.json");
|
|
5
8
|
const REGISTRY_URL = "https://registry.npmjs.org/kubeagent/latest";
|
|
9
|
+
const CACHE_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
10
|
+
function cacheFilePath() {
|
|
11
|
+
return join(homedir(), ".kubeagent", "update-check.json");
|
|
12
|
+
}
|
|
13
|
+
function readCache() {
|
|
14
|
+
try {
|
|
15
|
+
return JSON.parse(readFileSync(cacheFilePath(), "utf-8"));
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function writeCache(cache) {
|
|
22
|
+
try {
|
|
23
|
+
mkdirSync(join(homedir(), ".kubeagent"), { recursive: true });
|
|
24
|
+
writeFileSync(cacheFilePath(), JSON.stringify(cache), "utf-8");
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
// Non-fatal
|
|
28
|
+
}
|
|
29
|
+
}
|
|
6
30
|
function isNewer(latest, current) {
|
|
7
31
|
const parse = (v) => v.replace(/^v/, "").split(".").map(Number);
|
|
8
32
|
const [lMaj, lMin, lPatch] = parse(latest);
|
|
@@ -15,22 +39,40 @@ function isNewer(latest, current) {
|
|
|
15
39
|
}
|
|
16
40
|
export async function checkForUpdate() {
|
|
17
41
|
try {
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if (
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
42
|
+
const now = Date.now();
|
|
43
|
+
const cache = readCache();
|
|
44
|
+
// Fetch from registry if no cache or cache is stale (> 24h)
|
|
45
|
+
let latest;
|
|
46
|
+
if (cache && now - cache.checkedAt < CACHE_TTL_MS) {
|
|
47
|
+
latest = cache.latestVersion;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
const controller = new AbortController();
|
|
51
|
+
const timeout = setTimeout(() => controller.abort(), 3000);
|
|
52
|
+
const res = await fetch(REGISTRY_URL, { signal: controller.signal });
|
|
53
|
+
clearTimeout(timeout);
|
|
54
|
+
if (!res.ok)
|
|
55
|
+
return;
|
|
56
|
+
const data = (await res.json());
|
|
57
|
+
latest = data.version;
|
|
58
|
+
if (latest) {
|
|
59
|
+
writeCache({ checkedAt: now, latestVersion: latest, shownAt: cache?.shownAt });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
26
62
|
if (!latest || !isNewer(latest, currentVersion))
|
|
27
63
|
return;
|
|
64
|
+
// Show notification at most once per 24h regardless of how many commands are run
|
|
65
|
+
if (cache?.shownAt && now - cache.shownAt < CACHE_TTL_MS)
|
|
66
|
+
return;
|
|
28
67
|
console.log("\n" +
|
|
29
68
|
chalk.yellow(` Update available: ${currentVersion} → ${latest}`) +
|
|
30
69
|
"\n" +
|
|
31
70
|
chalk.dim(" Run ") +
|
|
32
71
|
chalk.cyan("npm update -g kubeagent") +
|
|
33
72
|
chalk.dim(" to update\n"));
|
|
73
|
+
// Record that we showed the notification; always use `now` for checkedAt so
|
|
74
|
+
// a fresh registry fetch isn't overwritten by the stale cached timestamp.
|
|
75
|
+
writeCache({ checkedAt: now, latestVersion: latest, shownAt: now });
|
|
34
76
|
}
|
|
35
77
|
catch {
|
|
36
78
|
// Network errors, timeouts — silently ignore
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kubeagent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.31",
|
|
4
4
|
"description": "AI-powered Kubernetes management CLI",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"type": "module",
|
|
@@ -33,6 +33,14 @@
|
|
|
33
33
|
"zod": "^3.24.0",
|
|
34
34
|
"zod-to-json-schema": "^3.25.2"
|
|
35
35
|
},
|
|
36
|
+
"fallow": {
|
|
37
|
+
"ignoreDependencies": [
|
|
38
|
+
"bcrypt",
|
|
39
|
+
"drizzle-orm",
|
|
40
|
+
"hono",
|
|
41
|
+
"jose"
|
|
42
|
+
]
|
|
43
|
+
},
|
|
36
44
|
"devDependencies": {
|
|
37
45
|
"@kubeagent/shared": "*",
|
|
38
46
|
"@types/js-yaml": "^4.0.9",
|