@vfarcic/dot-ai 1.2.4 → 1.4.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/dist/core/artifacthub.d.ts.map +1 -1
- package/dist/core/artifacthub.js +16 -10
- package/dist/core/base-vector-service.d.ts.map +1 -1
- package/dist/core/base-vector-service.js +19 -10
- package/dist/core/capabilities.d.ts.map +1 -1
- package/dist/core/capabilities.js +17 -11
- package/dist/core/capability-operations.d.ts.map +1 -1
- package/dist/core/capability-operations.js +118 -98
- package/dist/core/crd-availability.d.ts.map +1 -1
- package/dist/core/crd-availability.js +2 -2
- package/dist/core/deploy-operation.d.ts.map +1 -1
- package/dist/core/deploy-operation.js +9 -6
- package/dist/core/discovery.d.ts.map +1 -1
- package/dist/core/discovery.js +157 -56
- package/dist/core/embedding-service.d.ts +1 -1
- package/dist/core/embedding-service.d.ts.map +1 -1
- package/dist/core/embedding-service.js +76 -43
- package/dist/core/model-config.d.ts +3 -3
- package/dist/core/model-config.js +3 -3
- package/dist/core/packaging.d.ts.map +1 -1
- package/dist/core/packaging.js +11 -10
- package/dist/core/platform-utils.d.ts.map +1 -1
- package/dist/core/platform-utils.js +6 -2
- package/dist/core/plugin-manager.d.ts.map +1 -1
- package/dist/core/plugin-manager.js +26 -20
- package/dist/core/policy-operations.d.ts.map +1 -1
- package/dist/core/policy-operations.js +111 -65
- package/dist/core/providers/host-provider.d.ts.map +1 -1
- package/dist/core/providers/host-provider.js +12 -5
- package/dist/core/providers/vercel-provider.d.ts.map +1 -1
- package/dist/core/providers/vercel-provider.js +4 -2
- package/dist/core/schema.d.ts.map +1 -1
- package/dist/core/schema.js +173 -95
- package/dist/core/session-utils.d.ts.map +1 -1
- package/dist/core/session-utils.js +3 -3
- package/dist/core/user-prompts-loader.d.ts.map +1 -1
- package/dist/core/user-prompts-loader.js +2 -4
- package/dist/evaluation/datasets/loader.d.ts.map +1 -1
- package/dist/evaluation/datasets/loader.js +7 -3
- package/dist/interfaces/embedding-migration-handler.d.ts +16 -0
- package/dist/interfaces/embedding-migration-handler.d.ts.map +1 -0
- package/dist/interfaces/embedding-migration-handler.js +296 -0
- package/dist/interfaces/rest-api.d.ts +8 -0
- package/dist/interfaces/rest-api.d.ts.map +1 -1
- package/dist/interfaces/rest-api.js +294 -132
- package/dist/interfaces/rest-route-registry.js +3 -3
- package/dist/interfaces/routes/index.d.ts.map +1 -1
- package/dist/interfaces/routes/index.js +72 -12
- package/dist/interfaces/schemas/common.d.ts +2 -0
- package/dist/interfaces/schemas/common.d.ts.map +1 -1
- package/dist/interfaces/schemas/common.js +2 -0
- package/dist/interfaces/schemas/embeddings.d.ts +138 -0
- package/dist/interfaces/schemas/embeddings.d.ts.map +1 -0
- package/dist/interfaces/schemas/embeddings.js +79 -0
- package/dist/interfaces/schemas/index.d.ts +2 -1
- package/dist/interfaces/schemas/index.d.ts.map +1 -1
- package/dist/interfaces/schemas/index.js +14 -2
- package/dist/interfaces/schemas/prompts.d.ts +37 -0
- package/dist/interfaces/schemas/prompts.d.ts.map +1 -1
- package/dist/interfaces/schemas/prompts.js +18 -1
- package/dist/tools/answer-question.d.ts.map +1 -1
- package/dist/tools/answer-question.js +109 -81
- package/dist/tools/generate-manifests.d.ts.map +1 -1
- package/dist/tools/generate-manifests.js +163 -103
- package/dist/tools/operate-analysis.d.ts.map +1 -1
- package/dist/tools/operate-analysis.js +35 -18
- package/dist/tools/operate.d.ts.map +1 -1
- package/dist/tools/operate.js +47 -17
- package/dist/tools/prompts.d.ts.map +1 -1
- package/dist/tools/prompts.js +1 -1
- package/dist/tools/remediate.d.ts.map +1 -1
- package/dist/tools/remediate.js +205 -116
- package/package.json +6 -6
- package/prompts/remediate-system.md +1 -1
|
@@ -84,7 +84,7 @@ function buildHelmCommandForDisplay(chart, releaseName, namespace, valuesPath) {
|
|
|
84
84
|
releaseName,
|
|
85
85
|
`${chart.repositoryName}/${chart.chartName}`,
|
|
86
86
|
`--namespace ${namespace}`,
|
|
87
|
-
'--create-namespace'
|
|
87
|
+
'--create-namespace',
|
|
88
88
|
];
|
|
89
89
|
if (chart.version) {
|
|
90
90
|
parts.push(`--version ${chart.version}`);
|
|
@@ -100,8 +100,14 @@ exports.GENERATEMANIFESTS_TOOL_NAME = 'generateManifests';
|
|
|
100
100
|
exports.GENERATEMANIFESTS_TOOL_DESCRIPTION = 'Generate final Kubernetes manifests from fully configured solution (ONLY after completing ALL stages: required, basic, advanced, and open)';
|
|
101
101
|
// Zod schema for MCP registration
|
|
102
102
|
exports.GENERATEMANIFESTS_TOOL_INPUT_SCHEMA = {
|
|
103
|
-
solutionId: zod_1.z
|
|
104
|
-
|
|
103
|
+
solutionId: zod_1.z
|
|
104
|
+
.string()
|
|
105
|
+
.regex(/^sol-\d+-[a-f0-9]{8}$/)
|
|
106
|
+
.describe('The solution ID to generate manifests for (e.g., sol-1762983784617-9ddae2b8)'),
|
|
107
|
+
interaction_id: zod_1.z
|
|
108
|
+
.string()
|
|
109
|
+
.optional()
|
|
110
|
+
.describe('INTERNAL ONLY - Do not populate. Used for evaluation dataset generation.'),
|
|
105
111
|
};
|
|
106
112
|
/**
|
|
107
113
|
* Retrieve schemas for resources specified in the solution
|
|
@@ -112,7 +118,7 @@ async function retrieveResourceSchemas(solution, dotAI, logger) {
|
|
|
112
118
|
const resourceRefs = (solution.resources || []).map((resource) => ({
|
|
113
119
|
kind: resource.kind,
|
|
114
120
|
apiVersion: resource.apiVersion,
|
|
115
|
-
group: resource.group
|
|
121
|
+
group: resource.group,
|
|
116
122
|
}));
|
|
117
123
|
if (resourceRefs.length === 0) {
|
|
118
124
|
logger.warn('No resources found in solution for schema retrieval');
|
|
@@ -120,7 +126,7 @@ async function retrieveResourceSchemas(solution, dotAI, logger) {
|
|
|
120
126
|
}
|
|
121
127
|
logger.info('Retrieving schemas for solution resources', {
|
|
122
128
|
resourceCount: resourceRefs.length,
|
|
123
|
-
resources: resourceRefs.map((r) => `${r.kind}@${r.apiVersion}`)
|
|
129
|
+
resources: resourceRefs.map((r) => `${r.kind}@${r.apiVersion}`),
|
|
124
130
|
});
|
|
125
131
|
const schemas = {};
|
|
126
132
|
// Retrieve schema for each resource
|
|
@@ -134,29 +140,29 @@ async function retrieveResourceSchemas(solution, dotAI, logger) {
|
|
|
134
140
|
kind: resourceRef.kind,
|
|
135
141
|
apiVersion: resourceRef.apiVersion,
|
|
136
142
|
explanation,
|
|
137
|
-
retrievedAt: new Date().toISOString()
|
|
143
|
+
retrievedAt: new Date().toISOString(),
|
|
138
144
|
};
|
|
139
145
|
logger.debug('Schema retrieved successfully', {
|
|
140
146
|
resourceKey,
|
|
141
|
-
schemaLength: explanation.length
|
|
147
|
+
schemaLength: explanation.length,
|
|
142
148
|
});
|
|
143
149
|
}
|
|
144
150
|
catch (error) {
|
|
145
151
|
logger.error('Failed to retrieve schema for resource', error, {
|
|
146
|
-
resource: resourceRef
|
|
152
|
+
resource: resourceRef,
|
|
147
153
|
});
|
|
148
154
|
// Fail fast - if we can't get schemas, manifest generation will likely fail
|
|
149
|
-
throw new Error(`Failed to retrieve schema for ${resourceRef.kind}: ${error instanceof Error ? error.message : String(error)}
|
|
155
|
+
throw new Error(`Failed to retrieve schema for ${resourceRef.kind}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
150
156
|
}
|
|
151
157
|
}
|
|
152
158
|
logger.info('All resource schemas retrieved successfully', {
|
|
153
|
-
schemaCount: Object.keys(schemas).length
|
|
159
|
+
schemaCount: Object.keys(schemas).length,
|
|
154
160
|
});
|
|
155
161
|
return schemas;
|
|
156
162
|
}
|
|
157
163
|
catch (error) {
|
|
158
164
|
logger.error('Schema retrieval failed', error);
|
|
159
|
-
throw new Error(`Failed to retrieve resource schemas: ${error instanceof Error ? error.message : String(error)}
|
|
165
|
+
throw new Error(`Failed to retrieve resource schemas: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
160
166
|
}
|
|
161
167
|
}
|
|
162
168
|
/**
|
|
@@ -170,7 +176,7 @@ function validateYamlSyntax(yamlContent) {
|
|
|
170
176
|
catch (error) {
|
|
171
177
|
return {
|
|
172
178
|
valid: false,
|
|
173
|
-
error: error instanceof Error ? error.message : 'Unknown YAML syntax error'
|
|
179
|
+
error: error instanceof Error ? error.message : 'Unknown YAML syntax error',
|
|
174
180
|
};
|
|
175
181
|
}
|
|
176
182
|
}
|
|
@@ -227,7 +233,7 @@ async function validateManifests(yamlPath) {
|
|
|
227
233
|
return {
|
|
228
234
|
valid: false,
|
|
229
235
|
errors: [`Manifest file not found: ${yamlPath}`],
|
|
230
|
-
warnings: []
|
|
236
|
+
warnings: [],
|
|
231
237
|
};
|
|
232
238
|
}
|
|
233
239
|
// Read YAML content for syntax validation
|
|
@@ -238,7 +244,7 @@ async function validateManifests(yamlPath) {
|
|
|
238
244
|
return {
|
|
239
245
|
valid: false,
|
|
240
246
|
errors: [`YAML syntax error: ${syntaxCheck.error}`],
|
|
241
|
-
warnings: []
|
|
247
|
+
warnings: [],
|
|
242
248
|
};
|
|
243
249
|
}
|
|
244
250
|
// 2. kubectl dry-run validation using ManifestValidator
|
|
@@ -254,17 +260,21 @@ async function generateManifestsWithAI(solution, solutionId, dotAI, logger, erro
|
|
|
254
260
|
const resourceSchemas = await retrieveResourceSchemas(solution, dotAI, logger);
|
|
255
261
|
// Prepare template variables
|
|
256
262
|
const solutionData = JSON.stringify(solution, null, 2);
|
|
257
|
-
const previousAttempt = errorContext
|
|
263
|
+
const previousAttempt = errorContext
|
|
264
|
+
? `
|
|
258
265
|
### Generated Manifests:
|
|
259
266
|
\`\`\`yaml
|
|
260
267
|
${errorContext.previousManifests}
|
|
261
268
|
\`\`\`
|
|
262
|
-
`
|
|
263
|
-
|
|
269
|
+
`
|
|
270
|
+
: 'None - this is the first attempt.';
|
|
271
|
+
const errorDetails = errorContext
|
|
272
|
+
? `
|
|
264
273
|
**Attempt**: ${errorContext.attempt}
|
|
265
274
|
**Validation Errors**: ${errorContext.validationResult.errors.join(', ')}
|
|
266
275
|
**Validation Warnings**: ${errorContext.validationResult.warnings.join(', ')}
|
|
267
|
-
`
|
|
276
|
+
`
|
|
277
|
+
: 'None - this is the first attempt.';
|
|
268
278
|
// Prepare template variables
|
|
269
279
|
const schemasData = JSON.stringify(resourceSchemas, null, 2);
|
|
270
280
|
const labelsData = dotAiLabels ? JSON.stringify(dotAiLabels, null, 2) : '{}';
|
|
@@ -273,21 +283,21 @@ ${errorContext.previousManifests}
|
|
|
273
283
|
schemas: schemasData,
|
|
274
284
|
previous_attempt: previousAttempt,
|
|
275
285
|
error_details: errorDetails,
|
|
276
|
-
labels: labelsData
|
|
286
|
+
labels: labelsData,
|
|
277
287
|
});
|
|
278
288
|
const isRetry = !!errorContext;
|
|
279
289
|
logger.info('Generating manifests with AI', {
|
|
280
290
|
isRetry,
|
|
281
291
|
attempt: errorContext?.attempt,
|
|
282
292
|
hasErrorContext: !!errorContext,
|
|
283
|
-
solutionId
|
|
293
|
+
solutionId,
|
|
284
294
|
});
|
|
285
295
|
// Get AI provider from dotAI
|
|
286
296
|
const aiProvider = dotAI.ai;
|
|
287
297
|
// Send prompt to AI
|
|
288
298
|
const response = await aiProvider.sendMessage(aiPrompt, 'recommend-manifests-generation', {
|
|
289
299
|
user_intent: solution.initialIntent || 'Kubernetes manifest generation',
|
|
290
|
-
interaction_id: interaction_id
|
|
300
|
+
interaction_id: interaction_id,
|
|
291
301
|
});
|
|
292
302
|
// Extract YAML content from response
|
|
293
303
|
// Use shared utility to extract from code blocks if wrapped
|
|
@@ -295,7 +305,7 @@ ${errorContext.previousManifests}
|
|
|
295
305
|
logger.info('AI manifest generation completed', {
|
|
296
306
|
manifestLength: manifestContent.length,
|
|
297
307
|
isRetry,
|
|
298
|
-
solutionId
|
|
308
|
+
solutionId,
|
|
299
309
|
});
|
|
300
310
|
return manifestContent;
|
|
301
311
|
}
|
|
@@ -308,22 +318,26 @@ async function generateHelmValuesWithAI(solution, solutionId, dotAI, logger, err
|
|
|
308
318
|
const { valuesYaml } = await dotAI.schema.fetchHelmChartContent(chart);
|
|
309
319
|
// Prepare template variables
|
|
310
320
|
const solutionData = JSON.stringify(solution, null, 2);
|
|
311
|
-
const previousAttempt = errorContext
|
|
321
|
+
const previousAttempt = errorContext
|
|
322
|
+
? `
|
|
312
323
|
### Generated Values:
|
|
313
324
|
\`\`\`yaml
|
|
314
325
|
${errorContext.previousValues}
|
|
315
326
|
\`\`\`
|
|
316
|
-
`
|
|
317
|
-
|
|
327
|
+
`
|
|
328
|
+
: 'None - this is the first attempt.';
|
|
329
|
+
const errorDetails = errorContext
|
|
330
|
+
? `
|
|
318
331
|
**Attempt**: ${errorContext.attempt}
|
|
319
332
|
**Validation Errors**: ${errorContext.validationResult.errors.join(', ')}
|
|
320
333
|
**Validation Warnings**: ${errorContext.validationResult.warnings.join(', ')}
|
|
321
|
-
`
|
|
334
|
+
`
|
|
335
|
+
: 'None - this is the first attempt.';
|
|
322
336
|
const aiPrompt = (0, shared_prompt_loader_1.loadPrompt)('helm-generation', {
|
|
323
337
|
solution: solutionData,
|
|
324
338
|
chart_values: valuesYaml || '# No default values available',
|
|
325
339
|
previous_attempt: previousAttempt,
|
|
326
|
-
error_details: errorDetails
|
|
340
|
+
error_details: errorDetails,
|
|
327
341
|
});
|
|
328
342
|
const isRetry = !!errorContext;
|
|
329
343
|
logger.info('Generating Helm values with AI', {
|
|
@@ -331,21 +345,21 @@ ${errorContext.previousValues}
|
|
|
331
345
|
attempt: errorContext?.attempt,
|
|
332
346
|
hasErrorContext: !!errorContext,
|
|
333
347
|
solutionId,
|
|
334
|
-
chart: `${chart.repositoryName}/${chart.chartName}
|
|
348
|
+
chart: `${chart.repositoryName}/${chart.chartName}`,
|
|
335
349
|
});
|
|
336
350
|
// Get AI provider from dotAI
|
|
337
351
|
const aiProvider = dotAI.ai;
|
|
338
352
|
// Send prompt to AI
|
|
339
353
|
const response = await aiProvider.sendMessage(aiPrompt, 'helm-values-generation', {
|
|
340
354
|
user_intent: solution.intent || 'Helm chart installation',
|
|
341
|
-
interaction_id: interaction_id
|
|
355
|
+
interaction_id: interaction_id,
|
|
342
356
|
});
|
|
343
357
|
// Extract YAML content from response
|
|
344
358
|
const valuesContent = (0, platform_utils_1.extractContentFromMarkdownCodeBlocks)(response.content, 'yaml');
|
|
345
359
|
logger.info('AI Helm values generation completed', {
|
|
346
360
|
valuesLength: valuesContent.length,
|
|
347
361
|
isRetry,
|
|
348
|
-
solutionId
|
|
362
|
+
solutionId,
|
|
349
363
|
});
|
|
350
364
|
return valuesContent;
|
|
351
365
|
}
|
|
@@ -360,20 +374,20 @@ async function validateHelmInstallation(chart, releaseName, namespace, valuesYam
|
|
|
360
374
|
logger.info('Running Helm dry-run validation via plugin', {
|
|
361
375
|
chart: `${chart.repositoryName}/${chart.chartName}`,
|
|
362
376
|
releaseName,
|
|
363
|
-
namespace
|
|
377
|
+
namespace,
|
|
364
378
|
});
|
|
365
379
|
try {
|
|
366
380
|
// PRD #359: First, add/update the Helm repository via unified registry
|
|
367
381
|
const repoResult = await (0, plugin_registry_1.invokePluginTool)('agentic-tools', 'helm_repo_add', {
|
|
368
382
|
name: chart.repositoryName,
|
|
369
|
-
url: chart.repository
|
|
383
|
+
url: chart.repository,
|
|
370
384
|
});
|
|
371
385
|
if (!repoResult.success) {
|
|
372
386
|
logger.warn('Helm repo add failed', { error: repoResult.error?.message });
|
|
373
387
|
return {
|
|
374
388
|
valid: false,
|
|
375
389
|
errors: [repoResult.error?.message || 'Failed to add Helm repository'],
|
|
376
|
-
warnings: []
|
|
390
|
+
warnings: [],
|
|
377
391
|
};
|
|
378
392
|
}
|
|
379
393
|
// Run helm install with dry-run
|
|
@@ -384,21 +398,23 @@ async function validateHelmInstallation(chart, releaseName, namespace, valuesYam
|
|
|
384
398
|
values: valuesYaml,
|
|
385
399
|
version: chart.version,
|
|
386
400
|
dryRun: true,
|
|
387
|
-
createNamespace: true
|
|
401
|
+
createNamespace: true,
|
|
388
402
|
});
|
|
389
403
|
if (installResult.success) {
|
|
390
404
|
logger.info('Helm dry-run validation successful');
|
|
391
405
|
return {
|
|
392
406
|
valid: true,
|
|
393
407
|
errors: [],
|
|
394
|
-
warnings: []
|
|
408
|
+
warnings: [],
|
|
395
409
|
};
|
|
396
410
|
}
|
|
397
|
-
logger.warn('Helm dry-run validation failed', {
|
|
411
|
+
logger.warn('Helm dry-run validation failed', {
|
|
412
|
+
error: installResult.error?.message,
|
|
413
|
+
});
|
|
398
414
|
return {
|
|
399
415
|
valid: false,
|
|
400
416
|
errors: [installResult.error?.message || 'Unknown Helm validation error'],
|
|
401
|
-
warnings: []
|
|
417
|
+
warnings: [],
|
|
402
418
|
};
|
|
403
419
|
}
|
|
404
420
|
catch (error) {
|
|
@@ -407,7 +423,7 @@ async function validateHelmInstallation(chart, releaseName, namespace, valuesYam
|
|
|
407
423
|
return {
|
|
408
424
|
valid: false,
|
|
409
425
|
errors: [errorMessage],
|
|
410
|
-
warnings: []
|
|
426
|
+
warnings: [],
|
|
411
427
|
};
|
|
412
428
|
}
|
|
413
429
|
}
|
|
@@ -427,7 +443,9 @@ async function handleHelmGeneration(solution, solutionId, dotAI, logger, request
|
|
|
427
443
|
operation: 'helm_generation',
|
|
428
444
|
component: 'GenerateManifestsTool',
|
|
429
445
|
requestId,
|
|
430
|
-
suggestedActions: [
|
|
446
|
+
suggestedActions: [
|
|
447
|
+
'Ensure the "name" question was answered in the configuration',
|
|
448
|
+
],
|
|
431
449
|
});
|
|
432
450
|
}
|
|
433
451
|
// Prepare file paths using shared utilities
|
|
@@ -441,14 +459,18 @@ async function handleHelmGeneration(solution, solutionId, dotAI, logger, request
|
|
|
441
459
|
maxAttempts,
|
|
442
460
|
isRetry: attempt > 1,
|
|
443
461
|
requestId,
|
|
444
|
-
chart: `${chart.repositoryName}/${chart.chartName}
|
|
462
|
+
chart: `${chart.repositoryName}/${chart.chartName}`,
|
|
445
463
|
});
|
|
446
464
|
try {
|
|
447
465
|
// Generate values.yaml with AI
|
|
448
466
|
const valuesYaml = await generateHelmValuesWithAI(solution, solutionId, dotAI, logger, lastError, interaction_id);
|
|
449
467
|
// Save values to file
|
|
450
468
|
fs.writeFileSync(valuesPath, valuesYaml, 'utf8');
|
|
451
|
-
logger.info('Helm values saved to file', {
|
|
469
|
+
logger.info('Helm values saved to file', {
|
|
470
|
+
valuesPath,
|
|
471
|
+
attempt,
|
|
472
|
+
requestId,
|
|
473
|
+
});
|
|
452
474
|
// Save attempt for debugging
|
|
453
475
|
const attemptPath = valuesPath.replace('.yaml', `_attempt_${attempt.toString().padStart(2, '0')}.yaml`);
|
|
454
476
|
fs.writeFileSync(attemptPath, valuesYaml, 'utf8');
|
|
@@ -459,7 +481,7 @@ async function handleHelmGeneration(solution, solutionId, dotAI, logger, request
|
|
|
459
481
|
logger.info('Helm validation successful', {
|
|
460
482
|
attempt,
|
|
461
483
|
valuesPath,
|
|
462
|
-
requestId
|
|
484
|
+
requestId,
|
|
463
485
|
});
|
|
464
486
|
// Build user-friendly helm command with generic values file path
|
|
465
487
|
// (internal valuesPath is used for actual execution, not shown to user)
|
|
@@ -476,12 +498,12 @@ async function handleHelmGeneration(solution, solutionId, dotAI, logger, request
|
|
|
476
498
|
repository: chart.repository,
|
|
477
499
|
repositoryName: chart.repositoryName,
|
|
478
500
|
chartName: chart.chartName,
|
|
479
|
-
version: chart.version || 'latest'
|
|
501
|
+
version: chart.version || 'latest',
|
|
480
502
|
},
|
|
481
503
|
releaseName: releaseName,
|
|
482
504
|
namespace: namespace,
|
|
483
|
-
validationAttempts: attempt
|
|
484
|
-
}
|
|
505
|
+
validationAttempts: attempt,
|
|
506
|
+
},
|
|
485
507
|
});
|
|
486
508
|
// PRD #320: Generate visualization URL
|
|
487
509
|
const visualizationUrl = (0, visualization_1.getVisualizationUrl)(solutionId);
|
|
@@ -496,19 +518,21 @@ async function handleHelmGeneration(solution, solutionId, dotAI, logger, request
|
|
|
496
518
|
repository: chart.repository,
|
|
497
519
|
repositoryName: chart.repositoryName,
|
|
498
520
|
chartName: chart.chartName,
|
|
499
|
-
version: chart.version
|
|
521
|
+
version: chart.version,
|
|
500
522
|
},
|
|
501
523
|
releaseName: releaseName,
|
|
502
524
|
namespace: namespace,
|
|
503
525
|
validationAttempts: attempt,
|
|
504
526
|
timestamp: new Date().toISOString(),
|
|
505
|
-
...(visualizationUrl ? { visualizationUrl } : {})
|
|
527
|
+
...(visualizationUrl ? { visualizationUrl } : {}),
|
|
506
528
|
};
|
|
507
529
|
// Build content blocks - JSON for REST API, agent instruction for MCP agents
|
|
508
|
-
const content = [
|
|
530
|
+
const content = [
|
|
531
|
+
{
|
|
509
532
|
type: 'text',
|
|
510
|
-
text: JSON.stringify(response, null, 2)
|
|
511
|
-
}
|
|
533
|
+
text: JSON.stringify(response, null, 2),
|
|
534
|
+
},
|
|
535
|
+
];
|
|
512
536
|
// Add agent instruction block if visualization URL is present
|
|
513
537
|
const agentDisplayBlock = (0, index_1.buildAgentDisplayBlock)({ visualizationUrl });
|
|
514
538
|
if (agentDisplayBlock) {
|
|
@@ -520,14 +544,14 @@ async function handleHelmGeneration(solution, solutionId, dotAI, logger, request
|
|
|
520
544
|
lastError = {
|
|
521
545
|
attempt,
|
|
522
546
|
previousValues: valuesYaml,
|
|
523
|
-
validationResult: validation
|
|
547
|
+
validationResult: validation,
|
|
524
548
|
};
|
|
525
549
|
logger.warn('Helm validation failed', {
|
|
526
550
|
attempt,
|
|
527
551
|
maxAttempts,
|
|
528
552
|
validationErrors: validation.errors,
|
|
529
553
|
validationWarnings: validation.warnings,
|
|
530
|
-
requestId
|
|
554
|
+
requestId,
|
|
531
555
|
});
|
|
532
556
|
}
|
|
533
557
|
catch (error) {
|
|
@@ -542,8 +566,8 @@ async function handleHelmGeneration(solution, solutionId, dotAI, logger, request
|
|
|
542
566
|
validationResult: {
|
|
543
567
|
valid: false,
|
|
544
568
|
errors: [error instanceof Error ? error.message : String(error)],
|
|
545
|
-
warnings: []
|
|
546
|
-
}
|
|
569
|
+
warnings: [],
|
|
570
|
+
},
|
|
547
571
|
};
|
|
548
572
|
}
|
|
549
573
|
}
|
|
@@ -581,7 +605,7 @@ async function renderPackageToYaml(packageDir, format, logger) {
|
|
|
581
605
|
return {
|
|
582
606
|
success: false,
|
|
583
607
|
error: errorMessage,
|
|
584
|
-
isTerminalError // Signal to caller to not retry
|
|
608
|
+
isTerminalError, // Signal to caller to not retry
|
|
585
609
|
};
|
|
586
610
|
}
|
|
587
611
|
}
|
|
@@ -620,12 +644,19 @@ async function packageAndValidate(rawManifests, solution, outputFormat, outputPa
|
|
|
620
644
|
fs.rmSync(packageDir, { recursive: true });
|
|
621
645
|
}
|
|
622
646
|
catch (cleanupError) {
|
|
623
|
-
logger.warn('Failed to cleanup temp package directory', {
|
|
647
|
+
logger.warn('Failed to cleanup temp package directory', {
|
|
648
|
+
packageDir,
|
|
649
|
+
error: cleanupError,
|
|
650
|
+
});
|
|
624
651
|
}
|
|
625
652
|
}
|
|
626
653
|
};
|
|
627
654
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
628
|
-
logger.info('Packaging attempt', {
|
|
655
|
+
logger.info('Packaging attempt', {
|
|
656
|
+
attempt,
|
|
657
|
+
maxAttempts,
|
|
658
|
+
format: outputFormat,
|
|
659
|
+
});
|
|
629
660
|
try {
|
|
630
661
|
const packagingResult = await (0, packaging_1.packageManifests)(rawManifests,
|
|
631
662
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Different SolutionData types between modules
|
|
@@ -643,9 +674,12 @@ async function packageAndValidate(rawManifests, solution, outputFormat, outputPa
|
|
|
643
674
|
packagingError = {
|
|
644
675
|
attempt,
|
|
645
676
|
previousOutput: JSON.stringify(packagingResult.files.map(f => f.relativePath)),
|
|
646
|
-
validationError: `helm lint failed: ${lintResult.errors.join(', ')}
|
|
677
|
+
validationError: `helm lint failed: ${lintResult.errors.join(', ')}`,
|
|
647
678
|
};
|
|
648
|
-
logger.warn('helm lint failed', {
|
|
679
|
+
logger.warn('helm lint failed', {
|
|
680
|
+
attempt,
|
|
681
|
+
errors: lintResult.errors,
|
|
682
|
+
});
|
|
649
683
|
continue;
|
|
650
684
|
}
|
|
651
685
|
// Log warnings but don't fail on them
|
|
@@ -660,16 +694,19 @@ async function packageAndValidate(rawManifests, solution, outputFormat, outputPa
|
|
|
660
694
|
if (renderResult.isTerminalError) {
|
|
661
695
|
const terminalError = new Error(`Infrastructure error (not retryable): ${renderResult.error}`);
|
|
662
696
|
logger.error('Terminal infrastructure error - cannot retry', terminalError, {
|
|
663
|
-
format: outputFormat
|
|
697
|
+
format: outputFormat,
|
|
664
698
|
});
|
|
665
699
|
throw terminalError;
|
|
666
700
|
}
|
|
667
701
|
packagingError = {
|
|
668
702
|
attempt,
|
|
669
703
|
previousOutput: JSON.stringify(packagingResult.files.map(f => f.relativePath)),
|
|
670
|
-
validationError: `Failed to render ${outputFormat}: ${renderResult.error}
|
|
704
|
+
validationError: `Failed to render ${outputFormat}: ${renderResult.error}`,
|
|
671
705
|
};
|
|
672
|
-
logger.warn('Package render failed', {
|
|
706
|
+
logger.warn('Package render failed', {
|
|
707
|
+
attempt,
|
|
708
|
+
error: renderResult.error,
|
|
709
|
+
});
|
|
673
710
|
continue;
|
|
674
711
|
}
|
|
675
712
|
// Validate rendered YAML
|
|
@@ -680,16 +717,22 @@ async function packageAndValidate(rawManifests, solution, outputFormat, outputPa
|
|
|
680
717
|
fs.writeFileSync(renderedYamlPath, renderResult.yaml, 'utf8');
|
|
681
718
|
const validation = await validateManifests(renderedYamlPath);
|
|
682
719
|
if (validation.valid) {
|
|
683
|
-
logger.info('Package validation successful', {
|
|
720
|
+
logger.info('Package validation successful', {
|
|
721
|
+
format: outputFormat,
|
|
722
|
+
attempt,
|
|
723
|
+
});
|
|
684
724
|
cleanupPackageDir();
|
|
685
725
|
return { files: packagingResult.files, attempts: attempt };
|
|
686
726
|
}
|
|
687
727
|
packagingError = {
|
|
688
728
|
attempt,
|
|
689
729
|
previousOutput: JSON.stringify(packagingResult.files.map(f => f.relativePath)),
|
|
690
|
-
validationError: validation.errors.join(', ')
|
|
730
|
+
validationError: validation.errors.join(', '),
|
|
691
731
|
};
|
|
692
|
-
logger.warn('Package validation failed', {
|
|
732
|
+
logger.warn('Package validation failed', {
|
|
733
|
+
attempt,
|
|
734
|
+
errors: validation.errors,
|
|
735
|
+
});
|
|
693
736
|
}
|
|
694
737
|
catch (error) {
|
|
695
738
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -700,7 +743,7 @@ async function packageAndValidate(rawManifests, solution, outputFormat, outputPa
|
|
|
700
743
|
packagingError = {
|
|
701
744
|
attempt,
|
|
702
745
|
previousOutput: packagingError?.previousOutput || '',
|
|
703
|
-
validationError: errorMessage
|
|
746
|
+
validationError: errorMessage,
|
|
704
747
|
};
|
|
705
748
|
}
|
|
706
749
|
}
|
|
@@ -716,7 +759,7 @@ async function handleGenerateManifestsTool(args, dotAI, logger, requestId, plugi
|
|
|
716
759
|
const maxAttempts = 10;
|
|
717
760
|
logger.debug('Handling generateManifests request', {
|
|
718
761
|
requestId,
|
|
719
|
-
solutionId: args?.solutionId
|
|
762
|
+
solutionId: args?.solutionId,
|
|
720
763
|
});
|
|
721
764
|
// Input validation is handled automatically by MCP SDK with Zod schema
|
|
722
765
|
// args are already validated and typed when we reach this point
|
|
@@ -735,8 +778,8 @@ async function handleGenerateManifestsTool(args, dotAI, logger, requestId, plugi
|
|
|
735
778
|
'Verify the solution ID is correct',
|
|
736
779
|
'Ensure the solution was created by the recommend tool',
|
|
737
780
|
'Ensure all configuration stages were completed',
|
|
738
|
-
'Check that the session has not expired'
|
|
739
|
-
]
|
|
781
|
+
'Check that the session has not expired',
|
|
782
|
+
],
|
|
740
783
|
});
|
|
741
784
|
}
|
|
742
785
|
const solution = session.data;
|
|
@@ -744,19 +787,21 @@ async function handleGenerateManifestsTool(args, dotAI, logger, requestId, plugi
|
|
|
744
787
|
solutionId: args.solutionId,
|
|
745
788
|
solutionType: solution.type,
|
|
746
789
|
hasQuestions: !!solution.questions,
|
|
747
|
-
primaryResources: solution.resources
|
|
790
|
+
primaryResources: solution.resources,
|
|
748
791
|
});
|
|
749
792
|
// Branch based on solution type
|
|
750
793
|
if (solution.type === 'helm') {
|
|
751
794
|
logger.info('Detected Helm solution, using Helm generation flow', {
|
|
752
795
|
solutionId: args.solutionId,
|
|
753
|
-
chart: solution.chart
|
|
796
|
+
chart: solution.chart
|
|
797
|
+
? `${solution.chart.repositoryName}/${solution.chart.chartName}`
|
|
798
|
+
: 'unknown',
|
|
754
799
|
});
|
|
755
800
|
return await handleHelmGeneration(solution, args.solutionId, dotAI, logger, requestId, sessionManager, pluginManager, args.interaction_id);
|
|
756
801
|
}
|
|
757
802
|
// Capability-based solution: Generate Kubernetes manifests
|
|
758
803
|
logger.info('Using capability-based manifest generation flow', {
|
|
759
|
-
solutionId: args.solutionId
|
|
804
|
+
solutionId: args.solutionId,
|
|
760
805
|
});
|
|
761
806
|
// Prepare file path for manifests (store in tmp directory)
|
|
762
807
|
const tmpDir = path.join(process.cwd(), 'tmp');
|
|
@@ -771,7 +816,7 @@ async function handleGenerateManifestsTool(args, dotAI, logger, requestId, plugi
|
|
|
771
816
|
attempt,
|
|
772
817
|
maxAttempts,
|
|
773
818
|
isRetry: attempt > 1,
|
|
774
|
-
requestId
|
|
819
|
+
requestId,
|
|
775
820
|
});
|
|
776
821
|
try {
|
|
777
822
|
// Extract user answers and generate required labels
|
|
@@ -789,9 +834,11 @@ async function handleGenerateManifestsTool(args, dotAI, logger, requestId, plugi
|
|
|
789
834
|
solutionId: args.solutionId,
|
|
790
835
|
namespace: userAnswers.namespace || 'default',
|
|
791
836
|
solution: solution,
|
|
792
|
-
generatedManifestsYaml: aiManifests
|
|
837
|
+
generatedManifestsYaml: aiManifests,
|
|
838
|
+
});
|
|
839
|
+
logger.info('Solution CR generated successfully', {
|
|
840
|
+
solutionId: args.solutionId,
|
|
793
841
|
});
|
|
794
|
-
logger.info('Solution CR generated successfully', { solutionId: args.solutionId });
|
|
795
842
|
}
|
|
796
843
|
else {
|
|
797
844
|
logger.info('Solution CRD not available, skipping Solution CR generation (graceful degradation)', { solutionId: args.solutionId });
|
|
@@ -800,7 +847,7 @@ async function handleGenerateManifestsTool(args, dotAI, logger, requestId, plugi
|
|
|
800
847
|
catch (error) {
|
|
801
848
|
logger.warn('Failed to check CRD availability or generate Solution CR, skipping', {
|
|
802
849
|
solutionId: args.solutionId,
|
|
803
|
-
error: error instanceof Error ? error.message : String(error)
|
|
850
|
+
error: error instanceof Error ? error.message : String(error),
|
|
804
851
|
});
|
|
805
852
|
// Graceful degradation - continue without Solution CR
|
|
806
853
|
}
|
|
@@ -810,17 +857,23 @@ async function handleGenerateManifestsTool(args, dotAI, logger, requestId, plugi
|
|
|
810
857
|
manifestParts.push(solutionCR);
|
|
811
858
|
}
|
|
812
859
|
manifestParts.push(aiManifests);
|
|
813
|
-
const manifests = manifestParts.length > 1
|
|
860
|
+
const manifests = manifestParts.length > 1
|
|
861
|
+
? manifestParts.join('---\n')
|
|
862
|
+
: manifestParts[0];
|
|
814
863
|
// Save manifests to file
|
|
815
864
|
fs.writeFileSync(yamlPath, manifests, 'utf8');
|
|
816
|
-
logger.info('Manifests saved to file', {
|
|
865
|
+
logger.info('Manifests saved to file', {
|
|
866
|
+
yamlPath,
|
|
867
|
+
attempt,
|
|
868
|
+
requestId,
|
|
869
|
+
});
|
|
817
870
|
// Save a copy of this attempt for debugging
|
|
818
871
|
const attemptPath = yamlPath.replace('.yaml', `_attempt_${attempt.toString().padStart(2, '0')}.yaml`);
|
|
819
872
|
fs.writeFileSync(attemptPath, manifests, 'utf8');
|
|
820
873
|
logger.info('Saved manifest attempt for debugging', {
|
|
821
874
|
attempt,
|
|
822
875
|
attemptPath,
|
|
823
|
-
requestId
|
|
876
|
+
requestId,
|
|
824
877
|
});
|
|
825
878
|
// Validate manifests
|
|
826
879
|
// PRD #359: Uses unified plugin registry for kubectl operations
|
|
@@ -829,10 +882,11 @@ async function handleGenerateManifestsTool(args, dotAI, logger, requestId, plugi
|
|
|
829
882
|
logger.info('Manifest validation successful', {
|
|
830
883
|
attempt,
|
|
831
884
|
yamlPath,
|
|
832
|
-
requestId
|
|
885
|
+
requestId,
|
|
833
886
|
});
|
|
834
887
|
// Extract packaging options from user answers (with defaults)
|
|
835
|
-
const outputFormat = (userAnswers.outputFormat ||
|
|
888
|
+
const outputFormat = (userAnswers.outputFormat ||
|
|
889
|
+
'raw');
|
|
836
890
|
const outputPath = userAnswers.outputPath || './manifests';
|
|
837
891
|
// Handle packaging based on outputFormat
|
|
838
892
|
if (outputFormat === 'helm' || outputFormat === 'kustomize') {
|
|
@@ -846,8 +900,8 @@ async function handleGenerateManifestsTool(args, dotAI, logger, requestId, plugi
|
|
|
846
900
|
outputPath,
|
|
847
901
|
files: packagingResult.files,
|
|
848
902
|
validationAttempts: attempt,
|
|
849
|
-
packagingAttempts: packagingResult.attempts
|
|
850
|
-
}
|
|
903
|
+
packagingAttempts: packagingResult.attempts,
|
|
904
|
+
},
|
|
851
905
|
});
|
|
852
906
|
// PRD #320: Generate visualization URL
|
|
853
907
|
const visualizationUrl = (0, visualization_1.getVisualizationUrl)(args.solutionId);
|
|
@@ -862,15 +916,19 @@ async function handleGenerateManifestsTool(args, dotAI, logger, requestId, plugi
|
|
|
862
916
|
packagingAttempts: packagingResult.attempts,
|
|
863
917
|
timestamp: new Date().toISOString(),
|
|
864
918
|
agentInstructions: `Write the files to "${outputPath}". The output is a ${outputFormat === 'helm' ? 'Helm chart' : 'Kustomize overlay'}. If immediate deployment is desired, call the recommend tool with stage: "deployManifests".`,
|
|
865
|
-
...(visualizationUrl ? { visualizationUrl } : {})
|
|
919
|
+
...(visualizationUrl ? { visualizationUrl } : {}),
|
|
866
920
|
};
|
|
867
921
|
// Build content blocks - JSON for REST API, agent instruction for MCP agents
|
|
868
|
-
const content = [
|
|
922
|
+
const content = [
|
|
923
|
+
{
|
|
869
924
|
type: 'text',
|
|
870
|
-
text: JSON.stringify(response, null, 2)
|
|
871
|
-
}
|
|
925
|
+
text: JSON.stringify(response, null, 2),
|
|
926
|
+
},
|
|
927
|
+
];
|
|
872
928
|
// Add agent instruction block if visualization URL is present
|
|
873
|
-
const agentDisplayBlock = (0, index_1.buildAgentDisplayBlock)({
|
|
929
|
+
const agentDisplayBlock = (0, index_1.buildAgentDisplayBlock)({
|
|
930
|
+
visualizationUrl,
|
|
931
|
+
});
|
|
874
932
|
if (agentDisplayBlock) {
|
|
875
933
|
content.push(agentDisplayBlock);
|
|
876
934
|
}
|
|
@@ -884,8 +942,8 @@ async function handleGenerateManifestsTool(args, dotAI, logger, requestId, plugi
|
|
|
884
942
|
type: 'raw',
|
|
885
943
|
outputPath,
|
|
886
944
|
files: [{ relativePath: 'manifests.yaml', content: manifests }],
|
|
887
|
-
validationAttempts: attempt
|
|
888
|
-
}
|
|
945
|
+
validationAttempts: attempt,
|
|
946
|
+
},
|
|
889
947
|
});
|
|
890
948
|
// PRD #320: Generate visualization URL
|
|
891
949
|
const visualizationUrl = (0, visualization_1.getVisualizationUrl)(args.solutionId);
|
|
@@ -896,21 +954,23 @@ async function handleGenerateManifestsTool(args, dotAI, logger, requestId, plugi
|
|
|
896
954
|
solutionId: args.solutionId,
|
|
897
955
|
outputFormat,
|
|
898
956
|
outputPath,
|
|
899
|
-
files: [
|
|
900
|
-
{ relativePath: 'manifests.yaml', content: manifests }
|
|
901
|
-
],
|
|
957
|
+
files: [{ relativePath: 'manifests.yaml', content: manifests }],
|
|
902
958
|
validationAttempts: attempt,
|
|
903
959
|
timestamp: new Date().toISOString(),
|
|
904
960
|
agentInstructions: `Write the files to "${outputPath}". If immediate deployment is desired, call the recommend tool with stage: "deployManifests".`,
|
|
905
|
-
...(visualizationUrl ? { visualizationUrl } : {})
|
|
961
|
+
...(visualizationUrl ? { visualizationUrl } : {}),
|
|
906
962
|
};
|
|
907
963
|
// Build content blocks - JSON for REST API, agent instruction for MCP agents
|
|
908
|
-
const content = [
|
|
964
|
+
const content = [
|
|
965
|
+
{
|
|
909
966
|
type: 'text',
|
|
910
|
-
text: JSON.stringify(response, null, 2)
|
|
911
|
-
}
|
|
967
|
+
text: JSON.stringify(response, null, 2),
|
|
968
|
+
},
|
|
969
|
+
];
|
|
912
970
|
// Add agent instruction block if visualization URL is present
|
|
913
|
-
const agentDisplayBlock = (0, index_1.buildAgentDisplayBlock)({
|
|
971
|
+
const agentDisplayBlock = (0, index_1.buildAgentDisplayBlock)({
|
|
972
|
+
visualizationUrl,
|
|
973
|
+
});
|
|
914
974
|
if (agentDisplayBlock) {
|
|
915
975
|
content.push(agentDisplayBlock);
|
|
916
976
|
}
|
|
@@ -921,14 +981,14 @@ async function handleGenerateManifestsTool(args, dotAI, logger, requestId, plugi
|
|
|
921
981
|
lastError = {
|
|
922
982
|
attempt,
|
|
923
983
|
previousManifests: aiManifests,
|
|
924
|
-
validationResult: validation
|
|
984
|
+
validationResult: validation,
|
|
925
985
|
};
|
|
926
986
|
logger.warn('Manifest validation failed', {
|
|
927
987
|
attempt,
|
|
928
988
|
maxAttempts,
|
|
929
989
|
validationErrors: validation.errors,
|
|
930
990
|
validationWarnings: validation.warnings,
|
|
931
|
-
requestId
|
|
991
|
+
requestId,
|
|
932
992
|
});
|
|
933
993
|
}
|
|
934
994
|
catch (error) {
|
|
@@ -948,8 +1008,8 @@ async function handleGenerateManifestsTool(args, dotAI, logger, requestId, plugi
|
|
|
948
1008
|
validationResult: {
|
|
949
1009
|
valid: false,
|
|
950
1010
|
errors: [errorMessage],
|
|
951
|
-
warnings: []
|
|
952
|
-
}
|
|
1011
|
+
warnings: [],
|
|
1012
|
+
},
|
|
953
1013
|
};
|
|
954
1014
|
}
|
|
955
1015
|
}
|
|
@@ -959,6 +1019,6 @@ async function handleGenerateManifestsTool(args, dotAI, logger, requestId, plugi
|
|
|
959
1019
|
operation: 'generate_manifests',
|
|
960
1020
|
component: 'GenerateManifestsTool',
|
|
961
1021
|
requestId,
|
|
962
|
-
input: args
|
|
1022
|
+
input: args,
|
|
963
1023
|
});
|
|
964
1024
|
}
|