@vfarcic/dot-ai 0.138.0 → 0.140.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/capability-scan-workflow.d.ts.map +1 -1
- package/dist/core/capability-scan-workflow.js +3 -2
- package/dist/core/command-executor.d.ts +39 -0
- package/dist/core/command-executor.d.ts.map +1 -0
- package/dist/core/command-executor.js +111 -0
- package/dist/core/deploy-operation.d.ts.map +1 -1
- package/dist/core/deploy-operation.js +3 -5
- package/dist/core/pattern-vector-service.d.ts +1 -1
- package/dist/core/pattern-vector-service.d.ts.map +1 -1
- package/dist/core/pattern-vector-service.js +2 -2
- package/dist/core/schema.js +1 -1
- package/dist/interfaces/mcp.d.ts.map +1 -1
- package/dist/interfaces/mcp.js +9 -1
- package/dist/tools/answer-question.d.ts.map +1 -1
- package/dist/tools/answer-question.js +27 -142
- package/dist/tools/choose-solution.d.ts.map +1 -1
- package/dist/tools/choose-solution.js +22 -85
- package/dist/tools/deploy-manifests.js +1 -1
- package/dist/tools/generate-manifests.d.ts.map +1 -1
- package/dist/tools/generate-manifests.js +33 -71
- package/dist/tools/operate-analysis.d.ts +15 -0
- package/dist/tools/operate-analysis.d.ts.map +1 -0
- package/dist/tools/operate-analysis.js +238 -0
- package/dist/tools/operate-execution.d.ts +18 -0
- package/dist/tools/operate-execution.d.ts.map +1 -0
- package/dist/tools/operate-execution.js +122 -0
- package/dist/tools/operate.d.ts +133 -0
- package/dist/tools/operate.d.ts.map +1 -0
- package/dist/tools/operate.js +237 -0
- package/dist/tools/recommend.d.ts +23 -1
- package/dist/tools/recommend.d.ts.map +1 -1
- package/dist/tools/recommend.js +14 -116
- package/dist/tools/remediate.d.ts +7 -4
- package/dist/tools/remediate.d.ts.map +1 -1
- package/dist/tools/remediate.js +38 -81
- package/dist/tools/version.js +1 -1
- package/package.json +1 -1
- package/prompts/operate-system.md +322 -0
- package/prompts/operate-user.md +25 -0
|
@@ -2,78 +2,20 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Choose Solution Tool - Select a solution and return its questions
|
|
4
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
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
6
|
exports.CHOOSESOLUTION_TOOL_INPUT_SCHEMA = exports.CHOOSESOLUTION_TOOL_DESCRIPTION = exports.CHOOSESOLUTION_TOOL_NAME = void 0;
|
|
40
7
|
exports.handleChooseSolutionTool = handleChooseSolutionTool;
|
|
41
8
|
const zod_1 = require("zod");
|
|
42
9
|
const error_handling_1 = require("../core/error-handling");
|
|
43
|
-
const
|
|
44
|
-
const path = __importStar(require("path"));
|
|
45
|
-
const session_utils_1 = require("../core/session-utils");
|
|
10
|
+
const generic_session_manager_1 = require("../core/generic-session-manager");
|
|
46
11
|
// Tool metadata for direct MCP registration
|
|
47
12
|
exports.CHOOSESOLUTION_TOOL_NAME = 'chooseSolution';
|
|
48
13
|
exports.CHOOSESOLUTION_TOOL_DESCRIPTION = 'Select a solution by ID and return its questions for configuration';
|
|
49
14
|
// Zod schema for MCP registration
|
|
50
15
|
exports.CHOOSESOLUTION_TOOL_INPUT_SCHEMA = {
|
|
51
|
-
solutionId: zod_1.z.string().regex(/^
|
|
16
|
+
solutionId: zod_1.z.string().regex(/^sol-\d+-[a-f0-9]{8}$/).describe('The solution ID to choose (e.g., sol-1762983784617-9ddae2b8)')
|
|
52
17
|
};
|
|
53
|
-
|
|
54
|
-
* Load solution file by ID
|
|
55
|
-
*/
|
|
56
|
-
function loadSolutionFile(solutionId, sessionDir) {
|
|
57
|
-
const solutionPath = path.join(sessionDir, `${solutionId}.json`);
|
|
58
|
-
if (!fs.existsSync(solutionPath)) {
|
|
59
|
-
throw new Error(`Solution file not found: ${solutionPath}. Available files: ${fs.readdirSync(sessionDir).filter(f => f.endsWith('.json')).join(', ')}`);
|
|
60
|
-
}
|
|
61
|
-
try {
|
|
62
|
-
const content = fs.readFileSync(solutionPath, 'utf8');
|
|
63
|
-
const solution = JSON.parse(content);
|
|
64
|
-
// Validate solution structure
|
|
65
|
-
if (!solution.solutionId || !solution.questions) {
|
|
66
|
-
throw new Error(`Invalid solution file structure: ${solutionId}. Missing required fields: solutionId or questions`);
|
|
67
|
-
}
|
|
68
|
-
return solution;
|
|
69
|
-
}
|
|
70
|
-
catch (error) {
|
|
71
|
-
if (error instanceof SyntaxError) {
|
|
72
|
-
throw new Error(`Invalid JSON in solution file: ${solutionId}. ${error.message}`);
|
|
73
|
-
}
|
|
74
|
-
throw error;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
18
|
+
// Session management now handled by GenericSessionManager
|
|
77
19
|
/**
|
|
78
20
|
* Direct MCP tool handler for chooseSolution functionality
|
|
79
21
|
*/
|
|
@@ -82,29 +24,26 @@ async function handleChooseSolutionTool(args, dotAI, logger, requestId) {
|
|
|
82
24
|
logger.debug('Handling chooseSolution request', { requestId, solutionId: args?.solutionId });
|
|
83
25
|
// Input validation is handled automatically by MCP SDK with Zod schema
|
|
84
26
|
// args are already validated and typed when we reach this point
|
|
85
|
-
//
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
operation: 'session_directory_validation',
|
|
27
|
+
// Initialize session manager
|
|
28
|
+
const sessionManager = new generic_session_manager_1.GenericSessionManager('sol');
|
|
29
|
+
logger.debug('Session manager initialized', { requestId });
|
|
30
|
+
// Load solution session
|
|
31
|
+
const session = sessionManager.getSession(args.solutionId);
|
|
32
|
+
if (!session) {
|
|
33
|
+
throw error_handling_1.ErrorHandler.createError(error_handling_1.ErrorCategory.VALIDATION, error_handling_1.ErrorSeverity.HIGH, `Solution not found: ${args.solutionId}`, {
|
|
34
|
+
operation: 'solution_loading',
|
|
94
35
|
component: 'ChooseSolutionTool',
|
|
95
36
|
requestId,
|
|
37
|
+
input: { solutionId: args.solutionId },
|
|
96
38
|
suggestedActions: [
|
|
97
|
-
'
|
|
98
|
-
'
|
|
99
|
-
'
|
|
100
|
-
'Verify DOT_AI_SESSION_DIR environment variable is correctly set'
|
|
39
|
+
'Verify the solution ID is correct',
|
|
40
|
+
'Ensure the solution was created by the recommend tool',
|
|
41
|
+
'Check that the session has not expired'
|
|
101
42
|
]
|
|
102
43
|
});
|
|
103
44
|
}
|
|
104
|
-
|
|
105
|
-
let solution;
|
|
45
|
+
const solution = session.data;
|
|
106
46
|
try {
|
|
107
|
-
solution = loadSolutionFile(args.solutionId, sessionDir);
|
|
108
47
|
logger.debug('Solution file loaded successfully', {
|
|
109
48
|
solutionId: args.solutionId,
|
|
110
49
|
hasQuestions: !!solution.questions,
|
|
@@ -117,23 +56,22 @@ async function handleChooseSolutionTool(args, dotAI, logger, requestId) {
|
|
|
117
56
|
});
|
|
118
57
|
}
|
|
119
58
|
catch (error) {
|
|
120
|
-
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
|
|
121
|
-
operation: '
|
|
59
|
+
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 session', {
|
|
60
|
+
operation: 'solution_session_load',
|
|
122
61
|
component: 'ChooseSolutionTool',
|
|
123
62
|
requestId,
|
|
124
|
-
input: { solutionId: args.solutionId
|
|
63
|
+
input: { solutionId: args.solutionId },
|
|
125
64
|
suggestedActions: [
|
|
126
65
|
'Check that the solution ID is correct',
|
|
127
|
-
'Verify the solution
|
|
128
|
-
'Ensure the solution was created by a recent recommend tool call'
|
|
129
|
-
'List available solution files in the session directory'
|
|
66
|
+
'Verify the solution session exists',
|
|
67
|
+
'Ensure the solution was created by a recent recommend tool call'
|
|
130
68
|
]
|
|
131
69
|
});
|
|
132
70
|
}
|
|
133
71
|
// Prepare response with solution details and questions
|
|
134
72
|
const response = {
|
|
135
73
|
status: 'stage_questions',
|
|
136
|
-
solutionId:
|
|
74
|
+
solutionId: args.solutionId,
|
|
137
75
|
currentStage: 'required',
|
|
138
76
|
questions: solution.questions.required || [],
|
|
139
77
|
nextStage: 'basic',
|
|
@@ -144,7 +82,6 @@ async function handleChooseSolutionTool(args, dotAI, logger, requestId) {
|
|
|
144
82
|
};
|
|
145
83
|
logger.info('Choose solution completed successfully', {
|
|
146
84
|
solutionId: args.solutionId,
|
|
147
|
-
sessionDir,
|
|
148
85
|
questionCategories: {
|
|
149
86
|
required: solution.questions.required?.length || 0,
|
|
150
87
|
basic: solution.questions.basic?.length || 0,
|
|
@@ -14,7 +14,7 @@ exports.DEPLOYMANIFESTS_TOOL_NAME = 'deployManifests';
|
|
|
14
14
|
exports.DEPLOYMANIFESTS_TOOL_DESCRIPTION = 'Deploy Kubernetes manifests from generated solution with kubectl apply --wait';
|
|
15
15
|
// Zod schema for MCP registration
|
|
16
16
|
exports.DEPLOYMANIFESTS_TOOL_INPUT_SCHEMA = {
|
|
17
|
-
solutionId: zod_1.z.string().regex(/^
|
|
17
|
+
solutionId: zod_1.z.string().regex(/^sol-\d+-[a-f0-9]{8}$/).describe('Solution ID to deploy (e.g., sol-1762983784617-9ddae2b8)'),
|
|
18
18
|
timeout: zod_1.z.number().min(1).max(600).optional().describe('Deployment timeout in seconds (default: 30)')
|
|
19
19
|
};
|
|
20
20
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate-manifests.d.ts","sourceRoot":"","sources":["../../src/tools/generate-manifests.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"generate-manifests.d.ts","sourceRoot":"","sources":["../../src/tools/generate-manifests.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAahD,eAAO,MAAM,2BAA2B,sBAAsB,CAAC;AAC/D,eAAO,MAAM,kCAAkC,+IAA+I,CAAC;AAG/L,eAAO,MAAM,mCAAmC;;;CAG/C,CAAC;AA2PF;;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,CAwLxD"}
|
|
@@ -46,7 +46,7 @@ const fs = __importStar(require("fs"));
|
|
|
46
46
|
const path = __importStar(require("path"));
|
|
47
47
|
const shared_prompt_loader_1 = require("../core/shared-prompt-loader");
|
|
48
48
|
const yaml = __importStar(require("js-yaml"));
|
|
49
|
-
const
|
|
49
|
+
const generic_session_manager_1 = require("../core/generic-session-manager");
|
|
50
50
|
const solution_utils_1 = require("../core/solution-utils");
|
|
51
51
|
const platform_utils_1 = require("../core/platform-utils");
|
|
52
52
|
// Tool metadata for direct MCP registration
|
|
@@ -54,32 +54,9 @@ exports.GENERATEMANIFESTS_TOOL_NAME = 'generateManifests';
|
|
|
54
54
|
exports.GENERATEMANIFESTS_TOOL_DESCRIPTION = 'Generate final Kubernetes manifests from fully configured solution (ONLY after completing ALL stages: required, basic, advanced, and open)';
|
|
55
55
|
// Zod schema for MCP registration
|
|
56
56
|
exports.GENERATEMANIFESTS_TOOL_INPUT_SCHEMA = {
|
|
57
|
-
solutionId: zod_1.z.string().regex(/^
|
|
57
|
+
solutionId: zod_1.z.string().regex(/^sol-\d+-[a-f0-9]{8}$/).describe('The solution ID to generate manifests for (e.g., sol-1762983784617-9ddae2b8)'),
|
|
58
58
|
interaction_id: zod_1.z.string().optional().describe('INTERNAL ONLY - Do not populate. Used for evaluation dataset generation.')
|
|
59
59
|
};
|
|
60
|
-
/**
|
|
61
|
-
* Load solution file and validate structure
|
|
62
|
-
*/
|
|
63
|
-
function loadSolutionFile(solutionId, sessionDir) {
|
|
64
|
-
const solutionPath = path.join(sessionDir, `${solutionId}.json`);
|
|
65
|
-
if (!fs.existsSync(solutionPath)) {
|
|
66
|
-
throw new Error(`Solution file not found: ${solutionPath}. Available files: ${fs.readdirSync(sessionDir).filter(f => f.endsWith('.json')).join(', ')}`);
|
|
67
|
-
}
|
|
68
|
-
try {
|
|
69
|
-
const content = fs.readFileSync(solutionPath, 'utf8');
|
|
70
|
-
const solution = JSON.parse(content);
|
|
71
|
-
if (!solution.solutionId || !solution.questions) {
|
|
72
|
-
throw new Error(`Invalid solution file structure: ${solutionId}. Missing required fields: solutionId or questions`);
|
|
73
|
-
}
|
|
74
|
-
return solution;
|
|
75
|
-
}
|
|
76
|
-
catch (error) {
|
|
77
|
-
if (error instanceof SyntaxError) {
|
|
78
|
-
throw new Error(`Invalid JSON in solution file: ${solutionId}`);
|
|
79
|
-
}
|
|
80
|
-
throw error;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
60
|
/**
|
|
84
61
|
* Retrieve schemas for resources specified in the solution
|
|
85
62
|
*/
|
|
@@ -181,7 +158,7 @@ async function validateManifests(yamlPath) {
|
|
|
181
158
|
/**
|
|
182
159
|
* Generate manifests using AI provider
|
|
183
160
|
*/
|
|
184
|
-
async function generateManifestsWithAI(solution, dotAI, logger, errorContext, dotAiLabels, interaction_id) {
|
|
161
|
+
async function generateManifestsWithAI(solution, solutionId, dotAI, logger, errorContext, dotAiLabels, interaction_id) {
|
|
185
162
|
// Retrieve schemas for solution resources
|
|
186
163
|
const resourceSchemas = await retrieveResourceSchemas(solution, dotAI, logger);
|
|
187
164
|
// Prepare template variables
|
|
@@ -212,7 +189,7 @@ ${errorContext.previousManifests}
|
|
|
212
189
|
isRetry,
|
|
213
190
|
attempt: errorContext?.attempt,
|
|
214
191
|
hasErrorContext: !!errorContext,
|
|
215
|
-
solutionId
|
|
192
|
+
solutionId
|
|
216
193
|
});
|
|
217
194
|
// Get AI provider from dotAI
|
|
218
195
|
const aiProvider = dotAI.ai;
|
|
@@ -227,17 +204,16 @@ ${errorContext.previousManifests}
|
|
|
227
204
|
logger.info('AI manifest generation completed', {
|
|
228
205
|
manifestLength: manifestContent.length,
|
|
229
206
|
isRetry,
|
|
230
|
-
solutionId
|
|
207
|
+
solutionId
|
|
231
208
|
});
|
|
232
209
|
return manifestContent;
|
|
233
210
|
}
|
|
234
211
|
/**
|
|
235
212
|
* Generate dot-ai application metadata ConfigMap
|
|
236
213
|
*/
|
|
237
|
-
function generateMetadataConfigMap(solution, userAnswers, logger) {
|
|
214
|
+
function generateMetadataConfigMap(solution, solutionId, userAnswers, logger) {
|
|
238
215
|
const appName = userAnswers.name;
|
|
239
216
|
const namespace = userAnswers.namespace || 'default';
|
|
240
|
-
const solutionId = solution.solutionId;
|
|
241
217
|
const originalIntent = solution.intent;
|
|
242
218
|
// Validate required fields (will throw if missing)
|
|
243
219
|
const dotAiLabels = (0, solution_utils_1.addDotAiLabels)(undefined, userAnswers, solution);
|
|
@@ -294,53 +270,39 @@ async function handleGenerateManifestsTool(args, dotAI, logger, requestId) {
|
|
|
294
270
|
});
|
|
295
271
|
// Input validation is handled automatically by MCP SDK with Zod schema
|
|
296
272
|
// args are already validated and typed when we reach this point
|
|
297
|
-
//
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
sessionDir = (0, session_utils_1.getAndValidateSessionDirectory)(true); // requireWrite=true for manifest generation
|
|
301
|
-
logger.debug('Session directory resolved and validated', { sessionDir });
|
|
302
|
-
}
|
|
303
|
-
catch (error) {
|
|
304
|
-
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', {
|
|
305
|
-
operation: 'session_directory_validation',
|
|
306
|
-
component: 'GenerateManifestsTool',
|
|
307
|
-
requestId,
|
|
308
|
-
suggestedActions: [
|
|
309
|
-
'Ensure session directory exists and is writable',
|
|
310
|
-
'Check directory permissions',
|
|
311
|
-
'Verify the directory path is correct',
|
|
312
|
-
'Verify DOT_AI_SESSION_DIR environment variable is correctly set'
|
|
313
|
-
]
|
|
314
|
-
});
|
|
315
|
-
}
|
|
273
|
+
// Initialize session manager
|
|
274
|
+
const sessionManager = new generic_session_manager_1.GenericSessionManager('sol');
|
|
275
|
+
logger.debug('Session manager initialized', { requestId });
|
|
316
276
|
// Ensure cluster connectivity before proceeding
|
|
317
277
|
await (0, cluster_utils_1.ensureClusterConnection)(dotAI, logger, requestId, 'GenerateManifestsTool');
|
|
318
|
-
// Load solution
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
solutionId: args.solutionId,
|
|
324
|
-
hasQuestions: !!solution.questions,
|
|
325
|
-
primaryResources: solution.resources
|
|
326
|
-
});
|
|
327
|
-
}
|
|
328
|
-
catch (error) {
|
|
329
|
-
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', {
|
|
330
|
-
operation: 'solution_file_load',
|
|
278
|
+
// Load solution session
|
|
279
|
+
const session = sessionManager.getSession(args.solutionId);
|
|
280
|
+
if (!session) {
|
|
281
|
+
throw error_handling_1.ErrorHandler.createError(error_handling_1.ErrorCategory.VALIDATION, error_handling_1.ErrorSeverity.HIGH, `Solution not found: ${args.solutionId}`, {
|
|
282
|
+
operation: 'solution_loading',
|
|
331
283
|
component: 'GenerateManifestsTool',
|
|
332
284
|
requestId,
|
|
333
|
-
input: { solutionId: args.solutionId
|
|
285
|
+
input: { solutionId: args.solutionId },
|
|
334
286
|
suggestedActions: [
|
|
335
|
-
'
|
|
336
|
-
'
|
|
337
|
-
'Ensure
|
|
338
|
-
'
|
|
287
|
+
'Verify the solution ID is correct',
|
|
288
|
+
'Ensure the solution was created by the recommend tool',
|
|
289
|
+
'Ensure all configuration stages were completed',
|
|
290
|
+
'Check that the session has not expired'
|
|
339
291
|
]
|
|
340
292
|
});
|
|
341
293
|
}
|
|
342
|
-
|
|
343
|
-
|
|
294
|
+
const solution = session.data;
|
|
295
|
+
logger.debug('Solution loaded successfully', {
|
|
296
|
+
solutionId: args.solutionId,
|
|
297
|
+
hasQuestions: !!solution.questions,
|
|
298
|
+
primaryResources: solution.resources
|
|
299
|
+
});
|
|
300
|
+
// Prepare file path for manifests (store in tmp directory)
|
|
301
|
+
const tmpDir = path.join(process.cwd(), 'tmp');
|
|
302
|
+
if (!fs.existsSync(tmpDir)) {
|
|
303
|
+
fs.mkdirSync(tmpDir, { recursive: true });
|
|
304
|
+
}
|
|
305
|
+
const yamlPath = path.join(tmpDir, `${args.solutionId}.yaml`);
|
|
344
306
|
// AI generation and validation loop
|
|
345
307
|
let lastError;
|
|
346
308
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
@@ -355,9 +317,9 @@ async function handleGenerateManifestsTool(args, dotAI, logger, requestId) {
|
|
|
355
317
|
const userAnswers = (0, solution_utils_1.extractUserAnswers)(solution);
|
|
356
318
|
const dotAiLabels = (0, solution_utils_1.addDotAiLabels)(undefined, userAnswers, solution);
|
|
357
319
|
// Generate manifests with AI (including labels)
|
|
358
|
-
const aiManifests = await generateManifestsWithAI(solution, dotAI, logger, lastError, dotAiLabels, args.interaction_id);
|
|
320
|
+
const aiManifests = await generateManifestsWithAI(solution, args.solutionId, dotAI, logger, lastError, dotAiLabels, args.interaction_id);
|
|
359
321
|
// Generate metadata ConfigMap
|
|
360
|
-
const metadataConfigMap = generateMetadataConfigMap(solution, userAnswers, logger);
|
|
322
|
+
const metadataConfigMap = generateMetadataConfigMap(solution, args.solutionId, userAnswers, logger);
|
|
361
323
|
// Combine ConfigMap with AI-generated manifests
|
|
362
324
|
const manifests = metadataConfigMap + '---\n' + aiManifests;
|
|
363
325
|
// Save manifests to file
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { GenericSessionManager } from '../core/generic-session-manager';
|
|
2
|
+
import { Logger } from '../core/error-handling';
|
|
3
|
+
import { OperateSessionData } from './operate';
|
|
4
|
+
/**
|
|
5
|
+
* Analyzes user intent and generates operational proposal using AI tool loop
|
|
6
|
+
*
|
|
7
|
+
* @param intent - User's operational intent (e.g., "update my-api to v2.0")
|
|
8
|
+
* @param logger - Logger instance
|
|
9
|
+
* @param sessionManager - Session manager instance
|
|
10
|
+
* @param sessionId - Optional session ID for refinement
|
|
11
|
+
* @param interaction_id - Optional interaction ID for eval datasets
|
|
12
|
+
* @returns Operation output with proposed changes
|
|
13
|
+
*/
|
|
14
|
+
export declare function analyzeIntent(intent: string, logger: Logger, sessionManager: GenericSessionManager<OperateSessionData>, sessionId?: string, interaction_id?: string): Promise<any>;
|
|
15
|
+
//# sourceMappingURL=operate-analysis.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"operate-analysis.d.ts","sourceRoot":"","sources":["../../src/tools/operate-analysis.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AAGxE,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EAEL,kBAAkB,EAMnB,MAAM,WAAW,CAAC;AAEnB;;;;;;;;;GASG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,qBAAqB,CAAC,kBAAkB,CAAC,EACzD,SAAS,CAAC,EAAE,MAAM,EAClB,cAAc,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,GAAG,CAAC,CAgDd"}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.analyzeIntent = analyzeIntent;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const kubectl_tools_1 = require("../core/kubectl-tools");
|
|
10
|
+
const ai_provider_factory_1 = require("../core/ai-provider-factory");
|
|
11
|
+
const operate_1 = require("./operate");
|
|
12
|
+
/**
|
|
13
|
+
* Analyzes user intent and generates operational proposal using AI tool loop
|
|
14
|
+
*
|
|
15
|
+
* @param intent - User's operational intent (e.g., "update my-api to v2.0")
|
|
16
|
+
* @param logger - Logger instance
|
|
17
|
+
* @param sessionManager - Session manager instance
|
|
18
|
+
* @param sessionId - Optional session ID for refinement
|
|
19
|
+
* @param interaction_id - Optional interaction ID for eval datasets
|
|
20
|
+
* @returns Operation output with proposed changes
|
|
21
|
+
*/
|
|
22
|
+
async function analyzeIntent(intent, logger, sessionManager, sessionId, interaction_id) {
|
|
23
|
+
logger.info('Starting operate analysis', { intent, sessionId });
|
|
24
|
+
// 1. Embed context (patterns, policies, capabilities)
|
|
25
|
+
const context = await (0, operate_1.embedContext)(intent, logger);
|
|
26
|
+
// 2. Load prompts (static system + dynamic user message)
|
|
27
|
+
const systemPrompt = loadSystemPrompt();
|
|
28
|
+
const userMessage = buildUserMessage(intent, context);
|
|
29
|
+
// 3. Execute AI tool loop with kubectl tools
|
|
30
|
+
const aiResult = await executeToolLoop(systemPrompt, userMessage, logger, interaction_id);
|
|
31
|
+
// 4. Parse AI response into structured format
|
|
32
|
+
const proposedChanges = parseAIResponse(aiResult, logger);
|
|
33
|
+
// 5. Create and save session
|
|
34
|
+
const session = await saveAnalysisSession(intent, context, proposedChanges, sessionManager, sessionId, interaction_id, logger);
|
|
35
|
+
logger.info('Operate analysis complete', { sessionId: session.sessionId });
|
|
36
|
+
// 6. Return formatted output for user
|
|
37
|
+
return {
|
|
38
|
+
status: 'awaiting_user_approval',
|
|
39
|
+
sessionId: session.sessionId,
|
|
40
|
+
analysis: {
|
|
41
|
+
summary: proposedChanges.analysis,
|
|
42
|
+
currentState: proposedChanges.currentState,
|
|
43
|
+
proposedChanges: session.data.proposedChanges,
|
|
44
|
+
commands: session.data.commands,
|
|
45
|
+
dryRunValidation: session.data.dryRunValidation,
|
|
46
|
+
patternsApplied: session.data.patternsApplied,
|
|
47
|
+
capabilitiesUsed: session.data.capabilitiesUsed,
|
|
48
|
+
policiesChecked: session.data.policiesChecked,
|
|
49
|
+
risks: session.data.risks,
|
|
50
|
+
validationIntent: session.data.validationIntent
|
|
51
|
+
},
|
|
52
|
+
message: 'Operational proposal generated successfully. Review changes and execute with operate(sessionId, executeChoice=1).',
|
|
53
|
+
nextAction: `Review the proposed changes and call operate({ sessionId: "${session.sessionId}", executeChoice: 1 }) to execute.`
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Loads static system prompt from prompts/operate-system.md
|
|
58
|
+
* This prompt is cacheable across all operate calls
|
|
59
|
+
*/
|
|
60
|
+
function loadSystemPrompt() {
|
|
61
|
+
const promptPath = path_1.default.join(process.cwd(), 'prompts', 'operate-system.md');
|
|
62
|
+
return fs_1.default.readFileSync(promptPath, 'utf8');
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Builds dynamic user message with intent and embedded context
|
|
66
|
+
* Uses template from prompts/operate-user.md and formatting functions from operate.ts
|
|
67
|
+
*/
|
|
68
|
+
function buildUserMessage(intent, context) {
|
|
69
|
+
const templatePath = path_1.default.join(process.cwd(), 'prompts', 'operate-user.md');
|
|
70
|
+
const template = fs_1.default.readFileSync(templatePath, 'utf8');
|
|
71
|
+
// Format context sections using shared formatting functions
|
|
72
|
+
const patternsText = (0, operate_1.formatPatterns)(context.patterns);
|
|
73
|
+
const policiesText = (0, operate_1.formatPolicies)(context.policies);
|
|
74
|
+
const capabilitiesText = (0, operate_1.formatCapabilities)(context.capabilities);
|
|
75
|
+
// Replace template placeholders (use double braces to match template syntax)
|
|
76
|
+
return template
|
|
77
|
+
.replace('{{intent}}', intent)
|
|
78
|
+
.replace('{{patterns}}', patternsText)
|
|
79
|
+
.replace('{{policies}}', policiesText)
|
|
80
|
+
.replace('{{capabilities}}', capabilitiesText);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Executes AI tool loop with kubectl investigation tools
|
|
84
|
+
* AI autonomously inspects cluster and validates changes with dry-run
|
|
85
|
+
*
|
|
86
|
+
* @param systemPrompt - Static instructions (cacheable)
|
|
87
|
+
* @param userMessage - Dynamic content with intent and context
|
|
88
|
+
* @param logger - Logger instance
|
|
89
|
+
* @param interaction_id - Optional interaction ID for eval datasets
|
|
90
|
+
* @returns AI's final response
|
|
91
|
+
* @throws Error if AI fails to converge within 30 iterations
|
|
92
|
+
*/
|
|
93
|
+
async function executeToolLoop(systemPrompt, userMessage, logger, interaction_id) {
|
|
94
|
+
logger.debug('Starting AI tool loop for operate analysis');
|
|
95
|
+
const aiProvider = (0, ai_provider_factory_1.createAIProvider)();
|
|
96
|
+
const result = await aiProvider.toolLoop({
|
|
97
|
+
systemPrompt,
|
|
98
|
+
userMessage,
|
|
99
|
+
tools: kubectl_tools_1.KUBECTL_INVESTIGATION_TOOLS,
|
|
100
|
+
toolExecutor: kubectl_tools_1.executeKubectlTools,
|
|
101
|
+
maxIterations: 30,
|
|
102
|
+
operation: 'operate-analysis',
|
|
103
|
+
evaluationContext: {
|
|
104
|
+
user_intent: userMessage.substring(0, 200) // First 200 chars as context
|
|
105
|
+
},
|
|
106
|
+
interaction_id
|
|
107
|
+
});
|
|
108
|
+
logger.debug('AI tool loop completed', {
|
|
109
|
+
iterations: result.iterations,
|
|
110
|
+
toolCallsExecuted: result.toolCallsExecuted.length,
|
|
111
|
+
responseLength: result.finalMessage.length
|
|
112
|
+
});
|
|
113
|
+
return result.finalMessage;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Parses AI response into structured ProposedChanges format
|
|
117
|
+
* Enforces strict JSON parsing with validation
|
|
118
|
+
*
|
|
119
|
+
* @param response - AI's final response
|
|
120
|
+
* @param logger - Logger instance
|
|
121
|
+
* @returns Parsed proposed changes
|
|
122
|
+
* @throws Error if response is not valid JSON or missing required fields
|
|
123
|
+
*/
|
|
124
|
+
function parseAIResponse(response, logger) {
|
|
125
|
+
logger.debug('Parsing AI response');
|
|
126
|
+
// Extract JSON from code block
|
|
127
|
+
const jsonMatch = response.match(/```json\n([\s\S]+?)\n```/);
|
|
128
|
+
if (!jsonMatch) {
|
|
129
|
+
const truncatedResponse = response.substring(0, 500);
|
|
130
|
+
logger.error(`AI response missing JSON code block. Response: ${truncatedResponse}`);
|
|
131
|
+
throw new Error('AI did not return structured JSON response. Expected ```json code block with proposal.');
|
|
132
|
+
}
|
|
133
|
+
try {
|
|
134
|
+
const parsed = JSON.parse(jsonMatch[1]);
|
|
135
|
+
// Validate required fields
|
|
136
|
+
if (!parsed.analysis || typeof parsed.analysis !== 'string') {
|
|
137
|
+
throw new Error('AI response missing required "analysis" field (string)');
|
|
138
|
+
}
|
|
139
|
+
if (!parsed.commands || !Array.isArray(parsed.commands)) {
|
|
140
|
+
throw new Error('AI response missing required "commands" array');
|
|
141
|
+
}
|
|
142
|
+
if (parsed.commands.length === 0) {
|
|
143
|
+
throw new Error('AI response has empty "commands" array - no operations proposed');
|
|
144
|
+
}
|
|
145
|
+
if (!parsed.dryRunValidation || typeof parsed.dryRunValidation !== 'object') {
|
|
146
|
+
throw new Error('AI response missing required "dryRunValidation" object');
|
|
147
|
+
}
|
|
148
|
+
// Trust AI's claim but log for audit trail
|
|
149
|
+
logger.info('AI dry-run validation status', {
|
|
150
|
+
validation: parsed.dryRunValidation,
|
|
151
|
+
status: parsed.dryRunValidation.status
|
|
152
|
+
});
|
|
153
|
+
// Ensure proposedChanges structure exists
|
|
154
|
+
if (!parsed.proposedChanges) {
|
|
155
|
+
parsed.proposedChanges = { create: [], update: [], delete: [] };
|
|
156
|
+
}
|
|
157
|
+
// Validate proposedChanges structure
|
|
158
|
+
const changes = parsed.proposedChanges;
|
|
159
|
+
if (!Array.isArray(changes.create))
|
|
160
|
+
changes.create = [];
|
|
161
|
+
if (!Array.isArray(changes.update))
|
|
162
|
+
changes.update = [];
|
|
163
|
+
if (!Array.isArray(changes.delete))
|
|
164
|
+
changes.delete = [];
|
|
165
|
+
// Ensure metadata arrays exist
|
|
166
|
+
if (!Array.isArray(parsed.patternsApplied))
|
|
167
|
+
parsed.patternsApplied = [];
|
|
168
|
+
if (!Array.isArray(parsed.capabilitiesUsed))
|
|
169
|
+
parsed.capabilitiesUsed = [];
|
|
170
|
+
if (!Array.isArray(parsed.policiesChecked))
|
|
171
|
+
parsed.policiesChecked = [];
|
|
172
|
+
// Ensure risks object exists
|
|
173
|
+
if (!parsed.risks) {
|
|
174
|
+
parsed.risks = { level: 'low', description: 'No specific risks identified' };
|
|
175
|
+
}
|
|
176
|
+
// Ensure validationIntent exists
|
|
177
|
+
if (!parsed.validationIntent || typeof parsed.validationIntent !== 'string') {
|
|
178
|
+
parsed.validationIntent = 'Validate that the operation completed successfully';
|
|
179
|
+
}
|
|
180
|
+
logger.debug('AI response parsed successfully', {
|
|
181
|
+
commandCount: parsed.commands.length,
|
|
182
|
+
createCount: changes.create.length,
|
|
183
|
+
updateCount: changes.update.length,
|
|
184
|
+
deleteCount: changes.delete.length
|
|
185
|
+
});
|
|
186
|
+
return parsed;
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
190
|
+
logger.error(`Failed to parse AI response: ${errorMsg}`);
|
|
191
|
+
throw new Error(`Invalid AI response format: ${errorMsg}`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Saves analysis session to disk using GenericSessionManager
|
|
196
|
+
*
|
|
197
|
+
* @param intent - User's operational intent
|
|
198
|
+
* @param context - Embedded context
|
|
199
|
+
* @param proposedChanges - Parsed AI proposal
|
|
200
|
+
* @param sessionManager - Session manager instance
|
|
201
|
+
* @param sessionId - Optional existing session ID for updates
|
|
202
|
+
* @param interaction_id - Optional interaction ID for eval datasets
|
|
203
|
+
* @param logger - Logger instance
|
|
204
|
+
* @returns Saved session
|
|
205
|
+
*/
|
|
206
|
+
async function saveAnalysisSession(intent, context, proposedChanges, sessionManager, sessionId, interaction_id, logger) {
|
|
207
|
+
const sessionData = {
|
|
208
|
+
intent,
|
|
209
|
+
interaction_id,
|
|
210
|
+
context,
|
|
211
|
+
proposedChanges: proposedChanges.proposedChanges,
|
|
212
|
+
commands: proposedChanges.commands,
|
|
213
|
+
dryRunValidation: proposedChanges.dryRunValidation,
|
|
214
|
+
patternsApplied: proposedChanges.patternsApplied,
|
|
215
|
+
capabilitiesUsed: proposedChanges.capabilitiesUsed,
|
|
216
|
+
policiesChecked: proposedChanges.policiesChecked,
|
|
217
|
+
risks: proposedChanges.risks,
|
|
218
|
+
validationIntent: proposedChanges.validationIntent,
|
|
219
|
+
status: 'analysis_complete'
|
|
220
|
+
};
|
|
221
|
+
if (sessionId) {
|
|
222
|
+
// Update existing session (refinement case)
|
|
223
|
+
logger.debug('Updating existing operate session', { sessionId });
|
|
224
|
+
await sessionManager.replaceSession(sessionId, sessionData);
|
|
225
|
+
const session = sessionManager.getSession(sessionId);
|
|
226
|
+
if (!session) {
|
|
227
|
+
throw new Error(`Failed to retrieve session ${sessionId} after update`);
|
|
228
|
+
}
|
|
229
|
+
return session;
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
// Create new session
|
|
233
|
+
logger.debug('Creating new operate session');
|
|
234
|
+
const session = await sessionManager.createSession(sessionData);
|
|
235
|
+
logger.info('Operate session created', { sessionId: session.sessionId });
|
|
236
|
+
return session;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Operate Execution Workflow
|
|
3
|
+
*
|
|
4
|
+
* Executes approved operational changes using shared command executor
|
|
5
|
+
* and validates results using remediate tool
|
|
6
|
+
*/
|
|
7
|
+
import { Logger } from '../core/error-handling';
|
|
8
|
+
import { GenericSessionManager } from '../core/generic-session-manager';
|
|
9
|
+
import { OperateSessionData, OperateOutput } from './operate';
|
|
10
|
+
/**
|
|
11
|
+
* Executes approved operational changes
|
|
12
|
+
* @param sessionId - Session ID with approved changes
|
|
13
|
+
* @param logger - Logger instance
|
|
14
|
+
* @param sessionManager - Session manager instance
|
|
15
|
+
* @returns Operation output with execution results
|
|
16
|
+
*/
|
|
17
|
+
export declare function executeOperations(sessionId: string, logger: Logger, sessionManager: GenericSessionManager<OperateSessionData>): Promise<OperateOutput>;
|
|
18
|
+
//# sourceMappingURL=operate-execution.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"operate-execution.d.ts","sourceRoot":"","sources":["../../src/tools/operate-execution.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,MAAM,EAA8C,MAAM,wBAAwB,CAAC;AAC5F,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AAExE,OAAO,EAAE,kBAAkB,EAAmB,aAAa,EAAE,MAAM,WAAW,CAAC;AAG/E;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,qBAAqB,CAAC,kBAAkB,CAAC,GACxD,OAAO,CAAC,aAAa,CAAC,CAwIxB"}
|