@vfarcic/dot-ai 0.49.0 → 0.50.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 +49 -2
- package/dist/core/base-vector-service.d.ts +4 -4
- package/dist/core/base-vector-service.d.ts.map +1 -1
- package/dist/core/base-vector-service.js +31 -39
- package/dist/core/capabilities.d.ts +71 -0
- package/dist/core/capabilities.d.ts.map +1 -0
- package/dist/core/capabilities.js +215 -0
- package/dist/core/capability-vector-service.d.ts +80 -0
- package/dist/core/capability-vector-service.d.ts.map +1 -0
- package/dist/core/capability-vector-service.js +142 -0
- package/dist/core/claude.d.ts +14 -1
- package/dist/core/claude.d.ts.map +1 -1
- package/dist/core/claude.js +109 -13
- package/dist/core/discovery.d.ts +6 -0
- package/dist/core/discovery.d.ts.map +1 -1
- package/dist/core/discovery.js +7 -1
- package/dist/core/embedding-service.d.ts +3 -3
- package/dist/core/embedding-service.d.ts.map +1 -1
- package/dist/core/embedding-service.js +6 -7
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +5 -4
- package/dist/core/schema.d.ts +11 -30
- package/dist/core/schema.d.ts.map +1 -1
- package/dist/core/schema.js +107 -126
- package/dist/core/vector-db-service.d.ts +6 -0
- package/dist/core/vector-db-service.d.ts.map +1 -1
- package/dist/core/vector-db-service.js +40 -10
- package/dist/tools/organizational-data.d.ts +5 -3
- package/dist/tools/organizational-data.d.ts.map +1 -1
- package/dist/tools/organizational-data.js +1673 -52
- package/dist/tools/recommend.d.ts.map +1 -1
- package/dist/tools/recommend.js +3 -7
- package/dist/tools/version.d.ts +9 -0
- package/dist/tools/version.d.ts.map +1 -1
- package/dist/tools/version.js +115 -5
- package/package.json +1 -1
- package/prompts/capability-inference.md +121 -0
- package/prompts/doc-testing-test-section.md +40 -2
- package/prompts/resource-selection.md +10 -3
- package/shared-prompts/prd-update-decisions.md +9 -0
- package/shared-prompts/prd-update-progress.md +33 -0
- package/prompts/concept-extraction.md +0 -95
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ DevOps AI Toolkit is an AI-powered development productivity platform that enhanc
|
|
|
12
12
|
|
|
13
13
|
### Kubernetes Deployment
|
|
14
14
|
- **Developers**: Deploy applications without needing deep Kubernetes expertise
|
|
15
|
-
- **Platform Engineers**: Create organizational deployment patterns that enhance AI recommendations with institutional knowledge and best practices
|
|
15
|
+
- **Platform Engineers**: Create organizational deployment patterns that enhance AI recommendations with institutional knowledge and best practices, and scan cluster resources to enable semantic matching for dramatically improved recommendation accuracy
|
|
16
16
|
|
|
17
17
|
### Documentation Testing
|
|
18
18
|
- **Documentation Maintainers**: Automatically validate documentation accuracy and catch outdated content
|
|
@@ -31,10 +31,34 @@ DevOps AI Toolkit is an AI-powered development productivity platform that enhanc
|
|
|
31
31
|
|
|
32
32
|
### Kubernetes Deployment Intelligence
|
|
33
33
|
🔍 **Smart Discovery**: Automatically finds all available resources and operators in your cluster
|
|
34
|
-
|
|
34
|
+
🧠 **Semantic Capability Management**: Discovers what each resource actually does for intelligent matching
|
|
35
|
+
🤖 **AI Recommendations**: Get deployment suggestions tailored to your specific cluster setup with enhanced semantic understanding
|
|
35
36
|
🔧 **Operator-Aware**: Leverages custom operators and CRDs when available
|
|
36
37
|
🚀 **Complete Workflow**: From discovery to deployment with automated Kubernetes integration
|
|
37
38
|
|
|
39
|
+
#### Capability-Enhanced Recommendations
|
|
40
|
+
Transform how AI understands your cluster by discovering semantic capabilities of each resource:
|
|
41
|
+
|
|
42
|
+
**The Problem**: Traditional discovery sees `sqls.devopstoolkit.live` as a meaningless name among hundreds of resources.
|
|
43
|
+
|
|
44
|
+
**The Solution**: Capability management teaches the system that `sqls.devopstoolkit.live` handles PostgreSQL databases with multi-cloud support.
|
|
45
|
+
|
|
46
|
+
**Before Capability Management:**
|
|
47
|
+
```
|
|
48
|
+
User: "I need a PostgreSQL database"
|
|
49
|
+
AI: Gets 400+ generic resource names → picks complex multi-resource solution
|
|
50
|
+
Result: Misses optimal single-resource solutions
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**After Capability Management:**
|
|
54
|
+
```
|
|
55
|
+
User: "I need a PostgreSQL database"
|
|
56
|
+
AI: Gets pre-filtered relevant resources with rich context
|
|
57
|
+
Result: Finds sqls.devopstoolkit.live as perfect match ✨
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**Get Started**: See the [Capability Management Guide](./docs/mcp-capability-management-guide.md) for complete workflows including cluster scanning, capability search, and recommendation enhancement.
|
|
61
|
+
|
|
38
62
|
### Documentation Testing & Validation
|
|
39
63
|
📖 **Automated Testing**: Validates documentation by executing commands and testing examples
|
|
40
64
|
🔍 **Two-Phase Validation**: Tests both functionality (does it work?) and semantic accuracy (are descriptions truthful?)
|
|
@@ -164,6 +188,29 @@ Agent: Perfect! Generating manifests and deploying now...
|
|
|
164
188
|
Agent: ✅ Successfully deployed! Your application is running.
|
|
165
189
|
```
|
|
166
190
|
|
|
191
|
+
**Example: Capability Management**
|
|
192
|
+
```
|
|
193
|
+
User: I want to scan my cluster for resource capabilities to improve recommendations
|
|
194
|
+
|
|
195
|
+
Agent: I'll help you scan your cluster to discover semantic capabilities. This will dramatically improve recommendation accuracy.
|
|
196
|
+
[Uses manageOrgData with capabilities scanning]
|
|
197
|
+
|
|
198
|
+
Agent: Would you like to scan all cluster resources or specify a subset?
|
|
199
|
+
Options: 1) All resources 2) Specific subset
|
|
200
|
+
|
|
201
|
+
User: All resources in auto mode
|
|
202
|
+
|
|
203
|
+
Agent: Starting comprehensive cluster capability scan...
|
|
204
|
+
✅ Capability scan completed! Processed cluster resources successfully.
|
|
205
|
+
|
|
206
|
+
User: Now I need a PostgreSQL database
|
|
207
|
+
|
|
208
|
+
Agent: Let me get enhanced recommendations using your capability data.
|
|
209
|
+
[Uses recommend tool with capability pre-filtering]
|
|
210
|
+
|
|
211
|
+
Agent: Perfect! I found sqls.devopstoolkit.live as the top match - it's a managed database solution supporting PostgreSQL with multi-cloud capabilities and low complexity. Much better than the generic StatefulSet approach I would have suggested before!
|
|
212
|
+
```
|
|
213
|
+
|
|
167
214
|
**Example: Documentation Testing**
|
|
168
215
|
```
|
|
169
216
|
User: I want to test my README.md file to make sure all the examples work
|
|
@@ -53,6 +53,10 @@ export declare abstract class BaseVectorService<T> {
|
|
|
53
53
|
* Delete data by ID
|
|
54
54
|
*/
|
|
55
55
|
deleteData(id: string): Promise<void>;
|
|
56
|
+
/**
|
|
57
|
+
* Delete all data (recreate collection)
|
|
58
|
+
*/
|
|
59
|
+
deleteAllData(): Promise<void>;
|
|
56
60
|
/**
|
|
57
61
|
* Get all data (limited)
|
|
58
62
|
*/
|
|
@@ -74,10 +78,6 @@ export declare abstract class BaseVectorService<T> {
|
|
|
74
78
|
* Hybrid search combining semantic and keyword matching
|
|
75
79
|
*/
|
|
76
80
|
private hybridSearch;
|
|
77
|
-
/**
|
|
78
|
-
* Keyword-only search (fallback when embeddings not available)
|
|
79
|
-
*/
|
|
80
|
-
private keywordOnlySearch;
|
|
81
81
|
/**
|
|
82
82
|
* Combine semantic and keyword results with hybrid ranking
|
|
83
83
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base-vector-service.d.ts","sourceRoot":"","sources":["../../src/core/base-vector-service.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,eAAe,EAAkB,MAAM,qBAAqB,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAEvD,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB,CAAC,CAAC;IACjC,IAAI,EAAE,CAAC,CAAC;IACR,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,SAAS,GAAG,UAAU,GAAG,QAAQ,CAAC;CAC9C;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,8BAAsB,iBAAiB,CAAC,CAAC;IACvC,SAAS,CAAC,QAAQ,EAAE,eAAe,CAAC;IACpC,SAAS,CAAC,gBAAgB,EAAE,gBAAgB,CAAC;IAC7C,SAAS,CAAC,cAAc,EAAE,MAAM,CAAC;gBAErB,cAAc,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,eAAe,EAAE,gBAAgB,CAAC,EAAE,gBAAgB;IAMnG;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAQjC;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAIrC;;OAEG;IACG,SAAS,CAAC,IAAI,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"base-vector-service.d.ts","sourceRoot":"","sources":["../../src/core/base-vector-service.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,eAAe,EAAkB,MAAM,qBAAqB,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAEvD,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB,CAAC,CAAC;IACjC,IAAI,EAAE,CAAC,CAAC;IACR,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,SAAS,GAAG,UAAU,GAAG,QAAQ,CAAC;CAC9C;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,8BAAsB,iBAAiB,CAAC,CAAC;IACvC,SAAS,CAAC,QAAQ,EAAE,eAAe,CAAC;IACpC,SAAS,CAAC,gBAAgB,EAAE,gBAAgB,CAAC;IAC7C,SAAS,CAAC,cAAc,EAAE,MAAM,CAAC;gBAErB,cAAc,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,eAAe,EAAE,gBAAgB,CAAC,EAAE,gBAAgB;IAMnG;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAQjC;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAIrC;;OAEG;IACG,SAAS,CAAC,IAAI,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IA+BvC;;OAEG;IACG,UAAU,CACd,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAC;IAyBjC;;OAEG;IACG,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAW5C;;OAEG;IACG,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI3C;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAIpC;;OAEG;IACG,UAAU,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IAS9C;;OAEG;IACG,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IAWrC;;OAEG;IACH,aAAa,IAAI,UAAU;IAU3B,SAAS,CAAC,QAAQ,CAAC,gBAAgB,CAAC,IAAI,EAAE,CAAC,GAAG,MAAM;IACpD,SAAS,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,GAAG,MAAM;IAC7C,SAAS,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAC9D,SAAS,CAAC,QAAQ,CAAC,aAAa,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC;IAGjE,SAAS,CAAC,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE;IAIlD;;OAEG;YACW,YAAY;IAkC1B;;OAEG;IACH,OAAO,CAAC,oBAAoB;CA2C7B"}
|
|
@@ -43,25 +43,29 @@ class BaseVectorService {
|
|
|
43
43
|
async storeData(data) {
|
|
44
44
|
const searchText = this.createSearchText(data);
|
|
45
45
|
const id = this.extractId(data);
|
|
46
|
-
//
|
|
47
|
-
let embedding
|
|
46
|
+
// Generate embedding - required for vector storage
|
|
47
|
+
let embedding;
|
|
48
48
|
if (this.embeddingService.isAvailable()) {
|
|
49
49
|
try {
|
|
50
50
|
embedding = await this.embeddingService.generateEmbedding(searchText);
|
|
51
51
|
}
|
|
52
52
|
catch (error) {
|
|
53
|
-
//
|
|
54
|
-
|
|
53
|
+
// Fail immediately with clear error about embedding generation
|
|
54
|
+
throw new Error(`Embedding generation failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
|
+
else {
|
|
58
|
+
// Embedding service not available - fail with clear error
|
|
59
|
+
throw new Error('Embedding service not available - cannot store data in vector collection');
|
|
60
|
+
}
|
|
57
61
|
const document = {
|
|
58
62
|
id,
|
|
59
63
|
payload: {
|
|
60
64
|
...this.createPayload(data),
|
|
61
65
|
searchText: searchText,
|
|
62
|
-
hasEmbedding:
|
|
66
|
+
hasEmbedding: true
|
|
63
67
|
},
|
|
64
|
-
vector: embedding
|
|
68
|
+
vector: embedding
|
|
65
69
|
};
|
|
66
70
|
await this.vectorDB.upsertDocument(document);
|
|
67
71
|
}
|
|
@@ -69,25 +73,25 @@ class BaseVectorService {
|
|
|
69
73
|
* Search for data using hybrid semantic + keyword matching
|
|
70
74
|
*/
|
|
71
75
|
async searchData(query, options = {}) {
|
|
76
|
+
// Fail immediately if embedding service not available - no graceful fallback
|
|
77
|
+
if (!this.embeddingService.isAvailable()) {
|
|
78
|
+
throw new Error('Embedding service not available - cannot perform semantic search');
|
|
79
|
+
}
|
|
72
80
|
// Extract keywords for keyword search
|
|
73
81
|
const queryKeywords = this.extractKeywords(query);
|
|
74
82
|
if (queryKeywords.length === 0) {
|
|
75
83
|
return [];
|
|
76
84
|
}
|
|
77
85
|
const limit = options.limit || 10;
|
|
78
|
-
const scoreThreshold = options.scoreThreshold || 0.
|
|
79
|
-
//
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
console.warn('Semantic search failed, falling back to keyword search:', error);
|
|
87
|
-
}
|
|
86
|
+
const scoreThreshold = options.scoreThreshold || 0.01;
|
|
87
|
+
// Perform hybrid search (semantic + keyword)
|
|
88
|
+
try {
|
|
89
|
+
return await this.hybridSearch(query, queryKeywords, { limit, scoreThreshold });
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
// Fail immediately - no fallback to keyword-only search
|
|
93
|
+
throw new Error(`Semantic search failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
88
94
|
}
|
|
89
|
-
// Keyword-only search (fallback or when embeddings not available)
|
|
90
|
-
return await this.keywordOnlySearch(queryKeywords, { limit, scoreThreshold });
|
|
91
95
|
}
|
|
92
96
|
/**
|
|
93
97
|
* Get data by ID
|
|
@@ -108,6 +112,12 @@ class BaseVectorService {
|
|
|
108
112
|
async deleteData(id) {
|
|
109
113
|
await this.vectorDB.deleteDocument(id);
|
|
110
114
|
}
|
|
115
|
+
/**
|
|
116
|
+
* Delete all data (recreate collection)
|
|
117
|
+
*/
|
|
118
|
+
async deleteAllData() {
|
|
119
|
+
await this.vectorDB.deleteAllDocuments();
|
|
120
|
+
}
|
|
111
121
|
/**
|
|
112
122
|
* Get all data (limited)
|
|
113
123
|
*/
|
|
@@ -152,16 +162,15 @@ class BaseVectorService {
|
|
|
152
162
|
* Hybrid search combining semantic and keyword matching
|
|
153
163
|
*/
|
|
154
164
|
async hybridSearch(query, queryKeywords, options) {
|
|
155
|
-
// Generate query embedding
|
|
165
|
+
// Generate query embedding - required for semantic search
|
|
156
166
|
const queryEmbedding = await this.embeddingService.generateEmbedding(query);
|
|
157
167
|
if (!queryEmbedding) {
|
|
158
|
-
|
|
159
|
-
return await this.keywordOnlySearch(queryKeywords, options);
|
|
168
|
+
throw new Error('Failed to generate query embedding for semantic search');
|
|
160
169
|
}
|
|
161
170
|
// Semantic search using vector similarity
|
|
162
171
|
const semanticResults = await this.vectorDB.searchSimilar(queryEmbedding, {
|
|
163
172
|
limit: options.limit * 2, // Get more candidates for hybrid ranking
|
|
164
|
-
scoreThreshold: 0.
|
|
173
|
+
scoreThreshold: 0.1 // Very permissive threshold for single-word queries
|
|
165
174
|
});
|
|
166
175
|
// Keyword search
|
|
167
176
|
const keywordResults = await this.vectorDB.searchByKeywords(queryKeywords, {
|
|
@@ -171,23 +180,6 @@ class BaseVectorService {
|
|
|
171
180
|
// Combine and rank results
|
|
172
181
|
return this.combineHybridResults(semanticResults, keywordResults, queryKeywords, options);
|
|
173
182
|
}
|
|
174
|
-
/**
|
|
175
|
-
* Keyword-only search (fallback when embeddings not available)
|
|
176
|
-
*/
|
|
177
|
-
async keywordOnlySearch(queryKeywords, options) {
|
|
178
|
-
const keywordResults = await this.vectorDB.searchByKeywords(queryKeywords, options);
|
|
179
|
-
return keywordResults
|
|
180
|
-
.map(result => {
|
|
181
|
-
const data = this.payloadToData(result.payload);
|
|
182
|
-
data.id = result.id;
|
|
183
|
-
return {
|
|
184
|
-
data,
|
|
185
|
-
score: result.score,
|
|
186
|
-
matchType: 'keyword'
|
|
187
|
-
};
|
|
188
|
-
})
|
|
189
|
-
.filter(result => result.score >= options.scoreThreshold); // Apply score filtering
|
|
190
|
-
}
|
|
191
183
|
/**
|
|
192
184
|
* Combine semantic and keyword results with hybrid ranking
|
|
193
185
|
*/
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resource Capability Discovery & Inference Engine
|
|
3
|
+
*
|
|
4
|
+
* PRD #48: Resource Capabilities Discovery & Integration
|
|
5
|
+
*
|
|
6
|
+
* This module provides capability inference for Kubernetes resources through
|
|
7
|
+
* AI-powered analysis of schemas and metadata.
|
|
8
|
+
*/
|
|
9
|
+
import { Logger } from './error-handling';
|
|
10
|
+
import { ClaudeIntegration } from './claude';
|
|
11
|
+
/**
|
|
12
|
+
* Complete resource capability data structure for Vector DB storage
|
|
13
|
+
*/
|
|
14
|
+
export interface ResourceCapability {
|
|
15
|
+
resourceName: string;
|
|
16
|
+
capabilities: string[];
|
|
17
|
+
providers: string[];
|
|
18
|
+
abstractions: string[];
|
|
19
|
+
complexity: 'low' | 'medium' | 'high';
|
|
20
|
+
description: string;
|
|
21
|
+
useCase: string;
|
|
22
|
+
embedding?: number[];
|
|
23
|
+
analyzedAt: string;
|
|
24
|
+
confidence: number;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Generic Capability Inference Engine
|
|
28
|
+
*
|
|
29
|
+
* Analyzes any Kubernetes CRD using AI to extract semantic capabilities
|
|
30
|
+
* for improved AI recommendations and resource matching.
|
|
31
|
+
*/
|
|
32
|
+
export declare class CapabilityInferenceEngine {
|
|
33
|
+
private logger;
|
|
34
|
+
private claudeIntegration;
|
|
35
|
+
constructor(claudeIntegration: ClaudeIntegration, logger: Logger);
|
|
36
|
+
/**
|
|
37
|
+
* Main entry point: analyze resource to infer complete capabilities
|
|
38
|
+
*
|
|
39
|
+
* @param resourceName - Full resource name (e.g., "resourcegroups.azure.upbound.io")
|
|
40
|
+
* @throws Error if capability inference fails for any reason
|
|
41
|
+
*/
|
|
42
|
+
inferCapabilities(resourceName: string, schema?: string, metadata?: any): Promise<ResourceCapability>;
|
|
43
|
+
/**
|
|
44
|
+
* Use AI to infer capabilities from all available resource context
|
|
45
|
+
*
|
|
46
|
+
* @throws Error if AI inference fails
|
|
47
|
+
*/
|
|
48
|
+
private inferWithAI;
|
|
49
|
+
/**
|
|
50
|
+
* Build AI inference prompt using standard prompt loading pattern
|
|
51
|
+
*
|
|
52
|
+
* @throws Error if prompt template cannot be loaded
|
|
53
|
+
*/
|
|
54
|
+
private buildInferencePrompt;
|
|
55
|
+
/**
|
|
56
|
+
* Parse AI response into structured capability data
|
|
57
|
+
*
|
|
58
|
+
* @throws Error if AI response cannot be parsed or is invalid
|
|
59
|
+
*/
|
|
60
|
+
private parseCapabilitiesFromAI;
|
|
61
|
+
/**
|
|
62
|
+
* Build final ResourceCapability from AI analysis result
|
|
63
|
+
*/
|
|
64
|
+
private buildResourceCapability;
|
|
65
|
+
/**
|
|
66
|
+
* Generate Vector DB ID for capability storage
|
|
67
|
+
* Creates deterministic UUID from resource name for Qdrant compatibility
|
|
68
|
+
*/
|
|
69
|
+
static generateCapabilityId(resourceName: string): string;
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=capabilities.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capabilities.d.ts","sourceRoot":"","sources":["../../src/core/capabilities.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAK7C;;GAEG;AACH,MAAM,WAAW,kBAAkB;IAEjC,YAAY,EAAE,MAAM,CAAC;IAGrB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,UAAU,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IAGtC,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAGhB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IAGrB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;GAKG;AACH,qBAAa,yBAAyB;IACpC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,iBAAiB,CAAoB;gBAEjC,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM;IAKhE;;;;;OAKG;IACG,iBAAiB,CACrB,YAAY,EAAE,MAAM,EACpB,MAAM,CAAC,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,GAAG,GACb,OAAO,CAAC,kBAAkB,CAAC;IA4B9B;;;;OAIG;YACW,WAAW;IA2BzB;;;;OAIG;YACW,oBAAoB;IAkClC;;;;OAIG;IACH,OAAO,CAAC,uBAAuB;IAwD/B;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAyB/B;;;OAGG;IACH,MAAM,CAAC,oBAAoB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM;CAQ1D"}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Resource Capability Discovery & Inference Engine
|
|
4
|
+
*
|
|
5
|
+
* PRD #48: Resource Capabilities Discovery & Integration
|
|
6
|
+
*
|
|
7
|
+
* This module provides capability inference for Kubernetes resources through
|
|
8
|
+
* AI-powered analysis of schemas and metadata.
|
|
9
|
+
*/
|
|
10
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
13
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
14
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
15
|
+
}
|
|
16
|
+
Object.defineProperty(o, k2, desc);
|
|
17
|
+
}) : (function(o, m, k, k2) {
|
|
18
|
+
if (k2 === undefined) k2 = k;
|
|
19
|
+
o[k2] = m[k];
|
|
20
|
+
}));
|
|
21
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
22
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
23
|
+
}) : function(o, v) {
|
|
24
|
+
o["default"] = v;
|
|
25
|
+
});
|
|
26
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
27
|
+
var ownKeys = function(o) {
|
|
28
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
29
|
+
var ar = [];
|
|
30
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
31
|
+
return ar;
|
|
32
|
+
};
|
|
33
|
+
return ownKeys(o);
|
|
34
|
+
};
|
|
35
|
+
return function (mod) {
|
|
36
|
+
if (mod && mod.__esModule) return mod;
|
|
37
|
+
var result = {};
|
|
38
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
39
|
+
__setModuleDefault(result, mod);
|
|
40
|
+
return result;
|
|
41
|
+
};
|
|
42
|
+
})();
|
|
43
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
44
|
+
exports.CapabilityInferenceEngine = void 0;
|
|
45
|
+
const fs = __importStar(require("fs"));
|
|
46
|
+
const path = __importStar(require("path"));
|
|
47
|
+
/**
|
|
48
|
+
* Generic Capability Inference Engine
|
|
49
|
+
*
|
|
50
|
+
* Analyzes any Kubernetes CRD using AI to extract semantic capabilities
|
|
51
|
+
* for improved AI recommendations and resource matching.
|
|
52
|
+
*/
|
|
53
|
+
class CapabilityInferenceEngine {
|
|
54
|
+
logger;
|
|
55
|
+
claudeIntegration;
|
|
56
|
+
constructor(claudeIntegration, logger) {
|
|
57
|
+
this.claudeIntegration = claudeIntegration;
|
|
58
|
+
this.logger = logger;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Main entry point: analyze resource to infer complete capabilities
|
|
62
|
+
*
|
|
63
|
+
* @param resourceName - Full resource name (e.g., "resourcegroups.azure.upbound.io")
|
|
64
|
+
* @throws Error if capability inference fails for any reason
|
|
65
|
+
*/
|
|
66
|
+
async inferCapabilities(resourceName, schema, metadata) {
|
|
67
|
+
const requestId = `capability-inference-${Date.now()}`;
|
|
68
|
+
this.logger.info('Starting capability inference', {
|
|
69
|
+
requestId,
|
|
70
|
+
resource: resourceName,
|
|
71
|
+
hasSchema: !!schema,
|
|
72
|
+
hasMetadata: !!metadata
|
|
73
|
+
});
|
|
74
|
+
// Use AI to analyze all available information
|
|
75
|
+
const aiResult = await this.inferWithAI(resourceName, schema, metadata, requestId);
|
|
76
|
+
// Convert AI result to final capability structure
|
|
77
|
+
const finalCapability = this.buildResourceCapability(resourceName, aiResult);
|
|
78
|
+
this.logger.info('Capability inference completed', {
|
|
79
|
+
requestId,
|
|
80
|
+
resource: resourceName,
|
|
81
|
+
capabilitiesFound: finalCapability.capabilities.length,
|
|
82
|
+
providersFound: finalCapability.providers.length,
|
|
83
|
+
complexity: finalCapability.complexity,
|
|
84
|
+
confidence: finalCapability.confidence
|
|
85
|
+
});
|
|
86
|
+
return finalCapability;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Use AI to infer capabilities from all available resource context
|
|
90
|
+
*
|
|
91
|
+
* @throws Error if AI inference fails
|
|
92
|
+
*/
|
|
93
|
+
async inferWithAI(resourceName, schema, metadata, requestId) {
|
|
94
|
+
try {
|
|
95
|
+
const prompt = await this.buildInferencePrompt(resourceName, schema, metadata);
|
|
96
|
+
const response = await this.claudeIntegration.sendMessage(prompt);
|
|
97
|
+
return this.parseCapabilitiesFromAI(response.content);
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
this.logger.error('AI capability inference failed', error, {
|
|
101
|
+
requestId,
|
|
102
|
+
resource: resourceName
|
|
103
|
+
});
|
|
104
|
+
throw error; // Re-throw to maintain fail-fast behavior
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Build AI inference prompt using standard prompt loading pattern
|
|
109
|
+
*
|
|
110
|
+
* @throws Error if prompt template cannot be loaded
|
|
111
|
+
*/
|
|
112
|
+
async buildInferencePrompt(resourceName, schema, metadata) {
|
|
113
|
+
// Load prompt template using standard pattern from existing codebase
|
|
114
|
+
const promptPath = path.join(__dirname, '..', '..', 'prompts', 'capability-inference.md');
|
|
115
|
+
if (!fs.existsSync(promptPath)) {
|
|
116
|
+
throw new Error(`Capability inference prompt template not found: ${promptPath}`);
|
|
117
|
+
}
|
|
118
|
+
let template;
|
|
119
|
+
try {
|
|
120
|
+
template = fs.readFileSync(promptPath, 'utf8');
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
throw new Error(`Failed to read capability inference prompt: ${error instanceof Error ? error.message : String(error)}`);
|
|
124
|
+
}
|
|
125
|
+
// Replace template variables using standard pattern
|
|
126
|
+
const analysisContext = schema && metadata ?
|
|
127
|
+
'Schema and metadata available' :
|
|
128
|
+
schema ? 'Schema only' :
|
|
129
|
+
metadata ? 'Metadata only' : 'Limited context';
|
|
130
|
+
const finalPrompt = template
|
|
131
|
+
.replace(/\{resourceName\}/g, resourceName)
|
|
132
|
+
.replace(/\{analysisContext\}/g, analysisContext)
|
|
133
|
+
.replace(/\{schema\}/g, schema || 'No schema provided')
|
|
134
|
+
.replace(/\{metadata\}/g, metadata ? JSON.stringify(metadata, null, 2) : 'No metadata provided');
|
|
135
|
+
return finalPrompt;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Parse AI response into structured capability data
|
|
139
|
+
*
|
|
140
|
+
* @throws Error if AI response cannot be parsed or is invalid
|
|
141
|
+
*/
|
|
142
|
+
parseCapabilitiesFromAI(response) {
|
|
143
|
+
// Look for JSON in the response using standard pattern
|
|
144
|
+
const jsonMatch = response.match(/\{[\s\S]*\}/);
|
|
145
|
+
if (!jsonMatch) {
|
|
146
|
+
throw new Error(`No JSON found in AI response. Response: ${response.substring(0, 200)}...`);
|
|
147
|
+
}
|
|
148
|
+
let parsed;
|
|
149
|
+
try {
|
|
150
|
+
parsed = JSON.parse(jsonMatch[0]);
|
|
151
|
+
}
|
|
152
|
+
catch (parseError) {
|
|
153
|
+
throw new Error(`Invalid JSON in AI response: ${parseError instanceof Error ? parseError.message : String(parseError)}. JSON: ${jsonMatch[0].substring(0, 200)}...`);
|
|
154
|
+
}
|
|
155
|
+
// Validate required fields with detailed error messages
|
|
156
|
+
if (!Array.isArray(parsed.capabilities)) {
|
|
157
|
+
throw new Error(`AI response missing or invalid capabilities array. Got: ${typeof parsed.capabilities}`);
|
|
158
|
+
}
|
|
159
|
+
if (!Array.isArray(parsed.providers)) {
|
|
160
|
+
throw new Error(`AI response missing or invalid providers array. Got: ${typeof parsed.providers}`);
|
|
161
|
+
}
|
|
162
|
+
if (!Array.isArray(parsed.abstractions)) {
|
|
163
|
+
throw new Error(`AI response missing or invalid abstractions array. Got: ${typeof parsed.abstractions}`);
|
|
164
|
+
}
|
|
165
|
+
if (!['low', 'medium', 'high'].includes(parsed.complexity)) {
|
|
166
|
+
throw new Error(`AI response invalid complexity: ${parsed.complexity}. Must be low, medium, or high`);
|
|
167
|
+
}
|
|
168
|
+
if (typeof parsed.description !== 'string' || parsed.description.trim() === '') {
|
|
169
|
+
throw new Error(`AI response missing or invalid description. Got: ${typeof parsed.description}`);
|
|
170
|
+
}
|
|
171
|
+
if (typeof parsed.useCase !== 'string' || parsed.useCase.trim() === '') {
|
|
172
|
+
throw new Error(`AI response missing or invalid useCase. Got: ${typeof parsed.useCase}`);
|
|
173
|
+
}
|
|
174
|
+
if (typeof parsed.confidence !== 'number' || parsed.confidence < 0 || parsed.confidence > 1) {
|
|
175
|
+
throw new Error(`AI response invalid confidence score: ${parsed.confidence}. Must be number between 0-1`);
|
|
176
|
+
}
|
|
177
|
+
return {
|
|
178
|
+
capabilities: parsed.capabilities,
|
|
179
|
+
providers: parsed.providers,
|
|
180
|
+
abstractions: parsed.abstractions,
|
|
181
|
+
complexity: parsed.complexity,
|
|
182
|
+
description: parsed.description.trim(),
|
|
183
|
+
useCase: parsed.useCase.trim(),
|
|
184
|
+
confidence: parsed.confidence
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Build final ResourceCapability from AI analysis result
|
|
189
|
+
*/
|
|
190
|
+
buildResourceCapability(resourceName, aiResult) {
|
|
191
|
+
return {
|
|
192
|
+
resourceName,
|
|
193
|
+
capabilities: aiResult.capabilities,
|
|
194
|
+
providers: aiResult.providers,
|
|
195
|
+
abstractions: aiResult.abstractions,
|
|
196
|
+
complexity: aiResult.complexity,
|
|
197
|
+
description: aiResult.description,
|
|
198
|
+
useCase: aiResult.useCase,
|
|
199
|
+
confidence: aiResult.confidence,
|
|
200
|
+
analyzedAt: new Date().toISOString()
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Generate Vector DB ID for capability storage
|
|
205
|
+
* Creates deterministic UUID from resource name for Qdrant compatibility
|
|
206
|
+
*/
|
|
207
|
+
static generateCapabilityId(resourceName) {
|
|
208
|
+
// Create deterministic UUID from resource name hash
|
|
209
|
+
const crypto = require('crypto');
|
|
210
|
+
const hash = crypto.createHash('sha256').update(`capability-${resourceName}`).digest('hex');
|
|
211
|
+
// Convert to UUID format: 8-4-4-4-12
|
|
212
|
+
return `${hash.substring(0, 8)}-${hash.substring(8, 12)}-${hash.substring(12, 16)}-${hash.substring(16, 20)}-${hash.substring(20, 32)}`;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
exports.CapabilityInferenceEngine = CapabilityInferenceEngine;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capability Vector Service
|
|
3
|
+
*
|
|
4
|
+
* Vector-based storage and retrieval for resource capabilities
|
|
5
|
+
* Extends BaseVectorService to provide capability-specific operations
|
|
6
|
+
*/
|
|
7
|
+
import { BaseVectorService, BaseSearchOptions, BaseSearchResult } from './base-vector-service';
|
|
8
|
+
import { VectorDBService } from './vector-db-service';
|
|
9
|
+
import { EmbeddingService } from './embedding-service';
|
|
10
|
+
export interface ResourceCapability {
|
|
11
|
+
resourceName: string;
|
|
12
|
+
capabilities: string[];
|
|
13
|
+
providers: string[];
|
|
14
|
+
abstractions: string[];
|
|
15
|
+
complexity: 'low' | 'medium' | 'high';
|
|
16
|
+
description: string;
|
|
17
|
+
useCase: string;
|
|
18
|
+
confidence: number;
|
|
19
|
+
analyzedAt: string;
|
|
20
|
+
}
|
|
21
|
+
export interface CapabilitySearchOptions extends BaseSearchOptions {
|
|
22
|
+
complexityFilter?: 'low' | 'medium' | 'high';
|
|
23
|
+
providerFilter?: string[];
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Vector service for storing and searching resource capabilities
|
|
27
|
+
*/
|
|
28
|
+
export declare class CapabilityVectorService extends BaseVectorService<ResourceCapability> {
|
|
29
|
+
constructor(vectorDB?: VectorDBService, embeddingService?: EmbeddingService);
|
|
30
|
+
/**
|
|
31
|
+
* Create searchable text from capability data for embedding generation
|
|
32
|
+
*/
|
|
33
|
+
protected createSearchText(capability: ResourceCapability): string;
|
|
34
|
+
/**
|
|
35
|
+
* Extract unique ID from capability data
|
|
36
|
+
*/
|
|
37
|
+
protected extractId(capability: ResourceCapability): string;
|
|
38
|
+
/**
|
|
39
|
+
* Convert capability to storage payload format
|
|
40
|
+
*/
|
|
41
|
+
protected createPayload(capability: ResourceCapability): Record<string, any>;
|
|
42
|
+
/**
|
|
43
|
+
* Convert storage payload back to capability object
|
|
44
|
+
*/
|
|
45
|
+
protected payloadToData(payload: Record<string, any>): ResourceCapability;
|
|
46
|
+
/**
|
|
47
|
+
* Store a capability in the vector database
|
|
48
|
+
*/
|
|
49
|
+
storeCapability(capability: ResourceCapability): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Search capabilities by user intent with optional filters
|
|
52
|
+
*/
|
|
53
|
+
searchCapabilities(intent: string, options?: CapabilitySearchOptions): Promise<BaseSearchResult<ResourceCapability>[]>;
|
|
54
|
+
/**
|
|
55
|
+
* Get capability by ID or resource name
|
|
56
|
+
* Handles both Vector DB IDs (from list operations) and resource names
|
|
57
|
+
*/
|
|
58
|
+
getCapability(idOrResourceName: string): Promise<ResourceCapability | null>;
|
|
59
|
+
/**
|
|
60
|
+
* Delete capability by resource name
|
|
61
|
+
*/
|
|
62
|
+
deleteCapability(resourceName: string): Promise<void>;
|
|
63
|
+
/**
|
|
64
|
+
* Delete capability by ID (for MCP tool interface)
|
|
65
|
+
*/
|
|
66
|
+
deleteCapabilityById(id: string): Promise<void>;
|
|
67
|
+
/**
|
|
68
|
+
* Delete all capabilities efficiently by recreating collection
|
|
69
|
+
*/
|
|
70
|
+
deleteAllCapabilities(): Promise<void>;
|
|
71
|
+
/**
|
|
72
|
+
* List all capabilities with optional pagination
|
|
73
|
+
*/
|
|
74
|
+
getAllCapabilities(limit?: number): Promise<ResourceCapability[]>;
|
|
75
|
+
/**
|
|
76
|
+
* Get count of stored capabilities
|
|
77
|
+
*/
|
|
78
|
+
getCapabilitiesCount(): Promise<number>;
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=capability-vector-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capability-vector-service.d.ts","sourceRoot":"","sources":["../../src/core/capability-vector-service.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC/F,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAGvD,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,UAAU,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IACtC,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,uBAAwB,SAAQ,iBAAiB;IAChE,gBAAgB,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IAC7C,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED;;GAEG;AACH,qBAAa,uBAAwB,SAAQ,iBAAiB,CAAC,kBAAkB,CAAC;gBAEpE,QAAQ,CAAC,EAAE,eAAe,EAAE,gBAAgB,CAAC,EAAE,gBAAgB;IAI3E;;OAEG;IACH,SAAS,CAAC,gBAAgB,CAAC,UAAU,EAAE,kBAAkB,GAAG,MAAM;IAYlE;;OAEG;IACH,SAAS,CAAC,SAAS,CAAC,UAAU,EAAE,kBAAkB,GAAG,MAAM;IAI3D;;OAEG;IACH,SAAS,CAAC,aAAa,CAAC,UAAU,EAAE,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAc5E;;OAEG;IACH,SAAS,CAAC,aAAa,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,kBAAkB;IAczE;;OAEG;IACG,eAAe,CAAC,UAAU,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAIpE;;OAEG;IACG,kBAAkB,CACtB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,gBAAgB,CAAC,kBAAkB,CAAC,EAAE,CAAC;IAoBlD;;;OAGG;IACG,aAAa,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC;IAgBjF;;OAEG;IACG,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAK3D;;OAEG;IACG,oBAAoB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrD;;OAEG;IACG,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5C;;OAEG;IACG,kBAAkB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAIvE;;OAEG;IACG,oBAAoB,IAAI,OAAO,CAAC,MAAM,CAAC;CAK9C"}
|