#
tokens: 48631/50000 4/82 files (page 4/5)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 4 of 5. Use http://codebase.md/blankcut/kubernetes-mcp-server?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .gitignore
├── docs
│   ├── .astro
│   │   ├── collections
│   │   │   └── docs.schema.json
│   │   ├── content-assets.mjs
│   │   ├── content-modules.mjs
│   │   ├── content.d.ts
│   │   ├── data-store.json
│   │   ├── settings.json
│   │   └── types.d.ts
│   ├── .gitignore
│   ├── astro.config.mjs
│   ├── package-lock.json
│   ├── package.json
│   ├── public
│   │   └── images
│   │       └── logo.svg
│   ├── README.md
│   ├── src
│   │   ├── components
│   │   │   ├── CodeBlock.astro
│   │   │   ├── DocSidebar.astro
│   │   │   ├── Footer.astro
│   │   │   ├── Header.astro
│   │   │   ├── HeadSEO.astro
│   │   │   ├── Search.astro
│   │   │   ├── Sidebar.astro
│   │   │   └── TableOfContents.astro
│   │   ├── content
│   │   │   ├── config.ts
│   │   │   └── docs
│   │   │       ├── api-overview.md
│   │   │       ├── configuration.md
│   │   │       ├── installation.md
│   │   │       ├── introduction.md
│   │   │       ├── model-context-protocol.md
│   │   │       ├── quick-start.md
│   │   │       └── troubleshooting-resources.md
│   │   ├── env.d.ts
│   │   ├── layouts
│   │   │   ├── BaseLayout.astro
│   │   │   └── DocLayout.astro
│   │   ├── pages
│   │   │   ├── [...slug].astro
│   │   │   ├── 404.astro
│   │   │   ├── docs
│   │   │   │   └── index.astro
│   │   │   ├── docs-test.astro
│   │   │   ├── examples
│   │   │   │   └── index.astro
│   │   │   └── index.astro
│   │   └── styles
│   │       └── global.css
│   ├── tailwind.config.cjs
│   └── tsconfig.json
├── go.mod
├── kubernetes-claude-mcp
│   ├── .gitignore
│   ├── cmd
│   │   └── server
│   │       └── main.go
│   ├── docker-compose.yml
│   ├── Dockerfile
│   ├── go.mod
│   ├── go.sum
│   ├── internal
│   │   ├── api
│   │   │   ├── namespace_routes.go
│   │   │   ├── routes.go
│   │   │   └── server.go
│   │   ├── argocd
│   │   │   ├── applications.go
│   │   │   ├── client.go
│   │   │   └── history.go
│   │   ├── auth
│   │   │   ├── credentials.go
│   │   │   ├── secrets.go
│   │   │   └── vault.go
│   │   ├── claude
│   │   │   ├── client.go
│   │   │   └── protocol.go
│   │   ├── correlator
│   │   │   ├── gitops.go
│   │   │   ├── helm_correlator.go
│   │   │   └── troubleshoot.go
│   │   ├── gitlab
│   │   │   ├── client.go
│   │   │   ├── mergerequests.go
│   │   │   ├── pipelines.go
│   │   │   └── repositories.go
│   │   ├── helm
│   │   │   └── parser.go
│   │   ├── k8s
│   │   │   ├── client.go
│   │   │   ├── enhanced_client.go
│   │   │   ├── events.go
│   │   │   ├── resource_mapper.go
│   │   │   └── resources.go
│   │   ├── mcp
│   │   │   ├── context.go
│   │   │   ├── namespace_analyzer.go
│   │   │   ├── prompt.go
│   │   │   └── protocol.go
│   │   └── models
│   │       ├── argocd.go
│   │       ├── context.go
│   │       ├── gitlab.go
│   │       └── kubernetes.go
│   └── pkg
│       ├── config
│       │   └── config.go
│       ├── logging
│       │   └── logging.go
│       └── utils
│           ├── serialization.go
│           └── truncation.go
├── LICENSE
└── README.md
```

# Files

--------------------------------------------------------------------------------
/kubernetes-claude-mcp/internal/correlator/gitops.go:
--------------------------------------------------------------------------------

```go
  1 | package correlator
  2 | 
  3 | import (
  4 | 	"context"
  5 | 	"fmt"
  6 | 	"strings"
  7 | 	"time"
  8 | 
  9 | 	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/internal/argocd"
 10 | 	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/internal/gitlab"
 11 | 	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/internal/k8s"
 12 | 	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/internal/models"
 13 | 	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/pkg/logging"
 14 | )
 15 | 
 16 | // GitOpsCorrelator correlates data between Kubernetes, ArgoCD, and GitLab
 17 | type GitOpsCorrelator struct {
 18 | 	k8sClient      *k8s.Client
 19 | 	argoClient     *argocd.Client
 20 | 	gitlabClient   *gitlab.Client
 21 | 	helmCorrelator *HelmCorrelator
 22 | 	logger         *logging.Logger
 23 | }
 24 | 
 25 | // NewGitOpsCorrelator creates a new GitOps correlator
 26 | func NewGitOpsCorrelator(k8sClient *k8s.Client, argoClient *argocd.Client, gitlabClient *gitlab.Client, logger *logging.Logger) *GitOpsCorrelator {
 27 | 	if logger == nil {
 28 | 		logger = logging.NewLogger().Named("correlator")
 29 | 	}
 30 | 
 31 | 	correlator := &GitOpsCorrelator{
 32 | 		k8sClient:    k8sClient,
 33 | 		argoClient:   argoClient,
 34 | 		gitlabClient: gitlabClient,
 35 | 		logger:       logger,
 36 | 	}
 37 | 
 38 | 	// Initialize the Helm correlator
 39 | 	correlator.helmCorrelator = NewHelmCorrelator(gitlabClient, logger.Named("helm"))
 40 | 
 41 | 	return correlator
 42 | }
 43 | 
 44 | // Add a new method to analyze merge requests
 45 | func (c *GitOpsCorrelator) AnalyzeMergeRequest(
 46 | 	ctx context.Context,
 47 | 	projectID string,
 48 | 	mergeRequestIID int,
 49 | ) ([]models.ResourceContext, error) {
 50 | 	c.logger.Info("Analyzing merge request", "projectID", projectID, "mergeRequestIID", mergeRequestIID)
 51 | 
 52 | 	// Get merge request details
 53 | 	mergeRequest, err := c.gitlabClient.AnalyzeMergeRequest(ctx, projectID, mergeRequestIID)
 54 | 	if err != nil {
 55 | 		return nil, fmt.Errorf("failed to analyze merge request: %w", err)
 56 | 	}
 57 | 
 58 | 	// Check if the MR affects Helm charts or Kubernetes manifests
 59 | 	if !mergeRequest.MergeRequestContext.HelmChartAffected && !mergeRequest.MergeRequestContext.KubernetesManifest {
 60 | 		c.logger.Info("Merge request does not affect Kubernetes resources")
 61 | 		return []models.ResourceContext{}, nil
 62 | 	}
 63 | 
 64 | 	// Get all ArgoCD applications
 65 | 	argoApps, err := c.argoClient.ListApplications(ctx)
 66 | 	if err != nil {
 67 | 		return nil, fmt.Errorf("failed to list ArgoCD applications: %w", err)
 68 | 	}
 69 | 
 70 | 	// Find the project path
 71 | 	projectPath := fmt.Sprintf("%s", projectID)
 72 | 	project, err := c.gitlabClient.GetProject(ctx, projectID)
 73 | 	if err == nil && project != nil {
 74 | 		projectPath = project.PathWithNamespace
 75 | 	}
 76 | 
 77 | 	// For Helm-affected MRs, analyze Helm changes
 78 | 	var helmAffectedResources []string
 79 | 	if mergeRequest.MergeRequestContext.HelmChartAffected {
 80 | 		helmResources, err := c.helmCorrelator.AnalyzeMergeRequestHelmChanges(ctx, projectID, mergeRequestIID)
 81 | 		if err != nil {
 82 | 			c.logger.Warn("Failed to analyze Helm changes in MR", "error", err)
 83 | 		} else if len(helmResources) > 0 {
 84 | 			helmAffectedResources = helmResources
 85 | 			c.logger.Info("Found resources affected by Helm changes in MR", "count", len(helmResources))
 86 | 		}
 87 | 	}
 88 | 
 89 | 	// Identify potentially affected applications
 90 | 	var affectedApps []models.ArgoApplication
 91 | 	for _, app := range argoApps {
 92 | 		if isAppSourcedFromProject(app, projectPath) {
 93 | 			// For each file changed in the MR, check if it affects the app
 94 | 			isAffected := false
 95 | 
 96 | 			// Check if any changed file affects the app
 97 | 			for _, file := range mergeRequest.MergeRequestContext.AffectedFiles {
 98 | 				if isFileInAppSourcePath(app, file) {
 99 | 					isAffected = true
100 | 					break
101 | 				}
102 | 			}
103 | 
104 | 			// Check Helm-derived resources
105 | 			if !isAffected && len(helmAffectedResources) > 0 {
106 | 				if appContainsAnyResource(ctx, c.argoClient, app, helmAffectedResources) {
107 | 					isAffected = true
108 | 				}
109 | 			}
110 | 
111 | 			if isAffected {
112 | 				affectedApps = append(affectedApps, app)
113 | 			}
114 | 		}
115 | 	}
116 | 
117 | 	// For each affected app, identify the resources that would be affected
118 | 	var result []models.ResourceContext
119 | 	for _, app := range affectedApps {
120 | 		c.logger.Info("Found potentially affected ArgoCD application", "app", app.Name)
121 | 
122 | 		// Get resources managed by this application
123 | 		tree, err := c.argoClient.GetResourceTree(ctx, app.Name)
124 | 		if err != nil {
125 | 			c.logger.Warn("Failed to get resource tree", "app", app.Name, "error", err)
126 | 			continue
127 | 		}
128 | 
129 | 		// For each resource in the app, create a deployment info object
130 | 		for _, node := range tree.Nodes {
131 | 			// Skip non-Kubernetes resources or resources with no name/namespace
132 | 			if node.Kind == "" || node.Name == "" {
133 | 				continue
134 | 			}
135 | 
136 | 			// Avoid unnecessary duplicates in the result
137 | 			if isResourceAlreadyInResults(result, node.Kind, node.Name, node.Namespace) {
138 | 				continue
139 | 			}
140 | 
141 | 			// Trace the deployment for this resource
142 | 			resourceContext, err := c.TraceResourceDeployment(
143 | 				ctx,
144 | 				node.Namespace,
145 | 				node.Kind,
146 | 				node.Name,
147 | 			)
148 | 			if err != nil {
149 | 				c.logger.Warn("Failed to trace resource deployment",
150 | 					"kind", node.Kind,
151 | 					"name", node.Name,
152 | 					"namespace", node.Namespace,
153 | 					"error", err)
154 | 				continue
155 | 			}
156 | 
157 | 			// Add source info
158 | 			resourceContext.RelatedResources = append(resourceContext.RelatedResources,
159 | 				fmt.Sprintf("MergeRequest/%d", mergeRequestIID))
160 | 
161 | 			// Add to results
162 | 			result = append(result, resourceContext)
163 | 		}
164 | 	}
165 | 
166 | 	// Add cleanup on exit
167 | 	defer func() {
168 | 		if c.helmCorrelator != nil {
169 | 			c.helmCorrelator.Cleanup()
170 | 		}
171 | 	}()
172 | 
173 | 	c.logger.Info("Analysis of merge request completed",
174 | 		"projectID", projectID,
175 | 		"mergeRequestIID", mergeRequestIID,
176 | 		"resourceCount", len(result))
177 | 
178 | 	return result, nil
179 | }
180 | 
181 | func (c *GitOpsCorrelator) TraceResourceDeployment(
182 | 	ctx context.Context,
183 | 	namespace, kind, name string,
184 | ) (models.ResourceContext, error) {
185 | 	c.logger.Info("Tracing resource deployment", "kind", kind, "name", name, "namespace", namespace)
186 | 
187 | 	resourceContext := models.ResourceContext{
188 | 		Kind:      kind,
189 | 		Name:      name,
190 | 		Namespace: namespace,
191 | 	}
192 | 
193 | 	var errors []string
194 | 
195 | 	// Get Kubernetes resource information
196 | 	resource, err := c.k8sClient.GetResource(ctx, kind, namespace, name)
197 | 	if err != nil {
198 | 		errMsg := fmt.Sprintf("Failed to get Kubernetes resource: %v", err)
199 | 		errors = append(errors, errMsg)
200 | 		c.logger.Warn(errMsg)
201 | 	} else {
202 | 		resourceContext.APIVersion = resource.GetAPIVersion()
203 | 
204 | 		// Get events related to this resource
205 | 		events, err := c.k8sClient.GetResourceEvents(ctx, namespace, kind, name)
206 | 		if err != nil {
207 | 			errMsg := fmt.Sprintf("Failed to get resource events: %v", err)
208 | 			errors = append(errors, errMsg)
209 | 			c.logger.Warn(errMsg)
210 | 		} else {
211 | 			resourceContext.Events = events
212 | 		}
213 | 	}
214 | 
215 | 	// Find the ArgoCD application managing this resource
216 | 	argoApps, err := c.argoClient.FindApplicationsByResource(ctx, kind, name, namespace)
217 | 	if err != nil {
218 | 		errMsg := fmt.Sprintf("Failed to find ArgoCD applications: %v", err)
219 | 		errors = append(errors, errMsg)
220 | 		c.logger.Warn(errMsg)
221 | 	} else if len(argoApps) > 0 {
222 | 		// Use the first application that manages this resource
223 | 		app := argoApps[0]
224 | 		resourceContext.ArgoApplication = &app
225 | 		resourceContext.ArgoSyncStatus = app.Status.Sync.Status
226 | 		resourceContext.ArgoHealthStatus = app.Status.Health.Status
227 | 
228 | 		// Get recent syncs
229 | 		history, err := c.argoClient.GetApplicationHistory(ctx, app.Name)
230 | 		if err != nil {
231 | 			errMsg := fmt.Sprintf("Failed to get application history: %v", err)
232 | 			errors = append(errors, errMsg)
233 | 			c.logger.Warn(errMsg)
234 | 		} else {
235 | 			// Limit to recent syncs (last 5)
236 | 			if len(history) > 5 {
237 | 				history = history[:5]
238 | 			}
239 | 			resourceContext.ArgoSyncHistory = history
240 | 		}
241 | 
242 | 		// Connect to GitLab if we have source information
243 | 		if app.Spec.Source.RepoURL != "" {
244 | 			// Extract GitLab project path from repo URL
245 | 			projectPath := extractGitLabProjectPath(app.Spec.Source.RepoURL)
246 | 			if projectPath != "" {
247 | 				project, err := c.gitlabClient.GetProjectByPath(ctx, projectPath)
248 | 				if err != nil {
249 | 					errMsg := fmt.Sprintf("Failed to get GitLab project: %v", err)
250 | 					errors = append(errors, errMsg)
251 | 					c.logger.Warn(errMsg)
252 | 				} else {
253 | 					resourceContext.GitLabProject = project
254 | 
255 | 					// Get recent pipelines
256 | 					pipelines, err := c.gitlabClient.ListPipelines(ctx, fmt.Sprintf("%d", project.ID))
257 | 					if err != nil {
258 | 						errMsg := fmt.Sprintf("Failed to list pipelines: %v", err)
259 | 						errors = append(errors, errMsg)
260 | 						c.logger.Warn(errMsg)
261 | 					} else {
262 | 						// Get the latest pipeline
263 | 						if len(pipelines) > 0 {
264 | 							resourceContext.LastPipeline = &pipelines[0]
265 | 						}
266 | 					}
267 | 
268 | 					// Find environment from ArgoCD application
269 | 					environment := extractEnvironmentFromArgoApp(app)
270 | 					if environment != "" {
271 | 						// Get recent deployments to this environment
272 | 						deployments, err := c.gitlabClient.FindRecentDeployments(
273 | 							ctx,
274 | 							fmt.Sprintf("%d", project.ID),
275 | 							environment,
276 | 						)
277 | 						if err != nil {
278 | 							errMsg := fmt.Sprintf("Failed to find deployments: %v", err)
279 | 							errors = append(errors, errMsg)
280 | 							c.logger.Warn(errMsg)
281 | 						} else if len(deployments) > 0 {
282 | 							resourceContext.LastDeployment = &deployments[0]
283 | 						}
284 | 					}
285 | 
286 | 					// Get recent commits
287 | 					sinceTime := time.Now().Add(-24 * time.Hour) // Last 24 hours
288 | 					commits, err := c.gitlabClient.FindRecentChanges(
289 | 						ctx,
290 | 						fmt.Sprintf("%d", project.ID),
291 | 						sinceTime,
292 | 					)
293 | 					if err != nil {
294 | 						errMsg := fmt.Sprintf("Failed to find recent changes: %v", err)
295 | 						errors = append(errors, errMsg)
296 | 						c.logger.Warn(errMsg)
297 | 					} else {
298 | 						// Here we'll limit to recent commits (last 5)...
299 | 						if len(commits) > 5 {
300 | 							commits = commits[:5]
301 | 						}
302 | 						resourceContext.RecentCommits = commits
303 | 					}
304 | 				}
305 | 			}
306 | 		}
307 | 	}
308 | 
309 | 	// Collect any errors that occurred during correlation
310 | 	resourceContext.Errors = errors
311 | 
312 | 	c.logger.Info("Resource deployment traced",
313 | 		"kind", kind,
314 | 		"name", name,
315 | 		"namespace", namespace,
316 | 		"argoApp", resourceContext.ArgoApplication != nil,
317 | 		"gitlabProject", resourceContext.GitLabProject != nil,
318 | 		"errors", len(errors))
319 | 
320 | 	return resourceContext, nil
321 | }
322 | 
323 | // isFileInAppSourcePath checks if a file is in the application's source path
324 | func isFileInAppSourcePath(app models.ArgoApplication, file string) bool {
325 | 	sourcePath := app.Spec.Source.Path
326 | 	if sourcePath == "" {
327 | 		// If no specific path is provided, any file could affect the app
328 | 		return true
329 | 	}
330 | 
331 | 	return strings.HasPrefix(file, sourcePath)
332 | }
333 | 
334 | // hasHelmChanges checks if any of the changed files are related to Helm charts
335 | func hasHelmChanges(diffs []models.GitLabDiff) bool {
336 | 	for _, diff := range diffs {
337 | 		path := diff.NewPath
338 | 
339 | 		if strings.Contains(path, "Chart.yaml") ||
340 | 			strings.Contains(path, "values.yaml") ||
341 | 			(strings.Contains(path, "templates/") && strings.HasSuffix(path, ".yaml")) {
342 | 			return true
343 | 		}
344 | 	}
345 | 
346 | 	return false
347 | }
348 | 
349 | // appContainsAnyResource checks if an ArgoCD application contains any of the specified resources
350 | func appContainsAnyResource(ctx context.Context, argoClient *argocd.Client, app models.ArgoApplication, resources []string) bool {
351 | 	tree, err := argoClient.GetResourceTree(ctx, app.Name)
352 | 	if err != nil {
353 | 		return false
354 | 	}
355 | 
356 | 	for _, resource := range resources {
357 | 		parts := strings.Split(resource, "/")
358 | 
359 | 		if len(parts) == 2 {
360 | 			// Format: Kind/Name
361 | 			kind := parts[0]
362 | 			name := parts[1]
363 | 
364 | 			for _, node := range tree.Nodes {
365 | 				if strings.EqualFold(node.Kind, kind) && node.Name == name {
366 | 					return true
367 | 				}
368 | 			}
369 | 		} else if len(parts) == 3 {
370 | 			// Format: Namespace/Kind/Name
371 | 			namespace := parts[0]
372 | 			kind := parts[1]
373 | 			name := parts[2]
374 | 
375 | 			for _, node := range tree.Nodes {
376 | 				if strings.EqualFold(node.Kind, kind) && node.Name == name && node.Namespace == namespace {
377 | 					return true
378 | 				}
379 | 			}
380 | 		}
381 | 	}
382 | 
383 | 	return false
384 | }
385 | 
386 | // FindResourcesAffectedByCommit finds resources affected by a specific Git commit
387 | func (c *GitOpsCorrelator) FindResourcesAffectedByCommit(
388 | 	ctx context.Context,
389 | 	projectID string,
390 | 	commitSHA string,
391 | ) ([]models.ResourceContext, error) {
392 | 	c.logger.Info("Finding resources affected by commit", "projectID", projectID, "commitSHA", commitSHA)
393 | 
394 | 	var result []models.ResourceContext
395 | 
396 | 	// Get commit information from GitLab
397 | 	commit, err := c.gitlabClient.GetCommit(ctx, projectID, commitSHA)
398 | 	if err != nil {
399 | 		return nil, fmt.Errorf("failed to get commit: %w", err)
400 | 	}
401 | 	c.logger.Info("Processing commit", "author", commit.AuthorName, "message", commit.Title)
402 | 
403 | 	// Get commit diff to see what files were changed
404 | 	diffs, err := c.gitlabClient.GetCommitDiff(ctx, projectID, commitSHA)
405 | 	if err != nil {
406 | 		return nil, fmt.Errorf("failed to get commit diff: %w", err)
407 | 	}
408 | 
409 | 	// Get all ArgoCD applications
410 | 	argoApps, err := c.argoClient.ListApplications(ctx)
411 | 	if err != nil {
412 | 		return nil, fmt.Errorf("failed to list ArgoCD applications: %w", err)
413 | 	}
414 | 
415 | 	// Find applications that use this GitLab project as source
416 | 	projectPath := fmt.Sprintf("%s", projectID) // This might need more parsing depending on projectID format
417 | 	project, err := c.gitlabClient.GetProject(ctx, projectID)
418 | 	if err == nil && project != nil {
419 | 		projectPath = project.PathWithNamespace
420 | 	}
421 | 
422 | 	// For each application, check if it's affected by the changed files
423 | 	for _, app := range argoApps {
424 | 		if !isAppSourcedFromProject(app, projectPath) {
425 | 			continue
426 | 		}
427 | 
428 | 		// Check if the commit affects files used by this application
429 | 		if isAppAffectedByDiffs(app, diffs) {
430 | 			c.logger.Info("Found affected ArgoCD application", "app", app.Name)
431 | 
432 | 			// Get resources managed by this application
433 | 			tree, err := c.argoClient.GetResourceTree(ctx, app.Name)
434 | 			if err != nil {
435 | 				c.logger.Warn("Failed to get resource tree", "app", app.Name, "error", err)
436 | 				continue
437 | 			}
438 | 
439 | 			// For each resource in the app, create a deployment info object
440 | 			for _, node := range tree.Nodes {
441 | 				// Skip non-Kubernetes resources or resources with no name/namespace
442 | 				if node.Kind == "" || node.Name == "" {
443 | 					continue
444 | 				}
445 | 
446 | 				// Avoid unnecessary duplicates in the result
447 | 				if isResourceAlreadyInResults(result, node.Kind, node.Name, node.Namespace) {
448 | 					continue
449 | 				}
450 | 
451 | 				// Trace the deployment for this resource
452 | 				resourceContext, err := c.TraceResourceDeployment(
453 | 					ctx,
454 | 					node.Namespace,
455 | 					node.Kind,
456 | 					node.Name,
457 | 				)
458 | 				if err != nil {
459 | 					c.logger.Warn("Failed to trace resource deployment",
460 | 						"kind", node.Kind,
461 | 						"name", node.Name,
462 | 						"namespace", node.Namespace,
463 | 						"error", err)
464 | 					continue
465 | 				}
466 | 
467 | 				result = append(result, resourceContext)
468 | 			}
469 | 		}
470 | 	}
471 | 
472 | 	c.logger.Info("Found resources affected by commit",
473 | 		"projectID", projectID,
474 | 		"commitSHA", commitSHA,
475 | 		"resourceCount", len(result))
476 | 
477 | 	return result, nil
478 | }
479 | 
480 | // Helper functions
481 | 
482 | // extractGitLabProjectPath extracts the GitLab project path from a repo URL
483 | func extractGitLabProjectPath(repoURL string) string {
484 | 	// Handle different URL formats
485 | 
486 | 	// Format: https://gitlab.com/namespace/project.git
487 | 	if strings.HasPrefix(repoURL, "https://") || strings.HasPrefix(repoURL, "http://") {
488 | 		parts := strings.Split(repoURL, "/")
489 | 		if len(parts) < 3 {
490 | 			return ""
491 | 		}
492 | 
493 | 		// Remove ".git" suffix if present
494 | 		lastPart := parts[len(parts)-1]
495 | 		if strings.HasSuffix(lastPart, ".git") {
496 | 			parts[len(parts)-1] = lastPart[:len(lastPart)-4]
497 | 		}
498 | 
499 | 		// Reconstruct path without protocol and domain
500 | 		domainIndex := 2 // After http:// or https://
501 | 		if len(parts) <= domainIndex+1 {
502 | 			return ""
503 | 		}
504 | 
505 | 		return strings.Join(parts[domainIndex+1:], "/")
506 | 	}
507 | 
508 | 	// Format: [email protected]:namespace/project.git
509 | 	if strings.HasPrefix(repoURL, "git@") {
510 | 		// Split at ":" to get the path part
511 | 		parts := strings.Split(repoURL, ":")
512 | 		if len(parts) != 2 {
513 | 			return ""
514 | 		}
515 | 
516 | 		// Remove ".git" suffix if present
517 | 		pathPart := parts[1]
518 | 		if strings.HasSuffix(pathPart, ".git") {
519 | 			pathPart = pathPart[:len(pathPart)-4]
520 | 		}
521 | 
522 | 		return pathPart
523 | 	}
524 | 
525 | 	return ""
526 | }
527 | 
528 | // extractEnvironmentFromArgoApp tries to determine the environment from an ArgoCD application
529 | func extractEnvironmentFromArgoApp(app models.ArgoApplication) string {
530 | 	// Check for environment in labels
531 | 	if env, ok := app.Metadata.Labels["environment"]; ok {
532 | 		return env
533 | 	}
534 | 	if env, ok := app.Metadata.Labels["env"]; ok {
535 | 		return env
536 | 	}
537 | 
538 | 	// Check if environment is in the destination namespace
539 | 	if strings.Contains(app.Spec.Destination.Namespace, "prod") {
540 | 		return "production"
541 | 	}
542 | 	if strings.Contains(app.Spec.Destination.Namespace, "staging") {
543 | 		return "staging"
544 | 	}
545 | 	if strings.Contains(app.Spec.Destination.Namespace, "dev") {
546 | 		return "development"
547 | 	}
548 | 
549 | 	// Check path in source for environment indicators
550 | 	if app.Spec.Source.Path != "" {
551 | 		if strings.Contains(app.Spec.Source.Path, "prod") {
552 | 			return "production"
553 | 		}
554 | 		if strings.Contains(app.Spec.Source.Path, "staging") {
555 | 			return "staging"
556 | 		}
557 | 		if strings.Contains(app.Spec.Source.Path, "dev") {
558 | 			return "development"
559 | 		}
560 | 	}
561 | 
562 | 	// Default to destination namespace as a fallback
563 | 	return app.Spec.Destination.Namespace
564 | }
565 | 
566 | // isAppSourcedFromProject checks if an ArgoCD application uses a specific GitLab project
567 | func isAppSourcedFromProject(app models.ArgoApplication, projectPath string) bool {
568 | 	// Extract project path from app's repo URL
569 | 	appProjectPath := extractGitLabProjectPath(app.Spec.Source.RepoURL)
570 | 
571 | 	// Compare paths
572 | 	return strings.EqualFold(appProjectPath, projectPath)
573 | }
574 | 
575 | // isAppAffectedByDiffs checks if application manifests are affected by file changes
576 | func isAppAffectedByDiffs(app models.ArgoApplication, diffs []models.GitLabDiff) bool {
577 | 	sourcePath := app.Spec.Source.Path
578 | 	if sourcePath == "" {
579 | 		// If no specific path is provided, any change could affect the app
580 | 		return true
581 | 	}
582 | 
583 | 	// Check if any changed file is in the application's source path
584 | 	for _, diff := range diffs {
585 | 		if strings.HasPrefix(diff.NewPath, sourcePath) || strings.HasPrefix(diff.OldPath, sourcePath) {
586 | 			return true
587 | 		}
588 | 	}
589 | 
590 | 	return false
591 | }
592 | 
593 | // isResourceAlreadyInResults checks if a resource is already in the results list
594 | func isResourceAlreadyInResults(results []models.ResourceContext, kind, name, namespace string) bool {
595 | 	for _, rc := range results {
596 | 		if rc.Kind == kind && rc.Name == name && rc.Namespace == namespace {
597 | 			return true
598 | 		}
599 | 	}
600 | 	return false
601 | }
602 | 
```

--------------------------------------------------------------------------------
/kubernetes-claude-mcp/internal/k8s/resource_mapper.go:
--------------------------------------------------------------------------------

```go
  1 | package k8s
  2 | 
  3 | import (
  4 | 	"context"
  5 | 	"fmt"
  6 | 	"strings"
  7 | 
  8 | 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  9 | 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 10 | 	"k8s.io/apimachinery/pkg/runtime/schema"
 11 | 	
 12 | 	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/pkg/logging"
 13 | )
 14 | 
 15 | // ResourceMapper maps relationships between Kubernetes resources
 16 | type ResourceMapper struct {
 17 | 	client *Client
 18 | 	logger *logging.Logger
 19 | }
 20 | 
 21 | // ResourceRelationship represents a relationship between two resources
 22 | type ResourceRelationship struct {
 23 | 	SourceKind      string `json:"sourceKind"`
 24 | 	SourceName      string `json:"sourceName"`
 25 | 	SourceNamespace string `json:"sourceNamespace"`
 26 | 	TargetKind      string `json:"targetKind"`
 27 | 	TargetName      string `json:"targetName"`
 28 | 	TargetNamespace string `json:"targetNamespace"`
 29 | 	RelationType    string `json:"relationType"`
 30 | }
 31 | 
 32 | // NamespaceTopology represents the topology of resources in a namespace
 33 | type NamespaceTopology struct {
 34 | 	Namespace    string                         `json:"namespace"`
 35 | 	Resources    map[string][]string            `json:"resources"`
 36 | 	Relationships []ResourceRelationship        `json:"relationships"`
 37 | 	Metrics      map[string]map[string]int      `json:"metrics"`
 38 | 	Health       map[string]map[string]string   `json:"health"`
 39 | }
 40 | 
 41 | // NewResourceMapper creates a new resource mapper
 42 | func NewResourceMapper(client *Client) *ResourceMapper {
 43 | 	return &ResourceMapper{
 44 | 		client: client,
 45 | 		logger: client.logger.Named("resource-mapper"),
 46 | 	}
 47 | }
 48 | 
 49 | // GetNamespaceTopology maps all resources and their relationships in a namespace
 50 | func (m *ResourceMapper) GetNamespaceTopology(ctx context.Context, namespace string) (*NamespaceTopology, error) {
 51 | 	m.logger.Info("Mapping namespace topology", "namespace", namespace)
 52 | 	
 53 | 	// Initialize topology
 54 | 	topology := &NamespaceTopology{
 55 | 		Namespace:    namespace,
 56 | 		Resources:    make(map[string][]string),
 57 | 		Relationships: []ResourceRelationship{},
 58 | 		Metrics:      make(map[string]map[string]int),
 59 | 		Health:       make(map[string]map[string]string),
 60 | 	}
 61 | 
 62 | 	// Discover all available resource types
 63 | 	resources, err := m.client.discoveryClient.ServerPreferredResources()
 64 | 	if err != nil {
 65 | 		return nil, fmt.Errorf("failed to get server resources: %w", err)
 66 | 	}
 67 | 
 68 | 	// Collect all namespaced resources
 69 | 	for _, resourceList := range resources {
 70 | 		gv, err := schema.ParseGroupVersion(resourceList.GroupVersion)
 71 | 		if err != nil {
 72 | 			m.logger.Warn("Failed to parse group version", "groupVersion", resourceList.GroupVersion)
 73 | 			continue
 74 | 		}
 75 | 
 76 | 		for _, r := range resourceList.APIResources {
 77 | 			// Skip resources that can't be listed or aren't namespaced
 78 | 			if !strings.Contains(r.Verbs.String(), "list") || !r.Namespaced {
 79 | 				continue
 80 | 			}
 81 | 
 82 | 			// Build GVR for this resource type
 83 | 			gvr := schema.GroupVersionResource{
 84 | 				Group:    gv.Group,
 85 | 				Version:  gv.Version,
 86 | 				Resource: r.Name,
 87 | 			}
 88 | 
 89 | 			// List resources of this type
 90 | 			m.logger.Debug("Listing resources", "namespace", namespace, "resource", r.Name)
 91 | 			list, err := m.client.dynamicClient.Resource(gvr).Namespace(namespace).List(ctx, metav1.ListOptions{})
 92 | 			if err != nil {
 93 | 				m.logger.Warn("Failed to list resources", 
 94 | 					"namespace", namespace, 
 95 | 					"resource", r.Name, 
 96 | 					"error", err)
 97 | 				continue
 98 | 			}
 99 | 
100 | 			// Add to topology
101 | 			if len(list.Items) > 0 {
102 | 				topology.Resources[r.Kind] = make([]string, len(list.Items))
103 | 				topology.Metrics[r.Kind] = map[string]int{"count": len(list.Items)}
104 | 				topology.Health[r.Kind] = make(map[string]string)
105 | 				
106 | 				for i, item := range list.Items {
107 | 					topology.Resources[r.Kind][i] = item.GetName()
108 | 					
109 | 					// Determine health status
110 | 					health := m.determineResourceHealth(&item)
111 | 					topology.Health[r.Kind][item.GetName()] = health
112 | 				}
113 | 				
114 | 				// Find relationships for this resource type
115 | 				relationships := m.findRelationships(ctx, list.Items, namespace)
116 | 				topology.Relationships = append(topology.Relationships, relationships...)
117 | 			}
118 | 		}
119 | 	}
120 | 
121 | 	m.logger.Info("Namespace topology mapped", 
122 | 		"namespace", namespace, 
123 | 		"resourceTypes", len(topology.Resources),
124 | 		"relationships", len(topology.Relationships))
125 | 	
126 | 	return topology, nil
127 | }
128 | 
129 | // GetResourceGraph returns a resource graph for visualization
130 | func (m *ResourceMapper) GetResourceGraph(ctx context.Context, namespace string) (map[string]interface{}, error) {
131 | 	topology, err := m.GetNamespaceTopology(ctx, namespace)
132 | 	if err != nil {
133 | 		return nil, err
134 | 	}
135 | 	
136 | 	// Convert topology to graph format
137 | 	graph := map[string]interface{}{
138 | 		"nodes": []map[string]interface{}{},
139 | 		"edges": []map[string]interface{}{},
140 | 	}
141 | 	
142 | 	// Add nodes
143 | 	nodeIndex := make(map[string]int)
144 | 	nodeCount := 0
145 | 	
146 | 	for kind, resources := range topology.Resources {
147 | 		for _, name := range resources {
148 | 			health := "unknown"
149 | 			if h, ok := topology.Health[kind][name]; ok {
150 | 				health = h
151 | 			}
152 | 			
153 | 			node := map[string]interface{}{
154 | 				"id":       nodeCount,
155 | 				"kind":     kind,
156 | 				"name":     name,
157 | 				"health":   health,
158 | 				"group":    kind,
159 | 			}
160 | 			
161 | 			// Add to nodes array
162 | 			graph["nodes"] = append(graph["nodes"].([]map[string]interface{}), node)
163 | 			
164 | 			// Save index for edge creation
165 | 			nodeIndex[fmt.Sprintf("%s/%s", kind, name)] = nodeCount
166 | 			nodeCount++
167 | 		}
168 | 	}
169 | 	
170 | 	// Add edges
171 | 	for _, rel := range topology.Relationships {
172 | 		sourceKey := fmt.Sprintf("%s/%s", rel.SourceKind, rel.SourceName)
173 | 		targetKey := fmt.Sprintf("%s/%s", rel.TargetKind, rel.TargetName)
174 | 		
175 | 		sourceIdx, sourceOk := nodeIndex[sourceKey]
176 | 		targetIdx, targetOk := nodeIndex[targetKey]
177 | 		
178 | 		if sourceOk && targetOk {
179 | 			edge := map[string]interface{}{
180 | 				"source":      sourceIdx,
181 | 				"target":      targetIdx,
182 | 				"relationship": rel.RelationType,
183 | 			}
184 | 			
185 | 			graph["edges"] = append(graph["edges"].([]map[string]interface{}), edge)
186 | 		}
187 | 	}
188 | 	
189 | 	return graph, nil
190 | }
191 | 
192 | // findRelationships discovers relationships between resources
193 | func (m *ResourceMapper) findRelationships(ctx context.Context, resources []unstructured.Unstructured, namespace string) []ResourceRelationship {
194 | 	var relationships []ResourceRelationship
195 | 	
196 | 	for _, resource := range resources {
197 | 		// Check owner references
198 | 		for _, ownerRef := range resource.GetOwnerReferences() {
199 | 			rel := ResourceRelationship{
200 | 				SourceKind:      ownerRef.Kind,
201 | 				SourceName:      ownerRef.Name,
202 | 				SourceNamespace: namespace,
203 | 				TargetKind:      resource.GetKind(),
204 | 				TargetName:      resource.GetName(),
205 | 				TargetNamespace: namespace,
206 | 				RelationType:    "owns",
207 | 			}
208 | 			relationships = append(relationships, rel)
209 | 		}
210 | 		
211 | 		// Check for Pod -> Service relationships (via labels/selectors)
212 | 		if resource.GetKind() == "Service" {
213 | 			selector, found, _ := unstructured.NestedMap(resource.Object, "spec", "selector")
214 | 			if found && len(selector) > 0 {
215 | 				// Find pods matching this selector
216 | 				pods, err := m.client.clientset.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{
217 | 					LabelSelector: m.labelsToSelector(selector),
218 | 				})
219 | 				
220 | 				if err == nil {
221 | 					for _, pod := range pods.Items {
222 | 						rel := ResourceRelationship{
223 | 							SourceKind:      "Service",
224 | 							SourceName:      resource.GetName(),
225 | 							SourceNamespace: namespace,
226 | 							TargetKind:      "Pod",
227 | 							TargetName:      pod.Name,
228 | 							TargetNamespace: namespace,
229 | 							RelationType:    "selects",
230 | 						}
231 | 						relationships = append(relationships, rel)
232 | 					}
233 | 				}
234 | 			}
235 | 		}
236 | 		
237 | 		// Check for ConfigMap/Secret references in Pods
238 | 		if resource.GetKind() == "Pod" {
239 | 			// Check volumes for ConfigMap references
240 | 			volumes, found, _ := unstructured.NestedSlice(resource.Object, "spec", "volumes")
241 | 			if found {
242 | 				for _, v := range volumes {
243 | 					volume, ok := v.(map[string]interface{})
244 | 					if !ok {
245 | 						continue
246 | 					}
247 | 					
248 | 					// Check for ConfigMap references
249 | 					if configMap, hasConfigMap, _ := unstructured.NestedMap(volume, "configMap"); hasConfigMap {
250 | 						if cmName, hasName, _ := unstructured.NestedString(configMap, "name"); hasName {
251 | 							rel := ResourceRelationship{
252 | 								SourceKind:      "Pod",
253 | 								SourceName:      resource.GetName(),
254 | 								SourceNamespace: namespace,
255 | 								TargetKind:      "ConfigMap",
256 | 								TargetName:      cmName,
257 | 								TargetNamespace: namespace,
258 | 								RelationType:    "mounts",
259 | 							}
260 | 							relationships = append(relationships, rel)
261 | 						}
262 | 					}
263 | 					
264 | 					// Check for Secret references
265 | 					if secret, hasSecret, _ := unstructured.NestedMap(volume, "secret"); hasSecret {
266 | 						if secretName, hasName, _ := unstructured.NestedString(secret, "secretName"); hasName {
267 | 							rel := ResourceRelationship{
268 | 								SourceKind:      "Pod",
269 | 								SourceName:      resource.GetName(),
270 | 								SourceNamespace: namespace,
271 | 								TargetKind:      "Secret",
272 | 								TargetName:      secretName,
273 | 								TargetNamespace: namespace,
274 | 								RelationType:    "mounts",
275 | 							}
276 | 							relationships = append(relationships, rel)
277 | 						}
278 | 					}
279 | 				}
280 | 			}
281 | 			
282 | 			// Check environment variables for ConfigMap/Secret references
283 | 			containers, found, _ := unstructured.NestedSlice(resource.Object, "spec", "containers")
284 | 			if found {
285 | 				for _, c := range containers {
286 | 					container, ok := c.(map[string]interface{})
287 | 					if !ok {
288 | 						continue
289 | 					}
290 | 					
291 | 					// Check for EnvFrom references
292 | 					envFrom, hasEnvFrom, _ := unstructured.NestedSlice(container, "envFrom")
293 | 					if hasEnvFrom {
294 | 						for _, ef := range envFrom {
295 | 							envFromObj, ok := ef.(map[string]interface{})
296 | 							if !ok {
297 | 								continue
298 | 							}
299 | 							
300 | 							// Check for ConfigMap references
301 | 							if configMap, hasConfigMap, _ := unstructured.NestedMap(envFromObj, "configMapRef"); hasConfigMap {
302 | 								if cmName, hasName, _ := unstructured.NestedString(configMap, "name"); hasName {
303 | 									rel := ResourceRelationship{
304 | 										SourceKind:      "Pod",
305 | 										SourceName:      resource.GetName(),
306 | 										SourceNamespace: namespace,
307 | 										TargetKind:      "ConfigMap",
308 | 										TargetName:      cmName,
309 | 										TargetNamespace: namespace,
310 | 										RelationType:    "configures",
311 | 									}
312 | 									relationships = append(relationships, rel)
313 | 								}
314 | 							}
315 | 							
316 | 							// Check for Secret references
317 | 							if secret, hasSecret, _ := unstructured.NestedMap(envFromObj, "secretRef"); hasSecret {
318 | 								if secretName, hasName, _ := unstructured.NestedString(secret, "name"); hasName {
319 | 									rel := ResourceRelationship{
320 | 										SourceKind:      "Pod",
321 | 										SourceName:      resource.GetName(),
322 | 										SourceNamespace: namespace,
323 | 										TargetKind:      "Secret",
324 | 										TargetName:      secretName,
325 | 										TargetNamespace: namespace,
326 | 										RelationType:    "configures",
327 | 									}
328 | 									relationships = append(relationships, rel)
329 | 								}
330 | 							}
331 | 						}
332 | 					}
333 | 					
334 | 					// Check individual env vars for ConfigMap/Secret references
335 | 					env, hasEnv, _ := unstructured.NestedSlice(container, "env")
336 | 					if hasEnv {
337 | 						for _, e := range env {
338 | 							envVar, ok := e.(map[string]interface{})
339 | 							if !ok {
340 | 								continue
341 | 							}
342 | 							
343 | 							// Check for ConfigMap references
344 | 							if valueFrom, hasValueFrom, _ := unstructured.NestedMap(envVar, "valueFrom"); hasValueFrom {
345 | 								if configMap, hasConfigMap, _ := unstructured.NestedMap(valueFrom, "configMapKeyRef"); hasConfigMap {
346 | 									if cmName, hasName, _ := unstructured.NestedString(configMap, "name"); hasName {
347 | 										rel := ResourceRelationship{
348 | 											SourceKind:      "Pod",
349 | 											SourceName:      resource.GetName(),
350 | 											SourceNamespace: namespace,
351 | 											TargetKind:      "ConfigMap",
352 | 											TargetName:      cmName,
353 | 											TargetNamespace: namespace,
354 | 											RelationType:    "configures",
355 | 										}
356 | 										relationships = append(relationships, rel)
357 | 									}
358 | 								}
359 | 								
360 | 								// Check for Secret references
361 | 								if secret, hasSecret, _ := unstructured.NestedMap(valueFrom, "secretKeyRef"); hasSecret {
362 | 									if secretName, hasName, _ := unstructured.NestedString(secret, "name"); hasName {
363 | 										rel := ResourceRelationship{
364 | 											SourceKind:      "Pod",
365 | 											SourceName:      resource.GetName(),
366 | 											SourceNamespace: namespace,
367 | 											TargetKind:      "Secret",
368 | 											TargetName:      secretName,
369 | 											TargetNamespace: namespace,
370 | 											RelationType:    "configures",
371 | 										}
372 | 										relationships = append(relationships, rel)
373 | 									}
374 | 								}
375 | 							}
376 | 						}
377 | 					}
378 | 				}
379 | 			}
380 | 		}
381 | 		
382 | 		// Check for PVC -> PV relationships
383 | 		if resource.GetKind() == "PersistentVolumeClaim" {
384 | 			volumeName, found, _ := unstructured.NestedString(resource.Object, "spec", "volumeName")
385 | 			if found && volumeName != "" {
386 | 				rel := ResourceRelationship{
387 | 					SourceKind:      "PersistentVolumeClaim",
388 | 					SourceName:      resource.GetName(),
389 | 					SourceNamespace: namespace,
390 | 					TargetKind:      "PersistentVolume",
391 | 					TargetName:      volumeName,
392 | 					TargetNamespace: "",
393 | 					RelationType:    "binds",
394 | 				}
395 | 				relationships = append(relationships, rel)
396 | 			}
397 | 		}
398 | 		
399 | 		// Check for Ingress -> Service relationships
400 | 		if resource.GetKind() == "Ingress" {
401 | 			rules, found, _ := unstructured.NestedSlice(resource.Object, "spec", "rules")
402 | 			if found {
403 | 				for _, r := range rules {
404 | 					rule, ok := r.(map[string]interface{})
405 | 					if !ok {
406 | 						continue
407 | 					}
408 | 					
409 | 					http, found, _ := unstructured.NestedMap(rule, "http")
410 | 					if !found {
411 | 						continue
412 | 					}
413 | 					
414 | 					paths, found, _ := unstructured.NestedSlice(http, "paths")
415 | 					if !found {
416 | 						continue
417 | 					}
418 | 					
419 | 					for _, p := range paths {
420 | 						path, ok := p.(map[string]interface{})
421 | 						if !ok {
422 | 							continue
423 | 						}
424 | 						
425 | 						backend, found, _ := unstructured.NestedMap(path, "backend")
426 | 						if !found {
427 | 							// Check for newer API version format
428 | 							backend, found, _ = unstructured.NestedMap(path, "backend", "service")
429 | 							if !found {
430 | 								continue
431 | 							}
432 | 						}
433 | 						
434 | 						serviceName, found, _ := unstructured.NestedString(backend, "name")
435 | 						if found {
436 | 							rel := ResourceRelationship{
437 | 								SourceKind:      "Ingress",
438 | 								SourceName:      resource.GetName(),
439 | 								SourceNamespace: namespace,
440 | 								TargetKind:      "Service",
441 | 								TargetName:      serviceName,
442 | 								TargetNamespace: namespace,
443 | 								RelationType:    "routes",
444 | 							}
445 | 							relationships = append(relationships, rel)
446 | 						}
447 | 					}
448 | 				}
449 | 			}
450 | 		}
451 | 	}
452 | 	
453 | 	// Deduplicate relationships
454 | 	deduplicatedRelationships := make([]ResourceRelationship, 0)
455 | 	relMap := make(map[string]bool)
456 | 	
457 | 	for _, rel := range relationships {
458 | 		key := fmt.Sprintf("%s/%s/%s/%s/%s/%s/%s", 
459 | 			rel.SourceKind, rel.SourceName, rel.SourceNamespace,
460 | 			rel.TargetKind, rel.TargetName, rel.TargetNamespace,
461 | 			rel.RelationType)
462 | 			
463 | 		if _, exists := relMap[key]; !exists {
464 | 			relMap[key] = true
465 | 			deduplicatedRelationships = append(deduplicatedRelationships, rel)
466 | 		}
467 | 	}
468 | 	
469 | 	return deduplicatedRelationships
470 | }
471 | 
472 | // labelsToSelector converts a map of labels to a selector string
473 | func (m *ResourceMapper) labelsToSelector(labels map[string]interface{}) string {
474 | 	var selectors []string
475 | 	
476 | 	for key, value := range labels {
477 | 		if strValue, ok := value.(string); ok {
478 | 			selectors = append(selectors, fmt.Sprintf("%s=%s", key, strValue))
479 | 		}
480 | 	}
481 | 	
482 | 	return strings.Join(selectors, ",")
483 | }
484 | 
485 | // determineResourceHealth determines the health status of a resource
486 | func (m *ResourceMapper) determineResourceHealth(obj *unstructured.Unstructured) string {
487 | 	kind := obj.GetKind()
488 | 	
489 | 	// Check common status fields
490 | 	status, found, _ := unstructured.NestedMap(obj.Object, "status")
491 | 	if !found {
492 | 		return "unknown"
493 | 	}
494 | 	
495 | 	// Check different resource types
496 | 	switch kind {
497 | 	case "Pod":
498 | 		phase, found, _ := unstructured.NestedString(status, "phase")
499 | 		if found {
500 | 			switch phase {
501 | 			case "Running", "Succeeded":
502 | 				return "healthy"
503 | 			case "Pending":
504 | 				return "progressing"
505 | 			case "Failed":
506 | 				return "unhealthy"
507 | 			default:
508 | 				return "unknown"
509 | 			}
510 | 		}
511 | 		
512 | 	case "Deployment", "StatefulSet", "DaemonSet", "ReplicaSet":
513 | 		// Check if all replicas are available
514 | 		replicas, foundReplicas, _ := unstructured.NestedInt64(obj.Object, "spec", "replicas")
515 | 		if !foundReplicas {
516 | 			replicas = 1 // Default to 1 if not specified
517 | 		}
518 | 		
519 | 		availableReplicas, foundAvailable, _ := unstructured.NestedInt64(status, "availableReplicas")
520 | 		if foundAvailable && availableReplicas == replicas {
521 | 			return "healthy"
522 | 		} else if foundAvailable && availableReplicas > 0 {
523 | 			return "progressing"
524 | 		} else {
525 | 			return "unhealthy"
526 | 		}
527 | 		
528 | 	case "Service":
529 | 		// Services are typically healthy unless they have no endpoints
530 | 		// We'd need to check endpoints separately
531 | 		return "healthy"
532 | 		
533 | 	case "Ingress":
534 | 		// Check if LoadBalancer has assigned addresses
535 | 		ingress, found, _ := unstructured.NestedSlice(status, "loadBalancer", "ingress")
536 | 		if found && len(ingress) > 0 {
537 | 			return "healthy"
538 | 		}
539 | 		return "progressing"
540 | 		
541 | 	case "PersistentVolumeClaim":
542 | 		phase, found, _ := unstructured.NestedString(status, "phase")
543 | 		if found && phase == "Bound" {
544 | 			return "healthy"
545 | 		} else if found && phase == "Pending" {
546 | 			return "progressing"
547 | 		} else {
548 | 			return "unhealthy"
549 | 		}
550 | 	
551 | 	case "Job":
552 | 		conditions, found, _ := unstructured.NestedSlice(status, "conditions")
553 | 		if found {
554 | 			for _, c := range conditions {
555 | 				condition, ok := c.(map[string]interface{})
556 | 				if !ok {
557 | 					continue
558 | 				}
559 | 				
560 | 				condType, typeFound, _ := unstructured.NestedString(condition, "type")
561 | 				condStatus, statusFound, _ := unstructured.NestedString(condition, "status")
562 | 				
563 | 				if typeFound && statusFound && condType == "Complete" && condStatus == "True" {
564 | 					return "healthy"
565 | 				} else if typeFound && statusFound && condType == "Failed" && condStatus == "True" {
566 | 					return "unhealthy"
567 | 				}
568 | 			}
569 | 			return "progressing"
570 | 		}
571 | 		
572 | 	default:
573 | 		// For other resources, try to check common status conditions
574 | 		conditions, found, _ := unstructured.NestedSlice(status, "conditions")
575 | 		if found {
576 | 			for _, c := range conditions {
577 | 				condition, ok := c.(map[string]interface{})
578 | 				if !ok {
579 | 					continue
580 | 				}
581 | 				
582 | 				condType, typeFound, _ := unstructured.NestedString(condition, "type")
583 | 				condStatus, statusFound, _ := unstructured.NestedString(condition, "status")
584 | 				
585 | 				if typeFound && statusFound {
586 | 					// Check for common condition types indicating health
587 | 					if (condType == "Ready" || condType == "Available") && condStatus == "True" {
588 | 						return "healthy"
589 | 					} else if condType == "Progressing" && condStatus == "True" {
590 | 						return "progressing"
591 | 					} else if (condType == "Failed" || condType == "Error") && condStatus == "True" {
592 | 						return "unhealthy"
593 | 					}
594 | 				}
595 | 			}
596 | 		}
597 | 	}
598 | 	
599 | 	return "unknown"
600 | }
```

--------------------------------------------------------------------------------
/kubernetes-claude-mcp/internal/correlator/troubleshoot.go:
--------------------------------------------------------------------------------

```go
  1 | package correlator
  2 | 
  3 | import (
  4 | 	"context"
  5 | 	"fmt"
  6 | 	"strings"
  7 | 
  8 | 	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/internal/k8s"
  9 | 	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/internal/models"
 10 | 	"github.com/Blankcut/kubernetes-mcp-server/kubernetes-claude-mcp/pkg/logging"
 11 | 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 12 | )
 13 | 
 14 | // TroubleshootCorrelator provides specialized logic for troubleshooting
 15 | type TroubleshootCorrelator struct {
 16 | 	gitOpsCorrelator *GitOpsCorrelator
 17 | 	k8sClient        *k8s.Client
 18 | 	logger           *logging.Logger
 19 | }
 20 | 
 21 | // NewTroubleshootCorrelator creates a new troubleshooting correlator
 22 | func NewTroubleshootCorrelator(gitOpsCorrelator *GitOpsCorrelator, k8sClient *k8s.Client, logger *logging.Logger) *TroubleshootCorrelator {
 23 | 	if logger == nil {
 24 | 		logger = logging.NewLogger().Named("troubleshoot")
 25 | 	}
 26 | 	
 27 | 	return &TroubleshootCorrelator{
 28 | 		gitOpsCorrelator: gitOpsCorrelator,
 29 | 		k8sClient:        k8sClient,
 30 | 		logger:           logger,
 31 | 	}
 32 | }
 33 | 
 34 | // TroubleshootResource analyzes a resource for common issues
 35 | func (tc *TroubleshootCorrelator) TroubleshootResource(ctx context.Context, namespace, kind, name string) (*models.TroubleshootResult, error) {
 36 | 	tc.logger.Info("Troubleshooting resource", "kind", kind, "name", name, "namespace", namespace)
 37 | 	
 38 | 	// First, trace the resource deployment
 39 | 	resourceContext, err := tc.gitOpsCorrelator.TraceResourceDeployment(ctx, namespace, kind, name)
 40 | 	if err != nil {
 41 | 		return nil, fmt.Errorf("failed to trace resource deployment: %w", err)
 42 | 	}
 43 | 	
 44 | 	// Get the raw resource for detailed analysis
 45 | 	resource, err := tc.k8sClient.GetResource(ctx, kind, namespace, name)
 46 | 	if err != nil {
 47 | 		tc.logger.Warn("Failed to get resource for detailed analysis", "error", err)
 48 | 	}
 49 | 	
 50 | 	// Initialize troubleshooting result
 51 | 	result := &models.TroubleshootResult{
 52 | 		ResourceContext: resourceContext,
 53 | 		Issues:          []models.Issue{},
 54 | 		Recommendations: []string{},
 55 | 	}
 56 | 	
 57 | 	// Analyze Kubernetes events for issues
 58 | 	tc.analyzeKubernetesEvents(resourceContext, result)
 59 | 	
 60 | 	// Analyze resource status and conditions if resource was retrieved
 61 | 	if resource != nil {
 62 | 		// Pod-specific analysis
 63 | 		if strings.EqualFold(kind, "pod") {
 64 | 			tc.analyzePodStatus(ctx, resource, result)
 65 | 		}
 66 | 		
 67 | 		// Deployment-specific analysis
 68 | 		if strings.EqualFold(kind, "deployment") {
 69 | 			tc.analyzeDeploymentStatus(resource, result)
 70 | 		}
 71 | 	}
 72 | 	
 73 | 	// Analyze ArgoCD sync status
 74 | 	tc.analyzeArgoStatus(resourceContext, result)
 75 | 	
 76 | 	// Analyze GitLab pipeline status
 77 | 	tc.analyzeGitLabStatus(resourceContext, result)
 78 | 	
 79 | 	// Check if resource is healthy
 80 | 	if len(result.Issues) == 0 && resource != nil && !tc.isResourceHealthy(resource) {
 81 | 		issue := models.Issue{
 82 | 			Source:      "Kubernetes",
 83 | 			Category:    "UnknownIssue",
 84 | 			Severity:    "Warning",
 85 | 			Title:       "Resource Not Healthy",
 86 | 			Description: fmt.Sprintf("%s %s/%s is not in a healthy state", kind, namespace, name),
 87 | 		}
 88 | 		result.Issues = append(result.Issues, issue)
 89 | 	}
 90 | 	
 91 | 	// Generate recommendations based on issues
 92 | 	tc.generateRecommendations(result)
 93 | 	
 94 | 	tc.logger.Info("Troubleshooting completed", 
 95 | 		"kind", kind, 
 96 | 		"name", name, 
 97 | 		"namespace", namespace,
 98 | 		"issueCount", len(result.Issues),
 99 | 		"recommendationCount", len(result.Recommendations))
100 | 	
101 | 	return result, nil
102 | }
103 | 
104 | // isResourceHealthy checks if a resource is in a healthy state
105 | func (tc *TroubleshootCorrelator) isResourceHealthy(resource *unstructured.Unstructured) bool {
106 | 	kind := resource.GetKind()
107 | 	
108 | 	// Pod health check
109 | 	if strings.EqualFold(kind, "pod") {
110 | 		phase, found, _ := unstructured.NestedString(resource.Object, "status", "phase")
111 | 		return found && phase == "Running"
112 | 	}
113 | 	
114 | 	// Deployment health check
115 | 	if strings.EqualFold(kind, "deployment") {
116 | 		// Check if available replicas match desired replicas
117 | 		desiredReplicas, found1, _ := unstructured.NestedInt64(resource.Object, "spec", "replicas")
118 | 		availableReplicas, found2, _ := unstructured.NestedInt64(resource.Object, "status", "availableReplicas")
119 | 		return found1 && found2 && desiredReplicas == availableReplicas && availableReplicas > 0
120 | 	}
121 | 	
122 | 	// Default: assume healthy
123 | 	return true
124 | }
125 | 
126 | // analyzeDeploymentStatus analyzes deployment-specific status
127 | func (tc *TroubleshootCorrelator) analyzeDeploymentStatus(deployment *unstructured.Unstructured, result *models.TroubleshootResult) {
128 | 	// Check if deployment is ready
129 | 	desiredReplicas, found1, _ := unstructured.NestedInt64(deployment.Object, "spec", "replicas")
130 | 	availableReplicas, found2, _ := unstructured.NestedInt64(deployment.Object, "status", "availableReplicas")
131 | 	readyReplicas, found3, _ := unstructured.NestedInt64(deployment.Object, "status", "readyReplicas")
132 | 	
133 | 	if !found1 || !found2 || availableReplicas < desiredReplicas {
134 | 		issue := models.Issue{
135 | 			Source:      "Kubernetes",
136 | 			Category:    "DeploymentNotAvailable",
137 | 			Severity:    "Warning",
138 | 			Title:       "Deployment Not Fully Available",
139 | 			Description: fmt.Sprintf("Deployment has %d/%d available replicas", availableReplicas, desiredReplicas),
140 | 		}
141 | 		result.Issues = append(result.Issues, issue)
142 | 	}
143 | 	
144 | 	if !found1 || !found3 || readyReplicas < desiredReplicas {
145 | 		issue := models.Issue{
146 | 			Source:      "Kubernetes",
147 | 			Category:    "DeploymentNotReady",
148 | 			Severity:    "Warning",
149 | 			Title:       "Deployment Not Fully Ready",
150 | 			Description: fmt.Sprintf("Deployment has %d/%d ready replicas", readyReplicas, desiredReplicas),
151 | 		}
152 | 		result.Issues = append(result.Issues, issue)
153 | 	}
154 | 	
155 | 	// Check deployment conditions
156 | 	conditions, found, _ := unstructured.NestedSlice(deployment.Object, "status", "conditions")
157 | 	if found {
158 | 		for _, c := range conditions {
159 | 			condition, ok := c.(map[string]interface{})
160 | 			if !ok {
161 | 				continue
162 | 			}
163 | 			
164 | 			conditionType, _, _ := unstructured.NestedString(condition, "type")
165 | 			status, _, _ := unstructured.NestedString(condition, "status")
166 | 			reason, _, _ := unstructured.NestedString(condition, "reason")
167 | 			message, _, _ := unstructured.NestedString(condition, "message")
168 | 			
169 | 			if conditionType == "Available" && status != "True" {
170 | 				issue := models.Issue{
171 | 					Source:      "Kubernetes",
172 | 					Category:    "DeploymentNotAvailable",
173 | 					Severity:    "Warning",
174 | 					Title:       "Deployment Not Available",
175 | 					Description: fmt.Sprintf("Deployment availability issue: %s - %s", reason, message),
176 | 				}
177 | 				result.Issues = append(result.Issues, issue)
178 | 			}
179 | 			
180 | 			if conditionType == "Progressing" && status != "True" {
181 | 				issue := models.Issue{
182 | 					Source:      "Kubernetes",
183 | 					Category:    "DeploymentNotProgressing",
184 | 					Severity:    "Warning",
185 | 					Title:       "Deployment Not Progressing",
186 | 					Description: fmt.Sprintf("Deployment progress issue: %s - %s", reason, message),
187 | 				}
188 | 				result.Issues = append(result.Issues, issue)
189 | 			}
190 | 		}
191 | 	}
192 | }
193 | 
194 | // analyzePodStatus analyzes pod-specific status information
195 | func (tc *TroubleshootCorrelator) analyzePodStatus(ctx context.Context, pod *unstructured.Unstructured, result *models.TroubleshootResult) {
196 | 	// Check pod phase
197 | 	phase, found, _ := unstructured.NestedString(pod.Object, "status", "phase")
198 | 	if found && phase != "Running" && phase != "Succeeded" {
199 | 		issue := models.Issue{
200 | 			Source:      "Kubernetes",
201 | 			Category:    "PodNotRunning",
202 | 			Severity:    "Warning",
203 | 			Title:       "Pod Not Running",
204 | 			Description: fmt.Sprintf("Pod is in %s state", phase),
205 | 		}
206 | 		
207 | 		if phase == "Pending" {
208 | 			issue.Title = "Pod Pending"
209 | 			issue.Description = "Pod is still in Pending state and hasn't started running"
210 | 		} else if phase == "Failed" {
211 | 			issue.Severity = "Error"
212 | 			issue.Title = "Pod Failed"
213 | 		}
214 | 		
215 | 		result.Issues = append(result.Issues, issue)
216 | 	}
217 | 	
218 | 	// Check pod conditions
219 | 	conditions, found, _ := unstructured.NestedSlice(pod.Object, "status", "conditions")
220 | 	if found {
221 | 		for _, c := range conditions {
222 | 			condition, ok := c.(map[string]interface{})
223 | 			if !ok {
224 | 				continue
225 | 			}
226 | 			
227 | 			conditionType, _, _ := unstructured.NestedString(condition, "type")
228 | 			status, _, _ := unstructured.NestedString(condition, "status")
229 | 			
230 | 			if conditionType == "PodScheduled" && status != "True" {
231 | 				issue := models.Issue{
232 | 					Source:      "Kubernetes",
233 | 					Category:    "SchedulingIssue",
234 | 					Severity:    "Warning",
235 | 					Title:       "Pod Scheduling Issue",
236 | 					Description: "Pod cannot be scheduled onto a node",
237 | 				}
238 | 				result.Issues = append(result.Issues, issue)
239 | 			}
240 | 			
241 | 			if conditionType == "Initialized" && status != "True" {
242 | 				issue := models.Issue{
243 | 					Source:      "Kubernetes",
244 | 					Category:    "InitializationIssue",
245 | 					Severity:    "Warning",
246 | 					Title:       "Pod Initialization Issue",
247 | 					Description: "Pod initialization containers have not completed successfully",
248 | 				}
249 | 				result.Issues = append(result.Issues, issue)
250 | 			}
251 | 			
252 | 			if conditionType == "ContainersReady" && status != "True" {
253 | 				issue := models.Issue{
254 | 					Source:      "Kubernetes",
255 | 					Category:    "ContainerReadinessIssue",
256 | 					Severity:    "Warning",
257 | 					Title:       "Container Readiness Issue",
258 | 					Description: "One or more containers are not ready",
259 | 				}
260 | 				result.Issues = append(result.Issues, issue)
261 | 			}
262 | 			
263 | 			if conditionType == "Ready" && status != "True" {
264 | 				issue := models.Issue{
265 | 					Source:      "Kubernetes",
266 | 					Category:    "PodNotReady",
267 | 					Severity:    "Warning",
268 | 					Title:       "Pod Not Ready",
269 | 					Description: "Pod is not ready to serve traffic",
270 | 				}
271 | 				result.Issues = append(result.Issues, issue)
272 | 			}
273 | 		}
274 | 	}
275 | 	
276 | 	// Check container statuses
277 | 	containerStatuses, found, _ := unstructured.NestedSlice(pod.Object, "status", "containerStatuses")
278 | 	if found {
279 | 		tc.analyzeContainerStatuses(containerStatuses, false, result)
280 | 	}
281 | 	
282 | 	// Check init container statuses if they exist
283 | 	initContainerStatuses, found, _ := unstructured.NestedSlice(pod.Object, "status", "initContainerStatuses")
284 | 	if found {
285 | 		tc.analyzeContainerStatuses(initContainerStatuses, true, result)
286 | 	}
287 | 	
288 | 	// Check for volume issues
289 | 	volumes, found, _ := unstructured.NestedSlice(pod.Object, "spec", "volumes")
290 | 	if found {
291 | 		// Track PVC usage
292 | 		pvcVolumes := []string{}
293 | 		
294 | 		for _, v := range volumes {
295 | 			volume, ok := v.(map[string]interface{})
296 | 			if !ok {
297 | 				continue
298 | 			}
299 | 						
300 | 			// Check for PVC volumes
301 | 			pvc, pvcFound, _ := unstructured.NestedMap(volume, "persistentVolumeClaim")
302 | 			if pvcFound && pvc != nil {
303 | 				claimName, nameFound, _ := unstructured.NestedString(pvc, "claimName")
304 | 				if nameFound && claimName != "" {
305 | 					pvcVolumes = append(pvcVolumes, claimName)
306 | 				}
307 | 			}
308 | 		}
309 | 		
310 | 		// If PVC volumes found, check their status
311 | 		if len(pvcVolumes) > 0 {
312 | 			for _, pvcName := range pvcVolumes {
313 | 				pvc, err := tc.k8sClient.GetResource(ctx, "persistentvolumeclaim", pod.GetNamespace(), pvcName)
314 | 				if err != nil {
315 | 					issue := models.Issue{
316 | 						Source:      "Kubernetes",
317 | 						Category:    "VolumeIssue",
318 | 						Severity:    "Warning",
319 | 						Title:       "PVC Not Found",
320 | 						Description: fmt.Sprintf("PersistentVolumeClaim %s not found", pvcName),
321 | 					}
322 | 					result.Issues = append(result.Issues, issue)
323 | 					continue
324 | 				}
325 | 				
326 | 				phase, phaseFound, _ := unstructured.NestedString(pvc.Object, "status", "phase")
327 | 				if !phaseFound || phase != "Bound" {
328 | 					issue := models.Issue{
329 | 						Source:      "Kubernetes",
330 | 						Category:    "VolumeIssue",
331 | 						Severity:    "Warning",
332 | 						Title:       "PVC Not Bound",
333 | 						Description: fmt.Sprintf("PersistentVolumeClaim %s is in %s state", pvcName, phase),
334 | 					}
335 | 					result.Issues = append(result.Issues, issue)
336 | 				}
337 | 			}
338 | 		}
339 | 	}
340 | }
341 | 
342 | // analyzeContainerStatuses analyzes container status information
343 | func (tc *TroubleshootCorrelator) analyzeContainerStatuses(statuses []interface{}, isInit bool, result *models.TroubleshootResult) {
344 | 	containerType := "Container"
345 | 	if isInit {
346 | 		containerType = "Init Container"
347 | 	}
348 | 	
349 | 	for _, cs := range statuses {
350 | 		containerStatus, ok := cs.(map[string]interface{})
351 | 		if !ok {
352 | 			continue
353 | 		}
354 | 		
355 | 		containerName, _, _ := unstructured.NestedString(containerStatus, "name")
356 | 		ready, _, _ := unstructured.NestedBool(containerStatus, "ready")
357 | 		restartCount, _, _ := unstructured.NestedInt64(containerStatus, "restartCount")
358 | 		
359 | 		if !ready {
360 | 			// Check for specific container state
361 | 			state, stateExists, _ := unstructured.NestedMap(containerStatus, "state")
362 | 			if stateExists && state != nil {
363 | 				waitingState, waitingExists, _ := unstructured.NestedMap(state, "waiting")
364 | 				if waitingExists && waitingState != nil {
365 | 					reason, reasonFound, _ := unstructured.NestedString(waitingState, "reason")
366 | 					message, messageFound, _ := unstructured.NestedString(waitingState, "message")
367 | 					
368 | 					reasonStr := ""
369 | 					if reasonFound {
370 | 						reasonStr = reason
371 | 					}
372 | 					
373 | 					messageStr := ""
374 | 					if messageFound {
375 | 						messageStr = message
376 | 					}
377 | 					
378 | 					issue := models.Issue{
379 | 						Source:      "Kubernetes",
380 | 						Category:    "ContainerWaiting",
381 | 						Severity:    "Warning",
382 | 						Title:       fmt.Sprintf("%s %s Waiting", containerType, containerName),
383 | 						Description: fmt.Sprintf("%s is waiting: %s - %s", containerType, reasonStr, messageStr),
384 | 					}
385 | 					
386 | 					if reason == "CrashLoopBackOff" {
387 | 						issue.Category = "CrashLoopBackOff"
388 | 						issue.Severity = "Error"
389 | 						issue.Title = fmt.Sprintf("%s %s CrashLoopBackOff", containerType, containerName)
390 | 					} else if reason == "ImagePullBackOff" || reason == "ErrImagePull" {
391 | 						issue.Category = "ImagePullError"
392 | 						issue.Title = fmt.Sprintf("%s %s Image Pull Error", containerType, containerName)
393 | 					} else if reason == "PodInitializing" || reason == "ContainerCreating" {
394 | 						issue.Category = "PodInitializing"
395 | 						issue.Title = fmt.Sprintf("%s Still Initializing", containerType)
396 | 						issue.Description = fmt.Sprintf("%s is still being created or initialized", containerType)
397 | 					}
398 | 					
399 | 					result.Issues = append(result.Issues, issue)
400 | 				}
401 | 				
402 | 				terminatedState, terminatedExists, _ := unstructured.NestedMap(state, "terminated")
403 | 				if terminatedExists && terminatedState != nil {
404 | 					reason, reasonFound, _ := unstructured.NestedString(terminatedState, "reason")
405 | 					exitCode, exitCodeFound, _ := unstructured.NestedInt64(terminatedState, "exitCode")
406 | 					message, messageFound, _ := unstructured.NestedString(terminatedState, "message")
407 | 					
408 | 					reasonStr := ""
409 | 					if reasonFound {
410 | 						reasonStr = reason
411 | 					}
412 | 					
413 | 					messageStr := ""
414 | 					if messageFound {
415 | 						messageStr = message
416 | 					}
417 | 					
418 | 					var exitCodeVal int64 = 0
419 | 					if exitCodeFound {
420 | 						exitCodeVal = exitCode
421 | 					}
422 | 					
423 | 					if exitCodeVal != 0 {
424 | 						issue := models.Issue{
425 | 							Source:      "Kubernetes",
426 | 							Category:    "ContainerTerminated",
427 | 							Severity:    "Error",
428 | 							Title:       fmt.Sprintf("%s %s Terminated", containerType, containerName),
429 | 							Description: fmt.Sprintf("%s terminated with exit code %d: %s - %s", containerType, exitCodeVal, reasonStr, messageStr),
430 | 						}
431 | 						result.Issues = append(result.Issues, issue)
432 | 					}
433 | 				}
434 | 			}
435 | 		}
436 | 		
437 | 		if restartCount > 3 {
438 | 			issue := models.Issue{
439 | 				Source:      "Kubernetes",
440 | 				Category:    "FrequentRestarts",
441 | 				Severity:    "Warning",
442 | 				Title:       fmt.Sprintf("%s %s Frequent Restarts", containerType, containerName),
443 | 				Description: fmt.Sprintf("%s has restarted %d times", containerType, restartCount),
444 | 			}
445 | 			result.Issues = append(result.Issues, issue)
446 | 		}
447 | 	}
448 | }
449 | 
450 | // analyzeKubernetesEvents looks for common issues in Kubernetes events
451 | func (tc *TroubleshootCorrelator) analyzeKubernetesEvents(rc models.ResourceContext, result *models.TroubleshootResult) {
452 | 	for _, event := range rc.Events {
453 | 		// Look for error events
454 | 		if event.Type == "Warning" {
455 | 			issue := models.Issue{
456 | 				Source:      "Kubernetes",
457 | 				Severity:    "Warning",
458 | 				Description: fmt.Sprintf("%s: %s", event.Reason, event.Message),
459 | 			}
460 | 			
461 | 			// Categorize common issues
462 | 			switch {
463 | 			case strings.Contains(event.Reason, "Failed") && strings.Contains(event.Message, "ImagePull"):
464 | 				issue.Category = "ImagePullError"
465 | 				issue.Title = "Image Pull Failure"
466 | 			
467 | 			case strings.Contains(event.Reason, "Unhealthy"):
468 | 				issue.Category = "HealthCheckFailure"
469 | 				issue.Title = "Health Check Failure"
470 | 			
471 | 			case strings.Contains(event.Message, "memory"):
472 | 				issue.Category = "ResourceIssue"
473 | 				issue.Title = "Memory Resource Issue"
474 | 				
475 | 			case strings.Contains(event.Message, "cpu"):
476 | 				issue.Category = "ResourceIssue"
477 | 				issue.Title = "CPU Resource Issue"
478 | 				
479 | 			case strings.Contains(event.Reason, "BackOff"):
480 | 				issue.Category = "CrashLoopBackOff"
481 | 				issue.Title = "Container Crash Loop"
482 | 				
483 | 			default:
484 | 				issue.Category = "OtherWarning"
485 | 				issue.Title = "Kubernetes Warning"
486 | 			}
487 | 			
488 | 			result.Issues = append(result.Issues, issue)
489 | 		}
490 | 	}
491 | }
492 | 
493 | // analyzeArgoStatus looks for issues in ArgoCD status
494 | func (tc *TroubleshootCorrelator) analyzeArgoStatus(rc models.ResourceContext, result *models.TroubleshootResult) {
495 | 	if rc.ArgoApplication == nil {
496 | 		// No ArgoCD application managing this resource
497 | 		return
498 | 	}
499 | 	
500 | 	// Check sync status
501 | 	if rc.ArgoSyncStatus != "Synced" {
502 | 		issue := models.Issue{
503 | 			Source:      "ArgoCD",
504 | 			Category:    "SyncIssue",
505 | 			Severity:    "Warning",
506 | 			Title:       "ArgoCD Sync Issue",
507 | 			Description: fmt.Sprintf("Application %s is not synced (status: %s)", rc.ArgoApplication.Name, rc.ArgoSyncStatus),
508 | 		}
509 | 		result.Issues = append(result.Issues, issue)
510 | 	}
511 | 	
512 | 	// Check health status
513 | 	if rc.ArgoHealthStatus != "Healthy" {
514 | 		issue := models.Issue{
515 | 			Source:      "ArgoCD",
516 | 			Category:    "HealthIssue",
517 | 			Severity:    "Warning",
518 | 			Title:       "ArgoCD Health Issue",
519 | 			Description: fmt.Sprintf("Application %s is not healthy (status: %s)", rc.ArgoApplication.Name, rc.ArgoHealthStatus),
520 | 		}
521 | 		result.Issues = append(result.Issues, issue)
522 | 	}
523 | 	
524 | 	// Check for recent sync failures
525 | 	for _, history := range rc.ArgoSyncHistory {
526 | 		if history.Status == "Failed" {
527 | 			issue := models.Issue{
528 | 				Source:      "ArgoCD",
529 | 				Category:    "SyncFailure",
530 | 				Severity:    "Error",
531 | 				Title:       "Recent Sync Failure",
532 | 				Description: fmt.Sprintf("Sync at %s failed with revision %s", history.DeployedAt.Format("2006-01-02 15:04:05"), history.Revision),
533 | 			}
534 | 			result.Issues = append(result.Issues, issue)
535 | 			break // Only report the most recent failure
536 | 		}
537 | 	}
538 | }
539 | 
540 | // analyzeGitLabStatus looks for issues in GitLab pipelines and deployments
541 | func (tc *TroubleshootCorrelator) analyzeGitLabStatus(rc models.ResourceContext, result *models.TroubleshootResult) {
542 | 	if rc.GitLabProject == nil {
543 | 		// No GitLab project information
544 | 		return
545 | 	}
546 | 	
547 | 	// Check last pipeline status
548 | 	if rc.LastPipeline != nil && rc.LastPipeline.Status != "success" {
549 | 		severity := "Warning"
550 | 		if rc.LastPipeline.Status == "failed" {
551 | 			severity = "Error"
552 | 		}
553 | 		
554 | 		issue := models.Issue{
555 | 			Source:      "GitLab",
556 | 			Category:    "PipelineIssue",
557 | 			Severity:    severity,
558 | 			Title:       "GitLab Pipeline Issue",
559 | 			Description: fmt.Sprintf("Pipeline #%d status: %s", rc.LastPipeline.ID, rc.LastPipeline.Status),
560 | 		}
561 | 		result.Issues = append(result.Issues, issue)
562 | 	}
563 | 	
564 | 	// Check last deployment status
565 | 	if rc.LastDeployment != nil && rc.LastDeployment.Status != "success" {
566 | 		severity := "Warning"
567 | 		if rc.LastDeployment.Status == "failed" {
568 | 			severity = "Error"
569 | 		}
570 | 		
571 | 		issue := models.Issue{
572 | 			Source:      "GitLab",
573 | 			Category:    "DeploymentIssue",
574 | 			Severity:    severity,
575 | 			Title:       "GitLab Deployment Issue",
576 | 			Description: fmt.Sprintf("Deployment to %s status: %s", rc.LastDeployment.Environment.Name, rc.LastDeployment.Status),
577 | 		}
578 | 		result.Issues = append(result.Issues, issue)
579 | 	}
580 | }
581 | 
582 | // generateRecommendations creates recommendations based on identified issues
583 | func (tc *TroubleshootCorrelator) generateRecommendations(result *models.TroubleshootResult) {
584 |     // Update the original implementation to include more recommendations
585 |     recommendationMap := make(map[string]bool)
586 |     
587 |     for _, issue := range result.Issues {
588 |         switch issue.Category {
589 |         case "ImagePullError":
590 |             recommendationMap["Check image name and credentials for accessing private registries."] = true
591 |             recommendationMap["Verify that the image tag exists in the registry."] = true
592 |             
593 |         case "HealthCheckFailure":
594 |             recommendationMap["Review liveness and readiness probe configuration."] = true
595 |             recommendationMap["Check application logs for errors during startup."] = true
596 |             
597 |         case "ResourceIssue":
598 |             recommendationMap["Review resource requests and limits in the deployment."] = true
599 |             recommendationMap["Monitor resource usage to determine appropriate values."] = true
600 |             
601 |         case "CrashLoopBackOff":
602 |             recommendationMap["Check container logs for errors."] = true
603 |             recommendationMap["Verify environment variables and configuration."] = true
604 |             
605 |         case "SyncIssue", "SyncFailure":
606 |             recommendationMap["Check ArgoCD application manifest for errors."] = true
607 |             recommendationMap["Verify that the target revision exists in the Git repository."] = true
608 |             
609 |         case "PipelineIssue":
610 |             recommendationMap["Review GitLab pipeline logs for errors."] = true
611 |             recommendationMap["Check if the pipeline configuration is valid."] = true
612 |             
613 |         case "DeploymentIssue":
614 |             recommendationMap["Check GitLab deployment job logs for errors."] = true
615 |             recommendationMap["Verify deployment environment configuration."] = true
616 |             
617 |         case "PodNotRunning", "PodNotReady", "PodInitializing":
618 |             recommendationMap["Check pod events for scheduling or initialization issues."] = true
619 |             recommendationMap["Examine init container logs for errors."] = true
620 |             
621 |         case "InitializationIssue":
622 |             recommendationMap["Check init container logs for errors."] = true
623 |             recommendationMap["Verify that volumes can be mounted properly."] = true
624 |             
625 |         case "ContainerReadinessIssue":
626 |             recommendationMap["Review readiness probe configuration."] = true
627 |             recommendationMap["Check container logs for application startup issues."] = true
628 |             
629 |         case "VolumeIssue":
630 |             recommendationMap["Verify that PersistentVolumeClaims are bound."] = true
631 |             recommendationMap["Check if storage classes are properly configured."] = true
632 |             recommendationMap["Ensure sufficient storage space is available on the nodes."] = true
633 |             
634 |         case "SchedulingIssue":
635 |             recommendationMap["Check if nodes have sufficient resources for the pod."] = true
636 |             recommendationMap["Verify that node selectors or taints are not preventing scheduling."] = true
637 |         }
638 |     }
639 |     
640 |     // Add generic recommendations if no specific issues found
641 |     if len(result.Issues) == 0 {
642 |         recommendationMap["Check pod logs for errors."] = true
643 |         recommendationMap["Examine Kubernetes events for the resource."] = true
644 |         recommendationMap["Verify network connectivity between components."] = true
645 |     }
646 |     
647 |     // Convert map to slice
648 |     for rec := range recommendationMap {
649 |         result.Recommendations = append(result.Recommendations, rec)
650 |     }
651 | }
652 | 
653 | 
```

--------------------------------------------------------------------------------
/docs/public/images/logo.svg:
--------------------------------------------------------------------------------

```
1 | <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="500" zoomAndPan="magnify" viewBox="0 0 375 374.999991" height="500" preserveAspectRatio="xMidYMid meet" version="1.0"><path fill="#ffffff" d="M 176.261719 185.164062 L 170.148438 223.289062 L 203.796875 223.289062 L 196.632812 185.164062 L 176.261719 185.164062 " fill-opacity="1" fill-rule="nonzero"/><path fill="#4c44cb" d="M 176.261719 185.164062 L 175.214844 184.996094 L 169.101562 223.121094 C 169.054688 223.425781 169.140625 223.738281 169.34375 223.972656 C 169.542969 224.210938 169.835938 224.347656 170.148438 224.347656 L 203.796875 224.347656 C 204.113281 224.347656 204.410156 224.207031 204.613281 223.960938 C 204.8125 223.722656 204.894531 223.402344 204.839844 223.089844 L 197.675781 184.96875 C 197.578125 184.46875 197.140625 184.105469 196.632812 184.105469 L 176.261719 184.105469 C 175.742188 184.105469 175.296875 184.484375 175.214844 184.996094 L 176.261719 185.164062 L 176.261719 186.222656 L 195.753906 186.222656 L 202.523438 222.230469 L 171.390625 222.230469 L 177.304688 185.332031 L 176.261719 185.164062 L 176.261719 186.222656 L 176.261719 185.164062 " fill-opacity="1" fill-rule="nonzero"/><path fill="#ffffff" d="M 180.769531 185.164062 L 177.230469 223.289062 L 196.71875 223.289062 L 192.566406 185.164062 L 180.769531 185.164062 " fill-opacity="1" fill-rule="nonzero"/><path fill="#4c44cb" d="M 180.769531 185.164062 L 179.71875 185.066406 L 176.175781 223.191406 C 176.152344 223.488281 176.246094 223.78125 176.449219 224 C 176.648438 224.21875 176.929688 224.347656 177.230469 224.347656 L 196.71875 224.347656 C 197.019531 224.347656 197.304688 224.21875 197.503906 223.996094 C 197.707031 223.769531 197.800781 223.472656 197.769531 223.171875 L 193.621094 185.050781 C 193.5625 184.515625 193.105469 184.105469 192.566406 184.105469 L 180.769531 184.105469 C 180.226562 184.105469 179.765625 184.523438 179.71875 185.066406 L 180.769531 185.164062 L 180.769531 186.222656 L 191.617188 186.222656 L 195.535156 222.230469 L 178.390625 222.230469 L 181.824219 185.261719 L 180.769531 185.164062 L 180.769531 186.222656 L 180.769531 185.164062 " fill-opacity="1" fill-rule="nonzero"/><path fill="#ffffff" d="M 186.582031 120.640625 C 186.582031 120.640625 210.292969 131.441406 196.632812 185.164062 C 196.632812 185.164062 194.464844 189.5 186.582031 189.5 L 186.3125 189.5 C 178.429688 189.5 176.261719 185.164062 176.261719 185.164062 C 162.601562 131.441406 186.3125 120.640625 186.3125 120.640625 L 186.582031 120.640625 " fill-opacity="1" fill-rule="nonzero"/><path fill="#4c44cb" d="M 186.582031 120.640625 L 186.140625 121.605469 L 186.390625 121.058594 L 186.136719 121.601562 L 186.140625 121.605469 L 186.390625 121.058594 L 186.136719 121.601562 L 186.171875 121.621094 C 186.558594 121.8125 189.953125 123.585938 193.261719 128.597656 C 196.566406 133.613281 199.8125 141.890625 199.816406 155.199219 C 199.816406 163.175781 198.644531 172.957031 195.605469 184.902344 L 196.632812 185.164062 L 195.683594 184.691406 L 196.09375 184.894531 L 195.691406 184.679688 L 195.683594 184.691406 L 196.09375 184.894531 L 195.691406 184.679688 C 195.679688 184.699219 195.1875 185.628906 193.820312 186.558594 C 192.453125 187.484375 190.210938 188.4375 186.582031 188.441406 L 186.3125 188.441406 C 182.566406 188.4375 180.296875 187.425781 178.941406 186.46875 C 178.269531 185.988281 177.820312 185.515625 177.546875 185.179688 C 177.410156 185.007812 177.320312 184.875 177.265625 184.789062 C 177.238281 184.746094 177.21875 184.714844 177.210938 184.695312 L 177.203125 184.679688 L 176.796875 184.898438 L 177.207031 184.691406 L 177.203125 184.679688 L 176.796875 184.898438 L 177.207031 184.691406 L 176.261719 185.164062 L 177.289062 184.902344 C 174.25 172.957031 173.074219 163.175781 173.078125 155.199219 C 173.082031 141.351562 176.597656 132.953125 180.035156 128.007812 C 181.757812 125.535156 183.464844 123.917969 184.730469 122.929688 C 185.363281 122.4375 185.882812 122.097656 186.238281 121.886719 C 186.417969 121.78125 186.550781 121.707031 186.640625 121.660156 C 186.683594 121.636719 186.71875 121.621094 186.734375 121.613281 L 186.753906 121.605469 L 186.753906 121.601562 L 186.5 121.050781 L 186.75 121.605469 L 186.753906 121.601562 L 186.5 121.050781 L 186.75 121.605469 L 186.3125 120.640625 L 186.3125 121.699219 L 186.582031 121.699219 L 186.582031 120.640625 L 186.140625 121.605469 L 186.582031 120.640625 L 186.582031 119.582031 L 186.3125 119.582031 C 186.164062 119.582031 186.007812 119.617188 185.875 119.679688 C 185.742188 119.734375 182 121.476562 178.296875 126.796875 C 174.589844 132.117188 170.957031 140.996094 170.957031 155.199219 C 170.960938 163.382812 172.160156 173.339844 175.234375 185.425781 C 175.253906 185.5 175.28125 185.570312 175.316406 185.636719 C 175.367188 185.75 176.011719 186.984375 177.71875 188.195312 C 179.417969 189.410156 182.175781 190.5625 186.3125 190.558594 L 186.582031 190.558594 C 190.714844 190.5625 193.476562 189.410156 195.175781 188.195312 C 196.878906 186.984375 197.523438 185.75 197.578125 185.636719 C 197.613281 185.570312 197.640625 185.5 197.65625 185.425781 C 200.734375 173.339844 201.933594 163.382812 201.933594 155.199219 C 201.9375 140.996094 198.300781 132.117188 194.59375 126.796875 C 190.890625 121.476562 187.148438 119.734375 187.019531 119.679688 C 186.886719 119.617188 186.726562 119.582031 186.582031 119.582031 L 186.582031 120.640625 " fill-opacity="1" fill-rule="nonzero"/><path fill="#ffffff" d="M 186.3125 120.640625 C 181.777344 123.351562 178.625 127.820312 176.347656 132.347656 C 176.347656 132.347656 188.722656 136.957031 196.21875 131.667969 C 195.710938 130.625 195.152344 129.609375 194.539062 128.628906 C 193.066406 126.28125 191.277344 124.101562 189.125 122.347656 C 188.339844 121.707031 187.5 121.085938 186.582031 120.640625 " fill-opacity="1" fill-rule="nonzero"/><path fill="#4c44cb" d="M 185.769531 119.734375 C 183.371094 121.164062 181.363281 123.046875 179.660156 125.144531 C 177.957031 127.246094 176.566406 129.558594 175.402344 131.871094 C 175.269531 132.140625 175.25 132.453125 175.363281 132.734375 C 175.472656 133.015625 175.695312 133.234375 175.976562 133.339844 C 176.011719 133.351562 177.296875 133.828125 179.292969 134.308594 C 181.296875 134.785156 184.011719 135.265625 186.910156 135.265625 C 188.574219 135.265625 190.304688 135.109375 191.996094 134.6875 C 193.679688 134.269531 195.339844 133.585938 196.832031 132.53125 C 197.253906 132.230469 197.398438 131.671875 197.171875 131.203125 C 196.648438 130.128906 196.070312 129.082031 195.433594 128.066406 C 193.917969 125.640625 192.058594 123.371094 189.796875 121.527344 C 188.980469 120.863281 188.078125 120.191406 187.042969 119.6875 C 186.511719 119.433594 185.878906 119.65625 185.625 120.183594 C 185.371094 120.710938 185.59375 121.339844 186.121094 121.59375 C 186.917969 121.976562 187.699219 122.546875 188.457031 123.164062 C 190.5 124.828125 192.21875 126.921875 193.640625 129.1875 L 193.636719 129.1875 C 194.234375 130.136719 194.777344 131.121094 195.269531 132.132812 L 196.21875 131.667969 L 195.609375 130.804688 C 194.367188 131.679688 192.964844 132.265625 191.484375 132.632812 C 190 133.003906 188.441406 133.152344 186.910156 133.148438 C 184.246094 133.152344 181.679688 132.703125 179.789062 132.25 C 178.84375 132.023438 178.070312 131.796875 177.535156 131.628906 C 177.265625 131.542969 177.058594 131.472656 176.921875 131.425781 C 176.851562 131.402344 176.800781 131.382812 176.765625 131.371094 L 176.726562 131.359375 L 176.71875 131.355469 L 176.347656 132.347656 L 177.292969 132.820312 C 178.40625 130.609375 179.726562 128.425781 181.304688 126.480469 C 182.882812 124.535156 184.714844 122.828125 186.855469 121.550781 C 187.355469 121.25 187.519531 120.601562 187.21875 120.097656 C 186.921875 119.597656 186.269531 119.433594 185.769531 119.734375 " fill-opacity="1" fill-rule="nonzero"/><path fill="#ffffff" d="M 173.035156 169.011719 C 173.035156 169.011719 156.296875 176.589844 165.527344 197.429688 C 165.527344 197.429688 165.996094 186.835938 176.046875 184.324219 C 176.046875 184.324219 173.890625 175.25 173.035156 169.011719 " fill-opacity="1" fill-rule="nonzero"/><path fill="#4c44cb" d="M 173.035156 169.011719 L 172.597656 168.042969 C 172.519531 168.082031 169.84375 169.296875 167.128906 172.113281 C 165.777344 173.515625 164.414062 175.328125 163.390625 177.585938 C 162.367188 179.847656 161.691406 182.558594 161.691406 185.71875 C 161.691406 189.210938 162.511719 193.242188 164.554688 197.859375 C 164.757812 198.308594 165.238281 198.5625 165.71875 198.472656 C 166.203125 198.378906 166.558594 197.96875 166.582031 197.476562 L 166.546875 197.476562 L 166.582031 197.476562 L 166.546875 197.476562 L 166.582031 197.476562 C 166.582031 197.398438 166.734375 194.863281 168.039062 192.136719 C 168.6875 190.769531 169.613281 189.359375 170.941406 188.152344 C 172.269531 186.945312 173.996094 185.929688 176.304688 185.351562 C 176.863281 185.210938 177.210938 184.644531 177.074219 184.082031 C 177.074219 184.082031 177.039062 183.945312 176.984375 183.683594 C 176.773438 182.78125 176.230469 180.421875 175.652344 177.617188 C 175.070312 174.8125 174.453125 171.554688 174.082031 168.867188 C 174.039062 168.535156 173.839844 168.246094 173.546875 168.082031 C 173.257812 167.925781 172.902344 167.90625 172.597656 168.042969 L 173.035156 169.011719 L 171.988281 169.152344 C 172.421875 172.324219 173.175781 176.164062 173.828125 179.226562 C 174.476562 182.289062 175.015625 184.566406 175.015625 184.570312 L 176.046875 184.324219 L 175.789062 183.296875 C 173.105469 183.96875 171.023438 185.195312 169.441406 186.65625 C 167.074219 188.847656 165.839844 191.523438 165.191406 193.632812 C 164.542969 195.75 164.472656 197.320312 164.46875 197.382812 L 165.527344 197.429688 L 166.492188 197.003906 C 164.546875 192.613281 163.808594 188.875 163.808594 185.71875 C 163.808594 182.859375 164.414062 180.464844 165.320312 178.460938 C 166.679688 175.457031 168.730469 173.320312 170.441406 171.941406 C 171.300781 171.253906 172.074219 170.753906 172.625 170.429688 C 172.898438 170.269531 173.117188 170.152344 173.265625 170.078125 C 173.339844 170.039062 173.390625 170.007812 173.429688 169.996094 L 173.464844 169.976562 L 173.472656 169.972656 L 173.378906 169.769531 L 173.472656 169.972656 L 173.378906 169.769531 L 173.472656 169.972656 L 173.035156 169.011719 L 171.988281 169.152344 L 173.035156 169.011719 " fill-opacity="1" fill-rule="nonzero"/><path fill="#ffffff" d="M 199.9375 168.421875 C 199.9375 168.421875 217.230469 176.589844 208.003906 197.429688 C 208.003906 197.429688 206.9375 186.664062 196.886719 184.15625 C 196.886719 184.15625 199.054688 176.238281 199.9375 168.421875 " fill-opacity="1" fill-rule="nonzero"/><path fill="#4c44cb" d="M 199.9375 168.421875 L 199.484375 169.382812 L 199.699219 168.929688 L 199.484375 169.378906 L 199.484375 169.382812 L 199.699219 168.929688 L 199.484375 169.378906 C 199.492188 169.382812 202.082031 170.625 204.621094 173.3125 C 205.890625 174.652344 207.148438 176.351562 208.089844 178.433594 C 209.03125 180.519531 209.652344 182.984375 209.652344 185.910156 C 209.652344 189.046875 208.925781 192.722656 207.035156 197.003906 L 208.003906 197.429688 L 209.058594 197.324219 C 209.046875 197.246094 208.769531 194.445312 207.15625 191.257812 C 206.351562 189.664062 205.203125 187.96875 203.574219 186.5 C 201.941406 185.035156 199.824219 183.796875 197.140625 183.128906 L 196.886719 184.15625 L 197.90625 184.433594 C 197.914062 184.40625 200.089844 176.464844 200.988281 168.542969 L 199.9375 168.421875 L 199.484375 169.382812 L 199.9375 168.421875 L 198.882812 168.304688 C 198.449219 172.15625 197.695312 176.058594 197.046875 178.992188 C 196.726562 180.457031 196.429688 181.683594 196.214844 182.539062 C 196.105469 182.96875 196.019531 183.300781 195.957031 183.53125 C 195.929688 183.644531 195.90625 183.730469 195.890625 183.789062 C 195.875 183.847656 195.863281 183.875 195.863281 183.875 C 195.789062 184.152344 195.828125 184.441406 195.972656 184.6875 C 196.117188 184.933594 196.351562 185.113281 196.628906 185.183594 C 198.972656 185.769531 200.757812 186.820312 202.152344 188.074219 C 204.246094 189.957031 205.464844 192.328125 206.136719 194.246094 C 206.476562 195.199219 206.683594 196.039062 206.804688 196.628906 C 206.859375 196.925781 206.898438 197.160156 206.921875 197.3125 C 206.933594 197.390625 206.941406 197.453125 206.945312 197.488281 L 206.949219 197.53125 L 206.953125 197.535156 L 207.199219 197.511719 L 206.953125 197.535156 L 207.199219 197.511719 L 206.953125 197.535156 C 206.996094 198.011719 207.363281 198.402344 207.835938 198.476562 C 208.308594 198.550781 208.78125 198.296875 208.972656 197.859375 C 210.964844 193.355469 211.773438 189.378906 211.773438 185.910156 C 211.773438 182.65625 211.0625 179.855469 209.992188 177.507812 C 208.386719 173.984375 206 171.488281 204.015625 169.867188 C 202.03125 168.246094 200.441406 167.492188 200.386719 167.464844 C 200.078125 167.320312 199.71875 167.332031 199.421875 167.5 C 199.121094 167.667969 198.921875 167.964844 198.882812 168.304688 L 199.9375 168.421875 " fill-opacity="1" fill-rule="nonzero"/><path fill="#ffffff" d="M 186.582031 120.640625 C 186.582031 120.640625 210.292969 131.441406 196.632812 185.164062 C 196.632812 185.164062 194.464844 189.5 186.582031 189.5 L 186.3125 189.5 C 178.429688 189.5 176.261719 185.164062 176.261719 185.164062 C 162.601562 131.441406 186.3125 120.640625 186.3125 120.640625 L 186.582031 120.640625 " fill-opacity="1" fill-rule="nonzero"/><path fill="#4c44cb" d="M 186.582031 120.640625 L 186.140625 121.605469 L 186.390625 121.058594 L 186.136719 121.601562 L 186.140625 121.605469 L 186.390625 121.058594 L 186.136719 121.601562 L 186.171875 121.621094 C 186.558594 121.8125 189.953125 123.585938 193.261719 128.597656 C 196.566406 133.613281 199.8125 141.890625 199.816406 155.199219 C 199.816406 163.175781 198.644531 172.957031 195.605469 184.902344 L 196.632812 185.164062 L 195.683594 184.691406 L 196.09375 184.894531 L 195.691406 184.679688 L 195.683594 184.691406 L 196.09375 184.894531 L 195.691406 184.679688 C 195.679688 184.699219 195.1875 185.628906 193.820312 186.558594 C 192.453125 187.484375 190.210938 188.4375 186.582031 188.441406 L 186.3125 188.441406 C 182.566406 188.4375 180.296875 187.425781 178.941406 186.46875 C 178.269531 185.988281 177.820312 185.515625 177.546875 185.179688 C 177.410156 185.007812 177.320312 184.875 177.265625 184.789062 C 177.238281 184.746094 177.21875 184.714844 177.210938 184.695312 L 177.203125 184.679688 L 176.796875 184.898438 L 177.207031 184.691406 L 177.203125 184.679688 L 176.796875 184.898438 L 177.207031 184.691406 L 176.261719 185.164062 L 177.289062 184.902344 C 174.25 172.957031 173.074219 163.175781 173.078125 155.199219 C 173.082031 141.351562 176.597656 132.953125 180.035156 128.007812 C 181.757812 125.535156 183.464844 123.917969 184.730469 122.929688 C 185.363281 122.4375 185.882812 122.097656 186.238281 121.886719 C 186.417969 121.78125 186.550781 121.707031 186.640625 121.660156 C 186.683594 121.636719 186.71875 121.621094 186.734375 121.613281 L 186.753906 121.605469 L 186.753906 121.601562 L 186.5 121.050781 L 186.75 121.605469 L 186.753906 121.601562 L 186.5 121.050781 L 186.75 121.605469 L 186.3125 120.640625 L 186.3125 121.699219 L 186.582031 121.699219 L 186.582031 120.640625 L 186.140625 121.605469 L 186.582031 120.640625 L 186.582031 119.582031 L 186.3125 119.582031 C 186.164062 119.582031 186.007812 119.617188 185.875 119.679688 C 185.742188 119.734375 182 121.476562 178.296875 126.796875 C 174.589844 132.117188 170.957031 140.996094 170.957031 155.199219 C 170.960938 163.382812 172.160156 173.339844 175.234375 185.425781 C 175.253906 185.5 175.28125 185.570312 175.316406 185.636719 C 175.367188 185.75 176.011719 186.984375 177.71875 188.195312 C 179.417969 189.410156 182.175781 190.5625 186.3125 190.558594 L 186.582031 190.558594 C 190.714844 190.5625 193.476562 189.410156 195.175781 188.195312 C 196.878906 186.984375 197.523438 185.75 197.578125 185.636719 C 197.613281 185.570312 197.640625 185.5 197.65625 185.425781 C 200.734375 173.339844 201.933594 163.382812 201.933594 155.199219 C 201.9375 140.996094 198.300781 132.117188 194.59375 126.796875 C 190.890625 121.476562 187.148438 119.734375 187.019531 119.679688 C 186.886719 119.617188 186.726562 119.582031 186.582031 119.582031 L 186.582031 120.640625 " fill-opacity="1" fill-rule="nonzero"/><path fill="#ffffff" d="M 186.3125 120.640625 C 181.777344 123.351562 178.625 127.820312 176.347656 132.347656 C 176.347656 132.347656 188.722656 136.957031 196.21875 131.667969 C 195.710938 130.625 195.152344 129.609375 194.539062 128.628906 C 193.066406 126.28125 191.277344 124.101562 189.125 122.347656 C 188.339844 121.707031 187.5 121.085938 186.582031 120.640625 " fill-opacity="1" fill-rule="nonzero"/><path fill="#4c44cb" d="M 185.769531 119.734375 C 183.371094 121.164062 181.363281 123.046875 179.660156 125.144531 C 177.957031 127.246094 176.566406 129.558594 175.402344 131.871094 C 175.269531 132.140625 175.25 132.453125 175.363281 132.734375 C 175.472656 133.015625 175.695312 133.234375 175.976562 133.339844 C 176.011719 133.351562 177.296875 133.828125 179.292969 134.308594 C 181.296875 134.785156 184.011719 135.265625 186.910156 135.265625 C 188.574219 135.265625 190.304688 135.109375 191.996094 134.6875 C 193.679688 134.269531 195.339844 133.585938 196.832031 132.53125 C 197.253906 132.230469 197.398438 131.671875 197.171875 131.203125 C 196.648438 130.128906 196.070312 129.082031 195.433594 128.066406 C 193.917969 125.640625 192.058594 123.371094 189.796875 121.527344 C 188.980469 120.863281 188.078125 120.191406 187.042969 119.6875 C 186.511719 119.433594 185.878906 119.65625 185.625 120.183594 C 185.371094 120.710938 185.59375 121.339844 186.121094 121.59375 C 186.917969 121.976562 187.699219 122.546875 188.457031 123.164062 C 190.5 124.828125 192.21875 126.921875 193.640625 129.1875 L 193.636719 129.1875 C 194.234375 130.136719 194.777344 131.121094 195.269531 132.132812 L 196.21875 131.667969 L 195.609375 130.804688 C 194.367188 131.679688 192.964844 132.265625 191.484375 132.632812 C 190 133.003906 188.441406 133.152344 186.910156 133.148438 C 184.246094 133.152344 181.679688 132.703125 179.789062 132.25 C 178.84375 132.023438 178.070312 131.796875 177.535156 131.628906 C 177.265625 131.542969 177.058594 131.472656 176.921875 131.425781 C 176.851562 131.402344 176.800781 131.382812 176.765625 131.371094 L 176.726562 131.359375 L 176.71875 131.355469 L 176.347656 132.347656 L 177.292969 132.820312 C 178.40625 130.609375 179.726562 128.425781 181.304688 126.480469 C 182.882812 124.535156 184.714844 122.828125 186.855469 121.550781 C 187.355469 121.25 187.519531 120.601562 187.21875 120.097656 C 186.921875 119.597656 186.269531 119.433594 185.769531 119.734375 " fill-opacity="1" fill-rule="nonzero"/><path fill="#ffffff" d="M 196.441406 150.234375 C 196.441406 155.753906 191.964844 160.230469 186.445312 160.230469 C 180.925781 160.230469 176.449219 155.753906 176.449219 150.234375 C 176.449219 144.710938 180.925781 140.238281 186.445312 140.238281 C 191.964844 140.238281 196.441406 144.710938 196.441406 150.234375 " fill-opacity="1" fill-rule="nonzero"/><path fill="#4c44cb" d="M 196.441406 150.234375 L 195.382812 150.234375 C 195.382812 152.707031 194.386719 154.933594 192.765625 156.554688 C 191.144531 158.171875 188.917969 159.171875 186.445312 159.171875 C 183.976562 159.171875 181.746094 158.171875 180.128906 156.554688 C 178.507812 154.933594 177.507812 152.707031 177.507812 150.234375 C 177.507812 147.761719 178.507812 145.535156 180.128906 143.914062 C 181.746094 142.296875 183.976562 141.296875 186.445312 141.296875 C 188.917969 141.296875 191.144531 142.296875 192.765625 143.914062 C 194.386719 145.535156 195.382812 147.761719 195.382812 150.234375 L 197.5 150.234375 C 197.5 147.183594 196.261719 144.414062 194.261719 142.417969 C 192.265625 140.417969 189.496094 139.179688 186.445312 139.179688 C 183.398438 139.179688 180.628906 140.417969 178.632812 142.417969 C 176.632812 144.414062 175.394531 147.183594 175.394531 150.234375 C 175.394531 153.285156 176.632812 156.054688 178.632812 158.050781 C 180.628906 160.050781 183.398438 161.289062 186.445312 161.289062 C 189.496094 161.289062 192.265625 160.050781 194.261719 158.050781 C 196.261719 156.054688 197.5 153.285156 197.5 150.234375 L 196.441406 150.234375 " fill-opacity="1" fill-rule="nonzero"/><path fill="#ffffff" d="M 173.035156 169.011719 C 173.035156 169.011719 156.296875 176.589844 165.527344 197.429688 C 165.527344 197.429688 165.996094 186.835938 176.046875 184.324219 C 176.046875 184.324219 173.890625 175.25 173.035156 169.011719 " fill-opacity="1" fill-rule="nonzero"/><path fill="#4c44cb" d="M 173.035156 169.011719 L 172.597656 168.042969 C 172.519531 168.082031 169.84375 169.296875 167.128906 172.113281 C 165.777344 173.515625 164.414062 175.328125 163.390625 177.585938 C 162.367188 179.847656 161.691406 182.558594 161.691406 185.71875 C 161.691406 189.210938 162.511719 193.242188 164.554688 197.859375 C 164.757812 198.308594 165.238281 198.5625 165.71875 198.472656 C 166.203125 198.378906 166.558594 197.96875 166.582031 197.476562 L 166.546875 197.476562 L 166.582031 197.476562 L 166.546875 197.476562 L 166.582031 197.476562 C 166.582031 197.398438 166.734375 194.863281 168.039062 192.136719 C 168.6875 190.769531 169.613281 189.359375 170.941406 188.152344 C 172.269531 186.945312 173.996094 185.929688 176.304688 185.351562 C 176.863281 185.210938 177.210938 184.644531 177.074219 184.082031 C 177.074219 184.082031 177.039062 183.945312 176.984375 183.683594 C 176.773438 182.78125 176.230469 180.421875 175.652344 177.617188 C 175.070312 174.8125 174.453125 171.554688 174.082031 168.867188 C 174.039062 168.535156 173.839844 168.246094 173.546875 168.082031 C 173.257812 167.925781 172.902344 167.90625 172.597656 168.042969 L 173.035156 169.011719 L 171.988281 169.152344 C 172.421875 172.324219 173.175781 176.164062 173.828125 179.226562 C 174.476562 182.289062 175.015625 184.566406 175.015625 184.570312 L 176.046875 184.324219 L 175.789062 183.296875 C 173.105469 183.96875 171.023438 185.195312 169.441406 186.65625 C 167.074219 188.847656 165.839844 191.523438 165.191406 193.632812 C 164.542969 195.75 164.472656 197.320312 164.46875 197.382812 L 165.527344 197.429688 L 166.492188 197.003906 C 164.546875 192.613281 163.808594 188.875 163.808594 185.71875 C 163.808594 182.859375 164.414062 180.464844 165.320312 178.460938 C 166.679688 175.457031 168.730469 173.320312 170.441406 171.941406 C 171.300781 171.253906 172.074219 170.753906 172.625 170.429688 C 172.898438 170.269531 173.117188 170.152344 173.265625 170.078125 C 173.339844 170.039062 173.390625 170.007812 173.429688 169.996094 L 173.464844 169.976562 L 173.472656 169.972656 L 173.378906 169.769531 L 173.472656 169.972656 L 173.378906 169.769531 L 173.472656 169.972656 L 173.035156 169.011719 L 171.988281 169.152344 L 173.035156 169.011719 " fill-opacity="1" fill-rule="nonzero"/><path fill="#ffffff" d="M 199.9375 168.421875 C 199.9375 168.421875 217.230469 176.589844 208.003906 197.429688 C 208.003906 197.429688 206.9375 186.664062 196.886719 184.15625 C 196.886719 184.15625 199.054688 176.238281 199.9375 168.421875 " fill-opacity="1" fill-rule="nonzero"/><path fill="#4c44cb" d="M 199.9375 168.421875 L 199.484375 169.382812 L 199.699219 168.929688 L 199.484375 169.378906 L 199.484375 169.382812 L 199.699219 168.929688 L 199.484375 169.378906 C 199.492188 169.382812 202.082031 170.625 204.621094 173.3125 C 205.890625 174.652344 207.148438 176.351562 208.089844 178.433594 C 209.03125 180.519531 209.652344 182.984375 209.652344 185.910156 C 209.652344 189.046875 208.925781 192.722656 207.035156 197.003906 L 208.003906 197.429688 L 209.058594 197.324219 C 209.046875 197.246094 208.769531 194.445312 207.15625 191.257812 C 206.351562 189.664062 205.203125 187.96875 203.574219 186.5 C 201.941406 185.035156 199.824219 183.796875 197.140625 183.128906 L 196.886719 184.15625 L 197.90625 184.433594 C 197.914062 184.40625 200.089844 176.464844 200.988281 168.542969 L 199.9375 168.421875 L 199.484375 169.382812 L 199.9375 168.421875 L 198.882812 168.304688 C 198.449219 172.15625 197.695312 176.058594 197.046875 178.992188 C 196.726562 180.457031 196.429688 181.683594 196.214844 182.539062 C 196.105469 182.96875 196.019531 183.300781 195.957031 183.53125 C 195.929688 183.644531 195.90625 183.730469 195.890625 183.789062 C 195.875 183.847656 195.863281 183.875 195.863281 183.875 C 195.789062 184.152344 195.828125 184.441406 195.972656 184.6875 C 196.117188 184.933594 196.351562 185.113281 196.628906 185.183594 C 198.972656 185.769531 200.757812 186.820312 202.152344 188.074219 C 204.246094 189.957031 205.464844 192.328125 206.136719 194.246094 C 206.476562 195.199219 206.683594 196.039062 206.804688 196.628906 C 206.859375 196.925781 206.898438 197.160156 206.921875 197.3125 C 206.933594 197.390625 206.941406 197.453125 206.945312 197.488281 L 206.949219 197.53125 L 206.953125 197.535156 L 207.199219 197.511719 L 206.953125 197.535156 L 207.199219 197.511719 L 206.953125 197.535156 C 206.996094 198.011719 207.363281 198.402344 207.835938 198.476562 C 208.308594 198.550781 208.78125 198.296875 208.972656 197.859375 C 210.964844 193.355469 211.773438 189.378906 211.773438 185.910156 C 211.773438 182.65625 211.0625 179.855469 209.992188 177.507812 C 208.386719 173.984375 206 171.488281 204.015625 169.867188 C 202.03125 168.246094 200.441406 167.492188 200.386719 167.464844 C 200.078125 167.320312 199.71875 167.332031 199.421875 167.5 C 199.121094 167.667969 198.921875 167.964844 198.882812 168.304688 L 199.9375 168.421875 " fill-opacity="1" fill-rule="nonzero"/><path fill="#ffffff" d="M 193.109375 150.234375 C 193.109375 153.914062 190.128906 156.898438 186.445312 156.898438 C 182.765625 156.898438 179.785156 153.914062 179.785156 150.234375 C 179.785156 146.554688 182.765625 143.570312 186.445312 143.570312 C 190.128906 143.570312 193.109375 146.554688 193.109375 150.234375 " fill-opacity="1" fill-rule="nonzero"/><path fill="#4c44cb" d="M 193.109375 150.234375 L 192.050781 150.234375 C 192.050781 151.785156 191.425781 153.179688 190.410156 154.199219 C 189.390625 155.214844 187.996094 155.839844 186.445312 155.839844 C 184.898438 155.839844 183.503906 155.214844 182.484375 154.199219 C 181.46875 153.179688 180.84375 151.785156 180.84375 150.234375 C 180.84375 148.683594 181.46875 147.289062 182.484375 146.269531 C 183.503906 145.253906 184.898438 144.628906 186.445312 144.628906 C 187.996094 144.628906 189.390625 145.257812 190.410156 146.269531 C 191.425781 147.289062 192.050781 148.683594 192.050781 150.234375 L 194.167969 150.234375 C 194.167969 148.105469 193.304688 146.167969 191.90625 144.773438 C 190.511719 143.378906 188.574219 142.511719 186.445312 142.511719 C 184.316406 142.511719 182.382812 143.378906 180.988281 144.773438 C 179.589844 146.167969 178.722656 148.105469 178.726562 150.234375 C 178.722656 152.363281 179.589844 154.300781 180.988281 155.695312 C 182.382812 157.089844 184.316406 157.953125 186.445312 157.953125 C 188.574219 157.953125 190.511719 157.089844 191.90625 155.695312 C 193.304688 154.300781 194.167969 152.363281 194.167969 150.234375 L 193.109375 150.234375 " fill-opacity="1" fill-rule="nonzero"/><path fill="#ffffff" d="M 193.109375 150.234375 C 193.109375 153.914062 190.128906 156.898438 186.445312 156.898438 C 182.765625 156.898438 179.785156 153.914062 179.785156 150.234375 C 179.785156 146.554688 182.765625 143.570312 186.445312 143.570312 C 190.128906 143.570312 193.109375 146.554688 193.109375 150.234375 " fill-opacity="1" fill-rule="nonzero"/><path fill="#4c44cb" d="M 193.109375 150.234375 L 192.050781 150.234375 C 192.050781 151.785156 191.425781 153.179688 190.410156 154.199219 C 189.390625 155.214844 187.996094 155.839844 186.445312 155.839844 C 184.898438 155.839844 183.503906 155.214844 182.484375 154.199219 C 181.46875 153.179688 180.84375 151.785156 180.84375 150.234375 C 180.84375 148.683594 181.46875 147.289062 182.484375 146.269531 C 183.503906 145.253906 184.898438 144.628906 186.445312 144.628906 C 187.996094 144.628906 189.390625 145.257812 190.410156 146.269531 C 191.425781 147.289062 192.050781 148.683594 192.050781 150.234375 L 194.167969 150.234375 C 194.167969 148.105469 193.304688 146.167969 191.90625 144.773438 C 190.511719 143.378906 188.574219 142.511719 186.445312 142.511719 C 184.316406 142.511719 182.382812 143.378906 180.988281 144.773438 C 179.589844 146.167969 178.722656 148.105469 178.726562 150.234375 C 178.722656 152.363281 179.589844 154.300781 180.988281 155.695312 C 182.382812 157.089844 184.316406 157.953125 186.445312 157.953125 C 188.574219 157.953125 190.511719 157.089844 191.90625 155.695312 C 193.304688 154.300781 194.167969 152.363281 194.167969 150.234375 L 193.109375 150.234375 " fill-opacity="1" fill-rule="nonzero"/><path fill="#ffffff" d="M 197.230469 182.75 C 197.039062 183.542969 196.839844 184.347656 196.632812 185.164062 C 196.632812 185.164062 194.464844 189.5 186.582031 189.5 L 186.3125 189.5 C 178.429688 189.5 176.261719 185.164062 176.261719 185.164062 C 176.050781 184.347656 175.855469 183.542969 175.664062 182.75 C 175.664062 182.75 185.40625 186.84375 197.230469 182.75 " fill-opacity="1" fill-rule="nonzero"/><path fill="#4c44cb" d="M 197.230469 182.75 L 196.199219 182.5 C 196.007812 183.292969 195.8125 184.09375 195.605469 184.902344 L 196.632812 185.164062 L 195.683594 184.691406 L 196.09375 184.894531 L 195.691406 184.679688 L 195.683594 184.691406 L 196.09375 184.894531 L 195.691406 184.679688 C 195.679688 184.699219 195.1875 185.628906 193.820312 186.558594 C 192.453125 187.484375 190.210938 188.4375 186.582031 188.441406 L 186.3125 188.441406 C 182.566406 188.4375 180.296875 187.425781 178.941406 186.46875 C 178.269531 185.988281 177.820312 185.515625 177.546875 185.179688 C 177.410156 185.007812 177.320312 184.875 177.265625 184.789062 C 177.238281 184.746094 177.21875 184.714844 177.210938 184.695312 L 177.203125 184.679688 L 176.796875 184.898438 L 177.207031 184.691406 L 177.203125 184.679688 L 176.796875 184.898438 L 177.207031 184.691406 L 176.261719 185.164062 L 177.289062 184.902344 C 177.078125 184.09375 176.886719 183.292969 176.695312 182.5 L 175.664062 182.75 L 175.253906 183.722656 C 175.339844 183.757812 179.792969 185.625 186.382812 185.625 C 189.675781 185.625 193.507812 185.15625 197.574219 183.746094 L 197.230469 182.75 L 196.199219 182.5 L 197.230469 182.75 L 196.882812 181.746094 C 193.066406 183.070312 189.480469 183.511719 186.382812 183.511719 C 183.289062 183.511719 180.691406 183.070312 178.878906 182.628906 C 177.972656 182.410156 177.257812 182.191406 176.777344 182.027344 C 176.539062 181.949219 176.359375 181.878906 176.238281 181.839844 C 176.179688 181.8125 176.136719 181.796875 176.109375 181.785156 L 176.082031 181.773438 L 176.074219 181.769531 L 175.976562 182.011719 L 176.074219 181.769531 L 175.976562 182.011719 L 176.074219 181.769531 C 175.707031 181.621094 175.285156 181.683594 174.980469 181.941406 C 174.675781 182.199219 174.542969 182.605469 174.632812 182.992188 C 174.828125 183.792969 175.027344 184.605469 175.234375 185.425781 C 175.253906 185.5 175.28125 185.570312 175.316406 185.636719 C 175.367188 185.75 176.011719 186.984375 177.71875 188.195312 C 179.417969 189.410156 182.175781 190.5625 186.3125 190.558594 L 186.582031 190.558594 C 190.714844 190.5625 193.476562 189.410156 195.175781 188.195312 C 196.878906 186.984375 197.523438 185.75 197.578125 185.636719 C 197.613281 185.570312 197.640625 185.5 197.65625 185.425781 C 197.863281 184.605469 198.066406 183.792969 198.257812 182.992188 C 198.347656 182.617188 198.226562 182.222656 197.9375 181.960938 C 197.652344 181.699219 197.246094 181.621094 196.882812 181.746094 L 197.230469 182.75 " fill-opacity="1" fill-rule="nonzero"/><path fill="#ffffff" d="M 187.519531 168.023438 L 185.371094 168.023438 C 185.371094 168.023438 183.441406 168.15625 183.722656 171.570312 L 185.277344 199.953125 C 185.277344 199.953125 185.601562 201.660156 186.445312 201.398438 C 187.292969 201.660156 187.617188 199.953125 187.617188 199.953125 L 189.167969 171.570312 C 189.449219 168.15625 187.519531 168.023438 187.519531 168.023438 " fill-opacity="1" fill-rule="nonzero"/><path fill="#4c44cb" d="M 187.519531 168.023438 L 187.519531 166.964844 L 185.371094 166.964844 L 185.300781 166.96875 C 185.226562 166.976562 185.035156 166.992188 184.765625 167.085938 C 184.570312 167.15625 184.324219 167.265625 184.074219 167.445312 C 183.703125 167.707031 183.320312 168.128906 183.054688 168.710938 C 182.789062 169.292969 182.636719 170.019531 182.640625 170.921875 C 182.640625 171.15625 182.644531 171.398438 182.671875 171.664062 L 183.722656 171.570312 L 182.667969 171.632812 L 184.21875 200.011719 C 184.222656 200.054688 184.230469 200.109375 184.238281 200.152344 C 184.246094 200.195312 184.320312 200.613281 184.535156 201.109375 C 184.644531 201.359375 184.789062 201.640625 185.039062 201.914062 C 185.164062 202.046875 185.324219 202.1875 185.53125 202.300781 C 185.738281 202.410156 186 202.484375 186.273438 202.484375 C 186.441406 202.484375 186.609375 202.457031 186.753906 202.410156 L 186.445312 201.398438 L 186.132812 202.410156 C 186.285156 202.457031 186.453125 202.484375 186.621094 202.484375 C 186.894531 202.484375 187.152344 202.410156 187.363281 202.300781 C 187.730469 202.101562 187.9375 201.839844 188.097656 201.605469 C 188.324219 201.246094 188.445312 200.910156 188.53125 200.636719 C 188.613281 200.371094 188.648438 200.179688 188.65625 200.152344 C 188.664062 200.109375 188.671875 200.054688 188.675781 200.011719 L 190.226562 171.632812 L 189.167969 171.570312 L 190.222656 171.660156 C 190.246094 171.398438 190.253906 171.15625 190.253906 170.921875 C 190.253906 170.121094 190.136719 169.457031 189.921875 168.910156 C 189.761719 168.5 189.546875 168.15625 189.308594 167.890625 C 188.949219 167.484375 188.546875 167.246094 188.230469 167.125 C 187.910156 167 187.671875 166.976562 187.589844 166.96875 L 187.519531 166.964844 L 187.519531 168.023438 L 187.445312 169.082031 L 187.480469 168.652344 L 187.398438 169.078125 C 187.398438 169.078125 187.410156 169.078125 187.445312 169.082031 L 187.480469 168.652344 L 187.398438 169.078125 C 187.398438 169.078125 187.445312 169.085938 187.503906 169.113281 C 187.589844 169.164062 187.714844 169.242188 187.851562 169.476562 C 187.988281 169.714844 188.136719 170.148438 188.136719 170.921875 C 188.136719 171.09375 188.132812 171.28125 188.113281 171.488281 L 188.113281 171.515625 L 186.558594 199.898438 L 187.617188 199.953125 L 186.574219 199.753906 C 186.570312 199.792969 186.5 200.101562 186.394531 200.3125 C 186.371094 200.367188 186.347656 200.40625 186.328125 200.441406 L 186.300781 200.480469 L 186.304688 200.480469 L 186.300781 200.480469 L 186.304688 200.480469 L 186.300781 200.480469 L 186.527344 200.703125 L 186.390625 200.421875 C 186.328125 200.445312 186.304688 200.476562 186.300781 200.480469 L 186.527344 200.703125 L 186.390625 200.421875 L 186.621094 200.898438 L 186.621094 200.371094 C 186.535156 200.367188 186.441406 200.390625 186.390625 200.421875 L 186.621094 200.898438 L 186.621094 200.371094 L 186.621094 200.84375 L 186.757812 200.390625 C 186.726562 200.378906 186.675781 200.371094 186.621094 200.371094 L 186.621094 200.84375 L 186.757812 200.390625 C 186.558594 200.328125 186.339844 200.324219 186.136719 200.386719 L 186.273438 200.832031 L 186.273438 200.371094 C 186.214844 200.371094 186.167969 200.378906 186.136719 200.386719 L 186.273438 200.832031 L 186.273438 200.371094 L 186.273438 200.890625 L 186.519531 200.429688 C 186.460938 200.398438 186.367188 200.367188 186.273438 200.371094 L 186.273438 200.890625 L 186.519531 200.429688 L 186.382812 200.6875 L 186.59375 200.480469 C 186.589844 200.476562 186.570312 200.453125 186.519531 200.429688 L 186.382812 200.6875 L 186.59375 200.480469 L 186.585938 200.484375 L 186.59375 200.480469 L 186.585938 200.484375 L 186.59375 200.480469 C 186.589844 200.476562 186.550781 200.421875 186.511719 200.34375 C 186.457031 200.226562 186.398438 200.066406 186.363281 199.945312 C 186.347656 199.882812 186.332031 199.828125 186.324219 199.792969 L 186.316406 199.757812 L 186.316406 199.753906 L 185.882812 199.839844 L 186.316406 199.753906 L 185.882812 199.839844 L 186.316406 199.753906 L 185.277344 199.953125 L 186.335938 199.898438 L 184.78125 171.515625 L 184.78125 171.484375 C 184.761719 171.28125 184.753906 171.097656 184.753906 170.921875 C 184.753906 170.332031 184.84375 169.9375 184.941406 169.679688 C 185.019531 169.488281 185.101562 169.371094 185.171875 169.289062 C 185.277344 169.167969 185.371094 169.121094 185.4375 169.097656 L 185.492188 169.078125 L 185.492188 169.074219 L 185.492188 169.078125 L 185.492188 169.074219 L 185.492188 169.078125 L 185.410156 168.570312 L 185.441406 169.082031 L 185.492188 169.078125 L 185.410156 168.570312 L 185.441406 169.082031 L 185.371094 168.023438 L 185.371094 169.082031 L 187.519531 169.082031 L 187.519531 168.023438 L 187.445312 169.082031 L 187.519531 168.023438 " fill-opacity="1" fill-rule="nonzero"/><path fill="#ffffff" d="M 187.519531 168.023438 L 185.371094 168.023438 C 185.371094 168.023438 183.441406 168.15625 183.722656 171.570312 L 185.277344 199.953125 C 185.277344 199.953125 185.601562 201.660156 186.445312 201.398438 C 187.292969 201.660156 187.617188 199.953125 187.617188 199.953125 L 189.167969 171.570312 C 189.449219 168.15625 187.519531 168.023438 187.519531 168.023438 " fill-opacity="1" fill-rule="nonzero"/><path fill="#4c44cb" d="M 187.519531 168.023438 L 187.519531 166.964844 L 185.371094 166.964844 L 185.300781 166.96875 C 185.226562 166.976562 185.035156 166.992188 184.765625 167.085938 C 184.570312 167.15625 184.324219 167.265625 184.074219 167.445312 C 183.703125 167.707031 183.320312 168.128906 183.054688 168.710938 C 182.789062 169.292969 182.636719 170.019531 182.640625 170.921875 C 182.640625 171.15625 182.644531 171.398438 182.671875 171.664062 L 183.722656 171.570312 L 182.667969 171.632812 L 184.21875 200.011719 C 184.222656 200.054688 184.230469 200.109375 184.238281 200.152344 C 184.246094 200.195312 184.320312 200.613281 184.535156 201.109375 C 184.644531 201.359375 184.789062 201.640625 185.039062 201.914062 C 185.164062 202.046875 185.324219 202.1875 185.53125 202.300781 C 185.738281 202.410156 186 202.484375 186.273438 202.484375 C 186.441406 202.484375 186.609375 202.457031 186.753906 202.410156 L 186.445312 201.398438 L 186.132812 202.410156 C 186.285156 202.457031 186.453125 202.484375 186.621094 202.484375 C 186.894531 202.484375 187.152344 202.410156 187.363281 202.300781 C 187.730469 202.101562 187.9375 201.839844 188.097656 201.605469 C 188.324219 201.246094 188.445312 200.910156 188.53125 200.636719 C 188.613281 200.371094 188.648438 200.179688 188.65625 200.152344 C 188.664062 200.109375 188.671875 200.054688 188.675781 200.011719 L 190.226562 171.632812 L 189.167969 171.570312 L 190.222656 171.660156 C 190.246094 171.398438 190.253906 171.15625 190.253906 170.921875 C 190.253906 170.121094 190.136719 169.457031 189.921875 168.910156 C 189.761719 168.5 189.546875 168.15625 189.308594 167.890625 C 188.949219 167.484375 188.546875 167.246094 188.230469 167.125 C 187.910156 167 187.671875 166.976562 187.589844 166.96875 L 187.519531 166.964844 L 187.519531 168.023438 L 187.445312 169.082031 L 187.480469 168.652344 L 187.398438 169.078125 C 187.398438 169.078125 187.410156 169.078125 187.445312 169.082031 L 187.480469 168.652344 L 187.398438 169.078125 C 187.398438 169.078125 187.445312 169.085938 187.503906 169.113281 C 187.589844 169.164062 187.714844 169.242188 187.851562 169.476562 C 187.988281 169.714844 188.136719 170.148438 188.136719 170.921875 C 188.136719 171.09375 188.132812 171.28125 188.113281 171.488281 L 188.113281 171.515625 L 186.558594 199.898438 L 187.617188 199.953125 L 186.574219 199.753906 C 186.570312 199.792969 186.5 200.101562 186.394531 200.3125 C 186.371094 200.367188 186.347656 200.40625 186.328125 200.441406 L 186.300781 200.480469 L 186.304688 200.480469 L 186.300781 200.480469 L 186.304688 200.480469 L 186.300781 200.480469 L 186.527344 200.703125 L 186.390625 200.421875 C 186.328125 200.445312 186.304688 200.476562 186.300781 200.480469 L 186.527344 200.703125 L 186.390625 200.421875 L 186.621094 200.898438 L 186.621094 200.371094 C 186.535156 200.367188 186.441406 200.390625 186.390625 200.421875 L 186.621094 200.898438 L 186.621094 200.371094 L 186.621094 200.84375 L 186.757812 200.390625 C 186.726562 200.378906 186.675781 200.371094 186.621094 200.371094 L 186.621094 200.84375 L 186.757812 200.390625 C 186.558594 200.328125 186.339844 200.324219 186.136719 200.386719 L 186.273438 200.832031 L 186.273438 200.371094 C 186.214844 200.371094 186.167969 200.378906 186.136719 200.386719 L 186.273438 200.832031 L 186.273438 200.371094 L 186.273438 200.890625 L 186.519531 200.429688 C 186.460938 200.398438 186.367188 200.367188 186.273438 200.371094 L 186.273438 200.890625 L 186.519531 200.429688 L 186.382812 200.6875 L 186.59375 200.480469 C 186.589844 200.476562 186.570312 200.453125 186.519531 200.429688 L 186.382812 200.6875 L 186.59375 200.480469 L 186.585938 200.484375 L 186.59375 200.480469 L 186.585938 200.484375 L 186.59375 200.480469 C 186.589844 200.476562 186.550781 200.421875 186.511719 200.34375 C 186.457031 200.226562 186.398438 200.066406 186.363281 199.945312 C 186.347656 199.882812 186.332031 199.828125 186.324219 199.792969 L 186.316406 199.757812 L 186.316406 199.753906 L 185.882812 199.839844 L 186.316406 199.753906 L 185.882812 199.839844 L 186.316406 199.753906 L 185.277344 199.953125 L 186.335938 199.898438 L 184.78125 171.515625 L 184.78125 171.484375 C 184.761719 171.28125 184.753906 171.097656 184.753906 170.921875 C 184.753906 170.332031 184.84375 169.9375 184.941406 169.679688 C 185.019531 169.488281 185.101562 169.371094 185.171875 169.289062 C 185.277344 169.167969 185.371094 169.121094 185.4375 169.097656 L 185.492188 169.078125 L 185.492188 169.074219 L 185.492188 169.078125 L 185.492188 169.074219 L 185.492188 169.078125 L 185.410156 168.570312 L 185.441406 169.082031 L 185.492188 169.078125 L 185.410156 168.570312 L 185.441406 169.082031 L 185.371094 168.023438 L 185.371094 169.082031 L 187.519531 169.082031 L 187.519531 168.023438 L 187.445312 169.082031 L 187.519531 168.023438 " fill-opacity="1" fill-rule="nonzero"/><path fill="#ffffff" d="M 216.609375 220.285156 C 215.164062 220.359375 213.808594 220.753906 212.609375 221.386719 C 212.890625 220.394531 213.015625 219.339844 212.960938 218.257812 C 212.675781 212.914062 208.117188 208.816406 202.773438 209.101562 C 201.019531 209.195312 199.394531 209.753906 198.023438 210.644531 C 197.527344 205.515625 193.066406 201.640625 187.871094 201.914062 C 183.457031 202.152344 179.890625 205.308594 178.949219 209.410156 C 177.132812 207.886719 174.757812 207.019531 172.207031 207.152344 C 166.867188 207.4375 162.765625 212 163.050781 217.339844 C 163.070312 217.742188 163.121094 218.136719 163.191406 218.53125 C 161.523438 217.4375 159.507812 216.84375 157.367188 216.957031 C 152.023438 217.238281 147.925781 221.800781 148.210938 227.140625 C 148.492188 232.480469 153.050781 236.582031 158.390625 236.300781 C 158.726562 236.28125 159.054688 236.246094 159.378906 236.195312 C 158.816406 237.511719 158.539062 238.976562 158.617188 240.507812 C 158.902344 245.847656 163.460938 249.949219 168.804688 249.664062 C 171.023438 249.546875 173.027344 248.6875 174.59375 247.34375 C 175.804688 251.589844 179.816406 254.585938 184.414062 254.34375 C 189.519531 254.070312 193.484375 249.894531 193.578125 244.863281 C 195.269531 248.15625 198.773438 250.328125 202.707031 250.117188 C 208.050781 249.835938 212.148438 245.273438 211.863281 239.933594 C 211.828125 239.21875 211.710938 238.523438 211.527344 237.859375 C 213.242188 239.074219 215.367188 239.746094 217.632812 239.625 C 222.976562 239.34375 227.074219 234.78125 226.792969 229.441406 C 226.507812 224.105469 221.949219 220 216.609375 220.285156 " fill-opacity="1" fill-rule="nonzero"/><path fill="#4c44cb" d="M 216.609375 220.285156 L 216.550781 219.230469 C 214.949219 219.3125 213.445312 219.75 212.117188 220.449219 L 212.609375 221.386719 L 213.628906 221.671875 C 213.890625 220.75 214.027344 219.773438 214.027344 218.773438 C 214.027344 218.582031 214.023438 218.390625 214.015625 218.203125 L 214.015625 218.199219 C 213.863281 215.339844 212.601562 212.789062 210.667969 210.964844 C 208.738281 209.144531 206.128906 208.027344 203.296875 208.027344 C 203.105469 208.027344 202.914062 208.03125 202.714844 208.046875 L 202.71875 208.046875 C 200.769531 208.148438 198.96875 208.769531 197.445312 209.761719 L 198.023438 210.644531 L 199.074219 210.542969 C 198.539062 205.039062 193.910156 200.84375 188.390625 200.84375 C 188.203125 200.84375 188.007812 200.847656 187.8125 200.859375 C 185.371094 200.988281 183.148438 201.933594 181.421875 203.40625 C 179.691406 204.882812 178.441406 206.894531 177.914062 209.175781 L 178.949219 209.410156 L 179.628906 208.597656 C 177.757812 207.027344 175.335938 206.082031 172.726562 206.082031 C 172.535156 206.082031 172.339844 206.085938 172.148438 206.097656 L 172.152344 206.097656 C 169.285156 206.25 166.738281 207.511719 164.914062 209.441406 C 163.089844 211.375 161.976562 213.984375 161.976562 216.820312 C 161.976562 217.007812 161.984375 217.203125 161.996094 217.402344 L 161.996094 217.394531 C 162.015625 217.847656 162.070312 218.289062 162.144531 218.71875 L 163.191406 218.53125 L 163.769531 217.644531 C 162.074219 216.53125 160.046875 215.882812 157.886719 215.882812 C 157.695312 215.882812 157.503906 215.886719 157.308594 215.902344 C 154.449219 216.050781 151.898438 217.316406 150.074219 219.246094 C 148.25 221.175781 147.136719 223.78125 147.136719 226.621094 C 147.136719 226.808594 147.140625 227.003906 147.152344 227.203125 L 147.152344 227.199219 C 147.304688 230.058594 148.566406 232.609375 150.496094 234.433594 C 152.425781 236.257812 155.035156 237.367188 157.871094 237.367188 C 158.0625 237.367188 158.253906 237.363281 158.449219 237.355469 C 158.820312 237.332031 159.183594 237.296875 159.539062 237.242188 L 159.378906 236.195312 L 158.40625 235.78125 C 157.851562 237.070312 157.546875 238.496094 157.546875 239.980469 C 157.546875 240.171875 157.550781 240.367188 157.558594 240.5625 C 157.710938 243.425781 158.976562 245.976562 160.90625 247.800781 C 162.835938 249.625 165.445312 250.738281 168.28125 250.734375 C 168.472656 250.738281 168.664062 250.730469 168.855469 250.722656 L 168.859375 250.722656 C 171.316406 250.589844 173.546875 249.632812 175.28125 248.148438 L 174.59375 247.34375 L 173.574219 247.632812 C 174.222656 249.898438 175.585938 251.847656 177.398438 253.222656 C 179.207031 254.601562 181.464844 255.417969 183.886719 255.417969 C 184.082031 255.417969 184.273438 255.414062 184.464844 255.398438 C 187.296875 255.25 189.816406 254.015625 191.632812 252.125 C 193.453125 250.230469 194.582031 247.675781 194.632812 244.886719 L 193.578125 244.863281 L 192.636719 245.347656 C 193.53125 247.09375 194.886719 248.550781 196.53125 249.578125 C 198.179688 250.601562 200.121094 251.191406 202.183594 251.191406 C 202.375 251.191406 202.570312 251.183594 202.765625 251.171875 C 205.625 251.023438 208.175781 249.761719 210 247.828125 C 211.824219 245.898438 212.933594 243.292969 212.933594 240.453125 C 212.933594 240.265625 212.929688 240.070312 212.921875 239.878906 C 212.878906 239.082031 212.75 238.308594 212.546875 237.578125 L 211.527344 237.859375 L 210.914062 238.71875 C 212.671875 239.964844 214.820312 240.699219 217.113281 240.699219 C 217.304688 240.699219 217.496094 240.695312 217.691406 240.683594 C 220.550781 240.53125 223.101562 239.269531 224.925781 237.335938 C 226.75 235.40625 227.863281 232.800781 227.863281 229.96875 C 227.863281 229.773438 227.859375 229.578125 227.847656 229.386719 C 227.695312 226.523438 226.433594 223.972656 224.5 222.148438 C 222.574219 220.324219 219.960938 219.214844 217.128906 219.214844 C 216.9375 219.214844 216.746094 219.21875 216.546875 219.230469 L 216.550781 219.230469 L 216.609375 220.285156 L 216.664062 221.339844 C 216.820312 221.335938 216.972656 221.332031 217.128906 221.332031 C 219.410156 221.332031 221.492188 222.21875 223.046875 223.6875 C 224.601562 225.160156 225.609375 227.195312 225.734375 229.496094 C 225.742188 229.652344 225.746094 229.8125 225.746094 229.96875 C 225.746094 232.242188 224.855469 234.328125 223.386719 235.882812 C 221.917969 237.433594 219.882812 238.449219 217.578125 238.566406 C 217.421875 238.578125 217.265625 238.582031 217.113281 238.582031 C 215.265625 238.582031 213.550781 237.996094 212.136719 236.996094 C 211.773438 236.738281 211.285156 236.734375 210.917969 236.992188 C 210.554688 237.25 210.386719 237.710938 210.503906 238.140625 C 210.671875 238.734375 210.773438 239.351562 210.808594 239.988281 C 210.816406 240.144531 210.820312 240.304688 210.820312 240.453125 C 210.820312 242.734375 209.929688 244.824219 208.460938 246.375 C 206.992188 247.925781 204.957031 248.9375 202.652344 249.0625 C 202.496094 249.070312 202.335938 249.074219 202.183594 249.074219 C 200.527344 249.074219 198.972656 248.601562 197.652344 247.777344 C 196.328125 246.957031 195.238281 245.78125 194.519531 244.382812 C 194.296875 243.949219 193.8125 243.726562 193.339844 243.835938 C 192.867188 243.945312 192.53125 244.359375 192.519531 244.84375 C 192.476562 247.082031 191.574219 249.132812 190.109375 250.652344 C 188.648438 252.175781 186.632812 253.164062 184.355469 253.289062 C 184.199219 253.296875 184.042969 253.300781 183.886719 253.300781 C 181.941406 253.300781 180.132812 252.648438 178.675781 251.539062 C 177.222656 250.429688 176.125 248.867188 175.609375 247.050781 C 175.507812 246.707031 175.242188 246.433594 174.894531 246.328125 C 174.550781 246.226562 174.175781 246.304688 173.902344 246.539062 C 172.503906 247.738281 170.726562 248.5 168.746094 248.609375 C 168.585938 248.613281 168.433594 248.617188 168.28125 248.621094 C 166 248.617188 163.917969 247.730469 162.359375 246.261719 C 160.808594 244.789062 159.796875 242.753906 159.675781 240.453125 L 159.675781 240.449219 C 159.667969 240.292969 159.664062 240.136719 159.664062 239.980469 C 159.664062 238.789062 159.910156 237.652344 160.351562 236.613281 C 160.503906 236.257812 160.453125 235.851562 160.214844 235.546875 C 159.980469 235.246094 159.597656 235.09375 159.21875 235.148438 C 158.929688 235.195312 158.636719 235.226562 158.335938 235.242188 L 158.339844 235.242188 C 158.179688 235.25 158.027344 235.25 157.871094 235.25 C 155.59375 235.25 153.507812 234.363281 151.953125 232.894531 C 150.398438 231.425781 149.390625 229.386719 149.265625 227.085938 L 149.265625 227.082031 C 149.257812 226.929688 149.253906 226.773438 149.253906 226.621094 C 149.253906 224.34375 150.144531 222.253906 151.613281 220.699219 C 153.082031 219.148438 155.117188 218.136719 157.421875 218.011719 C 157.578125 218.003906 157.734375 218 157.886719 218 C 159.625 218 161.242188 218.519531 162.609375 219.414062 C 162.960938 219.644531 163.417969 219.644531 163.773438 219.410156 C 164.125 219.179688 164.304688 218.753906 164.230469 218.34375 C 164.167969 217.992188 164.125 217.640625 164.109375 217.285156 L 164.109375 217.28125 C 164.097656 217.125 164.09375 216.972656 164.09375 216.820312 C 164.09375 214.539062 164.980469 212.457031 166.453125 210.898438 C 167.925781 209.34375 169.960938 208.335938 172.261719 208.210938 C 172.417969 208.203125 172.574219 208.195312 172.726562 208.195312 C 174.824219 208.195312 176.757812 208.957031 178.269531 210.222656 C 178.546875 210.457031 178.933594 210.53125 179.285156 210.417969 C 179.632812 210.300781 179.898438 210.007812 179.980469 209.652344 C 180.398438 207.824219 181.402344 206.203125 182.792969 205.019531 C 184.1875 203.828125 185.957031 203.078125 187.925781 202.972656 L 187.929688 202.972656 C 188.082031 202.960938 188.238281 202.957031 188.390625 202.957031 C 192.816406 202.957031 196.542969 206.335938 196.96875 210.746094 C 197.003906 211.117188 197.230469 211.4375 197.5625 211.597656 C 197.894531 211.761719 198.289062 211.734375 198.597656 211.535156 C 199.824219 210.738281 201.261719 210.238281 202.828125 210.160156 L 202.832031 210.160156 C 202.988281 210.148438 203.140625 210.144531 203.296875 210.144531 C 205.578125 210.144531 207.660156 211.035156 209.214844 212.503906 C 210.765625 213.972656 211.777344 216.007812 211.902344 218.316406 L 211.902344 218.3125 C 211.910156 218.464844 211.910156 218.621094 211.910156 218.773438 C 211.910156 219.578125 211.800781 220.355469 211.589844 221.101562 C 211.476562 221.507812 211.621094 221.945312 211.945312 222.210938 C 212.273438 222.472656 212.730469 222.519531 213.105469 222.324219 C 214.171875 221.757812 215.375 221.414062 216.664062 221.339844 L 216.609375 220.285156 " fill-opacity="1" fill-rule="nonzero"/><path fill="#ffffff" d="M 175.125 248.769531 C 173.078125 245.347656 173.792969 238.472656 178.664062 236.183594 " fill-opacity="1" fill-rule="nonzero"/><path fill="#4c44cb" d="M 176.035156 248.226562 C 175.460938 247.273438 175.101562 245.875 175.105469 244.382812 C 175.105469 242.9375 175.429688 241.421875 176.09375 240.132812 C 176.761719 238.84375 177.746094 237.785156 179.109375 237.140625 C 179.640625 236.890625 179.871094 236.261719 179.617188 235.730469 C 179.371094 235.203125 178.742188 234.976562 178.210938 235.222656 C 176.371094 236.085938 175.046875 237.535156 174.210938 239.164062 C 173.371094 240.804688 172.992188 242.632812 172.992188 244.382812 C 172.992188 246.195312 173.390625 247.929688 174.21875 249.3125 C 174.519531 249.8125 175.167969 249.980469 175.667969 249.679688 C 176.167969 249.375 176.335938 248.726562 176.035156 248.226562 " fill-opacity="1" fill-rule="nonzero"/><path fill="#ffffff" d="M 163.191406 218.53125 C 166.910156 220.535156 168.28125 227.589844 165.128906 230.453125 " fill-opacity="1" fill-rule="nonzero"/><path fill="#4c44cb" d="M 162.6875 219.460938 C 163.582031 219.9375 164.382812 220.855469 164.945312 222.011719 C 165.507812 223.164062 165.824219 224.539062 165.824219 225.820312 C 165.824219 226.621094 165.703125 227.386719 165.464844 228.042969 C 165.226562 228.703125 164.875 229.253906 164.417969 229.671875 C 163.984375 230.0625 163.953125 230.734375 164.347656 231.167969 C 164.738281 231.597656 165.410156 231.628906 165.84375 231.238281 C 166.589844 230.550781 167.121094 229.691406 167.457031 228.765625 C 167.789062 227.835938 167.9375 226.835938 167.941406 225.820312 C 167.9375 224.199219 167.558594 222.542969 166.84375 221.085938 C 166.136719 219.628906 165.089844 218.355469 163.691406 217.59375 C 163.175781 217.320312 162.535156 217.515625 162.257812 218.027344 C 161.980469 218.539062 162.171875 219.183594 162.6875 219.460938 " fill-opacity="1" fill-rule="nonzero"/><path fill="#ffffff" d="M 180.515625 213.964844 C 171.566406 219.621094 182.125 232.152344 188.96875 224.671875 " fill-opacity="1" fill-rule="nonzero"/><path fill="#4c44cb" d="M 179.949219 213.070312 C 178.597656 213.921875 177.605469 214.953125 176.957031 216.082031 C 176.308594 217.210938 176.015625 218.429688 176.015625 219.613281 C 176.015625 220.710938 176.265625 221.777344 176.6875 222.765625 C 177.328125 224.25 178.375 225.546875 179.664062 226.5 C 180.957031 227.445312 182.519531 228.046875 184.175781 228.046875 C 185.128906 228.046875 186.109375 227.84375 187.054688 227.40625 C 188.003906 226.96875 188.914062 226.296875 189.746094 225.386719 C 190.140625 224.957031 190.109375 224.289062 189.679688 223.890625 C 189.25 223.496094 188.578125 223.527344 188.183594 223.960938 C 187.519531 224.6875 186.835938 225.171875 186.167969 225.488281 C 185.496094 225.796875 184.832031 225.929688 184.175781 225.929688 C 183.414062 225.929688 182.660156 225.746094 181.949219 225.410156 C 180.875 224.902344 179.90625 224.042969 179.21875 223.011719 C 178.53125 221.976562 178.128906 220.78125 178.132812 219.613281 C 178.132812 218.769531 178.335938 217.9375 178.792969 217.136719 C 179.253906 216.335938 179.980469 215.554688 181.082031 214.859375 C 181.578125 214.546875 181.726562 213.890625 181.410156 213.402344 C 181.101562 212.902344 180.449219 212.757812 179.949219 213.070312 " fill-opacity="1" fill-rule="nonzero"/><path fill="#ffffff" d="M 198.5625 222.84375 C 204.863281 228.929688 195.984375 238.382812 189.96875 231.152344 " fill-opacity="1" fill-rule="nonzero"/><path fill="#4c44cb" d="M 197.824219 223.605469 C 198.5 224.253906 198.953125 224.925781 199.242188 225.59375 C 199.535156 226.261719 199.667969 226.925781 199.667969 227.578125 C 199.667969 228.921875 199.101562 230.222656 198.203125 231.171875 C 197.753906 231.644531 197.226562 232.03125 196.65625 232.296875 C 196.085938 232.554688 195.476562 232.699219 194.851562 232.699219 C 194.210938 232.699219 193.546875 232.554688 192.863281 232.207031 C 192.179688 231.859375 191.472656 231.304688 190.78125 230.472656 C 190.40625 230.023438 189.738281 229.964844 189.292969 230.335938 C 188.839844 230.710938 188.78125 231.378906 189.15625 231.828125 C 190.003906 232.84375 190.933594 233.601562 191.902344 234.09375 C 192.871094 234.589844 193.878906 234.816406 194.851562 234.816406 C 195.804688 234.816406 196.71875 234.597656 197.542969 234.214844 C 198.785156 233.640625 199.835938 232.699219 200.589844 231.550781 C 201.332031 230.402344 201.785156 229.027344 201.785156 227.578125 C 201.785156 226.644531 201.59375 225.679688 201.183594 224.746094 C 200.777344 223.8125 200.152344 222.90625 199.296875 222.082031 C 198.878906 221.675781 198.207031 221.6875 197.800781 222.105469 C 197.394531 222.527344 197.40625 223.199219 197.824219 223.605469 " fill-opacity="1" fill-rule="nonzero"/><path fill="#ffffff" d="M 203.792969 230.453125 C 203.792969 230.453125 210.300781 229.828125 211.703125 238.628906 " fill-opacity="1" fill-rule="nonzero"/><path fill="#4c44cb" d="M 203.898438 231.503906 L 203.835938 230.878906 L 203.890625 231.503906 L 203.898438 231.503906 L 203.835938 230.878906 L 203.890625 231.503906 L 203.902344 231.503906 C 203.921875 231.503906 203.980469 231.5 204.074219 231.5 C 204.320312 231.5 204.792969 231.527344 205.371094 231.667969 C 206.238281 231.878906 207.324219 232.339844 208.308594 233.378906 C 209.296875 234.421875 210.21875 236.066406 210.65625 238.792969 C 210.746094 239.371094 211.292969 239.761719 211.871094 239.671875 C 212.445312 239.582031 212.839844 239.035156 212.75 238.457031 C 212.421875 236.421875 211.832031 234.8125 211.074219 233.554688 C 209.949219 231.660156 208.453125 230.585938 207.128906 230.027344 C 205.800781 229.460938 204.644531 229.386719 204.074219 229.386719 C 203.859375 229.386719 203.722656 229.394531 203.691406 229.398438 C 203.113281 229.457031 202.6875 229.972656 202.742188 230.550781 C 202.800781 231.132812 203.316406 231.5625 203.898438 231.503906 " fill-opacity="1" fill-rule="nonzero"/><path fill="#ffffff" d="M 198.566406 210.316406 C 198.566406 210.316406 193.628906 214.152344 195.347656 220.476562 " fill-opacity="1" fill-rule="nonzero"/><path fill="#4c44cb" d="M 197.914062 209.480469 C 197.859375 209.527344 196.886719 210.285156 195.90625 211.695312 C 195.417969 212.402344 194.921875 213.273438 194.546875 214.300781 C 194.175781 215.328125 193.925781 216.515625 193.925781 217.835938 C 193.925781 218.75 194.046875 219.726562 194.328125 220.757812 C 194.476562 221.316406 195.058594 221.652344 195.625 221.496094 C 196.1875 221.34375 196.519531 220.761719 196.367188 220.199219 C 196.136719 219.351562 196.039062 218.566406 196.039062 217.835938 C 196.039062 216.785156 196.238281 215.847656 196.539062 215.023438 C 196.984375 213.785156 197.667969 212.800781 198.238281 212.132812 C 198.519531 211.800781 198.777344 211.546875 198.953125 211.382812 C 199.042969 211.296875 199.113281 211.238281 199.160156 211.199219 L 199.207031 211.15625 L 199.214844 211.148438 L 199.03125 210.917969 L 199.214844 211.152344 L 199.214844 211.148438 L 199.03125 210.917969 L 199.214844 211.152344 C 199.675781 210.792969 199.757812 210.128906 199.402344 209.667969 C 199.042969 209.207031 198.375 209.121094 197.917969 209.480469 " fill-opacity="1" fill-rule="nonzero"/></svg>
```
Page 4/5FirstPrevNextLast