@vfarcic/dot-ai 0.195.0 → 1.0.0
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/README.md +2 -7
- package/dist/core/capability-scan-workflow.d.ts +4 -3
- package/dist/core/capability-scan-workflow.d.ts.map +1 -1
- package/dist/core/capability-scan-workflow.js +34 -39
- package/dist/core/circuit-breaker.d.ts +1 -0
- package/dist/core/circuit-breaker.d.ts.map +1 -1
- package/dist/core/circuit-breaker.js +11 -3
- package/dist/core/command-executor.d.ts +10 -1
- package/dist/core/command-executor.d.ts.map +1 -1
- package/dist/core/command-executor.js +63 -48
- package/dist/core/crd-availability.d.ts +6 -1
- package/dist/core/crd-availability.d.ts.map +1 -1
- package/dist/core/crd-availability.js +59 -49
- package/dist/core/deploy-operation.d.ts +17 -3
- package/dist/core/deploy-operation.d.ts.map +1 -1
- package/dist/core/deploy-operation.js +72 -21
- package/dist/core/discovery.d.ts +16 -43
- package/dist/core/discovery.d.ts.map +1 -1
- package/dist/core/discovery.js +128 -277
- package/dist/core/index.d.ts +10 -2
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +20 -9
- package/dist/core/pattern-operations.d.ts +3 -1
- package/dist/core/pattern-operations.d.ts.map +1 -1
- package/dist/core/pattern-operations.js +3 -2
- package/dist/core/plugin-client.d.ts +53 -0
- package/dist/core/plugin-client.d.ts.map +1 -0
- package/dist/core/plugin-client.js +148 -0
- package/dist/core/plugin-manager.d.ts +119 -0
- package/dist/core/plugin-manager.d.ts.map +1 -0
- package/dist/core/plugin-manager.js +366 -0
- package/dist/core/plugin-types.d.ts +100 -0
- package/dist/core/plugin-types.d.ts.map +1 -0
- package/dist/core/plugin-types.js +10 -0
- package/dist/core/policy-operations.d.ts +15 -7
- package/dist/core/policy-operations.d.ts.map +1 -1
- package/dist/core/policy-operations.js +59 -31
- package/dist/core/resource-tools.d.ts +2 -90
- package/dist/core/resource-tools.d.ts.map +1 -1
- package/dist/core/resource-tools.js +4 -178
- package/dist/core/schema.d.ts +18 -2
- package/dist/core/schema.d.ts.map +1 -1
- package/dist/core/schema.js +118 -16
- package/dist/core/telemetry/client.d.ts +7 -0
- package/dist/core/telemetry/client.d.ts.map +1 -1
- package/dist/core/telemetry/client.js +51 -51
- package/dist/core/telemetry/index.d.ts +1 -1
- package/dist/core/telemetry/index.d.ts.map +1 -1
- package/dist/core/telemetry/index.js +2 -1
- package/dist/core/telemetry/types.d.ts +1 -3
- package/dist/core/telemetry/types.d.ts.map +1 -1
- package/dist/core/tracing/index.d.ts +0 -1
- package/dist/core/tracing/index.d.ts.map +1 -1
- package/dist/core/tracing/index.js +1 -4
- package/dist/core/unified-creation-session.d.ts +6 -1
- package/dist/core/unified-creation-session.d.ts.map +1 -1
- package/dist/core/unified-creation-session.js +19 -11
- package/dist/interfaces/mcp.d.ts +8 -2
- package/dist/interfaces/mcp.d.ts.map +1 -1
- package/dist/interfaces/mcp.js +85 -34
- package/dist/interfaces/resource-sync-handler.d.ts.map +1 -1
- package/dist/interfaces/resource-sync-handler.js +37 -17
- package/dist/interfaces/rest-api.d.ts +4 -1
- package/dist/interfaces/rest-api.d.ts.map +1 -1
- package/dist/interfaces/rest-api.js +172 -49
- package/dist/mcp/server.js +39 -54
- package/dist/tools/deploy-manifests.d.ts +3 -1
- package/dist/tools/deploy-manifests.d.ts.map +1 -1
- package/dist/tools/deploy-manifests.js +112 -13
- package/dist/tools/generate-manifests.d.ts +3 -1
- package/dist/tools/generate-manifests.d.ts.map +1 -1
- package/dist/tools/generate-manifests.js +107 -33
- package/dist/tools/operate-analysis.d.ts +5 -1
- package/dist/tools/operate-analysis.d.ts.map +1 -1
- package/dist/tools/operate-analysis.js +37 -7
- package/dist/tools/operate-execution.d.ts +3 -1
- package/dist/tools/operate-execution.d.ts.map +1 -1
- package/dist/tools/operate-execution.js +6 -4
- package/dist/tools/operate.d.ts +7 -2
- package/dist/tools/operate.d.ts.map +1 -1
- package/dist/tools/operate.js +10 -6
- package/dist/tools/organizational-data.d.ts +3 -2
- package/dist/tools/organizational-data.d.ts.map +1 -1
- package/dist/tools/organizational-data.js +15 -13
- package/dist/tools/query.d.ts +5 -1
- package/dist/tools/query.d.ts.map +1 -1
- package/dist/tools/query.js +26 -18
- package/dist/tools/recommend.d.ts +3 -1
- package/dist/tools/recommend.d.ts.map +1 -1
- package/dist/tools/recommend.js +7 -7
- package/dist/tools/remediate.d.ts +5 -2
- package/dist/tools/remediate.d.ts.map +1 -1
- package/dist/tools/remediate.js +69 -20
- package/dist/tools/version.d.ts +20 -5
- package/dist/tools/version.d.ts.map +1 -1
- package/dist/tools/version.js +169 -161
- package/package.json +1 -1
- package/prompts/helm-generation.md +9 -0
- package/dist/core/cluster-utils.d.ts +0 -12
- package/dist/core/cluster-utils.d.ts.map +0 -1
- package/dist/core/cluster-utils.js +0 -27
- package/dist/core/helm-utils.d.ts +0 -66
- package/dist/core/helm-utils.d.ts.map +0 -1
- package/dist/core/helm-utils.js +0 -196
- package/dist/core/kubectl-tools.d.ts +0 -71
- package/dist/core/kubectl-tools.d.ts.map +0 -1
- package/dist/core/kubectl-tools.js +0 -546
- package/dist/core/kubernetes-utils.d.ts +0 -38
- package/dist/core/kubernetes-utils.d.ts.map +0 -1
- package/dist/core/kubernetes-utils.js +0 -288
- package/dist/core/tracing/k8s-tracing.d.ts +0 -57
- package/dist/core/tracing/k8s-tracing.d.ts.map +0 -1
- package/dist/core/tracing/k8s-tracing.js +0 -155
- package/scripts/toolhive.nu +0 -21
|
@@ -1,288 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Shared Kubernetes Utilities
|
|
4
|
-
*
|
|
5
|
-
* Common functions for interacting with Kubernetes clusters
|
|
6
|
-
*/
|
|
7
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
-
exports.ErrorClassifier = void 0;
|
|
9
|
-
exports.executeKubectl = executeKubectl;
|
|
10
|
-
exports.buildKubectlCommand = buildKubectlCommand;
|
|
11
|
-
const child_process_1 = require("child_process");
|
|
12
|
-
const util_1 = require("util");
|
|
13
|
-
const tracing_1 = require("./tracing");
|
|
14
|
-
const constants_1 = require("./constants");
|
|
15
|
-
const execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
16
|
-
/**
|
|
17
|
-
* Simple semaphore to limit concurrent kubectl executions
|
|
18
|
-
* Prevents overwhelming the Kubernetes API with too many parallel requests
|
|
19
|
-
*/
|
|
20
|
-
class KubectlSemaphore {
|
|
21
|
-
maxConcurrent;
|
|
22
|
-
currentCount = 0;
|
|
23
|
-
waitQueue = [];
|
|
24
|
-
constructor(maxConcurrent = 10) {
|
|
25
|
-
this.maxConcurrent = maxConcurrent;
|
|
26
|
-
}
|
|
27
|
-
async acquire() {
|
|
28
|
-
if (this.currentCount < this.maxConcurrent) {
|
|
29
|
-
this.currentCount++;
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
// Wait for a slot to become available
|
|
33
|
-
return new Promise((resolve) => {
|
|
34
|
-
this.waitQueue.push(resolve);
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
release() {
|
|
38
|
-
if (this.waitQueue.length > 0) {
|
|
39
|
-
// Give slot to next waiting request
|
|
40
|
-
const next = this.waitQueue.shift();
|
|
41
|
-
next();
|
|
42
|
-
}
|
|
43
|
-
else {
|
|
44
|
-
this.currentCount--;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
// Limit to 10 concurrent kubectl calls to prevent K8s API rate limiting
|
|
49
|
-
const kubectlSemaphore = new KubectlSemaphore(10);
|
|
50
|
-
/**
|
|
51
|
-
* Execute kubectl command with proper configuration
|
|
52
|
-
* Automatically traced with OpenTelemetry
|
|
53
|
-
* Uses semaphore to limit concurrent executions and prevent K8s API rate limiting
|
|
54
|
-
*/
|
|
55
|
-
async function executeKubectl(args, config) {
|
|
56
|
-
// Acquire semaphore slot before executing
|
|
57
|
-
await kubectlSemaphore.acquire();
|
|
58
|
-
try {
|
|
59
|
-
// Wrap entire execution with tracing
|
|
60
|
-
return await (0, tracing_1.withKubectlTracing)(args, config, async () => {
|
|
61
|
-
const command = buildKubectlCommand(args, config);
|
|
62
|
-
const timeout = config?.timeout || 30000;
|
|
63
|
-
try {
|
|
64
|
-
// If stdin is provided, use spawn for proper stdin piping
|
|
65
|
-
if (config?.stdin) {
|
|
66
|
-
const { spawn } = require('child_process');
|
|
67
|
-
return new Promise((resolve, reject) => {
|
|
68
|
-
let stdout = '';
|
|
69
|
-
let stderr = '';
|
|
70
|
-
const proc = spawn('sh', ['-c', command], {
|
|
71
|
-
timeout,
|
|
72
|
-
maxBuffer: 100 * 1024 * 1024
|
|
73
|
-
});
|
|
74
|
-
proc.stdout.on('data', (data) => { stdout += data.toString(); });
|
|
75
|
-
proc.stderr.on('data', (data) => { stderr += data.toString(); });
|
|
76
|
-
proc.on('error', (error) => reject(error));
|
|
77
|
-
proc.on('close', (code) => {
|
|
78
|
-
if (code !== 0) {
|
|
79
|
-
reject(new Error(`kubectl command failed: ${stderr || stdout}`));
|
|
80
|
-
}
|
|
81
|
-
else if (stderr && !stderr.includes('Warning') && !stderr.includes('No resources found')) {
|
|
82
|
-
reject(new Error(`kubectl command failed: ${stderr}`));
|
|
83
|
-
}
|
|
84
|
-
else {
|
|
85
|
-
resolve(stdout.trim());
|
|
86
|
-
}
|
|
87
|
-
});
|
|
88
|
-
// Write stdin and close
|
|
89
|
-
proc.stdin.write(config.stdin);
|
|
90
|
-
proc.stdin.end();
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
// No stdin - use regular execAsync
|
|
94
|
-
const { stdout, stderr } = await execAsync(command, {
|
|
95
|
-
timeout,
|
|
96
|
-
maxBuffer: 100 * 1024 * 1024 // 100MB buffer for large clusters with 1000+ CRDs
|
|
97
|
-
});
|
|
98
|
-
if (stderr && !stderr.includes('Warning') && !stderr.includes('No resources found')) {
|
|
99
|
-
throw new Error(`kubectl command failed: ${stderr}`);
|
|
100
|
-
}
|
|
101
|
-
return stdout.trim();
|
|
102
|
-
}
|
|
103
|
-
catch (error) {
|
|
104
|
-
if (error.code === 'ENOENT') {
|
|
105
|
-
throw new Error('kubectl binary not found. Please install kubectl and ensure it\'s in your PATH.');
|
|
106
|
-
}
|
|
107
|
-
// Use error classification for better error messages
|
|
108
|
-
const classified = ErrorClassifier.classifyError(error);
|
|
109
|
-
throw new Error(classified.enhancedMessage);
|
|
110
|
-
}
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
finally {
|
|
114
|
-
// Always release the semaphore slot
|
|
115
|
-
kubectlSemaphore.release();
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* Build kubectl command string with proper flags
|
|
120
|
-
*/
|
|
121
|
-
/**
|
|
122
|
-
* Safely escape shell arguments to prevent command injection
|
|
123
|
-
*/
|
|
124
|
-
function escapeShellArg(arg) {
|
|
125
|
-
if (!arg || typeof arg !== 'string') {
|
|
126
|
-
return '""';
|
|
127
|
-
}
|
|
128
|
-
// If the argument contains only safe characters, return as-is
|
|
129
|
-
if (/^[a-zA-Z0-9._/-]+$/.test(arg)) {
|
|
130
|
-
return arg;
|
|
131
|
-
}
|
|
132
|
-
// Otherwise, quote and escape
|
|
133
|
-
return `"${arg.replace(/["\\]/g, '\\$&')}"`;
|
|
134
|
-
}
|
|
135
|
-
function buildKubectlCommand(args, config) {
|
|
136
|
-
const cmdParts = ['kubectl'];
|
|
137
|
-
if (config?.kubeconfig) {
|
|
138
|
-
cmdParts.push('--kubeconfig', escapeShellArg(config.kubeconfig));
|
|
139
|
-
}
|
|
140
|
-
if (config?.context) {
|
|
141
|
-
cmdParts.push('--context', escapeShellArg(config.context));
|
|
142
|
-
}
|
|
143
|
-
if (config?.namespace) {
|
|
144
|
-
cmdParts.push('--namespace', escapeShellArg(config.namespace));
|
|
145
|
-
}
|
|
146
|
-
// Safely add all arguments
|
|
147
|
-
args.forEach(arg => cmdParts.push(escapeShellArg(arg)));
|
|
148
|
-
return cmdParts.join(' ');
|
|
149
|
-
}
|
|
150
|
-
// Enhanced Error Classification System
|
|
151
|
-
class ErrorClassifier {
|
|
152
|
-
static classifyError(error) {
|
|
153
|
-
const originalMessage = error.message;
|
|
154
|
-
// Connection and Network Errors
|
|
155
|
-
if (this.isNetworkError(originalMessage)) {
|
|
156
|
-
return {
|
|
157
|
-
type: 'network',
|
|
158
|
-
enhancedMessage: this.enhanceNetworkError(originalMessage)
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
|
-
// Authentication Errors
|
|
162
|
-
if (this.isAuthenticationError(originalMessage)) {
|
|
163
|
-
return {
|
|
164
|
-
type: 'authentication',
|
|
165
|
-
enhancedMessage: this.enhanceAuthenticationError(originalMessage)
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
// Authorization/RBAC Errors
|
|
169
|
-
if (this.isAuthorizationError(originalMessage)) {
|
|
170
|
-
return {
|
|
171
|
-
type: 'authorization',
|
|
172
|
-
enhancedMessage: this.enhanceAuthorizationError(originalMessage)
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
// API Availability Errors
|
|
176
|
-
if (this.isAPIAvailabilityError(originalMessage)) {
|
|
177
|
-
return {
|
|
178
|
-
type: 'api-availability',
|
|
179
|
-
enhancedMessage: this.enhanceAPIAvailabilityError(originalMessage)
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
// Kubeconfig Validation Errors
|
|
183
|
-
if (this.isKubeconfigError(originalMessage)) {
|
|
184
|
-
return {
|
|
185
|
-
type: 'kubeconfig',
|
|
186
|
-
enhancedMessage: this.enhanceKubeconfigError(originalMessage)
|
|
187
|
-
};
|
|
188
|
-
}
|
|
189
|
-
// Version Compatibility Errors
|
|
190
|
-
if (this.isVersionCompatibilityError(originalMessage)) {
|
|
191
|
-
return {
|
|
192
|
-
type: 'version',
|
|
193
|
-
enhancedMessage: this.enhanceVersionCompatibilityError(originalMessage)
|
|
194
|
-
};
|
|
195
|
-
}
|
|
196
|
-
// Default: return original message with basic enhancement
|
|
197
|
-
return {
|
|
198
|
-
type: 'unknown',
|
|
199
|
-
enhancedMessage: `${originalMessage}\n\nTroubleshooting steps:\n- Run 'kubectl cluster-info' to verify cluster connectivity\n- Check your kubeconfig with 'kubectl config view'\n- Verify cluster endpoint accessibility`
|
|
200
|
-
};
|
|
201
|
-
}
|
|
202
|
-
static isNetworkError(message) {
|
|
203
|
-
// Fixed: Avoid catastrophic backtracking by using non-overlapping alternation
|
|
204
|
-
const networkPatterns = [
|
|
205
|
-
'getaddrinfo ENOTFOUND',
|
|
206
|
-
'timeout',
|
|
207
|
-
'ECONNREFUSED',
|
|
208
|
-
'ENOTFOUND',
|
|
209
|
-
'network',
|
|
210
|
-
'unreachable'
|
|
211
|
-
];
|
|
212
|
-
return networkPatterns.some(pattern => message.toLowerCase().includes(pattern.toLowerCase()));
|
|
213
|
-
}
|
|
214
|
-
static isAuthenticationError(message) {
|
|
215
|
-
// Fixed: Avoid catastrophic backtracking by using non-overlapping alternation
|
|
216
|
-
const authPatterns = [
|
|
217
|
-
'unauthorized',
|
|
218
|
-
'invalid bearer token',
|
|
219
|
-
'certificate',
|
|
220
|
-
'auth',
|
|
221
|
-
'authentication'
|
|
222
|
-
];
|
|
223
|
-
return authPatterns.some(pattern => message.toLowerCase().includes(pattern.toLowerCase()));
|
|
224
|
-
}
|
|
225
|
-
static isAuthorizationError(message) {
|
|
226
|
-
return /forbidden|cannot list|cannot get|cannot create|RBAC|permission denied/i.test(message);
|
|
227
|
-
}
|
|
228
|
-
static isAPIAvailabilityError(message) {
|
|
229
|
-
return /server could not find|resource type.*not found|doesn't have a resource type|no matches for kind/i.test(message);
|
|
230
|
-
}
|
|
231
|
-
static isKubeconfigError(message) {
|
|
232
|
-
// Be more specific - don't match "path does not exist" errors which are about manifest files
|
|
233
|
-
return /context.*does not exist|kubeconfig.*not found|invalid.*kubeconfig|config.*not found|no Auth Provider/i.test(message) &&
|
|
234
|
-
!/the path.*does not exist/.test(message);
|
|
235
|
-
}
|
|
236
|
-
static isVersionCompatibilityError(message) {
|
|
237
|
-
return /server version|version.*old|unsupported.*version|api.*version/i.test(message);
|
|
238
|
-
}
|
|
239
|
-
static enhanceNetworkError(message) {
|
|
240
|
-
if (message.includes('getaddrinfo ENOTFOUND')) {
|
|
241
|
-
return `DNS resolution failed: Cannot resolve cluster endpoint hostname.\n\nTroubleshooting steps:\n- Check cluster endpoint in kubeconfig: kubectl config view\n- Verify network connectivity and DNS settings\n- Confirm cluster is running and accessible\n- Check VPN connection if using private cluster\n\nOriginal error: ${message}`;
|
|
242
|
-
}
|
|
243
|
-
if (message.includes('timeout')) {
|
|
244
|
-
return `Connection timeout: Unable to reach cluster within timeout period.\n\nTroubleshooting steps:\n- Check network latency to cluster endpoint\n- Increase timeout value if needed\n- Verify cluster is responsive: kubectl get nodes\n- Check firewall and proxy settings\n\nOriginal error: ${message}`;
|
|
245
|
-
}
|
|
246
|
-
return `Network connectivity issue detected.\n\nTroubleshooting steps:\n- Verify cluster endpoint accessibility\n- Run 'kubectl cluster-info' to test connectivity\n- Check network and firewall settings\n- Confirm cluster is running\n\nOriginal error: ${message}`;
|
|
247
|
-
}
|
|
248
|
-
static enhanceAuthenticationError(message) {
|
|
249
|
-
if (message.includes('invalid bearer token')) {
|
|
250
|
-
return constants_1.KUBERNETES_ERROR_TEMPLATES.AUTHENTICATION.INVALID_TOKEN(message);
|
|
251
|
-
}
|
|
252
|
-
if (message.includes('certificate')) {
|
|
253
|
-
return constants_1.KUBERNETES_ERROR_TEMPLATES.AUTHENTICATION.CERTIFICATE_FAILED(message);
|
|
254
|
-
}
|
|
255
|
-
if (message.includes('no Auth Provider found')) {
|
|
256
|
-
return constants_1.KUBERNETES_ERROR_TEMPLATES.AUTHENTICATION.PROVIDER_MISSING(message);
|
|
257
|
-
}
|
|
258
|
-
return constants_1.KUBERNETES_ERROR_TEMPLATES.AUTHENTICATION.GENERIC_FAILED(message);
|
|
259
|
-
}
|
|
260
|
-
static enhanceAuthorizationError(message) {
|
|
261
|
-
if (message.includes('customresourcedefinitions')) {
|
|
262
|
-
return constants_1.KUBERNETES_ERROR_TEMPLATES.AUTHORIZATION.CRD_PERMISSIONS(message);
|
|
263
|
-
}
|
|
264
|
-
if (message.includes('forbidden')) {
|
|
265
|
-
return constants_1.KUBERNETES_ERROR_TEMPLATES.AUTHORIZATION.FORBIDDEN(message);
|
|
266
|
-
}
|
|
267
|
-
return constants_1.KUBERNETES_ERROR_TEMPLATES.AUTHORIZATION.PERMISSION_DENIED(message);
|
|
268
|
-
}
|
|
269
|
-
static enhanceAPIAvailabilityError(message) {
|
|
270
|
-
if (message.includes('apps/v1beta1')) {
|
|
271
|
-
return constants_1.KUBERNETES_ERROR_TEMPLATES.API.VERSION_UNSUPPORTED(message);
|
|
272
|
-
}
|
|
273
|
-
return constants_1.KUBERNETES_ERROR_TEMPLATES.API.RESOURCE_UNAVAILABLE(message);
|
|
274
|
-
}
|
|
275
|
-
static enhanceKubeconfigError(message) {
|
|
276
|
-
if (message.includes('context') && message.includes('does not exist')) {
|
|
277
|
-
return constants_1.KUBERNETES_ERROR_TEMPLATES.KUBECONFIG.CONTEXT_NOT_FOUND(message);
|
|
278
|
-
}
|
|
279
|
-
if (message.includes('not found')) {
|
|
280
|
-
return constants_1.KUBERNETES_ERROR_TEMPLATES.KUBECONFIG.FILE_NOT_FOUND(message);
|
|
281
|
-
}
|
|
282
|
-
return constants_1.KUBERNETES_ERROR_TEMPLATES.KUBECONFIG.INVALID_FORMAT(message);
|
|
283
|
-
}
|
|
284
|
-
static enhanceVersionCompatibilityError(message) {
|
|
285
|
-
return constants_1.KUBERNETES_ERROR_TEMPLATES.VERSION_COMPATIBILITY(message);
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
exports.ErrorClassifier = ErrorClassifier;
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Kubernetes Client Tracing Utilities
|
|
3
|
-
*
|
|
4
|
-
* Generic tracing wrappers for Kubernetes operations:
|
|
5
|
-
* 1. Transparent proxy for @kubernetes/client-node API clients
|
|
6
|
-
* 2. Wrapper for kubectl CLI command execution
|
|
7
|
-
*
|
|
8
|
-
* Uses CLIENT span kind with k8s.* semantic conventions
|
|
9
|
-
*/
|
|
10
|
-
/**
|
|
11
|
-
* Configuration for kubectl command tracing
|
|
12
|
-
*/
|
|
13
|
-
export interface KubectlConfig {
|
|
14
|
-
context?: string;
|
|
15
|
-
namespace?: string;
|
|
16
|
-
kubeconfig?: string;
|
|
17
|
-
timeout?: number;
|
|
18
|
-
stdin?: string;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Create traced Kubernetes API client using JavaScript Proxy
|
|
22
|
-
*
|
|
23
|
-
* Transparently wraps ANY Kubernetes API client method with tracing.
|
|
24
|
-
* No code changes needed in calling code - just wrap the client once.
|
|
25
|
-
*
|
|
26
|
-
* @param apiClient Original K8s API client instance
|
|
27
|
-
* @param apiType API client type (e.g., 'CoreV1Api', 'AppsV1Api')
|
|
28
|
-
* @returns Proxied client with automatic tracing for all methods
|
|
29
|
-
*
|
|
30
|
-
* @example
|
|
31
|
-
* const coreApi = createTracedK8sClient(
|
|
32
|
-
* kc.makeApiClient(k8s.CoreV1Api),
|
|
33
|
-
* 'CoreV1Api'
|
|
34
|
-
* );
|
|
35
|
-
* await coreApi.listNamespace(); // Automatically traced as "k8s.listNamespace"
|
|
36
|
-
*/
|
|
37
|
-
export declare function createTracedK8sClient<T extends object>(apiClient: T, apiType: string): T;
|
|
38
|
-
/**
|
|
39
|
-
* Tracing wrapper for kubectl CLI command execution
|
|
40
|
-
*
|
|
41
|
-
* Wraps kubectl command execution with OpenTelemetry tracing spans.
|
|
42
|
-
* Captures command details, operation type, and execution results.
|
|
43
|
-
*
|
|
44
|
-
* @param args kubectl command arguments (e.g., ['get', 'pods', '-n', 'default'])
|
|
45
|
-
* @param config kubectl execution configuration
|
|
46
|
-
* @param handler Function that executes the actual kubectl command
|
|
47
|
-
* @returns kubectl command output
|
|
48
|
-
*
|
|
49
|
-
* @example
|
|
50
|
-
* const output = await withKubectlTracing(
|
|
51
|
-
* ['get', 'crd', '-o', 'json'],
|
|
52
|
-
* { kubeconfig: '/path/to/config' },
|
|
53
|
-
* async () => execAsync('kubectl get crd -o json')
|
|
54
|
-
* );
|
|
55
|
-
*/
|
|
56
|
-
export declare function withKubectlTracing(args: string[], config: KubectlConfig | undefined, handler: () => Promise<string>): Promise<string>;
|
|
57
|
-
//# sourceMappingURL=k8s-tracing.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"k8s-tracing.d.ts","sourceRoot":"","sources":["../../../src/core/tracing/k8s-tracing.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,SAAS,MAAM,EACpD,SAAS,EAAE,CAAC,EACZ,OAAO,EAAE,MAAM,GACd,CAAC,CAoEH;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,MAAM,EAAE,EACd,MAAM,EAAE,aAAa,GAAG,SAAS,EACjC,OAAO,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,GAC7B,OAAO,CAAC,MAAM,CAAC,CAgEjB"}
|
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Kubernetes Client Tracing Utilities
|
|
4
|
-
*
|
|
5
|
-
* Generic tracing wrappers for Kubernetes operations:
|
|
6
|
-
* 1. Transparent proxy for @kubernetes/client-node API clients
|
|
7
|
-
* 2. Wrapper for kubectl CLI command execution
|
|
8
|
-
*
|
|
9
|
-
* Uses CLIENT span kind with k8s.* semantic conventions
|
|
10
|
-
*/
|
|
11
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.createTracedK8sClient = createTracedK8sClient;
|
|
13
|
-
exports.withKubectlTracing = withKubectlTracing;
|
|
14
|
-
const api_1 = require("@opentelemetry/api");
|
|
15
|
-
/**
|
|
16
|
-
* Create traced Kubernetes API client using JavaScript Proxy
|
|
17
|
-
*
|
|
18
|
-
* Transparently wraps ANY Kubernetes API client method with tracing.
|
|
19
|
-
* No code changes needed in calling code - just wrap the client once.
|
|
20
|
-
*
|
|
21
|
-
* @param apiClient Original K8s API client instance
|
|
22
|
-
* @param apiType API client type (e.g., 'CoreV1Api', 'AppsV1Api')
|
|
23
|
-
* @returns Proxied client with automatic tracing for all methods
|
|
24
|
-
*
|
|
25
|
-
* @example
|
|
26
|
-
* const coreApi = createTracedK8sClient(
|
|
27
|
-
* kc.makeApiClient(k8s.CoreV1Api),
|
|
28
|
-
* 'CoreV1Api'
|
|
29
|
-
* );
|
|
30
|
-
* await coreApi.listNamespace(); // Automatically traced as "k8s.listNamespace"
|
|
31
|
-
*/
|
|
32
|
-
function createTracedK8sClient(apiClient, apiType) {
|
|
33
|
-
const tracer = api_1.trace.getTracer('dot-ai-mcp');
|
|
34
|
-
return new Proxy(apiClient, {
|
|
35
|
-
get(target, prop, receiver) {
|
|
36
|
-
const original = Reflect.get(target, prop, receiver);
|
|
37
|
-
// Only wrap functions (API methods), not properties
|
|
38
|
-
if (typeof original !== 'function') {
|
|
39
|
-
return original;
|
|
40
|
-
}
|
|
41
|
-
// Return wrapped function with tracing
|
|
42
|
-
return function (...args) {
|
|
43
|
-
const methodName = String(prop);
|
|
44
|
-
const spanName = `k8s.${methodName}`;
|
|
45
|
-
return tracer.startActiveSpan(spanName, {
|
|
46
|
-
kind: api_1.SpanKind.CLIENT,
|
|
47
|
-
attributes: {
|
|
48
|
-
'k8s.client': 'kubernetes-client-node',
|
|
49
|
-
'k8s.api': apiType,
|
|
50
|
-
'k8s.method': methodName,
|
|
51
|
-
},
|
|
52
|
-
}, async (span) => {
|
|
53
|
-
try {
|
|
54
|
-
// Execute the original K8s API method
|
|
55
|
-
const result = await original.apply(target, args);
|
|
56
|
-
// Add response metadata if available
|
|
57
|
-
if (result?.response?.statusCode) {
|
|
58
|
-
span.setAttribute('http.response.status_code', result.response.statusCode);
|
|
59
|
-
}
|
|
60
|
-
span.setStatus({ code: api_1.SpanStatusCode.OK });
|
|
61
|
-
return result;
|
|
62
|
-
}
|
|
63
|
-
catch (error) {
|
|
64
|
-
// Record K8s API error
|
|
65
|
-
span.recordException(error);
|
|
66
|
-
span.setStatus({
|
|
67
|
-
code: api_1.SpanStatusCode.ERROR,
|
|
68
|
-
message: error instanceof Error ? error.message : String(error),
|
|
69
|
-
});
|
|
70
|
-
// Add K8s-specific error attributes
|
|
71
|
-
if (error instanceof Error && 'statusCode' in error) {
|
|
72
|
-
span.setAttribute('k8s.error.status_code', error.statusCode);
|
|
73
|
-
}
|
|
74
|
-
throw error;
|
|
75
|
-
}
|
|
76
|
-
finally {
|
|
77
|
-
span.end();
|
|
78
|
-
}
|
|
79
|
-
});
|
|
80
|
-
};
|
|
81
|
-
},
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* Tracing wrapper for kubectl CLI command execution
|
|
86
|
-
*
|
|
87
|
-
* Wraps kubectl command execution with OpenTelemetry tracing spans.
|
|
88
|
-
* Captures command details, operation type, and execution results.
|
|
89
|
-
*
|
|
90
|
-
* @param args kubectl command arguments (e.g., ['get', 'pods', '-n', 'default'])
|
|
91
|
-
* @param config kubectl execution configuration
|
|
92
|
-
* @param handler Function that executes the actual kubectl command
|
|
93
|
-
* @returns kubectl command output
|
|
94
|
-
*
|
|
95
|
-
* @example
|
|
96
|
-
* const output = await withKubectlTracing(
|
|
97
|
-
* ['get', 'crd', '-o', 'json'],
|
|
98
|
-
* { kubeconfig: '/path/to/config' },
|
|
99
|
-
* async () => execAsync('kubectl get crd -o json')
|
|
100
|
-
* );
|
|
101
|
-
*/
|
|
102
|
-
async function withKubectlTracing(args, config, handler) {
|
|
103
|
-
const tracer = api_1.trace.getTracer('dot-ai-mcp');
|
|
104
|
-
// Parse operation and resource from args
|
|
105
|
-
const operation = args[0] || 'unknown'; // 'get', 'apply', 'delete', etc.
|
|
106
|
-
const resource = args[1] || 'unknown'; // 'pods', 'crd', 'deployments', etc.
|
|
107
|
-
const spanName = `kubectl ${operation} ${resource}`;
|
|
108
|
-
return await tracer.startActiveSpan(spanName, {
|
|
109
|
-
kind: api_1.SpanKind.CLIENT,
|
|
110
|
-
attributes: {
|
|
111
|
-
'k8s.client': 'kubectl',
|
|
112
|
-
'k8s.command': 'kubectl',
|
|
113
|
-
'k8s.operation': operation,
|
|
114
|
-
'k8s.resource': resource,
|
|
115
|
-
'k8s.args': args.join(' '),
|
|
116
|
-
...(config?.namespace && { 'k8s.namespace': config.namespace }),
|
|
117
|
-
...(config?.context && { 'k8s.context': config.context }),
|
|
118
|
-
...(config?.kubeconfig && { 'k8s.kubeconfig': config.kubeconfig }),
|
|
119
|
-
},
|
|
120
|
-
}, async (span) => {
|
|
121
|
-
const startTime = Date.now();
|
|
122
|
-
try {
|
|
123
|
-
// Execute the kubectl command
|
|
124
|
-
const result = await api_1.context.with(api_1.trace.setSpan(api_1.context.active(), span), handler);
|
|
125
|
-
// Add execution metrics
|
|
126
|
-
span.setAttribute('k8s.duration_ms', Date.now() - startTime);
|
|
127
|
-
span.setAttribute('k8s.output_size_bytes', result.length);
|
|
128
|
-
span.setStatus({ code: api_1.SpanStatusCode.OK });
|
|
129
|
-
return result;
|
|
130
|
-
}
|
|
131
|
-
catch (error) {
|
|
132
|
-
// Record kubectl error with details
|
|
133
|
-
span.recordException(error);
|
|
134
|
-
span.setStatus({
|
|
135
|
-
code: api_1.SpanStatusCode.ERROR,
|
|
136
|
-
message: error instanceof Error ? error.message : String(error),
|
|
137
|
-
});
|
|
138
|
-
// Parse kubectl error for additional context
|
|
139
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
140
|
-
if (errorMessage.includes('not found')) {
|
|
141
|
-
span.setAttribute('k8s.error.type', 'NotFound');
|
|
142
|
-
}
|
|
143
|
-
else if (errorMessage.includes('forbidden')) {
|
|
144
|
-
span.setAttribute('k8s.error.type', 'Forbidden');
|
|
145
|
-
}
|
|
146
|
-
else if (errorMessage.includes('timeout')) {
|
|
147
|
-
span.setAttribute('k8s.error.type', 'Timeout');
|
|
148
|
-
}
|
|
149
|
-
throw error;
|
|
150
|
-
}
|
|
151
|
-
finally {
|
|
152
|
-
span.end();
|
|
153
|
-
}
|
|
154
|
-
});
|
|
155
|
-
}
|
package/scripts/toolhive.nu
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env nu
|
|
2
|
-
|
|
3
|
-
# Installs Stacklock Toolhive
|
|
4
|
-
#
|
|
5
|
-
# Examples:
|
|
6
|
-
# > main apply toolhive
|
|
7
|
-
def "main apply toolhive" [] {
|
|
8
|
-
|
|
9
|
-
(
|
|
10
|
-
helm upgrade --install toolhive-operator-crds
|
|
11
|
-
oci://ghcr.io/stacklok/toolhive/toolhive-operator-crds
|
|
12
|
-
)
|
|
13
|
-
|
|
14
|
-
(
|
|
15
|
-
helm upgrade --install toolhive-operator
|
|
16
|
-
oci://ghcr.io/stacklok/toolhive/toolhive-operator
|
|
17
|
-
--namespace toolhive-system --create-namespace
|
|
18
|
-
--wait
|
|
19
|
-
)
|
|
20
|
-
|
|
21
|
-
}
|