@vfarcic/dot-ai 0.150.0 → 0.152.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 +1 -1
- package/dist/core/ai-provider-factory.d.ts +0 -9
- package/dist/core/ai-provider-factory.d.ts.map +1 -1
- package/dist/core/ai-provider-factory.js +2 -34
- package/dist/core/ai-provider.interface.d.ts +2 -14
- package/dist/core/ai-provider.interface.d.ts.map +1 -1
- package/dist/core/artifacthub.d.ts +85 -0
- package/dist/core/artifacthub.d.ts.map +1 -0
- package/dist/core/artifacthub.js +106 -0
- package/dist/core/embedding-service.js +1 -1
- package/dist/core/helm-types.d.ts +39 -0
- package/dist/core/helm-types.d.ts.map +1 -0
- package/dist/core/helm-types.js +5 -0
- package/dist/core/helm-utils.d.ts +66 -0
- package/dist/core/helm-utils.d.ts.map +1 -0
- package/dist/core/helm-utils.js +196 -0
- package/dist/core/index.d.ts +7 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +12 -0
- package/dist/core/providers/noop-provider.d.ts +0 -4
- package/dist/core/providers/noop-provider.d.ts.map +1 -1
- package/dist/core/providers/noop-provider.js +0 -6
- package/dist/core/providers/vercel-provider.d.ts +0 -1
- package/dist/core/providers/vercel-provider.d.ts.map +1 -1
- package/dist/core/providers/vercel-provider.js +1 -4
- package/dist/core/schema.d.ts +32 -4
- package/dist/core/schema.d.ts.map +1 -1
- package/dist/core/schema.js +200 -18
- package/dist/core/solution-cr.d.ts.map +1 -1
- package/dist/core/solution-cr.js +2 -3
- package/dist/tools/answer-question.d.ts +1 -1
- package/dist/tools/answer-question.d.ts.map +1 -1
- package/dist/tools/answer-question.js +85 -16
- package/dist/tools/choose-solution.d.ts.map +1 -1
- package/dist/tools/choose-solution.js +36 -24
- package/dist/tools/deploy-manifests.d.ts +2 -1
- package/dist/tools/deploy-manifests.d.ts.map +1 -1
- package/dist/tools/deploy-manifests.js +86 -2
- package/dist/tools/generate-manifests.d.ts +1 -0
- package/dist/tools/generate-manifests.d.ts.map +1 -1
- package/dist/tools/generate-manifests.js +204 -1
- package/dist/tools/recommend.d.ts +3 -2
- package/dist/tools/recommend.d.ts.map +1 -1
- package/dist/tools/recommend.js +116 -3
- package/package.json +11 -12
- package/prompts/helm-chart-selection.md +65 -0
- package/prompts/helm-generation.md +85 -0
- package/prompts/intent-analysis.md +17 -0
- package/prompts/question-generation.md +34 -24
- package/prompts/resource-selection.md +52 -8
- package/shared-prompts/prd-start.md +20 -10
- package/shared-prompts/prd-update-progress.md +18 -8
- package/dist/core/providers/anthropic-provider.d.ts +0 -51
- package/dist/core/providers/anthropic-provider.d.ts.map +0 -1
- package/dist/core/providers/anthropic-provider.js +0 -468
- /package/prompts/{manifest-generation.md → capabilities-generation.md} +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* Deploy Manifests Tool - Apply Kubernetes manifests
|
|
3
|
+
* Deploy Manifests Tool - Apply Kubernetes manifests or execute Helm installations
|
|
4
|
+
* Supports both capability-based solutions (kubectl apply) and Helm-based solutions (helm install)
|
|
4
5
|
*/
|
|
5
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
7
|
exports.DEPLOYMANIFESTS_TOOL_INPUT_SCHEMA = exports.DEPLOYMANIFESTS_TOOL_DESCRIPTION = exports.DEPLOYMANIFESTS_TOOL_NAME = void 0;
|
|
@@ -9,6 +10,9 @@ const zod_1 = require("zod");
|
|
|
9
10
|
const error_handling_1 = require("../core/error-handling");
|
|
10
11
|
const deploy_operation_1 = require("../core/deploy-operation");
|
|
11
12
|
const cluster_utils_1 = require("../core/cluster-utils");
|
|
13
|
+
const generic_session_manager_1 = require("../core/generic-session-manager");
|
|
14
|
+
const solution_utils_1 = require("../core/solution-utils");
|
|
15
|
+
const helm_utils_1 = require("../core/helm-utils");
|
|
12
16
|
// Tool metadata for direct MCP registration
|
|
13
17
|
exports.DEPLOYMANIFESTS_TOOL_NAME = 'deployManifests';
|
|
14
18
|
exports.DEPLOYMANIFESTS_TOOL_DESCRIPTION = 'Deploy Kubernetes manifests from generated solution with kubectl apply --wait';
|
|
@@ -31,10 +35,89 @@ async function handleDeployManifestsTool(args, dotAI, logger, requestId) {
|
|
|
31
35
|
// args are already validated and typed when we reach this point
|
|
32
36
|
// Ensure cluster connectivity before proceeding
|
|
33
37
|
await (0, cluster_utils_1.ensureClusterConnection)(dotAI, logger, requestId, 'DeployManifestsTool');
|
|
38
|
+
// Load solution session to determine solution type
|
|
39
|
+
const sessionManager = new generic_session_manager_1.GenericSessionManager('sol');
|
|
40
|
+
const session = sessionManager.getSession(args.solutionId);
|
|
41
|
+
if (!session) {
|
|
42
|
+
throw new Error(`Solution not found: ${args.solutionId}`);
|
|
43
|
+
}
|
|
44
|
+
const solution = session.data;
|
|
45
|
+
const timeout = args.timeout || 30;
|
|
46
|
+
logger.debug('Solution loaded successfully', {
|
|
47
|
+
solutionId: args.solutionId,
|
|
48
|
+
solutionType: solution.type
|
|
49
|
+
});
|
|
50
|
+
// Branch based on solution type
|
|
51
|
+
if (solution.type === 'helm') {
|
|
52
|
+
logger.info('Detected Helm solution, using Helm deployment flow', {
|
|
53
|
+
solutionId: args.solutionId,
|
|
54
|
+
chart: solution.chart ? `${solution.chart.repositoryName}/${solution.chart.chartName}` : 'unknown'
|
|
55
|
+
});
|
|
56
|
+
if (!solution.chart) {
|
|
57
|
+
throw new Error('Helm solution missing chart information');
|
|
58
|
+
}
|
|
59
|
+
const chart = solution.chart;
|
|
60
|
+
const userAnswers = (0, solution_utils_1.extractUserAnswers)(solution);
|
|
61
|
+
const releaseName = userAnswers.name;
|
|
62
|
+
const namespace = userAnswers.namespace || 'default';
|
|
63
|
+
if (!releaseName) {
|
|
64
|
+
throw new Error('Release name (name) is required for Helm deployment');
|
|
65
|
+
}
|
|
66
|
+
// Get values path if values file exists
|
|
67
|
+
const valuesPath = (0, helm_utils_1.helmValuesExist)(args.solutionId)
|
|
68
|
+
? (0, helm_utils_1.getHelmValuesPath)(args.solutionId)
|
|
69
|
+
: undefined;
|
|
70
|
+
logger.info('Starting Helm deployment', {
|
|
71
|
+
solutionId: args.solutionId,
|
|
72
|
+
chart: `${chart.repositoryName}/${chart.chartName}`,
|
|
73
|
+
releaseName,
|
|
74
|
+
namespace,
|
|
75
|
+
hasValuesFile: !!valuesPath,
|
|
76
|
+
timeout,
|
|
77
|
+
requestId
|
|
78
|
+
});
|
|
79
|
+
const result = await (0, helm_utils_1.deployHelmRelease)(chart, releaseName, namespace, valuesPath, timeout);
|
|
80
|
+
logger.info('Helm deployment completed', {
|
|
81
|
+
success: result.success,
|
|
82
|
+
solutionId: args.solutionId,
|
|
83
|
+
releaseName,
|
|
84
|
+
namespace,
|
|
85
|
+
requestId
|
|
86
|
+
});
|
|
87
|
+
const response = {
|
|
88
|
+
success: result.success,
|
|
89
|
+
solutionId: args.solutionId,
|
|
90
|
+
solutionType: 'helm',
|
|
91
|
+
releaseName,
|
|
92
|
+
namespace,
|
|
93
|
+
chart: {
|
|
94
|
+
repository: chart.repository,
|
|
95
|
+
repositoryName: chart.repositoryName,
|
|
96
|
+
chartName: chart.chartName,
|
|
97
|
+
version: chart.version
|
|
98
|
+
},
|
|
99
|
+
message: result.success
|
|
100
|
+
? `Helm release "${releaseName}" deployed successfully to namespace "${namespace}"`
|
|
101
|
+
: `Helm deployment failed: ${result.error}`,
|
|
102
|
+
helmOutput: result.output || result.error,
|
|
103
|
+
deploymentComplete: result.success,
|
|
104
|
+
timestamp: new Date().toISOString()
|
|
105
|
+
};
|
|
106
|
+
return {
|
|
107
|
+
content: [{
|
|
108
|
+
type: 'text',
|
|
109
|
+
text: JSON.stringify(response, null, 2)
|
|
110
|
+
}]
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
// Capability-based solution: Use existing DeployOperation
|
|
114
|
+
logger.info('Using capability-based deployment flow', {
|
|
115
|
+
solutionId: args.solutionId
|
|
116
|
+
});
|
|
34
117
|
const deployOp = new deploy_operation_1.DeployOperation();
|
|
35
118
|
const deployOptions = {
|
|
36
119
|
solutionId: args.solutionId,
|
|
37
|
-
timeout
|
|
120
|
+
timeout
|
|
38
121
|
};
|
|
39
122
|
logger.info('Starting deployment operation', {
|
|
40
123
|
solutionId: args.solutionId,
|
|
@@ -53,6 +136,7 @@ async function handleDeployManifestsTool(args, dotAI, logger, requestId) {
|
|
|
53
136
|
const response = {
|
|
54
137
|
success: result.success,
|
|
55
138
|
solutionId: result.solutionId,
|
|
139
|
+
solutionType: 'capability',
|
|
56
140
|
manifestPath: result.manifestPath,
|
|
57
141
|
readinessTimeout: result.readinessTimeout,
|
|
58
142
|
message: result.message,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate-manifests.d.ts","sourceRoot":"","sources":["../../src/tools/generate-manifests.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"generate-manifests.d.ts","sourceRoot":"","sources":["../../src/tools/generate-manifests.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,KAAK,EAA2B,MAAM,eAAe,CAAC;AAC/D,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAsBhD,eAAO,MAAM,2BAA2B,sBAAsB,CAAC;AAC/D,eAAO,MAAM,kCAAkC,+IAA+I,CAAC;AAG/L,eAAO,MAAM,mCAAmC;;;CAG/C,CAAC;AAmdF;;GAEG;AACH,wBAAsB,2BAA2B,CAC/C,IAAI,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,EACrD,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,CA2OxD"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
3
|
* Generate Manifests Tool - AI-driven manifest generation with validation loop
|
|
4
|
+
* Supports both capability-based solutions (K8s manifests) and Helm-based solutions (values.yaml)
|
|
4
5
|
*/
|
|
5
6
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
6
7
|
if (k2 === undefined) k2 = k;
|
|
@@ -52,6 +53,7 @@ const solution_utils_1 = require("../core/solution-utils");
|
|
|
52
53
|
const platform_utils_1 = require("../core/platform-utils");
|
|
53
54
|
const crd_availability_1 = require("../core/crd-availability");
|
|
54
55
|
const solution_cr_1 = require("../core/solution-cr");
|
|
56
|
+
const helm_utils_1 = require("../core/helm-utils");
|
|
55
57
|
// Tool metadata for direct MCP registration
|
|
56
58
|
exports.GENERATEMANIFESTS_TOOL_NAME = 'generateManifests';
|
|
57
59
|
exports.GENERATEMANIFESTS_TOOL_DESCRIPTION = 'Generate final Kubernetes manifests from fully configured solution (ONLY after completing ALL stages: required, basic, advanced, and open)';
|
|
@@ -180,7 +182,7 @@ ${errorContext.previousManifests}
|
|
|
180
182
|
// Prepare template variables
|
|
181
183
|
const schemasData = JSON.stringify(resourceSchemas, null, 2);
|
|
182
184
|
const labelsData = dotAiLabels ? JSON.stringify(dotAiLabels, null, 2) : '{}';
|
|
183
|
-
const aiPrompt = (0, shared_prompt_loader_1.loadPrompt)('
|
|
185
|
+
const aiPrompt = (0, shared_prompt_loader_1.loadPrompt)('capabilities-generation', {
|
|
184
186
|
solution: solutionData,
|
|
185
187
|
schemas: schemasData,
|
|
186
188
|
previous_attempt: previousAttempt,
|
|
@@ -211,6 +213,194 @@ ${errorContext.previousManifests}
|
|
|
211
213
|
});
|
|
212
214
|
return manifestContent;
|
|
213
215
|
}
|
|
216
|
+
/**
|
|
217
|
+
* Generate Helm values.yaml using AI provider
|
|
218
|
+
*/
|
|
219
|
+
async function generateHelmValuesWithAI(solution, solutionId, dotAI, logger, errorContext, interaction_id) {
|
|
220
|
+
// Fetch chart values.yaml for reference
|
|
221
|
+
const chart = solution.chart;
|
|
222
|
+
const { valuesYaml } = await dotAI.schema.fetchHelmChartContent(chart);
|
|
223
|
+
// Prepare template variables
|
|
224
|
+
const solutionData = JSON.stringify(solution, null, 2);
|
|
225
|
+
const previousAttempt = errorContext ? `
|
|
226
|
+
### Generated Values:
|
|
227
|
+
\`\`\`yaml
|
|
228
|
+
${errorContext.previousValues}
|
|
229
|
+
\`\`\`
|
|
230
|
+
` : 'None - this is the first attempt.';
|
|
231
|
+
const errorDetails = errorContext ? `
|
|
232
|
+
**Attempt**: ${errorContext.attempt}
|
|
233
|
+
**Validation Errors**: ${errorContext.validationResult.errors.join(', ')}
|
|
234
|
+
**Validation Warnings**: ${errorContext.validationResult.warnings.join(', ')}
|
|
235
|
+
` : 'None - this is the first attempt.';
|
|
236
|
+
const aiPrompt = (0, shared_prompt_loader_1.loadPrompt)('helm-generation', {
|
|
237
|
+
solution: solutionData,
|
|
238
|
+
chart_values: valuesYaml || '# No default values available',
|
|
239
|
+
previous_attempt: previousAttempt,
|
|
240
|
+
error_details: errorDetails
|
|
241
|
+
});
|
|
242
|
+
const isRetry = !!errorContext;
|
|
243
|
+
logger.info('Generating Helm values with AI', {
|
|
244
|
+
isRetry,
|
|
245
|
+
attempt: errorContext?.attempt,
|
|
246
|
+
hasErrorContext: !!errorContext,
|
|
247
|
+
solutionId,
|
|
248
|
+
chart: `${chart.repositoryName}/${chart.chartName}`
|
|
249
|
+
});
|
|
250
|
+
// Get AI provider from dotAI
|
|
251
|
+
const aiProvider = dotAI.ai;
|
|
252
|
+
// Send prompt to AI
|
|
253
|
+
const response = await aiProvider.sendMessage(aiPrompt, 'helm-values-generation', {
|
|
254
|
+
user_intent: solution.intent || 'Helm chart installation',
|
|
255
|
+
interaction_id: interaction_id
|
|
256
|
+
});
|
|
257
|
+
// Extract YAML content from response
|
|
258
|
+
const valuesContent = (0, platform_utils_1.extractContentFromMarkdownCodeBlocks)(response.content, 'yaml');
|
|
259
|
+
logger.info('AI Helm values generation completed', {
|
|
260
|
+
valuesLength: valuesContent.length,
|
|
261
|
+
isRetry,
|
|
262
|
+
solutionId
|
|
263
|
+
});
|
|
264
|
+
return valuesContent;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Validate Helm installation using dry-run (wrapper around shared utility)
|
|
268
|
+
*/
|
|
269
|
+
async function validateHelmInstallation(chart, releaseName, namespace, valuesPath, logger) {
|
|
270
|
+
logger.info('Running Helm dry-run validation', {
|
|
271
|
+
chart: `${chart.repositoryName}/${chart.chartName}`,
|
|
272
|
+
releaseName,
|
|
273
|
+
namespace
|
|
274
|
+
});
|
|
275
|
+
const result = await (0, helm_utils_1.validateHelmDryRun)(chart, releaseName, namespace, valuesPath);
|
|
276
|
+
if (result.success) {
|
|
277
|
+
logger.info('Helm dry-run validation successful');
|
|
278
|
+
return {
|
|
279
|
+
valid: true,
|
|
280
|
+
errors: [],
|
|
281
|
+
warnings: []
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
logger.warn('Helm dry-run validation failed', { error: result.error });
|
|
285
|
+
return {
|
|
286
|
+
valid: false,
|
|
287
|
+
errors: [result.error || 'Unknown Helm validation error'],
|
|
288
|
+
warnings: []
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Handle Helm solution generation
|
|
293
|
+
*/
|
|
294
|
+
async function handleHelmGeneration(solution, solutionId, dotAI, logger, requestId, interaction_id) {
|
|
295
|
+
const maxAttempts = 10;
|
|
296
|
+
const chart = solution.chart;
|
|
297
|
+
const userAnswers = (0, solution_utils_1.extractUserAnswers)(solution);
|
|
298
|
+
// Extract release name and namespace from answers
|
|
299
|
+
const releaseName = userAnswers.name;
|
|
300
|
+
const namespace = userAnswers.namespace || 'default';
|
|
301
|
+
if (!releaseName) {
|
|
302
|
+
throw error_handling_1.ErrorHandler.createError(error_handling_1.ErrorCategory.VALIDATION, error_handling_1.ErrorSeverity.HIGH, 'Release name (name) is required for Helm installation', {
|
|
303
|
+
operation: 'helm_generation',
|
|
304
|
+
component: 'GenerateManifestsTool',
|
|
305
|
+
requestId,
|
|
306
|
+
suggestedActions: ['Ensure the "name" question was answered in the configuration']
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
// Prepare file paths using shared utilities
|
|
310
|
+
(0, helm_utils_1.ensureTmpDir)();
|
|
311
|
+
const valuesPath = (0, helm_utils_1.getHelmValuesPath)(solutionId);
|
|
312
|
+
// AI generation and validation loop
|
|
313
|
+
let lastError;
|
|
314
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
315
|
+
logger.info('Helm values generation attempt', {
|
|
316
|
+
attempt,
|
|
317
|
+
maxAttempts,
|
|
318
|
+
isRetry: attempt > 1,
|
|
319
|
+
requestId,
|
|
320
|
+
chart: `${chart.repositoryName}/${chart.chartName}`
|
|
321
|
+
});
|
|
322
|
+
try {
|
|
323
|
+
// Generate values.yaml with AI
|
|
324
|
+
const valuesYaml = await generateHelmValuesWithAI(solution, solutionId, dotAI, logger, lastError, interaction_id);
|
|
325
|
+
// Save values to file
|
|
326
|
+
fs.writeFileSync(valuesPath, valuesYaml, 'utf8');
|
|
327
|
+
logger.info('Helm values saved to file', { valuesPath, attempt, requestId });
|
|
328
|
+
// Save attempt for debugging
|
|
329
|
+
const attemptPath = valuesPath.replace('.yaml', `_attempt_${attempt.toString().padStart(2, '0')}.yaml`);
|
|
330
|
+
fs.writeFileSync(attemptPath, valuesYaml, 'utf8');
|
|
331
|
+
// Validate with helm dry-run
|
|
332
|
+
const validation = await validateHelmInstallation(chart, releaseName, namespace, valuesPath, logger);
|
|
333
|
+
if (validation.valid) {
|
|
334
|
+
logger.info('Helm validation successful', {
|
|
335
|
+
attempt,
|
|
336
|
+
valuesPath,
|
|
337
|
+
requestId
|
|
338
|
+
});
|
|
339
|
+
// Build user-friendly helm command with generic values file path
|
|
340
|
+
// (internal valuesPath is used for actual execution, not shown to user)
|
|
341
|
+
const helmCommand = (0, helm_utils_1.buildHelmCommand)(chart, releaseName, namespace, 'values.yaml');
|
|
342
|
+
// Check if we should show feedback message
|
|
343
|
+
const feedbackMessage = (0, index_1.maybeGetFeedbackMessage)();
|
|
344
|
+
const response = {
|
|
345
|
+
success: true,
|
|
346
|
+
status: 'helm_command_generated',
|
|
347
|
+
solutionId: solutionId,
|
|
348
|
+
solutionType: 'helm',
|
|
349
|
+
helmCommand: helmCommand,
|
|
350
|
+
valuesYaml: valuesYaml,
|
|
351
|
+
chart: {
|
|
352
|
+
repository: chart.repository,
|
|
353
|
+
repositoryName: chart.repositoryName,
|
|
354
|
+
chartName: chart.chartName,
|
|
355
|
+
version: chart.version
|
|
356
|
+
},
|
|
357
|
+
releaseName: releaseName,
|
|
358
|
+
namespace: namespace,
|
|
359
|
+
validationAttempts: attempt,
|
|
360
|
+
timestamp: new Date().toISOString(),
|
|
361
|
+
...(feedbackMessage ? { message: feedbackMessage } : {})
|
|
362
|
+
};
|
|
363
|
+
return {
|
|
364
|
+
content: [{
|
|
365
|
+
type: 'text',
|
|
366
|
+
text: JSON.stringify(response, null, 2)
|
|
367
|
+
}]
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
// Validation failed, prepare error context for next attempt
|
|
371
|
+
lastError = {
|
|
372
|
+
attempt,
|
|
373
|
+
previousValues: valuesYaml,
|
|
374
|
+
validationResult: validation
|
|
375
|
+
};
|
|
376
|
+
logger.warn('Helm validation failed', {
|
|
377
|
+
attempt,
|
|
378
|
+
maxAttempts,
|
|
379
|
+
validationErrors: validation.errors,
|
|
380
|
+
validationWarnings: validation.warnings,
|
|
381
|
+
requestId
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
catch (error) {
|
|
385
|
+
logger.error('Error during Helm values generation attempt', error);
|
|
386
|
+
if (attempt === maxAttempts) {
|
|
387
|
+
throw error;
|
|
388
|
+
}
|
|
389
|
+
// Prepare error context for retry
|
|
390
|
+
lastError = {
|
|
391
|
+
attempt,
|
|
392
|
+
previousValues: lastError?.previousValues || '',
|
|
393
|
+
validationResult: {
|
|
394
|
+
valid: false,
|
|
395
|
+
errors: [error instanceof Error ? error.message : String(error)],
|
|
396
|
+
warnings: []
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
// All attempts failed
|
|
402
|
+
throw new Error(`Failed to generate valid Helm values after ${maxAttempts} attempts. Last errors: ${lastError?.validationResult.errors.join(', ')}`);
|
|
403
|
+
}
|
|
214
404
|
/**
|
|
215
405
|
* Direct MCP tool handler for generateManifests functionality
|
|
216
406
|
*/
|
|
@@ -247,9 +437,22 @@ async function handleGenerateManifestsTool(args, dotAI, logger, requestId) {
|
|
|
247
437
|
const solution = session.data;
|
|
248
438
|
logger.debug('Solution loaded successfully', {
|
|
249
439
|
solutionId: args.solutionId,
|
|
440
|
+
solutionType: solution.type,
|
|
250
441
|
hasQuestions: !!solution.questions,
|
|
251
442
|
primaryResources: solution.resources
|
|
252
443
|
});
|
|
444
|
+
// Branch based on solution type
|
|
445
|
+
if (solution.type === 'helm') {
|
|
446
|
+
logger.info('Detected Helm solution, using Helm generation flow', {
|
|
447
|
+
solutionId: args.solutionId,
|
|
448
|
+
chart: solution.chart ? `${solution.chart.repositoryName}/${solution.chart.chartName}` : 'unknown'
|
|
449
|
+
});
|
|
450
|
+
return await handleHelmGeneration(solution, args.solutionId, dotAI, logger, requestId, args.interaction_id);
|
|
451
|
+
}
|
|
452
|
+
// Capability-based solution: Generate Kubernetes manifests
|
|
453
|
+
logger.info('Using capability-based manifest generation flow', {
|
|
454
|
+
solutionId: args.solutionId
|
|
455
|
+
});
|
|
253
456
|
// Prepare file path for manifests (store in tmp directory)
|
|
254
457
|
const tmpDir = path.join(process.cwd(), 'tmp');
|
|
255
458
|
if (!fs.existsSync(tmpDir)) {
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { DotAI } from '../core/index';
|
|
6
6
|
import { Logger } from '../core/error-handling';
|
|
7
|
+
import { HelmChartInfo } from '../core/helm-types';
|
|
7
8
|
export declare const RECOMMEND_TOOL_NAME = "recommend";
|
|
8
9
|
export declare const RECOMMEND_TOOL_DESCRIPTION = "Deploy applications, infrastructure, and services using Kubernetes resources with AI recommendations. Supports cloud resources via operators like Crossplane, cluster management via CAPI, and traditional Kubernetes workloads. Describe what you want to deploy. Does NOT handle policy creation, organizational patterns, or resource capabilities - use manageOrgData for those.";
|
|
9
10
|
export declare const RECOMMEND_TOOL_INPUT_SCHEMA: {
|
|
@@ -21,13 +22,13 @@ export interface SolutionData {
|
|
|
21
22
|
score: number;
|
|
22
23
|
description: string;
|
|
23
24
|
reasons: string[];
|
|
24
|
-
|
|
25
|
-
resources: Array<{
|
|
25
|
+
resources?: Array<{
|
|
26
26
|
kind: string;
|
|
27
27
|
apiVersion: string;
|
|
28
28
|
group: string;
|
|
29
29
|
description: string;
|
|
30
30
|
}>;
|
|
31
|
+
chart?: HelmChartInfo;
|
|
31
32
|
questions: {
|
|
32
33
|
required?: any[];
|
|
33
34
|
basic?: any[];
|
|
@@ -1 +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;
|
|
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;AAUhD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGnD,eAAO,MAAM,mBAAmB,cAAc,CAAC;AAC/C,eAAO,MAAM,0BAA0B,yXAAyX,CAAC;AAGja,eAAO,MAAM,2BAA2B;;;;;;;;CAWvC,CAAC;AAIF,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,EAAE,CAAC;IAElB,SAAS,CAAC,EAAE,KAAK,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,UAAU,EAAE,MAAM,CAAC;QACnB,KAAK,EAAE,MAAM,CAAC;QACd,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC,CAAC;IAEH,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,SAAS,EAAE;QACT,QAAQ,CAAC,EAAE,GAAG,EAAE,CAAC;QACjB,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC;QACd,QAAQ,CAAC,EAAE,GAAG,EAAE,CAAC;QACjB,IAAI,CAAC,EAAE,GAAG,CAAC;QACX,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;KAC7B,CAAC;IACF,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAuED;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,GAAG,EACT,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,CAuXxD"}
|
package/dist/tools/recommend.js
CHANGED
|
@@ -16,6 +16,7 @@ const generate_manifests_1 = require("./generate-manifests");
|
|
|
16
16
|
const deploy_manifests_1 = require("./deploy-manifests");
|
|
17
17
|
const shared_prompt_loader_1 = require("../core/shared-prompt-loader");
|
|
18
18
|
const platform_utils_1 = require("../core/platform-utils");
|
|
19
|
+
const artifacthub_1 = require("../core/artifacthub");
|
|
19
20
|
// Tool metadata for direct MCP registration
|
|
20
21
|
exports.RECOMMEND_TOOL_NAME = 'recommend';
|
|
21
22
|
exports.RECOMMEND_TOOL_DESCRIPTION = 'Deploy applications, infrastructure, and services using Kubernetes resources with AI recommendations. Supports cloud resources via operators like Crossplane, cluster management via CAPI, and traditional Kubernetes workloads. Describe what you want to deploy. Does NOT handle policy creation, organizational patterns, or resource capabilities - use manageOrgData for those.';
|
|
@@ -187,7 +188,121 @@ async function handleRecommendTool(args, dotAI, logger, requestId) {
|
|
|
187
188
|
};
|
|
188
189
|
// Find best solutions for the user intent
|
|
189
190
|
logger.debug('Generating recommendations with AI', { requestId });
|
|
190
|
-
const
|
|
191
|
+
const solutionResult = await recommender.findBestSolutions(args.intent, explainResourceFn, args.interaction_id);
|
|
192
|
+
// Handle Helm recommendation case
|
|
193
|
+
if (solutionResult.helmRecommendation) {
|
|
194
|
+
logger.info('Helm installation recommended, searching ArtifactHub', {
|
|
195
|
+
requestId,
|
|
196
|
+
suggestedTool: solutionResult.helmRecommendation.suggestedTool,
|
|
197
|
+
searchQuery: solutionResult.helmRecommendation.searchQuery,
|
|
198
|
+
reason: solutionResult.helmRecommendation.reason
|
|
199
|
+
});
|
|
200
|
+
// Search ArtifactHub for matching charts
|
|
201
|
+
const artifactHub = new artifacthub_1.ArtifactHubService();
|
|
202
|
+
const charts = await artifactHub.searchCharts(solutionResult.helmRecommendation.searchQuery, 10 // Get top 10 results for AI to analyze
|
|
203
|
+
);
|
|
204
|
+
if (charts.length === 0) {
|
|
205
|
+
// No charts found on ArtifactHub
|
|
206
|
+
logger.warn('No charts found on ArtifactHub', { requestId, searchQuery: solutionResult.helmRecommendation.searchQuery });
|
|
207
|
+
return {
|
|
208
|
+
content: [{
|
|
209
|
+
type: 'text',
|
|
210
|
+
text: JSON.stringify({
|
|
211
|
+
status: 'no_charts_found',
|
|
212
|
+
searchQuery: solutionResult.helmRecommendation.searchQuery,
|
|
213
|
+
reason: solutionResult.helmRecommendation.reason,
|
|
214
|
+
message: `No Helm charts found on ArtifactHub for "${solutionResult.helmRecommendation.suggestedTool}". We currently only support charts available on ArtifactHub. If you need support for charts from other sources, please open an issue at https://github.com/vfarcic/dot-ai/issues/new`
|
|
215
|
+
}, null, 2)
|
|
216
|
+
}]
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
// Format charts for AI analysis
|
|
220
|
+
const chartsText = artifactHub.formatChartsForAI(charts);
|
|
221
|
+
// Load prompt and send to AI for chart selection
|
|
222
|
+
const chartSelectionPrompt = (0, shared_prompt_loader_1.loadPrompt)('helm-chart-selection', {
|
|
223
|
+
intent: args.intent,
|
|
224
|
+
charts: chartsText
|
|
225
|
+
});
|
|
226
|
+
const aiResponse = await dotAI.ai.sendMessage(chartSelectionPrompt, 'recommend-helm-chart-selection', {
|
|
227
|
+
user_intent: args.intent,
|
|
228
|
+
interaction_id: args.interaction_id
|
|
229
|
+
});
|
|
230
|
+
// Parse AI response
|
|
231
|
+
const aiSelection = (0, platform_utils_1.extractJsonFromAIResponse)(aiResponse.content);
|
|
232
|
+
if (!aiSelection.solutions || aiSelection.solutions.length === 0) {
|
|
233
|
+
// AI couldn't find matching charts
|
|
234
|
+
logger.warn('AI found no matching charts', { requestId, noMatchReason: aiSelection.noMatchReason });
|
|
235
|
+
return {
|
|
236
|
+
content: [{
|
|
237
|
+
type: 'text',
|
|
238
|
+
text: JSON.stringify({
|
|
239
|
+
status: 'no_matching_charts',
|
|
240
|
+
reason: aiSelection.noMatchReason || 'No charts matched the user intent',
|
|
241
|
+
searchQuery: solutionResult.helmRecommendation.searchQuery,
|
|
242
|
+
instruction: 'Consider refining your request or manually specifying a Helm chart.'
|
|
243
|
+
}, null, 2)
|
|
244
|
+
}]
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
// Create sessions for Helm solutions
|
|
248
|
+
const timestamp = new Date().toISOString();
|
|
249
|
+
const helmSolutionSummaries = [];
|
|
250
|
+
for (const aiSolution of aiSelection.solutions) {
|
|
251
|
+
// Find the original chart data from ArtifactHub results
|
|
252
|
+
const originalChart = charts.find(c => c.name === aiSolution.chartName);
|
|
253
|
+
const solutionData = {
|
|
254
|
+
intent: args.intent,
|
|
255
|
+
type: 'helm',
|
|
256
|
+
score: aiSolution.score,
|
|
257
|
+
description: aiSolution.description,
|
|
258
|
+
reasons: aiSolution.reasons,
|
|
259
|
+
chart: {
|
|
260
|
+
repository: aiSolution.repositoryUrl,
|
|
261
|
+
repositoryName: aiSolution.repositoryName,
|
|
262
|
+
chartName: aiSolution.chartName,
|
|
263
|
+
version: aiSolution.version,
|
|
264
|
+
appVersion: aiSolution.appVersion,
|
|
265
|
+
official: originalChart?.official || originalChart?.repository?.official,
|
|
266
|
+
verifiedPublisher: originalChart?.verified_publisher || originalChart?.repository?.verified_publisher
|
|
267
|
+
},
|
|
268
|
+
questions: { required: [], basic: [], advanced: [] }, // Will be generated from chart values later
|
|
269
|
+
answers: {},
|
|
270
|
+
timestamp
|
|
271
|
+
};
|
|
272
|
+
const session = sessionManager.createSession(solutionData);
|
|
273
|
+
const solutionId = session.sessionId;
|
|
274
|
+
logger.debug('Helm solution session created', { requestId, solutionId });
|
|
275
|
+
helmSolutionSummaries.push({
|
|
276
|
+
solutionId,
|
|
277
|
+
type: 'helm',
|
|
278
|
+
score: aiSolution.score,
|
|
279
|
+
description: aiSolution.description,
|
|
280
|
+
chart: solutionData.chart,
|
|
281
|
+
reasons: aiSolution.reasons
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
// Build Helm solutions response
|
|
285
|
+
const helmResponse = {
|
|
286
|
+
intent: args.intent,
|
|
287
|
+
solutions: helmSolutionSummaries,
|
|
288
|
+
helmInstallation: true,
|
|
289
|
+
nextAction: 'Call recommend tool with stage: chooseSolution and your preferred solutionId',
|
|
290
|
+
guidance: '🔴 CRITICAL: Present these Helm chart options to the user and ask them to choose. DO NOT automatically call chooseSolution() without user input. Show the chart details (repository, version, official status) to help users decide.',
|
|
291
|
+
timestamp
|
|
292
|
+
};
|
|
293
|
+
logger.info('Helm solutions prepared', {
|
|
294
|
+
requestId,
|
|
295
|
+
solutionCount: helmSolutionSummaries.length,
|
|
296
|
+
topScore: helmSolutionSummaries[0]?.score
|
|
297
|
+
});
|
|
298
|
+
return {
|
|
299
|
+
content: [{
|
|
300
|
+
type: 'text',
|
|
301
|
+
text: JSON.stringify(helmResponse, null, 2)
|
|
302
|
+
}]
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
const solutions = solutionResult.solutions;
|
|
191
306
|
logger.info('Recommendation process completed', {
|
|
192
307
|
requestId,
|
|
193
308
|
solutionCount: solutions.length,
|
|
@@ -206,7 +321,6 @@ async function handleRecommendTool(args, dotAI, logger, requestId) {
|
|
|
206
321
|
score: solution.score,
|
|
207
322
|
description: solution.description,
|
|
208
323
|
reasons: solution.reasons,
|
|
209
|
-
analysis: solution.analysis,
|
|
210
324
|
resources: solution.resources.map(r => ({
|
|
211
325
|
kind: r.kind,
|
|
212
326
|
apiVersion: r.apiVersion,
|
|
@@ -236,7 +350,6 @@ async function handleRecommendTool(args, dotAI, logger, requestId) {
|
|
|
236
350
|
description: r.description?.split('\n')[0] || `${r.kind} resource` // Use first line of description or fallback
|
|
237
351
|
})),
|
|
238
352
|
reasons: solution.reasons,
|
|
239
|
-
analysis: solution.analysis,
|
|
240
353
|
appliedPatterns: solution.appliedPatterns || [],
|
|
241
354
|
relevantPolicies: solution.questions?.relevantPolicies || []
|
|
242
355
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vfarcic/dot-ai",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.152.0",
|
|
4
4
|
"description": "AI-powered development productivity platform that enhances software development workflows through intelligent automation and AI-driven assistance",
|
|
5
5
|
"mcpName": "io.github.vfarcic/dot-ai",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -18,16 +18,16 @@
|
|
|
18
18
|
"test:integration:server": "KUBECONFIG=./kubeconfig-test.yaml PORT=3456 DOT_AI_SESSION_DIR=./tmp/sessions TRANSPORT_TYPE=http QDRANT_URL=http://localhost:6335 QDRANT_CAPABILITIES_COLLECTION=capabilities-policies ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY OPENAI_API_KEY=$OPENAI_API_KEY node dist/mcp/server.js",
|
|
19
19
|
"test:integration": "./tests/integration/infrastructure/run-integration-tests.sh",
|
|
20
20
|
"test:integration:watch": "vitest --config=vitest.integration.config.ts --test-timeout=1200000",
|
|
21
|
-
"test:integration:sonnet": "AI_PROVIDER=anthropic
|
|
22
|
-
"test:integration:opus": "AI_PROVIDER=anthropic_opus
|
|
23
|
-
"test:integration:haiku": "AI_PROVIDER=anthropic_haiku
|
|
24
|
-
"test:integration:gpt": "AI_PROVIDER=openai
|
|
25
|
-
"test:integration:gemini": "AI_PROVIDER=google
|
|
26
|
-
"test:integration:grok": "AI_PROVIDER=xai
|
|
27
|
-
"test:integration:kimi": "AI_PROVIDER=kimi
|
|
28
|
-
"test:integration:kimi-thinking": "AI_PROVIDER=kimi_thinking
|
|
29
|
-
"test:integration:bedrock": "AI_PROVIDER=amazon_bedrock AI_MODEL=global.anthropic.claude-sonnet-4-20250514-v1:0
|
|
30
|
-
"test:integration:custom-endpoint": "AI_PROVIDER=openai
|
|
21
|
+
"test:integration:sonnet": "AI_PROVIDER=anthropic DEBUG_DOT_AI=true ./tests/integration/infrastructure/run-integration-tests.sh",
|
|
22
|
+
"test:integration:opus": "AI_PROVIDER=anthropic_opus DEBUG_DOT_AI=true ./tests/integration/infrastructure/run-integration-tests.sh",
|
|
23
|
+
"test:integration:haiku": "AI_PROVIDER=anthropic_haiku DEBUG_DOT_AI=true ./tests/integration/infrastructure/run-integration-tests.sh",
|
|
24
|
+
"test:integration:gpt": "AI_PROVIDER=openai DEBUG_DOT_AI=true ./tests/integration/infrastructure/run-integration-tests.sh",
|
|
25
|
+
"test:integration:gemini": "AI_PROVIDER=google DEBUG_DOT_AI=true ./tests/integration/infrastructure/run-integration-tests.sh",
|
|
26
|
+
"test:integration:grok": "AI_PROVIDER=xai DEBUG_DOT_AI=true ./tests/integration/infrastructure/run-integration-tests.sh",
|
|
27
|
+
"test:integration:kimi": "AI_PROVIDER=kimi DEBUG_DOT_AI=true ./tests/integration/infrastructure/run-integration-tests.sh",
|
|
28
|
+
"test:integration:kimi-thinking": "AI_PROVIDER=kimi_thinking DEBUG_DOT_AI=true ./tests/integration/infrastructure/run-integration-tests.sh",
|
|
29
|
+
"test:integration:bedrock": "AI_PROVIDER=amazon_bedrock AI_MODEL=global.anthropic.claude-sonnet-4-20250514-v1:0 DEBUG_DOT_AI=true ./tests/integration/infrastructure/run-integration-tests.sh",
|
|
30
|
+
"test:integration:custom-endpoint": "AI_PROVIDER=openai DEBUG_DOT_AI=true ./tests/integration/infrastructure/run-integration-tests.sh",
|
|
31
31
|
"eval:comparative": "DEBUG_DOT_AI=true npx tsx src/evaluation/eval-runner.ts",
|
|
32
32
|
"eval:platform-synthesis": "DEBUG_DOT_AI=true npx tsx src/evaluation/run-platform-synthesis.ts",
|
|
33
33
|
"clean": "rm -rf dist",
|
|
@@ -100,7 +100,6 @@
|
|
|
100
100
|
"@ai-sdk/google": "^2.0.17",
|
|
101
101
|
"@ai-sdk/openai": "^2.0.42",
|
|
102
102
|
"@ai-sdk/xai": "^2.0.26",
|
|
103
|
-
"@anthropic-ai/sdk": "^0.65.0",
|
|
104
103
|
"@kubernetes/client-node": "^1.3.0",
|
|
105
104
|
"@modelcontextprotocol/sdk": "^1.13.2",
|
|
106
105
|
"@openrouter/ai-sdk-provider": "^1.2.0",
|