@kwirthmagnify/kwirth-common-ai 0.5.13 → 0.5.16
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/back.d.ts +275 -0
- package/dist/back.js +456 -6
- package/dist/front.d.ts +12 -1
- package/dist/front.js +15 -2
- package/package.json +17 -5
package/dist/back.d.ts
CHANGED
|
@@ -7,3 +7,278 @@ export declare const loadModels: (providers: ILlmProvider[], log: IBackChannelOb
|
|
|
7
7
|
export { generateText, Output, stepCountIs, tool } from 'ai';
|
|
8
8
|
export { z } from 'zod';
|
|
9
9
|
export declare const zodFromExample: (example: Record<string, unknown>) => z.ZodObject<Record<string, z.ZodTypeAny>>;
|
|
10
|
+
export interface IToolContext {
|
|
11
|
+
origin: string;
|
|
12
|
+
nodes: Map<string, any>;
|
|
13
|
+
clusterInfo: any;
|
|
14
|
+
clusterMetrics: any[];
|
|
15
|
+
trace: (toolName: string, args: Record<string, unknown>) => void;
|
|
16
|
+
}
|
|
17
|
+
export interface IToolInfo {
|
|
18
|
+
name: string;
|
|
19
|
+
description: string;
|
|
20
|
+
}
|
|
21
|
+
export declare const runWithToolContext: <T>(context: IToolContext, fn: () => Promise<T>) => Promise<T>;
|
|
22
|
+
export declare const tools: {
|
|
23
|
+
readonly get_node_data: import("ai").Tool<Record<string, never>, any>;
|
|
24
|
+
readonly get_cluster_data: import("ai").Tool<Record<string, never>, {
|
|
25
|
+
name: any;
|
|
26
|
+
flavour: any;
|
|
27
|
+
vcpus: any;
|
|
28
|
+
memoryGB: number;
|
|
29
|
+
nodeCount: any;
|
|
30
|
+
nodes: any;
|
|
31
|
+
error?: undefined;
|
|
32
|
+
} | {
|
|
33
|
+
error: any;
|
|
34
|
+
name?: undefined;
|
|
35
|
+
flavour?: undefined;
|
|
36
|
+
vcpus?: undefined;
|
|
37
|
+
memoryGB?: undefined;
|
|
38
|
+
nodeCount?: undefined;
|
|
39
|
+
nodes?: undefined;
|
|
40
|
+
}>;
|
|
41
|
+
readonly get_workload_data: import("ai").Tool<{
|
|
42
|
+
namespace?: string | undefined;
|
|
43
|
+
}, {
|
|
44
|
+
deployments: any;
|
|
45
|
+
statefulSets: any;
|
|
46
|
+
daemonSets: any;
|
|
47
|
+
pods: any;
|
|
48
|
+
services: any;
|
|
49
|
+
error?: undefined;
|
|
50
|
+
} | {
|
|
51
|
+
error: any;
|
|
52
|
+
deployments?: undefined;
|
|
53
|
+
statefulSets?: undefined;
|
|
54
|
+
daemonSets?: undefined;
|
|
55
|
+
pods?: undefined;
|
|
56
|
+
services?: undefined;
|
|
57
|
+
}>;
|
|
58
|
+
readonly get_space_data: import("ai").Tool<{
|
|
59
|
+
namespace: string;
|
|
60
|
+
}, {
|
|
61
|
+
namespace: string;
|
|
62
|
+
pods: any;
|
|
63
|
+
deployments: any;
|
|
64
|
+
services: any;
|
|
65
|
+
configMaps: any;
|
|
66
|
+
error?: undefined;
|
|
67
|
+
} | {
|
|
68
|
+
error: any;
|
|
69
|
+
namespace?: undefined;
|
|
70
|
+
pods?: undefined;
|
|
71
|
+
deployments?: undefined;
|
|
72
|
+
services?: undefined;
|
|
73
|
+
configMaps?: undefined;
|
|
74
|
+
}>;
|
|
75
|
+
readonly get_cluster_usage: import("ai").Tool<Record<string, never>, {
|
|
76
|
+
error: string;
|
|
77
|
+
vcpus?: undefined;
|
|
78
|
+
memoryGB?: undefined;
|
|
79
|
+
cpuUsagePercent?: undefined;
|
|
80
|
+
memoryUsagePercent?: undefined;
|
|
81
|
+
networkTxMbps?: undefined;
|
|
82
|
+
networkRxMbps?: undefined;
|
|
83
|
+
metricsIntervalSeconds?: undefined;
|
|
84
|
+
} | {
|
|
85
|
+
vcpus: any;
|
|
86
|
+
memoryGB: number;
|
|
87
|
+
cpuUsagePercent: number;
|
|
88
|
+
memoryUsagePercent: number;
|
|
89
|
+
networkTxMbps: number;
|
|
90
|
+
networkRxMbps: number;
|
|
91
|
+
metricsIntervalSeconds: any;
|
|
92
|
+
error?: undefined;
|
|
93
|
+
}>;
|
|
94
|
+
readonly get_node_usage: import("ai").Tool<{
|
|
95
|
+
nodeName?: string | undefined;
|
|
96
|
+
}, any>;
|
|
97
|
+
readonly get_deployment_usage: import("ai").Tool<{
|
|
98
|
+
namespace: string;
|
|
99
|
+
name: string;
|
|
100
|
+
}, {
|
|
101
|
+
deployment: string;
|
|
102
|
+
namespace: string;
|
|
103
|
+
podCount: number;
|
|
104
|
+
cpuMillicores: number;
|
|
105
|
+
memoryMB: number;
|
|
106
|
+
timestamp: any;
|
|
107
|
+
error?: undefined;
|
|
108
|
+
} | {
|
|
109
|
+
error: any;
|
|
110
|
+
deployment?: undefined;
|
|
111
|
+
namespace?: undefined;
|
|
112
|
+
podCount?: undefined;
|
|
113
|
+
cpuMillicores?: undefined;
|
|
114
|
+
memoryMB?: undefined;
|
|
115
|
+
timestamp?: undefined;
|
|
116
|
+
}>;
|
|
117
|
+
readonly get_prev_cluster_usage: import("ai").Tool<{
|
|
118
|
+
count?: number | undefined;
|
|
119
|
+
}, {
|
|
120
|
+
vcpus: any;
|
|
121
|
+
memoryGB: number;
|
|
122
|
+
cpuUsagePercent: number;
|
|
123
|
+
memoryUsagePercent: number;
|
|
124
|
+
networkTxMbps: number;
|
|
125
|
+
networkRxMbps: number;
|
|
126
|
+
}[] | {
|
|
127
|
+
error: string;
|
|
128
|
+
}>;
|
|
129
|
+
readonly get_prev_node_usage: import("ai").Tool<{
|
|
130
|
+
nodeName?: string | undefined;
|
|
131
|
+
count?: number | undefined;
|
|
132
|
+
}, {
|
|
133
|
+
nodes: any;
|
|
134
|
+
}[] | {
|
|
135
|
+
error: string;
|
|
136
|
+
}>;
|
|
137
|
+
readonly get_prev_deployment_usage: import("ai").Tool<{
|
|
138
|
+
namespace: string;
|
|
139
|
+
name: string;
|
|
140
|
+
count?: number | undefined;
|
|
141
|
+
}, {
|
|
142
|
+
deployment: string;
|
|
143
|
+
namespace: string;
|
|
144
|
+
podCount: number;
|
|
145
|
+
cpuMillicores: number;
|
|
146
|
+
memoryMB: number;
|
|
147
|
+
timestamp: any;
|
|
148
|
+
}[] | {
|
|
149
|
+
error: any;
|
|
150
|
+
}>;
|
|
151
|
+
readonly get_prev_space_data: import("ai").Tool<{
|
|
152
|
+
namespace: string;
|
|
153
|
+
count?: number | undefined;
|
|
154
|
+
}, {
|
|
155
|
+
namespace: string;
|
|
156
|
+
podCount: number;
|
|
157
|
+
cpuMillicores: number;
|
|
158
|
+
memoryMB: number;
|
|
159
|
+
timestamp: any;
|
|
160
|
+
}[] | {
|
|
161
|
+
error: string;
|
|
162
|
+
}>;
|
|
163
|
+
readonly add_node: import("ai").Tool<{
|
|
164
|
+
nodeName?: string | undefined;
|
|
165
|
+
nodePoolName?: string | undefined;
|
|
166
|
+
}, {
|
|
167
|
+
success: boolean;
|
|
168
|
+
message: string;
|
|
169
|
+
stdout: string;
|
|
170
|
+
stderr: string;
|
|
171
|
+
error?: undefined;
|
|
172
|
+
} | {
|
|
173
|
+
success: boolean;
|
|
174
|
+
error: any;
|
|
175
|
+
message?: undefined;
|
|
176
|
+
stdout?: undefined;
|
|
177
|
+
stderr?: undefined;
|
|
178
|
+
} | {
|
|
179
|
+
success: boolean;
|
|
180
|
+
message: string;
|
|
181
|
+
stdout?: undefined;
|
|
182
|
+
stderr?: undefined;
|
|
183
|
+
error?: undefined;
|
|
184
|
+
}>;
|
|
185
|
+
readonly remove_node: import("ai").Tool<{
|
|
186
|
+
nodeName: string;
|
|
187
|
+
nodePoolName?: string | undefined;
|
|
188
|
+
}, {
|
|
189
|
+
success: boolean;
|
|
190
|
+
message: string;
|
|
191
|
+
stdout: string;
|
|
192
|
+
stderr: string;
|
|
193
|
+
error?: undefined;
|
|
194
|
+
} | {
|
|
195
|
+
success: boolean;
|
|
196
|
+
error: any;
|
|
197
|
+
message?: undefined;
|
|
198
|
+
stdout?: undefined;
|
|
199
|
+
stderr?: undefined;
|
|
200
|
+
} | {
|
|
201
|
+
success: boolean;
|
|
202
|
+
message: string;
|
|
203
|
+
stdout?: undefined;
|
|
204
|
+
stderr?: undefined;
|
|
205
|
+
error?: undefined;
|
|
206
|
+
}>;
|
|
207
|
+
readonly stop_node: import("ai").Tool<{
|
|
208
|
+
nodeName: string;
|
|
209
|
+
}, {
|
|
210
|
+
success: boolean;
|
|
211
|
+
error: string;
|
|
212
|
+
message?: undefined;
|
|
213
|
+
stdout?: undefined;
|
|
214
|
+
stderr?: undefined;
|
|
215
|
+
} | {
|
|
216
|
+
success: boolean;
|
|
217
|
+
message: string;
|
|
218
|
+
error?: undefined;
|
|
219
|
+
stdout?: undefined;
|
|
220
|
+
stderr?: undefined;
|
|
221
|
+
} | {
|
|
222
|
+
success: boolean;
|
|
223
|
+
message: string;
|
|
224
|
+
stdout: string;
|
|
225
|
+
stderr: string;
|
|
226
|
+
error?: undefined;
|
|
227
|
+
}>;
|
|
228
|
+
readonly start_node: import("ai").Tool<{
|
|
229
|
+
nodeName: string;
|
|
230
|
+
}, {
|
|
231
|
+
success: boolean;
|
|
232
|
+
message: string;
|
|
233
|
+
stdout: string;
|
|
234
|
+
stderr: string;
|
|
235
|
+
error?: undefined;
|
|
236
|
+
} | {
|
|
237
|
+
success: boolean;
|
|
238
|
+
error: any;
|
|
239
|
+
message?: undefined;
|
|
240
|
+
stdout?: undefined;
|
|
241
|
+
stderr?: undefined;
|
|
242
|
+
} | {
|
|
243
|
+
success: boolean;
|
|
244
|
+
message: string;
|
|
245
|
+
stdout?: undefined;
|
|
246
|
+
stderr?: undefined;
|
|
247
|
+
error?: undefined;
|
|
248
|
+
}>;
|
|
249
|
+
readonly add_replica: import("ai").Tool<{
|
|
250
|
+
namespace: string;
|
|
251
|
+
name: string;
|
|
252
|
+
}, {
|
|
253
|
+
success: boolean;
|
|
254
|
+
message: string;
|
|
255
|
+
error?: undefined;
|
|
256
|
+
} | {
|
|
257
|
+
success: boolean;
|
|
258
|
+
error: any;
|
|
259
|
+
message?: undefined;
|
|
260
|
+
}>;
|
|
261
|
+
readonly remove_replica: import("ai").Tool<{
|
|
262
|
+
namespace: string;
|
|
263
|
+
name: string;
|
|
264
|
+
}, {
|
|
265
|
+
success: boolean;
|
|
266
|
+
message: string;
|
|
267
|
+
error?: undefined;
|
|
268
|
+
} | {
|
|
269
|
+
success: boolean;
|
|
270
|
+
error: any;
|
|
271
|
+
message?: undefined;
|
|
272
|
+
}>;
|
|
273
|
+
readonly times_two: import("ai").Tool<{
|
|
274
|
+
data: number;
|
|
275
|
+
}, number>;
|
|
276
|
+
readonly father_of: import("ai").Tool<{
|
|
277
|
+
data: string;
|
|
278
|
+
}, string>;
|
|
279
|
+
readonly get_certificate_info: import("ai").Tool<{
|
|
280
|
+
hostname: string;
|
|
281
|
+
port?: number | undefined;
|
|
282
|
+
}, unknown>;
|
|
283
|
+
};
|
|
284
|
+
export declare const toolInfoList: IToolInfo[];
|
package/dist/back.js
CHANGED
|
@@ -1,7 +1,46 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.zodFromExample = exports.z = exports.tool = exports.stepCountIs = exports.Output = exports.generateText = exports.loadModels = exports.buildModel = void 0;
|
|
36
|
+
exports.toolInfoList = exports.tools = exports.runWithToolContext = exports.zodFromExample = exports.z = exports.tool = exports.stepCountIs = exports.Output = exports.generateText = exports.loadModels = exports.buildModel = void 0;
|
|
37
|
+
const ai_1 = require("ai");
|
|
4
38
|
const zod_1 = require("zod");
|
|
39
|
+
const async_hooks_1 = require("async_hooks");
|
|
40
|
+
const child_process_1 = require("child_process");
|
|
41
|
+
const util_1 = require("util");
|
|
42
|
+
const tls = __importStar(require("tls"));
|
|
43
|
+
const execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
5
44
|
const openai_1 = require("@ai-sdk/openai");
|
|
6
45
|
const groq_1 = require("@ai-sdk/groq");
|
|
7
46
|
const mistral_1 = require("@ai-sdk/mistral");
|
|
@@ -120,11 +159,11 @@ const inferZod = (value) => {
|
|
|
120
159
|
return zod_1.z.unknown();
|
|
121
160
|
};
|
|
122
161
|
// Re-export AI SDK symbols so plugins can use them without bundling the SDK
|
|
123
|
-
var
|
|
124
|
-
Object.defineProperty(exports, "generateText", { enumerable: true, get: function () { return
|
|
125
|
-
Object.defineProperty(exports, "Output", { enumerable: true, get: function () { return
|
|
126
|
-
Object.defineProperty(exports, "stepCountIs", { enumerable: true, get: function () { return
|
|
127
|
-
Object.defineProperty(exports, "tool", { enumerable: true, get: function () { return
|
|
162
|
+
var ai_2 = require("ai");
|
|
163
|
+
Object.defineProperty(exports, "generateText", { enumerable: true, get: function () { return ai_2.generateText; } });
|
|
164
|
+
Object.defineProperty(exports, "Output", { enumerable: true, get: function () { return ai_2.Output; } });
|
|
165
|
+
Object.defineProperty(exports, "stepCountIs", { enumerable: true, get: function () { return ai_2.stepCountIs; } });
|
|
166
|
+
Object.defineProperty(exports, "tool", { enumerable: true, get: function () { return ai_2.tool; } });
|
|
128
167
|
var zod_2 = require("zod");
|
|
129
168
|
Object.defineProperty(exports, "z", { enumerable: true, get: function () { return zod_2.z; } });
|
|
130
169
|
const zodFromExample = (example) => {
|
|
@@ -135,3 +174,414 @@ const zodFromExample = (example) => {
|
|
|
135
174
|
return zod_1.z.object(shape);
|
|
136
175
|
};
|
|
137
176
|
exports.zodFromExample = zodFromExample;
|
|
177
|
+
const toolContextStorage = new async_hooks_1.AsyncLocalStorage();
|
|
178
|
+
const runWithToolContext = (context, fn) => toolContextStorage.run(context, fn);
|
|
179
|
+
exports.runWithToolContext = runWithToolContext;
|
|
180
|
+
const ctx = () => {
|
|
181
|
+
const store = toolContextStorage.getStore();
|
|
182
|
+
if (!store)
|
|
183
|
+
throw new Error('[common-ai] Tool executed outside runWithToolContext');
|
|
184
|
+
return store;
|
|
185
|
+
};
|
|
186
|
+
function mapToJson(data) {
|
|
187
|
+
if (data instanceof Map) {
|
|
188
|
+
const obj = {};
|
|
189
|
+
for (const [key, value] of data.entries())
|
|
190
|
+
obj[String(key)] = mapToJson(value);
|
|
191
|
+
return obj;
|
|
192
|
+
}
|
|
193
|
+
if (Array.isArray(data))
|
|
194
|
+
return data.map(mapToJson);
|
|
195
|
+
if (data !== null && typeof data === 'object') {
|
|
196
|
+
const obj = {};
|
|
197
|
+
for (const key of Object.keys(data))
|
|
198
|
+
obj[key] = mapToJson(data[key]);
|
|
199
|
+
return obj;
|
|
200
|
+
}
|
|
201
|
+
return data;
|
|
202
|
+
}
|
|
203
|
+
exports.tools = {
|
|
204
|
+
// ── CLUSTER CONFIG ───────────────────────────────────────────────────────
|
|
205
|
+
get_node_data: (0, ai_1.tool)({
|
|
206
|
+
description: 'Returns configuration info about all Kubernetes nodes (name, IP). Configuration only — not workload or usage data.',
|
|
207
|
+
inputSchema: zod_1.z.object({}),
|
|
208
|
+
execute: async () => { ctx().trace('get_node_data', {}); return mapToJson(ctx().nodes); }
|
|
209
|
+
}),
|
|
210
|
+
get_cluster_data: (0, ai_1.tool)({
|
|
211
|
+
description: 'Returns general cluster info: name, flavour (AKS/EKS/GKE/k3s/k3d), total vCPUs, total memory, node count and readiness status.',
|
|
212
|
+
inputSchema: zod_1.z.object({}),
|
|
213
|
+
execute: async () => {
|
|
214
|
+
var _a;
|
|
215
|
+
ctx().trace('get_cluster_data', {});
|
|
216
|
+
try {
|
|
217
|
+
const resp = await ctx().clusterInfo.coreApi.listNode();
|
|
218
|
+
return { name: ctx().clusterInfo.name, flavour: ctx().clusterInfo.flavour, vcpus: ctx().clusterInfo.vcpus, memoryGB: Math.round(ctx().clusterInfo.memory / 1024 / 1024 / 1024 * 100) / 100, nodeCount: resp.items.length, nodes: resp.items.map((n) => { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k; return ({ name: (_a = n.metadata) === null || _a === void 0 ? void 0 : _a.name, cpu: (_c = (_b = n.status) === null || _b === void 0 ? void 0 : _b.capacity) === null || _c === void 0 ? void 0 : _c['cpu'], memoryKi: (_e = (_d = n.status) === null || _d === void 0 ? void 0 : _d.capacity) === null || _e === void 0 ? void 0 : _e['memory'], ready: ((_h = (_g = (_f = n.status) === null || _f === void 0 ? void 0 : _f.conditions) === null || _g === void 0 ? void 0 : _g.find((c) => c.type === 'Ready')) === null || _h === void 0 ? void 0 : _h.status) === 'True', unschedulable: (_k = (_j = n.spec) === null || _j === void 0 ? void 0 : _j.unschedulable) !== null && _k !== void 0 ? _k : false }); }) };
|
|
219
|
+
}
|
|
220
|
+
catch (err) {
|
|
221
|
+
return { error: (_a = err.message) !== null && _a !== void 0 ? _a : String(err) };
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}),
|
|
225
|
+
get_workload_data: (0, ai_1.tool)({
|
|
226
|
+
description: 'Returns all workloads in the cluster: deployments, statefulsets, daemonsets, pods and services. Optionally filter by namespace.',
|
|
227
|
+
inputSchema: zod_1.z.object({ namespace: zod_1.z.string().optional().describe('Namespace to filter results (omit for all namespaces)') }),
|
|
228
|
+
execute: async ({ namespace }) => {
|
|
229
|
+
var _a;
|
|
230
|
+
ctx().trace('get_workload_data', { namespace: namespace !== null && namespace !== void 0 ? namespace : '*' });
|
|
231
|
+
try {
|
|
232
|
+
const c = ctx().clusterInfo;
|
|
233
|
+
const [d, s, ds, p, svc] = await Promise.all([
|
|
234
|
+
namespace ? c.appsApi.listNamespacedDeployment({ namespace }) : c.appsApi.listDeploymentForAllNamespaces(),
|
|
235
|
+
namespace ? c.appsApi.listNamespacedStatefulSet({ namespace }) : c.appsApi.listStatefulSetForAllNamespaces(),
|
|
236
|
+
namespace ? c.appsApi.listNamespacedDaemonSet({ namespace }) : c.appsApi.listDaemonSetForAllNamespaces(),
|
|
237
|
+
namespace ? c.coreApi.listNamespacedPod({ namespace }) : c.coreApi.listPodForAllNamespaces(),
|
|
238
|
+
namespace ? c.coreApi.listNamespacedService({ namespace }) : c.coreApi.listServiceForAllNamespaces()
|
|
239
|
+
]);
|
|
240
|
+
return {
|
|
241
|
+
deployments: d.items.map((x) => { var _a, _b, _c, _d, _e, _f, _g; return ({ name: (_a = x.metadata) === null || _a === void 0 ? void 0 : _a.name, namespace: (_b = x.metadata) === null || _b === void 0 ? void 0 : _b.namespace, replicas: (_c = x.spec) === null || _c === void 0 ? void 0 : _c.replicas, readyReplicas: (_e = (_d = x.status) === null || _d === void 0 ? void 0 : _d.readyReplicas) !== null && _e !== void 0 ? _e : 0, availableReplicas: (_g = (_f = x.status) === null || _f === void 0 ? void 0 : _f.availableReplicas) !== null && _g !== void 0 ? _g : 0 }); }),
|
|
242
|
+
statefulSets: s.items.map((x) => { var _a, _b, _c, _d, _e; return ({ name: (_a = x.metadata) === null || _a === void 0 ? void 0 : _a.name, namespace: (_b = x.metadata) === null || _b === void 0 ? void 0 : _b.namespace, replicas: (_c = x.spec) === null || _c === void 0 ? void 0 : _c.replicas, readyReplicas: (_e = (_d = x.status) === null || _d === void 0 ? void 0 : _d.readyReplicas) !== null && _e !== void 0 ? _e : 0 }); }),
|
|
243
|
+
daemonSets: ds.items.map((x) => { var _a, _b, _c, _d; return ({ name: (_a = x.metadata) === null || _a === void 0 ? void 0 : _a.name, namespace: (_b = x.metadata) === null || _b === void 0 ? void 0 : _b.namespace, desired: (_c = x.status) === null || _c === void 0 ? void 0 : _c.desiredNumberScheduled, ready: (_d = x.status) === null || _d === void 0 ? void 0 : _d.numberReady }); }),
|
|
244
|
+
pods: p.items.map((x) => { var _a, _b, _c, _d, _e, _f, _g; return ({ name: (_a = x.metadata) === null || _a === void 0 ? void 0 : _a.name, namespace: (_b = x.metadata) === null || _b === void 0 ? void 0 : _b.namespace, nodeName: (_c = x.spec) === null || _c === void 0 ? void 0 : _c.nodeName, phase: (_d = x.status) === null || _d === void 0 ? void 0 : _d.phase, ready: ((_g = (_f = (_e = x.status) === null || _e === void 0 ? void 0 : _e.conditions) === null || _f === void 0 ? void 0 : _f.find((c) => c.type === 'Ready')) === null || _g === void 0 ? void 0 : _g.status) === 'True' }); }),
|
|
245
|
+
services: svc.items.map((x) => { var _a, _b, _c, _d; return ({ name: (_a = x.metadata) === null || _a === void 0 ? void 0 : _a.name, namespace: (_b = x.metadata) === null || _b === void 0 ? void 0 : _b.namespace, type: (_c = x.spec) === null || _c === void 0 ? void 0 : _c.type, clusterIP: (_d = x.spec) === null || _d === void 0 ? void 0 : _d.clusterIP }); })
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
catch (err) {
|
|
249
|
+
return { error: (_a = err.message) !== null && _a !== void 0 ? _a : String(err) };
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}),
|
|
253
|
+
get_space_data: (0, ai_1.tool)({
|
|
254
|
+
description: 'Returns all resources in a specific Kubernetes namespace: pods (with restart count), deployments, services, configmap names.',
|
|
255
|
+
inputSchema: zod_1.z.object({ namespace: zod_1.z.string().describe('Name of the namespace to retrieve data for') }),
|
|
256
|
+
execute: async ({ namespace }) => {
|
|
257
|
+
var _a;
|
|
258
|
+
ctx().trace('get_space_data', { namespace });
|
|
259
|
+
try {
|
|
260
|
+
const c = ctx().clusterInfo;
|
|
261
|
+
const [p, d, s, cm] = await Promise.all([c.coreApi.listNamespacedPod({ namespace }), c.appsApi.listNamespacedDeployment({ namespace }), c.coreApi.listNamespacedService({ namespace }), c.coreApi.listNamespacedConfigMap({ namespace })]);
|
|
262
|
+
return {
|
|
263
|
+
namespace,
|
|
264
|
+
pods: p.items.map((x) => { var _a, _b, _c, _d, _e, _f, _g, _h, _j; return ({ name: (_a = x.metadata) === null || _a === void 0 ? void 0 : _a.name, phase: (_b = x.status) === null || _b === void 0 ? void 0 : _b.phase, nodeName: (_c = x.spec) === null || _c === void 0 ? void 0 : _c.nodeName, ready: ((_f = (_e = (_d = x.status) === null || _d === void 0 ? void 0 : _d.conditions) === null || _e === void 0 ? void 0 : _e.find((c) => c.type === 'Ready')) === null || _f === void 0 ? void 0 : _f.status) === 'True', restartCount: (_j = (_h = (_g = x.status) === null || _g === void 0 ? void 0 : _g.containerStatuses) === null || _h === void 0 ? void 0 : _h.reduce((sum, cs) => sum + cs.restartCount, 0)) !== null && _j !== void 0 ? _j : 0 }); }),
|
|
265
|
+
deployments: d.items.map((x) => { var _a, _b, _c, _d, _e, _f, _g, _h, _j; return ({ name: (_a = x.metadata) === null || _a === void 0 ? void 0 : _a.name, replicas: (_b = x.spec) === null || _b === void 0 ? void 0 : _b.replicas, readyReplicas: (_d = (_c = x.status) === null || _c === void 0 ? void 0 : _c.readyReplicas) !== null && _d !== void 0 ? _d : 0, image: (_j = (_h = (_g = (_f = (_e = x.spec) === null || _e === void 0 ? void 0 : _e.template) === null || _f === void 0 ? void 0 : _f.spec) === null || _g === void 0 ? void 0 : _g.containers) === null || _h === void 0 ? void 0 : _h[0]) === null || _j === void 0 ? void 0 : _j.image }); }),
|
|
266
|
+
services: s.items.map((x) => { var _a, _b, _c; return ({ name: (_a = x.metadata) === null || _a === void 0 ? void 0 : _a.name, type: (_b = x.spec) === null || _b === void 0 ? void 0 : _b.type, clusterIP: (_c = x.spec) === null || _c === void 0 ? void 0 : _c.clusterIP }); }),
|
|
267
|
+
configMaps: cm.items.map((x) => { var _a; return (_a = x.metadata) === null || _a === void 0 ? void 0 : _a.name; })
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
catch (err) {
|
|
271
|
+
return { error: (_a = err.message) !== null && _a !== void 0 ? _a : String(err) };
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}),
|
|
275
|
+
// ── CURRENT USAGE ────────────────────────────────────────────────────────
|
|
276
|
+
get_cluster_usage: (0, ai_1.tool)({
|
|
277
|
+
description: 'Returns current overall cluster resource usage: CPU%, memory%, network Mbps, total vCPUs and total memory GB.',
|
|
278
|
+
inputSchema: zod_1.z.object({}),
|
|
279
|
+
execute: async () => {
|
|
280
|
+
ctx().trace('get_cluster_usage', {});
|
|
281
|
+
if (ctx().clusterMetrics.length === 0)
|
|
282
|
+
return { error: 'No metrics available yet' };
|
|
283
|
+
const latest = ctx().clusterMetrics[ctx().clusterMetrics.length - 1];
|
|
284
|
+
return { vcpus: latest.cluster.vcpus, memoryGB: Math.round(latest.cluster.memory / 1024 / 1024 / 1024 * 100) / 100, cpuUsagePercent: Math.round(latest.cluster.cpuUsage * 100) / 100, memoryUsagePercent: Math.round(latest.cluster.memoryUsage * 100) / 100, networkTxMbps: Math.round(latest.cluster.txmbps * 100) / 100, networkRxMbps: Math.round(latest.cluster.rxmbps * 100) / 100, metricsIntervalSeconds: latest.metricsInterval };
|
|
285
|
+
}
|
|
286
|
+
}),
|
|
287
|
+
get_node_usage: (0, ai_1.tool)({
|
|
288
|
+
description: 'Returns current CPU and memory usage for one node or all nodes from the latest metrics reading.',
|
|
289
|
+
inputSchema: zod_1.z.object({ nodeName: zod_1.z.string().optional().describe('Node name to filter (omit for all nodes)') }),
|
|
290
|
+
execute: async ({ nodeName }) => {
|
|
291
|
+
ctx().trace('get_node_usage', { nodeName: nodeName !== null && nodeName !== void 0 ? nodeName : '*' });
|
|
292
|
+
if (ctx().clusterMetrics.length === 0)
|
|
293
|
+
return { error: 'No metrics available yet' };
|
|
294
|
+
const latest = ctx().clusterMetrics[ctx().clusterMetrics.length - 1];
|
|
295
|
+
const nodes = nodeName ? latest.nodes.filter((n) => n.name === nodeName) : latest.nodes;
|
|
296
|
+
return nodes.map((n) => { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q; return ({ name: n.name, cpuMillicores: Math.round(((_c = (_b = (_a = n.summary) === null || _a === void 0 ? void 0 : _a.cpu) === null || _b === void 0 ? void 0 : _b.usageNanoCores) !== null && _c !== void 0 ? _c : 0) / 1000000), memoryMB: Math.round(((_f = (_e = (_d = n.summary) === null || _d === void 0 ? void 0 : _d.memory) === null || _e === void 0 ? void 0 : _e.workingSetBytes) !== null && _f !== void 0 ? _f : 0) / 1024 / 1024), networkRxMB: Math.round(((_j = (_h = (_g = n.summary) === null || _g === void 0 ? void 0 : _g.network) === null || _h === void 0 ? void 0 : _h.rxBytes) !== null && _j !== void 0 ? _j : 0) / 1024 / 1024), networkTxMB: Math.round(((_m = (_l = (_k = n.summary) === null || _k === void 0 ? void 0 : _k.network) === null || _l === void 0 ? void 0 : _l.txBytes) !== null && _m !== void 0 ? _m : 0) / 1024 / 1024), podCount: (_q = (_p = (_o = n.summary) === null || _o === void 0 ? void 0 : _o.pods) === null || _p === void 0 ? void 0 : _p.length) !== null && _q !== void 0 ? _q : 0, timestamp: n.timestamp }); });
|
|
297
|
+
}
|
|
298
|
+
}),
|
|
299
|
+
get_deployment_usage: (0, ai_1.tool)({
|
|
300
|
+
description: 'Returns current aggregated CPU and memory usage for all pods belonging to a specific deployment.',
|
|
301
|
+
inputSchema: zod_1.z.object({ namespace: zod_1.z.string().describe('Namespace of the deployment'), name: zod_1.z.string().describe('Name of the deployment') }),
|
|
302
|
+
execute: async ({ namespace, name }) => {
|
|
303
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
304
|
+
ctx().trace('get_deployment_usage', { namespace, name });
|
|
305
|
+
try {
|
|
306
|
+
if (ctx().clusterMetrics.length === 0)
|
|
307
|
+
return { error: 'No metrics available yet' };
|
|
308
|
+
const latest = ctx().clusterMetrics[ctx().clusterMetrics.length - 1];
|
|
309
|
+
const c = ctx().clusterInfo;
|
|
310
|
+
const deployResp = await c.appsApi.readNamespacedDeployment({ name, namespace });
|
|
311
|
+
const labelSelector = Object.entries((_c = (_b = (_a = deployResp.spec) === null || _a === void 0 ? void 0 : _a.selector) === null || _b === void 0 ? void 0 : _b.matchLabels) !== null && _c !== void 0 ? _c : {}).map(([k, v]) => `${k}=${v}`).join(',');
|
|
312
|
+
const podsResp = await c.coreApi.listNamespacedPod({ namespace, labelSelector });
|
|
313
|
+
const podNames = new Set(podsResp.items.map((p) => { var _a; return (_a = p.metadata) === null || _a === void 0 ? void 0 : _a.name; }));
|
|
314
|
+
let totalCpu = 0, totalMem = 0, podCount = 0;
|
|
315
|
+
for (const node of latest.nodes)
|
|
316
|
+
for (const pod of ((_e = (_d = node.summary) === null || _d === void 0 ? void 0 : _d.pods) !== null && _e !== void 0 ? _e : []))
|
|
317
|
+
if (((_f = pod.podRef) === null || _f === void 0 ? void 0 : _f.namespace) === namespace && podNames.has((_g = pod.podRef) === null || _g === void 0 ? void 0 : _g.name)) {
|
|
318
|
+
totalCpu += (_j = (_h = pod.cpu) === null || _h === void 0 ? void 0 : _h.usageNanoCores) !== null && _j !== void 0 ? _j : 0;
|
|
319
|
+
totalMem += (_l = (_k = pod.memory) === null || _k === void 0 ? void 0 : _k.workingSetBytes) !== null && _l !== void 0 ? _l : 0;
|
|
320
|
+
podCount++;
|
|
321
|
+
}
|
|
322
|
+
return { deployment: name, namespace, podCount, cpuMillicores: Math.round(totalCpu / 1000000), memoryMB: Math.round(totalMem / 1024 / 1024), timestamp: (_m = latest.nodes[0]) === null || _m === void 0 ? void 0 : _m.timestamp };
|
|
323
|
+
}
|
|
324
|
+
catch (err) {
|
|
325
|
+
return { error: (_o = err.message) !== null && _o !== void 0 ? _o : String(err) };
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}),
|
|
329
|
+
// ── HISTORICAL USAGE ─────────────────────────────────────────────────────
|
|
330
|
+
get_prev_cluster_usage: (0, ai_1.tool)({
|
|
331
|
+
description: 'Returns historical overall cluster usage over the last N metrics readings (CPU%, memory%, network Mbps).',
|
|
332
|
+
inputSchema: zod_1.z.object({ count: zod_1.z.number().optional().describe('Number of historical readings to return (default: 5)') }),
|
|
333
|
+
execute: async ({ count = 5 }) => {
|
|
334
|
+
ctx().trace('get_prev_cluster_usage', { count });
|
|
335
|
+
if (ctx().clusterMetrics.length === 0)
|
|
336
|
+
return { error: 'No metrics available yet' };
|
|
337
|
+
return ctx().clusterMetrics.slice(-count).map((r) => ({ vcpus: r.cluster.vcpus, memoryGB: Math.round(r.cluster.memory / 1024 / 1024 / 1024 * 100) / 100, cpuUsagePercent: Math.round(r.cluster.cpuUsage * 100) / 100, memoryUsagePercent: Math.round(r.cluster.memoryUsage * 100) / 100, networkTxMbps: Math.round(r.cluster.txmbps * 100) / 100, networkRxMbps: Math.round(r.cluster.rxmbps * 100) / 100 }));
|
|
338
|
+
}
|
|
339
|
+
}),
|
|
340
|
+
get_prev_node_usage: (0, ai_1.tool)({
|
|
341
|
+
description: 'Returns historical CPU and memory usage for one or all nodes over the last N metrics readings.',
|
|
342
|
+
inputSchema: zod_1.z.object({ nodeName: zod_1.z.string().optional().describe('Node name to filter (omit for all nodes)'), count: zod_1.z.number().optional().describe('Number of historical readings (default: 5)') }),
|
|
343
|
+
execute: async ({ nodeName, count = 5 }) => {
|
|
344
|
+
ctx().trace('get_prev_node_usage', { nodeName: nodeName !== null && nodeName !== void 0 ? nodeName : '*', count });
|
|
345
|
+
if (ctx().clusterMetrics.length === 0)
|
|
346
|
+
return { error: 'No metrics available yet' };
|
|
347
|
+
return ctx().clusterMetrics.slice(-count).map((r) => ({ nodes: (nodeName ? r.nodes.filter((n) => n.name === nodeName) : r.nodes).map((n) => { var _a, _b, _c, _d, _e, _f; return ({ name: n.name, cpuMillicores: Math.round(((_c = (_b = (_a = n.summary) === null || _a === void 0 ? void 0 : _a.cpu) === null || _b === void 0 ? void 0 : _b.usageNanoCores) !== null && _c !== void 0 ? _c : 0) / 1000000), memoryMB: Math.round(((_f = (_e = (_d = n.summary) === null || _d === void 0 ? void 0 : _d.memory) === null || _e === void 0 ? void 0 : _e.workingSetBytes) !== null && _f !== void 0 ? _f : 0) / 1024 / 1024), timestamp: n.timestamp }); }) }));
|
|
348
|
+
}
|
|
349
|
+
}),
|
|
350
|
+
get_prev_deployment_usage: (0, ai_1.tool)({
|
|
351
|
+
description: 'Returns historical aggregated CPU and memory usage for a deployment over the last N metrics readings.',
|
|
352
|
+
inputSchema: zod_1.z.object({ namespace: zod_1.z.string().describe('Namespace of the deployment'), name: zod_1.z.string().describe('Name of the deployment'), count: zod_1.z.number().optional().describe('Number of historical readings (default: 5)') }),
|
|
353
|
+
execute: async ({ namespace, name, count = 5 }) => {
|
|
354
|
+
var _a, _b, _c, _d;
|
|
355
|
+
ctx().trace('get_prev_deployment_usage', { namespace, name, count });
|
|
356
|
+
try {
|
|
357
|
+
if (ctx().clusterMetrics.length === 0)
|
|
358
|
+
return { error: 'No metrics available yet' };
|
|
359
|
+
const c = ctx().clusterInfo;
|
|
360
|
+
const deployResp = await c.appsApi.readNamespacedDeployment({ name, namespace });
|
|
361
|
+
const labelSelector = Object.entries((_c = (_b = (_a = deployResp.spec) === null || _a === void 0 ? void 0 : _a.selector) === null || _b === void 0 ? void 0 : _b.matchLabels) !== null && _c !== void 0 ? _c : {}).map(([k, v]) => `${k}=${v}`).join(',');
|
|
362
|
+
const podsResp = await c.coreApi.listNamespacedPod({ namespace, labelSelector });
|
|
363
|
+
const podNames = new Set(podsResp.items.map((p) => { var _a; return (_a = p.metadata) === null || _a === void 0 ? void 0 : _a.name; }));
|
|
364
|
+
return ctx().clusterMetrics.slice(-count).map((r) => { var _a, _b, _c, _d, _e, _f, _g, _h, _j; let cpu = 0, mem = 0, pc = 0; for (const node of r.nodes)
|
|
365
|
+
for (const pod of ((_b = (_a = node.summary) === null || _a === void 0 ? void 0 : _a.pods) !== null && _b !== void 0 ? _b : []))
|
|
366
|
+
if (((_c = pod.podRef) === null || _c === void 0 ? void 0 : _c.namespace) === namespace && podNames.has((_d = pod.podRef) === null || _d === void 0 ? void 0 : _d.name)) {
|
|
367
|
+
cpu += (_f = (_e = pod.cpu) === null || _e === void 0 ? void 0 : _e.usageNanoCores) !== null && _f !== void 0 ? _f : 0;
|
|
368
|
+
mem += (_h = (_g = pod.memory) === null || _g === void 0 ? void 0 : _g.workingSetBytes) !== null && _h !== void 0 ? _h : 0;
|
|
369
|
+
pc++;
|
|
370
|
+
} return { deployment: name, namespace, podCount: pc, cpuMillicores: Math.round(cpu / 1000000), memoryMB: Math.round(mem / 1024 / 1024), timestamp: (_j = r.nodes[0]) === null || _j === void 0 ? void 0 : _j.timestamp }; });
|
|
371
|
+
}
|
|
372
|
+
catch (err) {
|
|
373
|
+
return { error: (_d = err.message) !== null && _d !== void 0 ? _d : String(err) };
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}),
|
|
377
|
+
get_prev_space_data: (0, ai_1.tool)({
|
|
378
|
+
description: 'Returns historical aggregated CPU and memory usage for all pods in a namespace over the last N metrics readings.',
|
|
379
|
+
inputSchema: zod_1.z.object({ namespace: zod_1.z.string().describe('Namespace name'), count: zod_1.z.number().optional().describe('Number of historical readings (default: 5)') }),
|
|
380
|
+
execute: async ({ namespace, count = 5 }) => {
|
|
381
|
+
ctx().trace('get_prev_space_data', { namespace, count });
|
|
382
|
+
if (ctx().clusterMetrics.length === 0)
|
|
383
|
+
return { error: 'No metrics available yet' };
|
|
384
|
+
return ctx().clusterMetrics.slice(-count).map((r) => { var _a, _b, _c, _d, _e, _f, _g, _h; let cpu = 0, mem = 0, pc = 0; for (const node of r.nodes)
|
|
385
|
+
for (const pod of ((_b = (_a = node.summary) === null || _a === void 0 ? void 0 : _a.pods) !== null && _b !== void 0 ? _b : []))
|
|
386
|
+
if (((_c = pod.podRef) === null || _c === void 0 ? void 0 : _c.namespace) === namespace) {
|
|
387
|
+
cpu += (_e = (_d = pod.cpu) === null || _d === void 0 ? void 0 : _d.usageNanoCores) !== null && _e !== void 0 ? _e : 0;
|
|
388
|
+
mem += (_g = (_f = pod.memory) === null || _f === void 0 ? void 0 : _f.workingSetBytes) !== null && _g !== void 0 ? _g : 0;
|
|
389
|
+
pc++;
|
|
390
|
+
} return { namespace, podCount: pc, cpuMillicores: Math.round(cpu / 1000000), memoryMB: Math.round(mem / 1024 / 1024), timestamp: (_h = r.nodes[0]) === null || _h === void 0 ? void 0 : _h.timestamp }; });
|
|
391
|
+
}
|
|
392
|
+
}),
|
|
393
|
+
// ── CLUSTER ACTIONS ──────────────────────────────────────────────────────
|
|
394
|
+
add_node: (0, ai_1.tool)({
|
|
395
|
+
description: 'Adds a new agent node to the cluster. For k3d uses `k3d node create`. Cloud providers not yet implemented.',
|
|
396
|
+
inputSchema: zod_1.z.object({ nodeName: zod_1.z.string().optional().describe('Suffix for the new node name'), nodePoolName: zod_1.z.string().optional().describe('Node pool name (cloud provider specific, ignored for k3d)') }),
|
|
397
|
+
execute: async ({ nodeName, nodePoolName }) => {
|
|
398
|
+
var _a;
|
|
399
|
+
ctx().trace('add_node', { nodeName: nodeName !== null && nodeName !== void 0 ? nodeName : 'auto', nodePoolName: nodePoolName !== null && nodePoolName !== void 0 ? nodePoolName : 'default' });
|
|
400
|
+
if (ctx().clusterInfo.flavour === 'k3d') {
|
|
401
|
+
const suffix = nodeName !== null && nodeName !== void 0 ? nodeName : `agent-${Date.now()}`;
|
|
402
|
+
const clusterName = ctx().clusterInfo.name.replace(/^k3d-/, '');
|
|
403
|
+
try {
|
|
404
|
+
const { stdout, stderr } = await execAsync(`k3d node create ${suffix} --cluster ${clusterName} --role agent`, { timeout: 120000 });
|
|
405
|
+
return { success: true, message: `Node '${suffix}' added to cluster '${clusterName}'`, stdout, stderr };
|
|
406
|
+
}
|
|
407
|
+
catch (err) {
|
|
408
|
+
return { success: false, error: (_a = err.message) !== null && _a !== void 0 ? _a : String(err) };
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
return { success: false, message: `add_node not yet implemented for flavour '${ctx().clusterInfo.flavour}'` };
|
|
412
|
+
}
|
|
413
|
+
}),
|
|
414
|
+
remove_node: (0, ai_1.tool)({
|
|
415
|
+
description: 'Removes a node from the cluster (cordon + delete). For k3d uses `k3d node delete`. Cloud providers not yet implemented.',
|
|
416
|
+
inputSchema: zod_1.z.object({ nodeName: zod_1.z.string().describe('Name of the Kubernetes node to remove'), nodePoolName: zod_1.z.string().optional().describe('Node pool name (cloud provider specific, ignored for k3d)') }),
|
|
417
|
+
execute: async ({ nodeName, nodePoolName }) => {
|
|
418
|
+
var _a;
|
|
419
|
+
ctx().trace('remove_node', { nodeName, nodePoolName: nodePoolName !== null && nodePoolName !== void 0 ? nodePoolName : 'default' });
|
|
420
|
+
try {
|
|
421
|
+
await ctx().clusterInfo.coreApi.patchNode({ name: nodeName, body: [{ op: 'add', path: '/spec/unschedulable', value: true }] });
|
|
422
|
+
}
|
|
423
|
+
catch (_) { }
|
|
424
|
+
if (ctx().clusterInfo.flavour === 'k3d') {
|
|
425
|
+
try {
|
|
426
|
+
const { stdout, stderr } = await execAsync(`k3d node delete ${nodeName}`, { timeout: 60000 });
|
|
427
|
+
return { success: true, message: `Node '${nodeName}' removed`, stdout, stderr };
|
|
428
|
+
}
|
|
429
|
+
catch (err) {
|
|
430
|
+
return { success: false, error: (_a = err.message) !== null && _a !== void 0 ? _a : String(err) };
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
return { success: false, message: `remove_node not yet implemented for flavour '${ctx().clusterInfo.flavour}'` };
|
|
434
|
+
}
|
|
435
|
+
}),
|
|
436
|
+
stop_node: (0, ai_1.tool)({
|
|
437
|
+
description: 'Stops a running cluster node: cordons it then stops the container. For k3d uses `k3d node stop`.',
|
|
438
|
+
inputSchema: zod_1.z.object({ nodeName: zod_1.z.string().describe('Name of the Kubernetes node to stop') }),
|
|
439
|
+
execute: async ({ nodeName }) => {
|
|
440
|
+
var _a, _b;
|
|
441
|
+
ctx().trace('stop_node', { nodeName });
|
|
442
|
+
try {
|
|
443
|
+
await ctx().clusterInfo.coreApi.patchNode({ name: nodeName, body: [{ op: 'add', path: '/spec/unschedulable', value: true }] });
|
|
444
|
+
}
|
|
445
|
+
catch (err) {
|
|
446
|
+
return { success: false, error: `Failed to cordon node: ${(_a = err.message) !== null && _a !== void 0 ? _a : String(err)}` };
|
|
447
|
+
}
|
|
448
|
+
if (ctx().clusterInfo.flavour !== 'k3d')
|
|
449
|
+
return { success: false, message: `Node '${nodeName}' cordoned but stop only implemented for k3d` };
|
|
450
|
+
try {
|
|
451
|
+
const { stdout, stderr } = await execAsync(`k3d node stop ${nodeName}`, { timeout: 30000 });
|
|
452
|
+
return { success: true, message: `Node '${nodeName}' cordoned and stopped`, stdout, stderr };
|
|
453
|
+
}
|
|
454
|
+
catch (err) {
|
|
455
|
+
return { success: false, error: `Node cordoned but stop failed: ${(_b = err.message) !== null && _b !== void 0 ? _b : String(err)}` };
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}),
|
|
459
|
+
start_node: (0, ai_1.tool)({
|
|
460
|
+
description: 'Starts a previously stopped cluster node and uncordons it. For k3d uses `k3d node start`.',
|
|
461
|
+
inputSchema: zod_1.z.object({ nodeName: zod_1.z.string().describe('Name of the Kubernetes node to start') }),
|
|
462
|
+
execute: async ({ nodeName }) => {
|
|
463
|
+
var _a, _b;
|
|
464
|
+
ctx().trace('start_node', { nodeName });
|
|
465
|
+
if (ctx().clusterInfo.flavour === 'k3d') {
|
|
466
|
+
try {
|
|
467
|
+
const { stdout, stderr } = await execAsync(`k3d node start ${nodeName}`, { timeout: 30000 });
|
|
468
|
+
try {
|
|
469
|
+
await ctx().clusterInfo.coreApi.patchNode({ name: nodeName, body: [{ op: 'add', path: '/spec/unschedulable', value: false }] });
|
|
470
|
+
}
|
|
471
|
+
catch (_) { }
|
|
472
|
+
return { success: true, message: `Node '${nodeName}' started and uncordoned`, stdout, stderr };
|
|
473
|
+
}
|
|
474
|
+
catch (err) {
|
|
475
|
+
return { success: false, error: (_a = err.message) !== null && _a !== void 0 ? _a : String(err) };
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
try {
|
|
479
|
+
await ctx().clusterInfo.coreApi.patchNode({ name: nodeName, body: [{ op: 'add', path: '/spec/unschedulable', value: false }] });
|
|
480
|
+
return { success: false, message: `Node '${nodeName}' uncordoned but start only implemented for k3d` };
|
|
481
|
+
}
|
|
482
|
+
catch (err) {
|
|
483
|
+
return { success: false, error: (_b = err.message) !== null && _b !== void 0 ? _b : String(err) };
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}),
|
|
487
|
+
add_replica: (0, ai_1.tool)({
|
|
488
|
+
description: 'Scales up a deployment by adding one replica.',
|
|
489
|
+
inputSchema: zod_1.z.object({ namespace: zod_1.z.string().describe('Namespace of the deployment'), name: zod_1.z.string().describe('Name of the deployment') }),
|
|
490
|
+
execute: async ({ namespace, name }) => {
|
|
491
|
+
var _a, _b, _c;
|
|
492
|
+
ctx().trace('add_replica', { namespace, name });
|
|
493
|
+
try {
|
|
494
|
+
const c = ctx().clusterInfo;
|
|
495
|
+
const d = await c.appsApi.readNamespacedDeployment({ name, namespace });
|
|
496
|
+
const cur = (_b = (_a = d.spec) === null || _a === void 0 ? void 0 : _a.replicas) !== null && _b !== void 0 ? _b : 1;
|
|
497
|
+
await c.appsApi.patchNamespacedDeployment({ name, namespace, body: [{ op: 'replace', path: '/spec/replicas', value: cur + 1 }] });
|
|
498
|
+
return { success: true, message: `Deployment ${namespace}/${name} scaled from ${cur} to ${cur + 1} replicas` };
|
|
499
|
+
}
|
|
500
|
+
catch (err) {
|
|
501
|
+
return { success: false, error: (_c = err.message) !== null && _c !== void 0 ? _c : String(err) };
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}),
|
|
505
|
+
remove_replica: (0, ai_1.tool)({
|
|
506
|
+
description: 'Scales down a deployment by removing one replica. Minimum of 1 replica is enforced.',
|
|
507
|
+
inputSchema: zod_1.z.object({ namespace: zod_1.z.string().describe('Namespace of the deployment'), name: zod_1.z.string().describe('Name of the deployment') }),
|
|
508
|
+
execute: async ({ namespace, name }) => {
|
|
509
|
+
var _a, _b, _c;
|
|
510
|
+
ctx().trace('remove_replica', { namespace, name });
|
|
511
|
+
try {
|
|
512
|
+
const c = ctx().clusterInfo;
|
|
513
|
+
const d = await c.appsApi.readNamespacedDeployment({ name, namespace });
|
|
514
|
+
const cur = (_b = (_a = d.spec) === null || _a === void 0 ? void 0 : _a.replicas) !== null && _b !== void 0 ? _b : 1;
|
|
515
|
+
if (cur <= 1)
|
|
516
|
+
return { success: false, message: `Deployment ${namespace}/${name} already at minimum` };
|
|
517
|
+
await c.appsApi.patchNamespacedDeployment({ name, namespace, body: [{ op: 'replace', path: '/spec/replicas', value: cur - 1 }] });
|
|
518
|
+
return { success: true, message: `Deployment ${namespace}/${name} scaled from ${cur} to ${cur - 1} replicas` };
|
|
519
|
+
}
|
|
520
|
+
catch (err) {
|
|
521
|
+
return { success: false, error: (_c = err.message) !== null && _c !== void 0 ? _c : String(err) };
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
}),
|
|
525
|
+
// ── MISC ─────────────────────────────────────────────────────────────────
|
|
526
|
+
times_two: (0, ai_1.tool)({
|
|
527
|
+
description: 'Multiplies a number by two.',
|
|
528
|
+
inputSchema: zod_1.z.object({ data: zod_1.z.number() }),
|
|
529
|
+
execute: async ({ data }) => { ctx().trace('times_two', { data }); return data * 2; }
|
|
530
|
+
}),
|
|
531
|
+
father_of: (0, ai_1.tool)({
|
|
532
|
+
description: 'Returns the name of the father of a person.',
|
|
533
|
+
inputSchema: zod_1.z.object({ data: zod_1.z.string().describe('The name of the person whose father you want to discover') }),
|
|
534
|
+
execute: async ({ data }) => { ctx().trace('father_of', { data }); return 'Julio'; }
|
|
535
|
+
}),
|
|
536
|
+
get_certificate_info: (0, ai_1.tool)({
|
|
537
|
+
description: 'Connects to a hostname via HTTPS and returns TLS certificate details: subject, issuer, validity dates, SANs, fingerprint and whether it is currently valid.',
|
|
538
|
+
inputSchema: zod_1.z.object({ hostname: zod_1.z.string().describe('DNS name or IP to connect to'), port: zod_1.z.number().optional().describe('Port to connect to (default: 443)') }),
|
|
539
|
+
execute: async ({ hostname, port }) => {
|
|
540
|
+
ctx().trace('get_certificate_info', { hostname, port });
|
|
541
|
+
const targetPort = port !== null && port !== void 0 ? port : 443;
|
|
542
|
+
return new Promise((resolve) => {
|
|
543
|
+
const socket = tls.connect({ host: hostname, port: targetPort, servername: hostname, rejectUnauthorized: false }, () => {
|
|
544
|
+
var _a, _b;
|
|
545
|
+
try {
|
|
546
|
+
const cert = socket.getPeerCertificate(false);
|
|
547
|
+
socket.end();
|
|
548
|
+
if (!cert || !Object.keys(cert).length)
|
|
549
|
+
return resolve({ error: 'No certificate returned' });
|
|
550
|
+
const now = Date.now();
|
|
551
|
+
const validFrom = new Date(cert.valid_from);
|
|
552
|
+
const validTo = new Date(cert.valid_to);
|
|
553
|
+
resolve({ subject: cert.subject, issuer: cert.issuer, validFrom: cert.valid_from, validTo: cert.valid_to, daysUntilExpiry: Math.floor((validTo.getTime() - now) / 86400000), isCurrentlyValid: now >= validFrom.getTime() && now <= validTo.getTime(), subjectAltNames: (_a = cert.subjectaltname) !== null && _a !== void 0 ? _a : null, fingerprint: cert.fingerprint, serialNumber: cert.serialNumber, protocol: socket.getProtocol() });
|
|
554
|
+
}
|
|
555
|
+
catch (err) {
|
|
556
|
+
socket.end();
|
|
557
|
+
resolve({ error: (_b = err.message) !== null && _b !== void 0 ? _b : String(err) });
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
socket.setTimeout(5000, () => { socket.destroy(); resolve({ error: 'Connection timed out' }); });
|
|
561
|
+
socket.on('error', (err) => resolve({ error: err.message }));
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
}),
|
|
565
|
+
};
|
|
566
|
+
exports.toolInfoList = [
|
|
567
|
+
{ name: 'get_node_data', description: 'Returns configuration info about all Kubernetes nodes (name, IP). Configuration only — not workload or usage data.' },
|
|
568
|
+
{ name: 'get_cluster_data', description: 'Returns general cluster info: name, flavour (AKS/EKS/GKE/k3s/k3d), total vCPUs, total memory, node count and readiness status.' },
|
|
569
|
+
{ name: 'get_workload_data', description: 'Returns all workloads in the cluster: deployments, statefulsets, daemonsets, pods and services. Optionally filter by namespace.' },
|
|
570
|
+
{ name: 'get_space_data', description: 'Returns all resources in a specific Kubernetes namespace: pods (with restart count), deployments, services, configmap names.' },
|
|
571
|
+
{ name: 'get_cluster_usage', description: 'Returns current overall cluster resource usage: CPU%, memory%, network Mbps, total vCPUs and total memory GB.' },
|
|
572
|
+
{ name: 'get_node_usage', description: 'Returns current CPU and memory usage for one node or all nodes from the latest metrics reading.' },
|
|
573
|
+
{ name: 'get_deployment_usage', description: 'Returns current aggregated CPU and memory usage for all pods belonging to a specific deployment.' },
|
|
574
|
+
{ name: 'get_prev_cluster_usage', description: 'Returns historical overall cluster usage over the last N metrics readings (CPU%, memory%, network Mbps).' },
|
|
575
|
+
{ name: 'get_prev_node_usage', description: 'Returns historical CPU and memory usage for one or all nodes over the last N metrics readings.' },
|
|
576
|
+
{ name: 'get_prev_deployment_usage', description: 'Returns historical aggregated CPU and memory usage for a deployment over the last N metrics readings.' },
|
|
577
|
+
{ name: 'get_prev_space_data', description: 'Returns historical aggregated CPU and memory usage for all pods in a namespace over the last N metrics readings.' },
|
|
578
|
+
{ name: 'add_node', description: 'Adds a new agent node to the cluster. For k3d uses `k3d node create`. Cloud providers not yet implemented.' },
|
|
579
|
+
{ name: 'remove_node', description: 'Removes a node from the cluster (cordon + delete). For k3d uses `k3d node delete`. Cloud providers not yet implemented.' },
|
|
580
|
+
{ name: 'stop_node', description: 'Stops a running cluster node: cordons it then stops the container. For k3d uses `k3d node stop`.' },
|
|
581
|
+
{ name: 'start_node', description: 'Starts a previously stopped cluster node and uncordons it. For k3d uses `k3d node start`.' },
|
|
582
|
+
{ name: 'add_replica', description: 'Scales up a deployment by adding one replica.' },
|
|
583
|
+
{ name: 'remove_replica', description: 'Scales down a deployment by removing one replica. Minimum of 1 replica is enforced.' },
|
|
584
|
+
{ name: 'times_two', description: 'Multiplies a number by two.' },
|
|
585
|
+
{ name: 'father_of', description: 'Returns the name of the father of a person.' },
|
|
586
|
+
{ name: 'get_certificate_info', description: 'Connects to a hostname via HTTPS and returns TLS certificate details: subject, issuer, validity dates, SANs, fingerprint and whether it is currently valid.' },
|
|
587
|
+
];
|
package/dist/front.d.ts
CHANGED
|
@@ -21,4 +21,15 @@ interface IAiConfigProviderProps {
|
|
|
21
21
|
onClose: (providers: ILlmProvider[] | undefined) => void;
|
|
22
22
|
}
|
|
23
23
|
declare const AiConfigProvider: React.FC<IAiConfigProviderProps>;
|
|
24
|
-
|
|
24
|
+
interface IToolSelectorProps {
|
|
25
|
+
tools: {
|
|
26
|
+
name: string;
|
|
27
|
+
description: string;
|
|
28
|
+
}[];
|
|
29
|
+
selected: string[];
|
|
30
|
+
autoTools: boolean;
|
|
31
|
+
disabled?: boolean;
|
|
32
|
+
onChange: (selected: string[], autoTools: boolean) => void;
|
|
33
|
+
}
|
|
34
|
+
declare const ToolSelector: React.FC<IToolSelectorProps>;
|
|
35
|
+
export { LlmSelector, AiConfigLlm, AiConfigProvider, ToolSelector };
|
package/dist/front.js
CHANGED
|
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.AiConfigProvider = exports.AiConfigLlm = exports.LlmSelector = void 0;
|
|
36
|
+
exports.ToolSelector = exports.AiConfigProvider = exports.AiConfigLlm = exports.LlmSelector = void 0;
|
|
37
37
|
const react_1 = __importStar(require("react"));
|
|
38
38
|
const material_1 = require("@mui/material");
|
|
39
39
|
const icons_material_1 = require("@mui/icons-material");
|
|
@@ -136,7 +136,10 @@ const AiConfigLlm = (props) => {
|
|
|
136
136
|
react_1.default.createElement(material_1.TextField, { value: id, onChange: e => setId(e.target.value), placeholder: 'Enter LLM id', label: 'LLM ID', variant: 'standard', fullWidth: true }),
|
|
137
137
|
react_1.default.createElement(material_1.FormControl, { variant: 'standard', sx: { width: '100%' } },
|
|
138
138
|
react_1.default.createElement(material_1.InputLabel, null, "Provider"),
|
|
139
|
-
react_1.default.createElement(material_1.Select, { value: provider, onChange: e => { setProvider(e.target.value); setModel(''); }, variant: 'standard', fullWidth: true }, props.providers.map(p =>
|
|
139
|
+
react_1.default.createElement(material_1.Select, { value: provider, onChange: e => { setProvider(e.target.value); setModel(''); }, variant: 'standard', fullWidth: true }, props.providers.map(p => {
|
|
140
|
+
var _a;
|
|
141
|
+
return (react_1.default.createElement(material_1.MenuItem, { key: p.name, value: p.name, disabled: !((_a = p.models) === null || _a === void 0 ? void 0 : _a.length) }, p.name));
|
|
142
|
+
}))),
|
|
140
143
|
react_1.default.createElement(material_1.FormControl, { variant: 'standard', sx: { width: '100%' } },
|
|
141
144
|
react_1.default.createElement(material_1.InputLabel, null, "Model"),
|
|
142
145
|
react_1.default.createElement(material_1.Select, { value: model, onChange: e => setModel(e.target.value), variant: 'standard', fullWidth: true, displayEmpty: true }, (_a = props.providers.find(p => p.name === provider)) === null || _a === void 0 ? void 0 : _a.models.map((m, i) => (react_1.default.createElement(material_1.MenuItem, { key: i, value: m.id }, m.name))))),
|
|
@@ -258,3 +261,13 @@ const AiConfigProvider = (props) => {
|
|
|
258
261
|
react_1.default.createElement(material_1.Button, { onClick: () => props.onClose(undefined), color: 'inherit' }, "Cancel"))));
|
|
259
262
|
};
|
|
260
263
|
exports.AiConfigProvider = AiConfigProvider;
|
|
264
|
+
const ToolSelector = ({ tools, selected, autoTools, disabled, onChange }) => (react_1.default.createElement(material_1.Stack, { direction: 'row', alignItems: 'flex-end', spacing: 1, sx: { width: '100%' } },
|
|
265
|
+
react_1.default.createElement(material_1.FormControlLabel, { control: react_1.default.createElement(material_1.Switch, { size: 'small', checked: autoTools, onChange: e => onChange(selected, e.target.checked), disabled: disabled }), label: react_1.default.createElement(material_1.Typography, { variant: 'caption' }, "Auto"), sx: { ml: 1, mr: 0, flexShrink: 0 } }),
|
|
266
|
+
react_1.default.createElement(material_1.FormControl, { variant: 'standard', fullWidth: true },
|
|
267
|
+
react_1.default.createElement(material_1.InputLabel, null, "Tools"),
|
|
268
|
+
react_1.default.createElement(material_1.Select, { multiple: true, value: selected, onChange: e => onChange(e.target.value, autoTools), renderValue: sel => autoTools ? `all (${tools.length})` : sel.join(', '), variant: 'standard', disabled: disabled || autoTools }, tools.map(t => (react_1.default.createElement(material_1.MenuItem, { key: t.name, value: t.name },
|
|
269
|
+
react_1.default.createElement(material_1.Checkbox, { size: 'small', checked: selected.includes(t.name) }),
|
|
270
|
+
react_1.default.createElement(material_1.Stack, { direction: 'column' },
|
|
271
|
+
react_1.default.createElement(material_1.Typography, { variant: 'body2' }, t.name),
|
|
272
|
+
t.description && react_1.default.createElement(material_1.Typography, { variant: 'caption', color: 'text.secondary', sx: { fontSize: '0.65rem' } }, t.description)))))))));
|
|
273
|
+
exports.ToolSelector = ToolSelector;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kwirthmagnify/kwirth-common-ai",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.16",
|
|
4
4
|
"description": "Shared AI/LLM types and utilities for Kwirth AI plugins",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "del .\\dist\\* /s /q 2>nul & tsc"
|
|
@@ -12,9 +12,21 @@
|
|
|
12
12
|
"module": "dist/index.js",
|
|
13
13
|
"types": "dist/index.d.ts",
|
|
14
14
|
"exports": {
|
|
15
|
-
".": {
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"require": "./dist/index.js",
|
|
18
|
+
"default": "./dist/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./back": {
|
|
21
|
+
"types": "./dist/back.d.ts",
|
|
22
|
+
"require": "./dist/back.js",
|
|
23
|
+
"default": "./dist/back.js"
|
|
24
|
+
},
|
|
25
|
+
"./front": {
|
|
26
|
+
"types": "./dist/front.d.ts",
|
|
27
|
+
"require": "./dist/front.js",
|
|
28
|
+
"default": "./dist/front.js"
|
|
29
|
+
}
|
|
18
30
|
},
|
|
19
31
|
"typesVersions": {
|
|
20
32
|
"*": {
|
|
@@ -44,8 +56,8 @@
|
|
|
44
56
|
"@kwirthmagnify/kwirth-common": "^0.5.1",
|
|
45
57
|
"@mui/icons-material": "7.1.2",
|
|
46
58
|
"@mui/material": "7.1.2",
|
|
59
|
+
"@types/node": "^25.9.1",
|
|
47
60
|
"@types/react": "^18.3.0",
|
|
48
|
-
"react": "^18.3.0",
|
|
49
61
|
"typescript": "^5.4.0"
|
|
50
62
|
}
|
|
51
63
|
}
|