@vfarcic/dot-ai 0.1.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/LICENSE +21 -0
- package/README.md +203 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +51 -0
- package/dist/core/claude.d.ts +42 -0
- package/dist/core/claude.d.ts.map +1 -0
- package/dist/core/claude.js +229 -0
- package/dist/core/deploy-operation.d.ts +38 -0
- package/dist/core/deploy-operation.d.ts.map +1 -0
- package/dist/core/deploy-operation.js +101 -0
- package/dist/core/discovery.d.ts +162 -0
- package/dist/core/discovery.d.ts.map +1 -0
- package/dist/core/discovery.js +758 -0
- package/dist/core/error-handling.d.ts +167 -0
- package/dist/core/error-handling.d.ts.map +1 -0
- package/dist/core/error-handling.js +399 -0
- package/dist/core/index.d.ts +42 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +123 -0
- package/dist/core/kubernetes-utils.d.ts +38 -0
- package/dist/core/kubernetes-utils.d.ts.map +1 -0
- package/dist/core/kubernetes-utils.js +177 -0
- package/dist/core/memory.d.ts +45 -0
- package/dist/core/memory.d.ts.map +1 -0
- package/dist/core/memory.js +113 -0
- package/dist/core/schema.d.ts +187 -0
- package/dist/core/schema.d.ts.map +1 -0
- package/dist/core/schema.js +655 -0
- package/dist/core/session-utils.d.ts +29 -0
- package/dist/core/session-utils.d.ts.map +1 -0
- package/dist/core/session-utils.js +121 -0
- package/dist/core/workflow.d.ts +70 -0
- package/dist/core/workflow.d.ts.map +1 -0
- package/dist/core/workflow.js +161 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +32 -0
- package/dist/interfaces/cli.d.ts +74 -0
- package/dist/interfaces/cli.d.ts.map +1 -0
- package/dist/interfaces/cli.js +769 -0
- package/dist/interfaces/mcp.d.ts +30 -0
- package/dist/interfaces/mcp.d.ts.map +1 -0
- package/dist/interfaces/mcp.js +105 -0
- package/dist/mcp/server.d.ts +9 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +151 -0
- package/dist/tools/answer-question.d.ts +27 -0
- package/dist/tools/answer-question.d.ts.map +1 -0
- package/dist/tools/answer-question.js +696 -0
- package/dist/tools/choose-solution.d.ts +23 -0
- package/dist/tools/choose-solution.d.ts.map +1 -0
- package/dist/tools/choose-solution.js +171 -0
- package/dist/tools/deploy-manifests.d.ts +25 -0
- package/dist/tools/deploy-manifests.d.ts.map +1 -0
- package/dist/tools/deploy-manifests.js +74 -0
- package/dist/tools/generate-manifests.d.ts +23 -0
- package/dist/tools/generate-manifests.d.ts.map +1 -0
- package/dist/tools/generate-manifests.js +424 -0
- package/dist/tools/index.d.ts +11 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +34 -0
- package/dist/tools/recommend.d.ts +23 -0
- package/dist/tools/recommend.d.ts.map +1 -0
- package/dist/tools/recommend.js +332 -0
- package/package.json +124 -0
- package/prompts/intent-validation.md +65 -0
- package/prompts/manifest-generation.md +79 -0
- package/prompts/question-generation.md +128 -0
- package/prompts/resource-analysis.md +127 -0
- package/prompts/resource-selection.md +55 -0
- package/prompts/resource-solution-ranking.md +77 -0
- package/prompts/solution-enhancement.md +129 -0
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Generate Manifests Tool - AI-driven manifest generation with validation loop
|
|
4
|
+
*/
|
|
5
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
8
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
9
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
10
|
+
}
|
|
11
|
+
Object.defineProperty(o, k2, desc);
|
|
12
|
+
}) : (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
o[k2] = m[k];
|
|
15
|
+
}));
|
|
16
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
17
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
18
|
+
}) : function(o, v) {
|
|
19
|
+
o["default"] = v;
|
|
20
|
+
});
|
|
21
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
22
|
+
var ownKeys = function(o) {
|
|
23
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
24
|
+
var ar = [];
|
|
25
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
26
|
+
return ar;
|
|
27
|
+
};
|
|
28
|
+
return ownKeys(o);
|
|
29
|
+
};
|
|
30
|
+
return function (mod) {
|
|
31
|
+
if (mod && mod.__esModule) return mod;
|
|
32
|
+
var result = {};
|
|
33
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
34
|
+
__setModuleDefault(result, mod);
|
|
35
|
+
return result;
|
|
36
|
+
};
|
|
37
|
+
})();
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.GENERATEMANIFESTS_TOOL_INPUT_SCHEMA = exports.GENERATEMANIFESTS_TOOL_DESCRIPTION = exports.GENERATEMANIFESTS_TOOL_NAME = void 0;
|
|
40
|
+
exports.handleGenerateManifestsTool = handleGenerateManifestsTool;
|
|
41
|
+
const zod_1 = require("zod");
|
|
42
|
+
const error_handling_1 = require("../core/error-handling");
|
|
43
|
+
const claude_1 = require("../core/claude");
|
|
44
|
+
const fs = __importStar(require("fs"));
|
|
45
|
+
const path = __importStar(require("path"));
|
|
46
|
+
const yaml = __importStar(require("js-yaml"));
|
|
47
|
+
const child_process_1 = require("child_process");
|
|
48
|
+
const session_utils_1 = require("../core/session-utils");
|
|
49
|
+
// Tool metadata for direct MCP registration
|
|
50
|
+
exports.GENERATEMANIFESTS_TOOL_NAME = 'generateManifests';
|
|
51
|
+
exports.GENERATEMANIFESTS_TOOL_DESCRIPTION = 'Generate final Kubernetes manifests from fully configured solution (ONLY after completing ALL stages: required, basic, advanced, and open)';
|
|
52
|
+
// Zod schema for MCP registration
|
|
53
|
+
exports.GENERATEMANIFESTS_TOOL_INPUT_SCHEMA = {
|
|
54
|
+
solutionId: zod_1.z.string().regex(/^sol_[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{6}_[a-f0-9]+$/).describe('The solution ID to generate manifests for (e.g., sol_2025-07-01T154349_1e1e242592ff)')
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Load solution file and validate structure
|
|
58
|
+
*/
|
|
59
|
+
function loadSolutionFile(solutionId, sessionDir) {
|
|
60
|
+
const solutionPath = path.join(sessionDir, `${solutionId}.json`);
|
|
61
|
+
if (!fs.existsSync(solutionPath)) {
|
|
62
|
+
throw new Error(`Solution file not found: ${solutionPath}. Available files: ${fs.readdirSync(sessionDir).filter(f => f.endsWith('.json')).join(', ')}`);
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
const content = fs.readFileSync(solutionPath, 'utf8');
|
|
66
|
+
const solution = JSON.parse(content);
|
|
67
|
+
if (!solution.solutionId || !solution.questions) {
|
|
68
|
+
throw new Error(`Invalid solution file structure: ${solutionId}. Missing required fields: solutionId or questions`);
|
|
69
|
+
}
|
|
70
|
+
return solution;
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
if (error instanceof SyntaxError) {
|
|
74
|
+
throw new Error(`Invalid JSON in solution file: ${solutionId}`);
|
|
75
|
+
}
|
|
76
|
+
throw error;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Retrieve schemas for resources specified in the solution
|
|
81
|
+
*/
|
|
82
|
+
async function retrieveResourceSchemas(solution, dotAI, logger) {
|
|
83
|
+
try {
|
|
84
|
+
// Extract resource references from solution
|
|
85
|
+
const resourceRefs = (solution.resources || []).map((resource) => ({
|
|
86
|
+
kind: resource.kind,
|
|
87
|
+
apiVersion: resource.apiVersion,
|
|
88
|
+
group: resource.group
|
|
89
|
+
}));
|
|
90
|
+
if (resourceRefs.length === 0) {
|
|
91
|
+
logger.warn('No resources found in solution for schema retrieval');
|
|
92
|
+
return {};
|
|
93
|
+
}
|
|
94
|
+
logger.info('Retrieving schemas for solution resources', {
|
|
95
|
+
resourceCount: resourceRefs.length,
|
|
96
|
+
resources: resourceRefs.map((r) => `${r.kind}@${r.apiVersion}`)
|
|
97
|
+
});
|
|
98
|
+
const schemas = {};
|
|
99
|
+
// Retrieve schema for each resource
|
|
100
|
+
for (const resourceRef of resourceRefs) {
|
|
101
|
+
try {
|
|
102
|
+
const resourceKey = `${resourceRef.kind}.${resourceRef.apiVersion}`;
|
|
103
|
+
logger.debug('Retrieving schema', { resourceKey });
|
|
104
|
+
// Use discovery engine to explain the resource
|
|
105
|
+
const explanation = await dotAI.discovery.explainResource(resourceRef.kind);
|
|
106
|
+
schemas[resourceKey] = {
|
|
107
|
+
kind: resourceRef.kind,
|
|
108
|
+
apiVersion: resourceRef.apiVersion,
|
|
109
|
+
schema: explanation,
|
|
110
|
+
timestamp: new Date().toISOString()
|
|
111
|
+
};
|
|
112
|
+
logger.debug('Schema retrieved successfully', {
|
|
113
|
+
resourceKey,
|
|
114
|
+
schemaLength: explanation.length
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
logger.error('Failed to retrieve schema for resource', error, {
|
|
119
|
+
resource: resourceRef
|
|
120
|
+
});
|
|
121
|
+
// Fail fast - if we can't get schemas, manifest generation will likely fail
|
|
122
|
+
throw new Error(`Failed to retrieve schema for ${resourceRef.kind}: ${error instanceof Error ? error.message : String(error)}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
logger.info('All resource schemas retrieved successfully', {
|
|
126
|
+
schemaCount: Object.keys(schemas).length
|
|
127
|
+
});
|
|
128
|
+
return schemas;
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
logger.error('Schema retrieval failed', error);
|
|
132
|
+
throw new Error(`Failed to retrieve resource schemas: ${error instanceof Error ? error.message : String(error)}`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Validate YAML syntax
|
|
137
|
+
*/
|
|
138
|
+
function validateYamlSyntax(yamlContent) {
|
|
139
|
+
try {
|
|
140
|
+
yaml.loadAll(yamlContent);
|
|
141
|
+
return { valid: true };
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
return {
|
|
145
|
+
valid: false,
|
|
146
|
+
error: error instanceof Error ? error.message : 'Unknown YAML syntax error'
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Run kubectl dry-run validation
|
|
152
|
+
*/
|
|
153
|
+
async function runKubectlDryRun(yamlPath) {
|
|
154
|
+
return new Promise((resolve) => {
|
|
155
|
+
const kubectl = (0, child_process_1.spawn)('kubectl', ['apply', '--dry-run=server', '-f', yamlPath], {
|
|
156
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
157
|
+
});
|
|
158
|
+
let stdout = '';
|
|
159
|
+
let stderr = '';
|
|
160
|
+
kubectl.stdout.on('data', (data) => {
|
|
161
|
+
stdout += data.toString();
|
|
162
|
+
});
|
|
163
|
+
kubectl.stderr.on('data', (data) => {
|
|
164
|
+
stderr += data.toString();
|
|
165
|
+
});
|
|
166
|
+
kubectl.on('close', (code) => {
|
|
167
|
+
resolve({
|
|
168
|
+
success: code === 0,
|
|
169
|
+
yamlSyntaxValid: true, // If we get here, YAML was parseable
|
|
170
|
+
kubectlOutput: stderr || stdout,
|
|
171
|
+
exitCode: code || 0,
|
|
172
|
+
stderr,
|
|
173
|
+
stdout
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
kubectl.on('error', (error) => {
|
|
177
|
+
resolve({
|
|
178
|
+
success: false,
|
|
179
|
+
yamlSyntaxValid: true,
|
|
180
|
+
error: `Failed to run kubectl: ${error.message}`,
|
|
181
|
+
kubectlOutput: `kubectl command failed: ${error.message}`,
|
|
182
|
+
exitCode: -1,
|
|
183
|
+
stderr: error.message,
|
|
184
|
+
stdout: ''
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Validate manifests using multi-layer approach
|
|
191
|
+
*/
|
|
192
|
+
async function validateManifests(yamlPath) {
|
|
193
|
+
// First check if file exists
|
|
194
|
+
if (!fs.existsSync(yamlPath)) {
|
|
195
|
+
return {
|
|
196
|
+
success: false,
|
|
197
|
+
yamlSyntaxValid: false,
|
|
198
|
+
error: `Manifest file not found: ${yamlPath}`
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
// Read YAML content
|
|
202
|
+
const yamlContent = fs.readFileSync(yamlPath, 'utf8');
|
|
203
|
+
// 1. YAML syntax validation
|
|
204
|
+
const syntaxCheck = validateYamlSyntax(yamlContent);
|
|
205
|
+
if (!syntaxCheck.valid) {
|
|
206
|
+
return {
|
|
207
|
+
success: false,
|
|
208
|
+
yamlSyntaxValid: false,
|
|
209
|
+
error: `YAML syntax error: ${syntaxCheck.error}`,
|
|
210
|
+
kubectlOutput: `YAML parsing failed: ${syntaxCheck.error}`
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
// 2. kubectl dry-run validation
|
|
214
|
+
return await runKubectlDryRun(yamlPath);
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Generate manifests using AI with Claude integration
|
|
218
|
+
*/
|
|
219
|
+
async function generateManifestsWithAI(solution, dotAI, logger, errorContext) {
|
|
220
|
+
// Load prompt template
|
|
221
|
+
const promptPath = path.join(process.cwd(), 'prompts', 'manifest-generation.md');
|
|
222
|
+
const template = fs.readFileSync(promptPath, 'utf8');
|
|
223
|
+
// Retrieve schemas for solution resources
|
|
224
|
+
const resourceSchemas = await retrieveResourceSchemas(solution, dotAI, logger);
|
|
225
|
+
// Prepare template variables
|
|
226
|
+
const solutionData = JSON.stringify(solution, null, 2);
|
|
227
|
+
const previousAttempt = errorContext ? `
|
|
228
|
+
### Generated Manifests:
|
|
229
|
+
\`\`\`yaml
|
|
230
|
+
${errorContext.previousManifests}
|
|
231
|
+
\`\`\`
|
|
232
|
+
` : 'None - this is the first attempt.';
|
|
233
|
+
const errorDetails = errorContext ? `
|
|
234
|
+
**Attempt**: ${errorContext.attempt}
|
|
235
|
+
**YAML Syntax Valid**: ${errorContext.yamlSyntaxValid}
|
|
236
|
+
**kubectl Output**: ${errorContext.kubectlOutput}
|
|
237
|
+
**Exit Code**: ${errorContext.exitCode}
|
|
238
|
+
**Error Details**: ${errorContext.stderr}
|
|
239
|
+
` : 'None - this is the first attempt.';
|
|
240
|
+
// Replace template variables
|
|
241
|
+
const schemasData = JSON.stringify(resourceSchemas, null, 2);
|
|
242
|
+
const aiPrompt = template
|
|
243
|
+
.replace('{solution}', solutionData)
|
|
244
|
+
.replace('{schemas}', schemasData)
|
|
245
|
+
.replace('{previous_attempt}', previousAttempt)
|
|
246
|
+
.replace('{error_details}', errorDetails);
|
|
247
|
+
const isRetry = !!errorContext;
|
|
248
|
+
logger.info('Generating manifests with AI', {
|
|
249
|
+
isRetry,
|
|
250
|
+
attempt: errorContext?.attempt,
|
|
251
|
+
hasErrorContext: !!errorContext,
|
|
252
|
+
solutionId: solution.solutionId
|
|
253
|
+
});
|
|
254
|
+
// Initialize Claude integration
|
|
255
|
+
const apiKey = process.env.ANTHROPIC_API_KEY || 'test-key';
|
|
256
|
+
const claudeIntegration = new claude_1.ClaudeIntegration(apiKey);
|
|
257
|
+
// Send prompt to Claude
|
|
258
|
+
const response = await claudeIntegration.sendMessage(aiPrompt);
|
|
259
|
+
// Extract YAML content from response
|
|
260
|
+
let manifestContent = response.content;
|
|
261
|
+
// Try to extract YAML from code blocks if wrapped
|
|
262
|
+
const yamlBlockMatch = manifestContent.match(/```(?:yaml|yml)?\s*([\s\S]*?)\s*```/);
|
|
263
|
+
if (yamlBlockMatch) {
|
|
264
|
+
manifestContent = yamlBlockMatch[1];
|
|
265
|
+
}
|
|
266
|
+
// Clean up any leading/trailing whitespace
|
|
267
|
+
manifestContent = manifestContent.trim();
|
|
268
|
+
logger.info('AI manifest generation completed', {
|
|
269
|
+
manifestLength: manifestContent.length,
|
|
270
|
+
isRetry,
|
|
271
|
+
solutionId: solution.solutionId
|
|
272
|
+
});
|
|
273
|
+
return manifestContent;
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Direct MCP tool handler for generateManifests functionality
|
|
277
|
+
*/
|
|
278
|
+
async function handleGenerateManifestsTool(args, dotAI, logger, requestId) {
|
|
279
|
+
return await error_handling_1.ErrorHandler.withErrorHandling(async () => {
|
|
280
|
+
const maxAttempts = 10;
|
|
281
|
+
logger.debug('Handling generateManifests request', {
|
|
282
|
+
requestId,
|
|
283
|
+
solutionId: args?.solutionId
|
|
284
|
+
});
|
|
285
|
+
// Input validation is handled automatically by MCP SDK with Zod schema
|
|
286
|
+
// args are already validated and typed when we reach this point
|
|
287
|
+
// Get session directory from environment
|
|
288
|
+
let sessionDir;
|
|
289
|
+
try {
|
|
290
|
+
sessionDir = (0, session_utils_1.getAndValidateSessionDirectory)(args, true); // requireWrite=true for manifest generation
|
|
291
|
+
logger.debug('Session directory resolved and validated', { sessionDir });
|
|
292
|
+
}
|
|
293
|
+
catch (error) {
|
|
294
|
+
throw error_handling_1.ErrorHandler.createError(error_handling_1.ErrorCategory.VALIDATION, error_handling_1.ErrorSeverity.HIGH, error instanceof Error ? error.message : 'Session directory validation failed', {
|
|
295
|
+
operation: 'session_directory_validation',
|
|
296
|
+
component: 'GenerateManifestsTool',
|
|
297
|
+
requestId,
|
|
298
|
+
suggestedActions: [
|
|
299
|
+
'Ensure session directory exists and is writable',
|
|
300
|
+
'Check directory permissions',
|
|
301
|
+
'Verify the directory path is correct',
|
|
302
|
+
'Verify DOT_AI_SESSION_DIR environment variable is correctly set'
|
|
303
|
+
]
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
// Load solution file
|
|
307
|
+
let solution;
|
|
308
|
+
try {
|
|
309
|
+
solution = loadSolutionFile(args.solutionId, sessionDir);
|
|
310
|
+
logger.debug('Solution file loaded successfully', {
|
|
311
|
+
solutionId: args.solutionId,
|
|
312
|
+
hasQuestions: !!solution.questions,
|
|
313
|
+
primaryResources: solution.resources
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
catch (error) {
|
|
317
|
+
throw error_handling_1.ErrorHandler.createError(error_handling_1.ErrorCategory.STORAGE, error_handling_1.ErrorSeverity.HIGH, error instanceof Error ? error.message : 'Failed to load solution file', {
|
|
318
|
+
operation: 'solution_file_load',
|
|
319
|
+
component: 'GenerateManifestsTool',
|
|
320
|
+
requestId,
|
|
321
|
+
input: { solutionId: args.solutionId, sessionDir },
|
|
322
|
+
suggestedActions: [
|
|
323
|
+
'Check that the solution ID is correct',
|
|
324
|
+
'Verify the solution file exists in the session directory',
|
|
325
|
+
'Ensure the solution was fully configured with all stages complete',
|
|
326
|
+
'List available solution files in the session directory'
|
|
327
|
+
]
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
// Prepare file path for manifests
|
|
331
|
+
const yamlPath = path.join(sessionDir, `${args.solutionId}.yaml`);
|
|
332
|
+
// AI generation and validation loop
|
|
333
|
+
let lastError;
|
|
334
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
335
|
+
logger.info('AI manifest generation attempt', {
|
|
336
|
+
attempt,
|
|
337
|
+
maxAttempts,
|
|
338
|
+
isRetry: attempt > 1,
|
|
339
|
+
requestId
|
|
340
|
+
});
|
|
341
|
+
try {
|
|
342
|
+
// Generate manifests with AI
|
|
343
|
+
const manifests = await generateManifestsWithAI(solution, dotAI, logger, lastError);
|
|
344
|
+
// Save manifests to file
|
|
345
|
+
fs.writeFileSync(yamlPath, manifests, 'utf8');
|
|
346
|
+
logger.info('Manifests saved to file', { yamlPath, attempt, requestId });
|
|
347
|
+
// Save a copy of this attempt for debugging
|
|
348
|
+
const attemptPath = yamlPath.replace('.yaml', `_attempt_${attempt.toString().padStart(2, '0')}.yaml`);
|
|
349
|
+
fs.writeFileSync(attemptPath, manifests, 'utf8');
|
|
350
|
+
logger.info('Saved manifest attempt for debugging', {
|
|
351
|
+
attempt,
|
|
352
|
+
attemptPath,
|
|
353
|
+
requestId
|
|
354
|
+
});
|
|
355
|
+
// Validate manifests
|
|
356
|
+
const validation = await validateManifests(yamlPath);
|
|
357
|
+
if (validation.success) {
|
|
358
|
+
logger.info('Manifest validation successful', {
|
|
359
|
+
attempt,
|
|
360
|
+
yamlPath,
|
|
361
|
+
requestId
|
|
362
|
+
});
|
|
363
|
+
// Success! Return the validated manifests
|
|
364
|
+
const response = {
|
|
365
|
+
success: true,
|
|
366
|
+
status: 'manifests_generated',
|
|
367
|
+
solutionId: args.solutionId,
|
|
368
|
+
manifests: manifests,
|
|
369
|
+
yamlPath: yamlPath,
|
|
370
|
+
validationAttempts: attempt,
|
|
371
|
+
timestamp: new Date().toISOString()
|
|
372
|
+
};
|
|
373
|
+
return {
|
|
374
|
+
content: [{
|
|
375
|
+
type: 'text',
|
|
376
|
+
text: JSON.stringify(response, null, 2)
|
|
377
|
+
}]
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
// Validation failed, prepare error context for next attempt
|
|
381
|
+
lastError = {
|
|
382
|
+
attempt,
|
|
383
|
+
previousManifests: manifests,
|
|
384
|
+
yamlSyntaxValid: validation.yamlSyntaxValid,
|
|
385
|
+
kubectlOutput: validation.kubectlOutput,
|
|
386
|
+
exitCode: validation.exitCode,
|
|
387
|
+
stderr: validation.stderr,
|
|
388
|
+
stdout: validation.stdout
|
|
389
|
+
};
|
|
390
|
+
logger.warn('Manifest validation failed', {
|
|
391
|
+
attempt,
|
|
392
|
+
maxAttempts,
|
|
393
|
+
yamlSyntaxValid: validation.yamlSyntaxValid,
|
|
394
|
+
kubectlOutput: validation.kubectlOutput,
|
|
395
|
+
requestId
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
catch (error) {
|
|
399
|
+
logger.error('Error during manifest generation attempt', error);
|
|
400
|
+
// If this is the last attempt, throw the error
|
|
401
|
+
if (attempt === maxAttempts) {
|
|
402
|
+
throw error;
|
|
403
|
+
}
|
|
404
|
+
// Prepare error context for retry
|
|
405
|
+
lastError = {
|
|
406
|
+
attempt,
|
|
407
|
+
previousManifests: lastError?.previousManifests || '',
|
|
408
|
+
yamlSyntaxValid: false,
|
|
409
|
+
kubectlOutput: error instanceof Error ? error.message : 'Unknown error',
|
|
410
|
+
exitCode: -1,
|
|
411
|
+
stderr: error instanceof Error ? error.message : 'Unknown error',
|
|
412
|
+
stdout: ''
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
// If we reach here, all attempts failed
|
|
417
|
+
throw new Error(`Failed to generate valid manifests after ${maxAttempts} attempts. Last error: ${lastError?.kubectlOutput}`);
|
|
418
|
+
}, {
|
|
419
|
+
operation: 'generate_manifests',
|
|
420
|
+
component: 'GenerateManifestsTool',
|
|
421
|
+
requestId,
|
|
422
|
+
input: args
|
|
423
|
+
});
|
|
424
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool Exports Index
|
|
3
|
+
*
|
|
4
|
+
* Centralized exports for all available tools (direct handlers)
|
|
5
|
+
*/
|
|
6
|
+
export { RECOMMEND_TOOL_NAME, RECOMMEND_TOOL_DESCRIPTION, RECOMMEND_TOOL_INPUT_SCHEMA, handleRecommendTool } from './recommend';
|
|
7
|
+
export { CHOOSESOLUTION_TOOL_NAME, CHOOSESOLUTION_TOOL_DESCRIPTION, CHOOSESOLUTION_TOOL_INPUT_SCHEMA, handleChooseSolutionTool } from './choose-solution';
|
|
8
|
+
export { ANSWERQUESTION_TOOL_NAME, ANSWERQUESTION_TOOL_DESCRIPTION, ANSWERQUESTION_TOOL_INPUT_SCHEMA, handleAnswerQuestionTool } from './answer-question';
|
|
9
|
+
export { GENERATEMANIFESTS_TOOL_NAME, GENERATEMANIFESTS_TOOL_DESCRIPTION, GENERATEMANIFESTS_TOOL_INPUT_SCHEMA, handleGenerateManifestsTool } from './generate-manifests';
|
|
10
|
+
export { DEPLOYMANIFESTS_TOOL_NAME, DEPLOYMANIFESTS_TOOL_DESCRIPTION, DEPLOYMANIFESTS_TOOL_INPUT_SCHEMA, handleDeployManifestsTool } from './deploy-manifests';
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EACL,mBAAmB,EACnB,0BAA0B,EAC1B,2BAA2B,EAC3B,mBAAmB,EACpB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,wBAAwB,EACxB,+BAA+B,EAC/B,gCAAgC,EAChC,wBAAwB,EACzB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,wBAAwB,EACxB,+BAA+B,EAC/B,gCAAgC,EAChC,wBAAwB,EACzB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,2BAA2B,EAC3B,kCAAkC,EAClC,mCAAmC,EACnC,2BAA2B,EAC5B,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,yBAAyB,EACzB,gCAAgC,EAChC,iCAAiC,EACjC,yBAAyB,EAC1B,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Tool Exports Index
|
|
4
|
+
*
|
|
5
|
+
* Centralized exports for all available tools (direct handlers)
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.handleDeployManifestsTool = exports.DEPLOYMANIFESTS_TOOL_INPUT_SCHEMA = exports.DEPLOYMANIFESTS_TOOL_DESCRIPTION = exports.DEPLOYMANIFESTS_TOOL_NAME = exports.handleGenerateManifestsTool = exports.GENERATEMANIFESTS_TOOL_INPUT_SCHEMA = exports.GENERATEMANIFESTS_TOOL_DESCRIPTION = exports.GENERATEMANIFESTS_TOOL_NAME = exports.handleAnswerQuestionTool = exports.ANSWERQUESTION_TOOL_INPUT_SCHEMA = exports.ANSWERQUESTION_TOOL_DESCRIPTION = exports.ANSWERQUESTION_TOOL_NAME = exports.handleChooseSolutionTool = exports.CHOOSESOLUTION_TOOL_INPUT_SCHEMA = exports.CHOOSESOLUTION_TOOL_DESCRIPTION = exports.CHOOSESOLUTION_TOOL_NAME = exports.handleRecommendTool = exports.RECOMMEND_TOOL_INPUT_SCHEMA = exports.RECOMMEND_TOOL_DESCRIPTION = exports.RECOMMEND_TOOL_NAME = void 0;
|
|
9
|
+
// Export direct tool handlers for use in MCP server and CLI
|
|
10
|
+
var recommend_1 = require("./recommend");
|
|
11
|
+
Object.defineProperty(exports, "RECOMMEND_TOOL_NAME", { enumerable: true, get: function () { return recommend_1.RECOMMEND_TOOL_NAME; } });
|
|
12
|
+
Object.defineProperty(exports, "RECOMMEND_TOOL_DESCRIPTION", { enumerable: true, get: function () { return recommend_1.RECOMMEND_TOOL_DESCRIPTION; } });
|
|
13
|
+
Object.defineProperty(exports, "RECOMMEND_TOOL_INPUT_SCHEMA", { enumerable: true, get: function () { return recommend_1.RECOMMEND_TOOL_INPUT_SCHEMA; } });
|
|
14
|
+
Object.defineProperty(exports, "handleRecommendTool", { enumerable: true, get: function () { return recommend_1.handleRecommendTool; } });
|
|
15
|
+
var choose_solution_1 = require("./choose-solution");
|
|
16
|
+
Object.defineProperty(exports, "CHOOSESOLUTION_TOOL_NAME", { enumerable: true, get: function () { return choose_solution_1.CHOOSESOLUTION_TOOL_NAME; } });
|
|
17
|
+
Object.defineProperty(exports, "CHOOSESOLUTION_TOOL_DESCRIPTION", { enumerable: true, get: function () { return choose_solution_1.CHOOSESOLUTION_TOOL_DESCRIPTION; } });
|
|
18
|
+
Object.defineProperty(exports, "CHOOSESOLUTION_TOOL_INPUT_SCHEMA", { enumerable: true, get: function () { return choose_solution_1.CHOOSESOLUTION_TOOL_INPUT_SCHEMA; } });
|
|
19
|
+
Object.defineProperty(exports, "handleChooseSolutionTool", { enumerable: true, get: function () { return choose_solution_1.handleChooseSolutionTool; } });
|
|
20
|
+
var answer_question_1 = require("./answer-question");
|
|
21
|
+
Object.defineProperty(exports, "ANSWERQUESTION_TOOL_NAME", { enumerable: true, get: function () { return answer_question_1.ANSWERQUESTION_TOOL_NAME; } });
|
|
22
|
+
Object.defineProperty(exports, "ANSWERQUESTION_TOOL_DESCRIPTION", { enumerable: true, get: function () { return answer_question_1.ANSWERQUESTION_TOOL_DESCRIPTION; } });
|
|
23
|
+
Object.defineProperty(exports, "ANSWERQUESTION_TOOL_INPUT_SCHEMA", { enumerable: true, get: function () { return answer_question_1.ANSWERQUESTION_TOOL_INPUT_SCHEMA; } });
|
|
24
|
+
Object.defineProperty(exports, "handleAnswerQuestionTool", { enumerable: true, get: function () { return answer_question_1.handleAnswerQuestionTool; } });
|
|
25
|
+
var generate_manifests_1 = require("./generate-manifests");
|
|
26
|
+
Object.defineProperty(exports, "GENERATEMANIFESTS_TOOL_NAME", { enumerable: true, get: function () { return generate_manifests_1.GENERATEMANIFESTS_TOOL_NAME; } });
|
|
27
|
+
Object.defineProperty(exports, "GENERATEMANIFESTS_TOOL_DESCRIPTION", { enumerable: true, get: function () { return generate_manifests_1.GENERATEMANIFESTS_TOOL_DESCRIPTION; } });
|
|
28
|
+
Object.defineProperty(exports, "GENERATEMANIFESTS_TOOL_INPUT_SCHEMA", { enumerable: true, get: function () { return generate_manifests_1.GENERATEMANIFESTS_TOOL_INPUT_SCHEMA; } });
|
|
29
|
+
Object.defineProperty(exports, "handleGenerateManifestsTool", { enumerable: true, get: function () { return generate_manifests_1.handleGenerateManifestsTool; } });
|
|
30
|
+
var deploy_manifests_1 = require("./deploy-manifests");
|
|
31
|
+
Object.defineProperty(exports, "DEPLOYMANIFESTS_TOOL_NAME", { enumerable: true, get: function () { return deploy_manifests_1.DEPLOYMANIFESTS_TOOL_NAME; } });
|
|
32
|
+
Object.defineProperty(exports, "DEPLOYMANIFESTS_TOOL_DESCRIPTION", { enumerable: true, get: function () { return deploy_manifests_1.DEPLOYMANIFESTS_TOOL_DESCRIPTION; } });
|
|
33
|
+
Object.defineProperty(exports, "DEPLOYMANIFESTS_TOOL_INPUT_SCHEMA", { enumerable: true, get: function () { return deploy_manifests_1.DEPLOYMANIFESTS_TOOL_INPUT_SCHEMA; } });
|
|
34
|
+
Object.defineProperty(exports, "handleDeployManifestsTool", { enumerable: true, get: function () { return deploy_manifests_1.handleDeployManifestsTool; } });
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recommend Tool - AI-powered Kubernetes resource recommendations
|
|
3
|
+
*/
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { DotAI } from '../core/index';
|
|
6
|
+
import { Logger } from '../core/error-handling';
|
|
7
|
+
export declare const RECOMMEND_TOOL_NAME = "recommend";
|
|
8
|
+
export declare const RECOMMEND_TOOL_DESCRIPTION = "Deploy, create, run, or setup applications on Kubernetes with AI-powered recommendations. Ask the user to describe their application first, then use their response here.";
|
|
9
|
+
export declare const RECOMMEND_TOOL_INPUT_SCHEMA: {
|
|
10
|
+
intent: z.ZodString;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Direct MCP tool handler for recommend functionality
|
|
14
|
+
*/
|
|
15
|
+
export declare function handleRecommendTool(args: {
|
|
16
|
+
intent: string;
|
|
17
|
+
}, dotAI: DotAI, logger: Logger, requestId: string): Promise<{
|
|
18
|
+
content: {
|
|
19
|
+
type: 'text';
|
|
20
|
+
text: string;
|
|
21
|
+
}[];
|
|
22
|
+
}>;
|
|
23
|
+
//# sourceMappingURL=recommend.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recommend.d.ts","sourceRoot":"","sources":["../../src/tools/recommend.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAOhD,eAAO,MAAM,mBAAmB,cAAc,CAAC;AAC/C,eAAO,MAAM,0BAA0B,8KAA8K,CAAC;AAGtN,eAAO,MAAM,2BAA2B;;CAEvC,CAAC;AAyHF;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,EACxB,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC;IAAE,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAAE,CAAC,CA0NxD"}
|