From 6168f633e4ed410ad6d0a16410ba8859d078a42f Mon Sep 17 00:00:00 2001 From: yimsk Date: Fri, 9 Jan 2026 22:33:19 +0900 Subject: [PATCH] AI Chat: Bedrock integration with streaming, session persistence, AWS docs search (#126) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add AI chat overlay with Bedrock Converse API (#123) - internal/ai/: bedrock client, tools (6), session persistence - chat_overlay.go: modal UI with viewport + textinput - Context-aware: knows current service/resource/log group - Config: ai.model, ai.max_sessions in config.yaml - Keybind: A opens chat overlay * Enhance AI chat, add ECS task-definitions, improve filter UX - AI Chat: add ECS cluster context, fix tool input parsing, improve logging - Add ECS task-definitions resource type with navigation from clusters/services/tasks - ServiceBrowser: change filter to fuzzy match (was substring) - Fix ghost content on filter change with tea.ClearScreen (ServiceBrowser, LogView) - Update docs: add AI Chat keybinding (A key) to README and keybindings.md * Fix AI chat code review issues - bedrock.go: add ctx to processStream, handle stream.Close error, log json errors - chat_overlay.go: use ui helpers, apperrors.Wrap, check executor nil, fix tool msg handling - modal.go: add ModalWidthChat constant - app.go: use ModalWidthChat instead of hardcoded 80 - session.go: simplify prune error handling * Use ConverseStream for real-time AI response + refactor - chat_overlay.go: use ConverseStream instead of sync Converse API - Split into chat_overlay.go, chat_overlay_render.go, chat_overlay_prompt.go - tools.go: extract getResource helper to reduce GetDAO->Get->Unwrap repetition - Streaming handles text deltas, tool_use accumulation, multi-round tool calls * Add extended thinking, search_aws_docs, fix Data field in 62 resources AI Chat improvements: - Extended thinking support with collapsed display during streaming - Rename isThinking to isStreaming for clarity - Consistent AI: + newline format in streaming/final - Scroll to thinking block on expand Tools: - Implement search_aws_docs using AWS Documentation Search API - Return top 5 results with title, URL, context Resource fixes: - Add missing Data field to BaseResource in 62 dao.go files - Enables Raw() to return resource data for AI get_resource_detail * Fix AI chat multi-turn conversation with tool calls - Use []ContentBlock instead of string for Message.Content (Bedrock API) - Preserve streamMessages across turns instead of rebuilding from UI state - Only add non-empty ContentBlocks to prevent 'content.0 is blank' error - Fix toolUse/toolResult pairing for multi-turn tool conversations * Add AI chat list/diff context modes + multi-profile support * Add collapsible tool call params in AI chat (default collapsed) * AI chat: disable session persistence by default, increase max_sessions to 100 - Add save_sessions config option (default: false) for privacy - Increase DefaultAIMaxSessions 10 -> 100 - Document AI config in docs/configuration.md * Add AI module tests and Bedrock IAM permissions docs * Fix code review issues: remove duplicate DefaultModel, use parent ctx in searchDocs, revert ServiceBrowser to substring filter * Fix review issues: slice copy, log.Warn, ctx shadow, use go-runewidth * Fix race condition: remove duplicate streamMessages assignment in startStream * Add chat session history UI (Ctrl+H) and fix nav key conflicts - Session history: Ctrl+H opens history, j/k select, enter load, n new - Save sessions only when first message added (not on open) - Show (tool limit reached) when max tool rounds hit - Log pruneOldSessions errors at debug level - Change nav key d→D for task-definitions/data-sources (conflicts with detail view) * fix TestSessionPruning: add message to trigger file save * Fix review: nil guard loadSession, log session errors, warn negative thinking_budget * SecurityHub findings filter toggle + AI context expansion - Add 'r' key toggle for SecurityHub findings (active only / all) - Default: show only ACTIVE + non-RESOLVED findings - Add include_resolved param to query_resources AI tool - Pass toggle state to AI via system prompt + result message - Add expandable context params in AI chat (click to toggle) - Add Toggle/Toggler interface for reusable list-level toggles - Add DocsSearch timeout to config (timeouts.docs_search) - Fix: stop stream when loading session history - Fix: show session save failure in StatusLine (3s) - Fix: set IsError on tool input JSON parse failure * ai: make max_tool_rounds configurable (default 15) * ai: use UUID for session ID, add debug log on load failure * Fix code review issues: stream cancellation, file permissions, docs - Add stream cancellation to prevent goroutine leaks when closing chat overlay - Change AI session file permissions from 0644 to 0600 (security) - Add D key to keybindings.md for Data Sources/Task Definitions navigation - Move google/uuid and mattn/go-runewidth to direct dependencies in go.mod - Change log.Debug to log.Warn for session save failures * Fix: save tool use/result to session for persistence - Save assistant msg with ToolUse to session before tool exec - Save user msg with ToolResult to session after tool exec - Remove duplicate assistant msg append in handleToolExecute - Fixes session resume forgetting tool execution history * AI chat: fix profile ID handling, strengthen list mode instructions - Use internal profile IDs (__sdk_default__, etc) for LLM tool calls - Add formatProfileName() for UI display conversion - Clarify list mode: query ALL regions × ALL profiles explicitly - Add IMPORTANT directives to ensure complete cross-context queries * AI chat: region/profile context complete Config: - Add ai.profile/ai.region to config.yaml for Bedrock-specific credentials - NewClient() uses AI config to override default profile/region Context fields: - Rename: Regions→UserRegions, Profile→ResourceProfile - Add: UserProfiles for multi-profile support - Distinguish user selections (User*) from specific resource context (Resource*) Tools: - Use ProfileSelectionFromID() to convert profile IDs for AWS SDK Views: - Preserve resource wrappers (RegionalResource/ProfiledResource) on refresh - Use UnwrapResource() consistently for accessing underlying resource data Fixes: - ResourceRegion empty bug (contextForResource unwrap, mergeResources wrapper loss) - Profile ID conversion (__sdk_default__ → ProfileSelectionFromID) * Refactor: share mergeResources across views Move mergeResources from detail_view to view.go for reusability. Future-proofs DiffView for wrapper preservation when refresh added. - Addresses code review finding: design inconsistency - Enables consistent wrapper preservation pattern * AI chat: tool call limit, resource pagination, session pruning optimization 3 improvements for v0.10: 1. Tool call counter (防御層) - Config: ai.max_tool_calls_per_query (default: 50) - Limit per query (not session) to prevent LLM runaway - Reset on new user message 2. Resource limit relaxation (利便性) - query_resources: default 100 (was 50), max 2000 - Add offset param for pagination - SecurityHub large findings support 3. Session pruning optimization (性能) - Session ID: 20060102-150405-uuid (was 2006-01-02-uuid) - pruneOldSessions: filename sort (was JSON parse + UpdatedAt sort) - 1000x faster (~0.1ms vs ~100ms), works well even at 100+ sessions * Docs: add max_tool_calls_per_query config * Fix: toolCallCount naming, reset in loadSession, profile ID handling - Rename apiCallCount -> toolCallCount (clearer per-query scope) - Add toolCallCount reset in loadSession() (consistency with newSession) - Use ProfileSelectionFromID in bedrock.go (handle __sdk_default__) * Docs: AI chat feature w/ screenshot, usage guide - README: add AI chat screenshot section, fix keybinding (A not Ctrl+L) - docs/ai-chat.md: new doc w/ setup, usage, tools, examples - docs/configuration.md: add ai.profile/region fields - docs/images/ai-chat.png: screenshot showing security analysis * Fix: code review critical issues (imports, race, perms, tool limit) - Fix import order (goimports) in session.go, file.go, chat_overlay*.go - Fix race condition: add mutex for stream cancellation - Fix session dir permissions: 0755 -> 0700 (prevent info leak) - Fix tool call limit bypass: check limit inside loop, not just entry - Note: pagination bounds checking already implemented (line 322-324) Addresses code review: https://github.com/clawscli/claws/pull/126#issuecomment-3728634009 * Fix: session pruning by mtime, streamCancel race condition Session pruning improvements: - Use file modification time instead of filename for pruning - Ensures recently updated sessions are not deleted - Maintains performance (no JSON parsing required) Race condition fix: - Protect streamCancel assignment with mutex in startStream() - Previously only cancelStream() was protected Addresses PR review: https://github.com/clawscli/claws/pull/126#issuecomment-3728663792 * Docs: simplify ai-chat.md, remove tool details and examples - Remove detailed tool descriptions (list_resources, query_resources, etc.) - Remove example queries section - Add concise 'What the AI Can Do' section - Keep essential: setup, keyboard shortcuts, troubleshooting - Users can ask the AI directly how to use it Changed from 221 to 147 lines (-74 lines) * Docs: update IAM permissions for all Bedrock models - Change Resource from anthropic.claude-* to * (support all models) - Add note: Marketplace permissions required for first-time usage only - claws supports any Bedrock model via ai.model config * Fix: return error flag for invalid pagination offset When offset exceeds resource count, return isError=true so LLM recognizes it as an error condition. Addresses code review: https://github.com/clawscli/claws/pull/126#issuecomment-3728826091 --- README.md | 9 + cmd/claws/imports_custom.go | 1 + custom/accessanalyzer/analyzers/dao.go | 10 +- custom/accessanalyzer/findings/dao.go | 6 +- custom/apprunner/operations/dao.go | 5 +- custom/apprunner/services/dao.go | 10 +- custom/appsync/data-sources/dao.go | 5 +- custom/appsync/graphql-apis/dao.go | 5 +- custom/appsync/graphql-apis/render.go | 2 +- custom/athena/query-executions/dao.go | 5 +- custom/athena/workgroups/dao.go | 10 +- custom/batch/compute-environments/dao.go | 5 +- custom/batch/job-definitions/dao.go | 5 +- custom/batch/job-queues/dao.go | 5 +- custom/batch/jobs/dao.go | 10 +- custom/budgets/budgets/dao.go | 5 +- custom/budgets/notifications/dao.go | 5 +- custom/ce/costs/dao.go | 3 +- custom/cloudtrail/events/dao.go | 5 +- custom/cloudtrail/trails/dao.go | 5 +- custom/configservice/rules/dao.go | 5 +- custom/datasync/locations/dao.go | 5 +- custom/datasync/task-executions/dao.go | 5 +- custom/datasync/tasks/dao.go | 10 +- custom/detective/graphs/dao.go | 5 +- custom/detective/investigations/dao.go | 5 +- custom/directconnect/connections/dao.go | 5 +- .../directconnect/virtual-interfaces/dao.go | 5 +- custom/ecs/clusters/render.go | 6 + custom/ecs/services/render.go | 15 +- custom/ecs/task-definitions/constants.go | 7 + custom/ecs/task-definitions/dao.go | 239 ++++++ custom/ecs/task-definitions/register.go | 20 + custom/ecs/task-definitions/render.go | 289 +++++++ custom/ecs/tasks/render.go | 20 +- custom/emr/clusters/dao.go | 10 +- custom/emr/steps/dao.go | 10 +- custom/fms/policies/dao.go | 10 +- custom/glue/crawlers/dao.go | 5 +- custom/glue/databases/dao.go | 5 +- custom/glue/job-runs/dao.go | 5 +- custom/glue/jobs/dao.go | 5 +- custom/glue/tables/dao.go | 5 +- custom/health/events/dao.go | 5 +- custom/license-manager/configurations/dao.go | 10 +- custom/license-manager/grants/dao.go | 5 +- custom/license-manager/licenses/dao.go | 5 +- custom/macie2/buckets/dao.go | 5 +- custom/macie2/classification-jobs/dao.go | 10 +- custom/macie2/findings/dao.go | 5 +- .../network-firewall/firewall-policies/dao.go | 5 +- custom/network-firewall/firewalls/dao.go | 5 +- custom/network-firewall/rule-groups/dao.go | 10 +- custom/organizations/accounts/dao.go | 5 +- custom/organizations/ous/dao.go | 5 +- custom/organizations/policies/dao.go | 10 +- custom/organizations/roots/dao.go | 5 +- custom/redshift/clusters/dao.go | 5 +- custom/redshift/snapshots/dao.go | 5 +- custom/s3/buckets/dao.go | 2 + custom/sagemaker/endpoints/dao.go | 5 +- custom/sagemaker/models/dao.go | 5 +- custom/sagemaker/notebooks/dao.go | 5 +- custom/sagemaker/training-jobs/dao.go | 5 +- custom/securityhub/findings/dao.go | 21 +- custom/securityhub/findings/render.go | 6 + custom/transcribe/jobs/dao.go | 10 +- custom/transfer/servers/dao.go | 10 +- custom/transfer/users/dao.go | 10 +- custom/vpc/endpoints/dao.go | 5 +- custom/vpc/tgw-attachments/dao.go | 5 +- custom/vpc/transit-gateways/dao.go | 5 +- custom/xray/groups/dao.go | 5 +- docs/ai-chat.md | 147 ++++ docs/configuration.md | 11 + docs/iam-permissions.md | 42 +- docs/images/ai-chat.png | Bin 0 -> 837181 bytes docs/keybindings.md | 2 + go.mod | 4 +- go.sum | 4 + internal/ai/bedrock.go | 459 +++++++++++ internal/ai/bedrock_test.go | 287 +++++++ internal/ai/session.go | 350 ++++++++ internal/ai/session_test.go | 385 +++++++++ internal/ai/tools.go | 769 +++++++++++++++++ internal/ai/tools_test.go | 326 ++++++++ internal/app/app.go | 100 +++ internal/config/file.go | 118 ++- internal/config/file_test.go | 21 + internal/dao/dao.go | 12 + internal/render/render.go | 13 + internal/ui/theme.go | 18 + internal/view/chat_overlay.go | 775 ++++++++++++++++++ internal/view/chat_overlay_prompt.go | 230 ++++++ internal/view/chat_overlay_render.go | 237 ++++++ internal/view/detail_view.go | 29 +- internal/view/diff_view.go | 15 +- internal/view/log_view.go | 8 +- internal/view/modal.go | 1 + internal/view/resource_browser.go | 8 +- internal/view/resource_browser_fetch.go | 21 +- internal/view/resource_browser_input.go | 25 +- internal/view/resource_browser_nav.go | 51 +- internal/view/service_browser.go | 63 +- internal/view/session_history.go | 147 ++++ internal/view/view.go | 25 + 106 files changed, 5480 insertions(+), 234 deletions(-) create mode 100644 custom/ecs/task-definitions/constants.go create mode 100644 custom/ecs/task-definitions/dao.go create mode 100644 custom/ecs/task-definitions/register.go create mode 100644 custom/ecs/task-definitions/render.go create mode 100644 docs/ai-chat.md create mode 100644 docs/images/ai-chat.png create mode 100644 internal/ai/bedrock.go create mode 100644 internal/ai/bedrock_test.go create mode 100644 internal/ai/session.go create mode 100644 internal/ai/session_test.go create mode 100644 internal/ai/tools.go create mode 100644 internal/ai/tools_test.go create mode 100644 internal/view/chat_overlay.go create mode 100644 internal/view/chat_overlay_prompt.go create mode 100644 internal/view/chat_overlay_render.go create mode 100644 internal/view/session_history.go diff --git a/README.md b/README.md index 833bd8dc..f6e7fcf9 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ A terminal UI for AWS resource management - **Cross-resource navigation** - Jump from VPC to subnets, Lambda to CloudWatch - **Filtering & sorting** - Fuzzy search, tag filtering, column sorting - **Resource comparison** - Side-by-side diff view +- **AI Chat** - AI assistant with AWS context (via Bedrock) - **6 color themes** - dark, light, nord, dracula, gruvbox, catppuccin ## Screenshots @@ -31,6 +32,12 @@ A terminal UI for AWS resource management ![multi-region](docs/images/multi-account-region.png) +### AI Chat (Bedrock) + +![ai-chat](docs/images/ai-chat.png) + +Press `A` in list/detail/diff views to open AI chat. The assistant analyzes resources, compares configurations, and identifies risks using AWS Bedrock. + ## Installation ### Homebrew (macOS/Linux) @@ -89,6 +96,7 @@ claws --read-only | `:` | Command mode (e.g., `:ec2/instances`) | | `/` | Filter mode (fuzzy search) | | `a` | Open actions menu | +| `A` | AI Chat (in list/detail/diff views) | | `R` | Select region(s) | | `P` | Select profile(s) | | `?` | Show help | @@ -104,6 +112,7 @@ See [docs/keybindings.md](docs/keybindings.md) for complete reference. | [Supported Services](docs/services.md) | All 69 services and 163 resources | | [Configuration](docs/configuration.md) | Config file, themes, and options | | [IAM Permissions](docs/iam-permissions.md) | Required AWS permissions | +| [AI Chat](docs/ai-chat.md) | AI assistant usage and features | | [Architecture](docs/architecture.md) | Internal design and structure | | [Adding Resources](docs/adding-resources.md) | Guide for contributors | diff --git a/cmd/claws/imports_custom.go b/cmd/claws/imports_custom.go index 48191f0f..c716c173 100644 --- a/cmd/claws/imports_custom.go +++ b/cmd/claws/imports_custom.go @@ -150,6 +150,7 @@ import ( // ECS _ "github.com/clawscli/claws/custom/ecs/clusters" _ "github.com/clawscli/claws/custom/ecs/services" + _ "github.com/clawscli/claws/custom/ecs/task-definitions" _ "github.com/clawscli/claws/custom/ecs/tasks" // ElastiCache diff --git a/custom/accessanalyzer/analyzers/dao.go b/custom/accessanalyzer/analyzers/dao.go index bf99861f..00e2257d 100644 --- a/custom/accessanalyzer/analyzers/dao.go +++ b/custom/accessanalyzer/analyzers/dao.go @@ -85,8 +85,9 @@ type AnalyzerResource struct { func NewAnalyzerResource(analyzer types.AnalyzerSummary) *AnalyzerResource { return &AnalyzerResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(analyzer.Name), - ARN: appaws.Str(analyzer.Arn), + ID: appaws.Str(analyzer.Name), + ARN: appaws.Str(analyzer.Arn), + Data: analyzer, }, Summary: &analyzer, } @@ -96,8 +97,9 @@ func NewAnalyzerResource(analyzer types.AnalyzerSummary) *AnalyzerResource { func NewAnalyzerResourceFromDetail(analyzer types.AnalyzerSummary) *AnalyzerResource { return &AnalyzerResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(analyzer.Name), - ARN: appaws.Str(analyzer.Arn), + ID: appaws.Str(analyzer.Name), + ARN: appaws.Str(analyzer.Arn), + Data: analyzer, }, Detail: &analyzer, } diff --git a/custom/accessanalyzer/findings/dao.go b/custom/accessanalyzer/findings/dao.go index 21c46a47..f14ef213 100644 --- a/custom/accessanalyzer/findings/dao.go +++ b/custom/accessanalyzer/findings/dao.go @@ -93,7 +93,8 @@ type FindingResource struct { func NewFindingResource(finding types.FindingSummary, analyzerArn string) *FindingResource { return &FindingResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(finding.Id), + ID: appaws.Str(finding.Id), + Data: finding, }, Summary: &finding, AnalyzerArn: analyzerArn, @@ -104,7 +105,8 @@ func NewFindingResource(finding types.FindingSummary, analyzerArn string) *Findi func NewFindingResourceFromDetail(finding types.Finding, analyzerArn string) *FindingResource { return &FindingResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(finding.Id), + ID: appaws.Str(finding.Id), + Data: finding, }, Detail: &finding, AnalyzerArn: analyzerArn, diff --git a/custom/apprunner/operations/dao.go b/custom/apprunner/operations/dao.go index 977bee5c..624d1c32 100644 --- a/custom/apprunner/operations/dao.go +++ b/custom/apprunner/operations/dao.go @@ -113,8 +113,9 @@ type OperationResource struct { func NewOperationResource(op types.OperationSummary) *OperationResource { return &OperationResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(op.Id), - ARN: appaws.Str(op.TargetArn), + ID: appaws.Str(op.Id), + ARN: appaws.Str(op.TargetArn), + Data: op, }, Item: op, } diff --git a/custom/apprunner/services/dao.go b/custom/apprunner/services/dao.go index 07580134..cd674af2 100644 --- a/custom/apprunner/services/dao.go +++ b/custom/apprunner/services/dao.go @@ -85,8 +85,9 @@ type ServiceResource struct { func NewServiceResource(svc types.ServiceSummary) *ServiceResource { return &ServiceResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(svc.ServiceName), - ARN: appaws.Str(svc.ServiceArn), + ID: appaws.Str(svc.ServiceName), + ARN: appaws.Str(svc.ServiceArn), + Data: svc, }, Summary: &svc, } @@ -96,8 +97,9 @@ func NewServiceResource(svc types.ServiceSummary) *ServiceResource { func NewServiceResourceFromDetail(svc types.Service) *ServiceResource { return &ServiceResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(svc.ServiceName), - ARN: appaws.Str(svc.ServiceArn), + ID: appaws.Str(svc.ServiceName), + ARN: appaws.Str(svc.ServiceArn), + Data: svc, }, Detail: &svc, } diff --git a/custom/appsync/data-sources/dao.go b/custom/appsync/data-sources/dao.go index 066e0f0f..bd0517eb 100644 --- a/custom/appsync/data-sources/dao.go +++ b/custom/appsync/data-sources/dao.go @@ -103,8 +103,9 @@ type DataSourceResource struct { func NewDataSourceResource(ds types.DataSource, apiId string) *DataSourceResource { return &DataSourceResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(ds.Name), - ARN: appaws.Str(ds.DataSourceArn), + ID: appaws.Str(ds.Name), + ARN: appaws.Str(ds.DataSourceArn), + Data: ds, }, DataSource: &ds, apiId: apiId, diff --git a/custom/appsync/graphql-apis/dao.go b/custom/appsync/graphql-apis/dao.go index ce1ae147..5b2b78e7 100644 --- a/custom/appsync/graphql-apis/dao.go +++ b/custom/appsync/graphql-apis/dao.go @@ -83,8 +83,9 @@ type GraphQLApiResource struct { func NewGraphQLApiResource(api types.GraphqlApi) *GraphQLApiResource { return &GraphQLApiResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(api.ApiId), - ARN: appaws.Str(api.Arn), + ID: appaws.Str(api.ApiId), + ARN: appaws.Str(api.Arn), + Data: api, }, Api: &api, } diff --git a/custom/appsync/graphql-apis/render.go b/custom/appsync/graphql-apis/render.go index c9dc5e6f..4d33815b 100644 --- a/custom/appsync/graphql-apis/render.go +++ b/custom/appsync/graphql-apis/render.go @@ -172,7 +172,7 @@ func (r *GraphQLApiRenderer) Navigations(resource dao.Resource) []render.Navigat } return []render.Navigation{ { - Key: "d", + Key: "D", Label: "Data Sources", Service: "appsync", Resource: "data-sources", diff --git a/custom/athena/query-executions/dao.go b/custom/athena/query-executions/dao.go index 553c98ec..4b6852a6 100644 --- a/custom/athena/query-executions/dao.go +++ b/custom/athena/query-executions/dao.go @@ -122,8 +122,9 @@ type QueryExecutionResource struct { func NewQueryExecutionResource(qe types.QueryExecution) *QueryExecutionResource { return &QueryExecutionResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(qe.QueryExecutionId), - ARN: "", + ID: appaws.Str(qe.QueryExecutionId), + ARN: "", + Data: qe, }, Item: qe, } diff --git a/custom/athena/workgroups/dao.go b/custom/athena/workgroups/dao.go index e56fea61..4dd7dd90 100644 --- a/custom/athena/workgroups/dao.go +++ b/custom/athena/workgroups/dao.go @@ -85,8 +85,9 @@ type WorkgroupResource struct { func NewWorkgroupResource(wg types.WorkGroupSummary) *WorkgroupResource { return &WorkgroupResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(wg.Name), - ARN: "", + ID: appaws.Str(wg.Name), + ARN: "", + Data: wg, }, Summary: &wg, } @@ -96,8 +97,9 @@ func NewWorkgroupResource(wg types.WorkGroupSummary) *WorkgroupResource { func NewWorkgroupResourceFromDetail(wg types.WorkGroup) *WorkgroupResource { return &WorkgroupResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(wg.Name), - ARN: "", + ID: appaws.Str(wg.Name), + ARN: "", + Data: wg, }, Detail: &wg, } diff --git a/custom/batch/compute-environments/dao.go b/custom/batch/compute-environments/dao.go index 6fa58dc5..4ebf2fa3 100644 --- a/custom/batch/compute-environments/dao.go +++ b/custom/batch/compute-environments/dao.go @@ -97,8 +97,9 @@ type ComputeEnvironmentResource struct { func NewComputeEnvironmentResource(env types.ComputeEnvironmentDetail) *ComputeEnvironmentResource { return &ComputeEnvironmentResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(env.ComputeEnvironmentName), - ARN: appaws.Str(env.ComputeEnvironmentArn), + ID: appaws.Str(env.ComputeEnvironmentName), + ARN: appaws.Str(env.ComputeEnvironmentArn), + Data: env, }, Env: &env, } diff --git a/custom/batch/job-definitions/dao.go b/custom/batch/job-definitions/dao.go index 4428e803..9bb14534 100644 --- a/custom/batch/job-definitions/dao.go +++ b/custom/batch/job-definitions/dao.go @@ -96,8 +96,9 @@ func NewJobDefinitionResource(def types.JobDefinition) *JobDefinitionResource { } return &JobDefinitionResource{ BaseResource: dao.BaseResource{ - ID: name, - ARN: appaws.Str(def.JobDefinitionArn), + ID: name, + ARN: appaws.Str(def.JobDefinitionArn), + Data: def, }, Def: &def, } diff --git a/custom/batch/job-queues/dao.go b/custom/batch/job-queues/dao.go index ee68fd73..5af35155 100644 --- a/custom/batch/job-queues/dao.go +++ b/custom/batch/job-queues/dao.go @@ -97,8 +97,9 @@ type JobQueueResource struct { func NewJobQueueResource(queue types.JobQueueDetail) *JobQueueResource { return &JobQueueResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(queue.JobQueueName), - ARN: appaws.Str(queue.JobQueueArn), + ID: appaws.Str(queue.JobQueueName), + ARN: appaws.Str(queue.JobQueueArn), + Data: queue, }, Queue: &queue, } diff --git a/custom/batch/jobs/dao.go b/custom/batch/jobs/dao.go index 0f196249..ec7a5a4a 100644 --- a/custom/batch/jobs/dao.go +++ b/custom/batch/jobs/dao.go @@ -95,8 +95,9 @@ func (d *JobDAO) Get(ctx context.Context, id string) (dao.Resource, error) { return &JobResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(job.JobId), - ARN: appaws.Str(job.JobArn), + ID: appaws.Str(job.JobId), + ARN: appaws.Str(job.JobArn), + Data: job, }, Job: &types.JobSummary{ JobId: job.JobId, @@ -154,8 +155,9 @@ type JobResource struct { func NewJobResource(job types.JobSummary) *JobResource { return &JobResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(job.JobId), - ARN: appaws.Str(job.JobArn), + ID: appaws.Str(job.JobId), + ARN: appaws.Str(job.JobArn), + Data: job, }, Job: &job, } diff --git a/custom/budgets/budgets/dao.go b/custom/budgets/budgets/dao.go index 6b1e60eb..2b73a65c 100644 --- a/custom/budgets/budgets/dao.go +++ b/custom/budgets/budgets/dao.go @@ -112,8 +112,9 @@ type BudgetResource struct { func NewBudgetResource(budget types.Budget, accountID string) *BudgetResource { return &BudgetResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(budget.BudgetName), - ARN: fmt.Sprintf("arn:aws:budgets::%s:budget/%s", accountID, appaws.Str(budget.BudgetName)), + ID: appaws.Str(budget.BudgetName), + ARN: fmt.Sprintf("arn:aws:budgets::%s:budget/%s", accountID, appaws.Str(budget.BudgetName)), + Data: budget, }, Item: budget, AccountID: accountID, diff --git a/custom/budgets/notifications/dao.go b/custom/budgets/notifications/dao.go index 4799f97a..792fd15d 100644 --- a/custom/budgets/notifications/dao.go +++ b/custom/budgets/notifications/dao.go @@ -105,8 +105,9 @@ func NewNotificationResource(notif types.Notification, budgetName string, index ) return &NotificationResource{ BaseResource: dao.BaseResource{ - ID: id, - ARN: "", + ID: id, + ARN: "", + Data: notif, }, Item: notif, BudgetName: budgetName, diff --git a/custom/ce/costs/dao.go b/custom/ce/costs/dao.go index 420d7731..c07f8613 100644 --- a/custom/ce/costs/dao.go +++ b/custom/ce/costs/dao.go @@ -163,7 +163,8 @@ func NewCostResource(group types.Group, start, end string) *CostResource { ID: serviceName, // Pseudo-ARN: Cost Explorer aggregates don't have real ARNs. // Format "ce::" enables internal resource identification. - ARN: fmt.Sprintf("ce::%s", serviceName), + ARN: fmt.Sprintf("ce::%s", serviceName), + Data: serviceName, }, ServiceName: serviceName, Cost: cost, diff --git a/custom/cloudtrail/events/dao.go b/custom/cloudtrail/events/dao.go index b3c0b3bb..5e36685a 100644 --- a/custom/cloudtrail/events/dao.go +++ b/custom/cloudtrail/events/dao.go @@ -126,8 +126,9 @@ type EventResource struct { func NewEventResource(event types.Event) *EventResource { return &EventResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(event.EventId), - ARN: appaws.Str(event.EventId), + ID: appaws.Str(event.EventId), + ARN: appaws.Str(event.EventId), + Data: event, }, Item: event, } diff --git a/custom/cloudtrail/trails/dao.go b/custom/cloudtrail/trails/dao.go index c30b778b..7abcc8b8 100644 --- a/custom/cloudtrail/trails/dao.go +++ b/custom/cloudtrail/trails/dao.go @@ -79,8 +79,9 @@ type TrailResource struct { func NewTrailResource(trail types.Trail) *TrailResource { return &TrailResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(trail.Name), - ARN: appaws.Str(trail.TrailARN), + ID: appaws.Str(trail.Name), + ARN: appaws.Str(trail.TrailARN), + Data: trail, }, Item: trail, } diff --git a/custom/configservice/rules/dao.go b/custom/configservice/rules/dao.go index ff018d25..59667c4b 100644 --- a/custom/configservice/rules/dao.go +++ b/custom/configservice/rules/dao.go @@ -100,8 +100,9 @@ type RuleResource struct { func NewRuleResource(rule types.ConfigRule) *RuleResource { return &RuleResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(rule.ConfigRuleName), - ARN: appaws.Str(rule.ConfigRuleArn), + ID: appaws.Str(rule.ConfigRuleName), + ARN: appaws.Str(rule.ConfigRuleArn), + Data: rule, }, Item: rule, } diff --git a/custom/datasync/locations/dao.go b/custom/datasync/locations/dao.go index 02ed8cea..c136784b 100644 --- a/custom/datasync/locations/dao.go +++ b/custom/datasync/locations/dao.go @@ -96,8 +96,9 @@ func NewLocationResource(loc types.LocationListEntry) *LocationResource { arn := appaws.Str(loc.LocationArn) return &LocationResource{ BaseResource: dao.BaseResource{ - ID: extractLocationID(arn), - ARN: arn, + ID: extractLocationID(arn), + ARN: arn, + Data: loc, }, Location: &loc, } diff --git a/custom/datasync/task-executions/dao.go b/custom/datasync/task-executions/dao.go index 2d19627a..38709797 100644 --- a/custom/datasync/task-executions/dao.go +++ b/custom/datasync/task-executions/dao.go @@ -126,8 +126,9 @@ func NewTaskExecutionResource(exec types.TaskExecutionListEntry) *TaskExecutionR arn := appaws.Str(exec.TaskExecutionArn) return &TaskExecutionResource{ BaseResource: dao.BaseResource{ - ID: extractExecutionID(arn), - ARN: arn, + ID: extractExecutionID(arn), + ARN: arn, + Data: exec, }, Execution: &exec, } diff --git a/custom/datasync/tasks/dao.go b/custom/datasync/tasks/dao.go index 8ead4f40..27b74991 100644 --- a/custom/datasync/tasks/dao.go +++ b/custom/datasync/tasks/dao.go @@ -90,8 +90,9 @@ func (d *TaskDAO) Get(ctx context.Context, id string) (dao.Resource, error) { return &TaskResource{ BaseResource: dao.BaseResource{ - ID: extractTaskID(appaws.Str(output.TaskArn)), - ARN: appaws.Str(output.TaskArn), + ID: extractTaskID(appaws.Str(output.TaskArn)), + ARN: appaws.Str(output.TaskArn), + Data: output, }, Task: &types.TaskListEntry{ TaskArn: output.TaskArn, @@ -150,8 +151,9 @@ func NewTaskResource(task types.TaskListEntry) *TaskResource { arn := appaws.Str(task.TaskArn) return &TaskResource{ BaseResource: dao.BaseResource{ - ID: extractTaskID(arn), - ARN: arn, + ID: extractTaskID(arn), + ARN: arn, + Data: task, }, Task: &task, } diff --git a/custom/detective/graphs/dao.go b/custom/detective/graphs/dao.go index 2b42f9df..78205dcd 100644 --- a/custom/detective/graphs/dao.go +++ b/custom/detective/graphs/dao.go @@ -101,8 +101,9 @@ func NewGraphResource(graph types.Graph) *GraphResource { return &GraphResource{ BaseResource: dao.BaseResource{ - ID: id, - ARN: arn, + ID: id, + ARN: arn, + Data: graph, }, Graph: &graph, } diff --git a/custom/detective/investigations/dao.go b/custom/detective/investigations/dao.go index 14f69851..779f1121 100644 --- a/custom/detective/investigations/dao.go +++ b/custom/detective/investigations/dao.go @@ -109,8 +109,9 @@ func NewInvestigationResource(inv types.InvestigationDetail, graphArn string) *I id := appaws.Str(inv.InvestigationId) return &InvestigationResource{ BaseResource: dao.BaseResource{ - ID: id, - ARN: graphArn + "/investigation/" + id, + ID: id, + ARN: graphArn + "/investigation/" + id, + Data: inv, }, Investigation: &inv, graphArn: graphArn, diff --git a/custom/directconnect/connections/dao.go b/custom/directconnect/connections/dao.go index 3c5dc110..1191c1f9 100644 --- a/custom/directconnect/connections/dao.go +++ b/custom/directconnect/connections/dao.go @@ -79,8 +79,9 @@ type ConnectionResource struct { func NewConnectionResource(conn types.Connection) *ConnectionResource { return &ConnectionResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(conn.ConnectionId), - ARN: "", + ID: appaws.Str(conn.ConnectionId), + ARN: "", + Data: conn, }, Item: conn, } diff --git a/custom/directconnect/virtual-interfaces/dao.go b/custom/directconnect/virtual-interfaces/dao.go index 37416ec7..b1155fff 100644 --- a/custom/directconnect/virtual-interfaces/dao.go +++ b/custom/directconnect/virtual-interfaces/dao.go @@ -86,8 +86,9 @@ type VirtualInterfaceResource struct { func NewVirtualInterfaceResource(vi types.VirtualInterface) *VirtualInterfaceResource { return &VirtualInterfaceResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(vi.VirtualInterfaceId), - ARN: "", + ID: appaws.Str(vi.VirtualInterfaceId), + ARN: "", + Data: vi, }, Item: vi, } diff --git a/custom/ecs/clusters/render.go b/custom/ecs/clusters/render.go index d660ee49..47ed2576 100644 --- a/custom/ecs/clusters/render.go +++ b/custom/ecs/clusters/render.go @@ -203,5 +203,11 @@ func (r *ClusterRenderer) Navigations(resource dao.Resource) []render.Navigation FilterField: "ClusterName", FilterValue: clusterName, }, + { + Key: "D", + Label: "Task Definitions", + Service: "ecs", + Resource: "task-definitions", + }, } } diff --git a/custom/ecs/services/render.go b/custom/ecs/services/render.go index 607d2d9f..70e90835 100644 --- a/custom/ecs/services/render.go +++ b/custom/ecs/services/render.go @@ -363,7 +363,7 @@ func (r *ServiceRenderer) Navigations(resource dao.Resource) []render.Navigation // Extract cluster name from ARN for filtering clusterName := appaws.ExtractResourceName(svc.ClusterArn()) - return []render.Navigation{ + navs := []render.Navigation{ { Key: "t", Label: "Tasks", @@ -389,4 +389,17 @@ func (r *ServiceRenderer) Navigations(resource dao.Resource) []render.Navigation FilterValue: "/ecs/" + svc.GetName(), }, } + + if td := svc.TaskDefinition(); td != "" { + navs = append(navs, render.Navigation{ + Key: "D", + Label: "Task Definition", + Service: "ecs", + Resource: "task-definitions", + FilterField: "TaskDefinition", + FilterValue: appaws.ExtractResourceName(td), + }) + } + + return navs } diff --git a/custom/ecs/task-definitions/constants.go b/custom/ecs/task-definitions/constants.go new file mode 100644 index 00000000..24bc4520 --- /dev/null +++ b/custom/ecs/task-definitions/constants.go @@ -0,0 +1,7 @@ +// Code generated by go generate; DO NOT EDIT. +// To regenerate: task gen-imports + +package taskdefinitions + +// ServiceResourcePath is the canonical path for this resource type. +const ServiceResourcePath = "ecs/task-definitions" diff --git a/custom/ecs/task-definitions/dao.go b/custom/ecs/task-definitions/dao.go new file mode 100644 index 00000000..1d1ae452 --- /dev/null +++ b/custom/ecs/task-definitions/dao.go @@ -0,0 +1,239 @@ +package taskdefinitions + +import ( + "context" + "fmt" + "strings" + + "github.com/aws/aws-sdk-go-v2/service/ecs" + "github.com/aws/aws-sdk-go-v2/service/ecs/types" + + appaws "github.com/clawscli/claws/internal/aws" + "github.com/clawscli/claws/internal/dao" + apperrors "github.com/clawscli/claws/internal/errors" + "github.com/clawscli/claws/internal/log" +) + +type TaskDefinitionDAO struct { + dao.BaseDAO + client *ecs.Client +} + +func NewTaskDefinitionDAO(ctx context.Context) (dao.DAO, error) { + cfg, err := appaws.NewConfig(ctx) + if err != nil { + return nil, apperrors.Wrap(err, "new ecs/task-definitions dao") + } + return &TaskDefinitionDAO{ + BaseDAO: dao.NewBaseDAO("ecs", "task-definitions"), + client: ecs.NewFromConfig(cfg), + }, nil +} + +func (d *TaskDefinitionDAO) List(ctx context.Context) ([]dao.Resource, error) { + taskDefArns, err := appaws.Paginate(ctx, func(token *string) ([]string, *string, error) { + output, err := d.client.ListTaskDefinitions(ctx, &ecs.ListTaskDefinitionsInput{ + Status: types.TaskDefinitionStatusActive, + Sort: types.SortOrderDesc, + NextToken: token, + }) + if err != nil { + return nil, nil, apperrors.Wrap(err, "list task definitions") + } + return output.TaskDefinitionArns, output.NextToken, nil + }) + if err != nil { + return nil, err + } + + seenFamilies := make(map[string]bool) + var latestArns []string + for _, arn := range taskDefArns { + family := extractFamilyFromArn(arn) + if !seenFamilies[family] { + seenFamilies[family] = true + latestArns = append(latestArns, arn) + } + } + + resources := make([]dao.Resource, 0, len(latestArns)) + for _, arn := range latestArns { + output, err := d.client.DescribeTaskDefinition(ctx, &ecs.DescribeTaskDefinitionInput{ + TaskDefinition: &arn, + }) + if err != nil { + log.Warn("failed to describe task definition", "arn", arn, "error", err) + continue + } + if output.TaskDefinition != nil { + resources = append(resources, NewTaskDefinitionResource(*output.TaskDefinition)) + } + } + + return resources, nil +} + +func (d *TaskDefinitionDAO) Get(ctx context.Context, id string) (dao.Resource, error) { + output, err := d.client.DescribeTaskDefinition(ctx, &ecs.DescribeTaskDefinitionInput{ + TaskDefinition: &id, + }) + if err != nil { + return nil, apperrors.Wrapf(err, "describe task definition %s", id) + } + + if output.TaskDefinition == nil { + return nil, fmt.Errorf("task definition not found: %s", id) + } + + return NewTaskDefinitionResource(*output.TaskDefinition), nil +} + +func (d *TaskDefinitionDAO) Delete(ctx context.Context, id string) error { + _, err := d.client.DeregisterTaskDefinition(ctx, &ecs.DeregisterTaskDefinitionInput{ + TaskDefinition: &id, + }) + if err != nil { + return apperrors.Wrapf(err, "deregister task definition %s", id) + } + return nil +} + +func extractFamilyFromArn(arn string) string { + parts := strings.Split(arn, "/") + if len(parts) < 2 { + return arn + } + familyRevision := parts[len(parts)-1] + colonIdx := strings.LastIndex(familyRevision, ":") + if colonIdx == -1 { + return familyRevision + } + return familyRevision[:colonIdx] +} + +type TaskDefinitionResource struct { + dao.BaseResource + Item types.TaskDefinition +} + +func NewTaskDefinitionResource(td types.TaskDefinition) *TaskDefinitionResource { + family := appaws.Str(td.Family) + revision := td.Revision + id := fmt.Sprintf("%s:%d", family, revision) + + return &TaskDefinitionResource{ + BaseResource: dao.BaseResource{ + ID: id, + Name: family, + ARN: appaws.Str(td.TaskDefinitionArn), + Data: td, + }, + Item: td, + } +} + +func (r *TaskDefinitionResource) Family() string { + return appaws.Str(r.Item.Family) +} + +func (r *TaskDefinitionResource) Revision() int32 { + return r.Item.Revision +} + +func (r *TaskDefinitionResource) Status() string { + return string(r.Item.Status) +} + +func (r *TaskDefinitionResource) CPU() string { + return appaws.Str(r.Item.Cpu) +} + +func (r *TaskDefinitionResource) Memory() string { + return appaws.Str(r.Item.Memory) +} + +func (r *TaskDefinitionResource) NetworkMode() string { + return string(r.Item.NetworkMode) +} + +func (r *TaskDefinitionResource) RequiresCompatibilities() []types.Compatibility { + return r.Item.RequiresCompatibilities +} + +func (r *TaskDefinitionResource) ContainerDefinitions() []types.ContainerDefinition { + return r.Item.ContainerDefinitions +} + +func (r *TaskDefinitionResource) TaskRoleArn() string { + return appaws.Str(r.Item.TaskRoleArn) +} + +func (r *TaskDefinitionResource) ExecutionRoleArn() string { + return appaws.Str(r.Item.ExecutionRoleArn) +} + +func (r *TaskDefinitionResource) Volumes() []types.Volume { + return r.Item.Volumes +} + +func (r *TaskDefinitionResource) RuntimePlatform() *types.RuntimePlatform { + return r.Item.RuntimePlatform +} + +func (r *TaskDefinitionResource) GetLogConfiguration(containerName string) *types.LogConfiguration { + containers := r.Item.ContainerDefinitions + if len(containers) == 0 { + return nil + } + + if containerName != "" { + for _, c := range containers { + if appaws.Str(c.Name) == containerName { + return c.LogConfiguration + } + } + return nil + } + + return containers[0].LogConfiguration +} + +func (r *TaskDefinitionResource) GetCloudWatchLogGroup(containerName string) string { + logConfig := r.GetLogConfiguration(containerName) + if logConfig == nil { + return "" + } + + if logConfig.LogDriver != types.LogDriverAwslogs { + return "" + } + + if logConfig.Options == nil { + return "" + } + + return logConfig.Options["awslogs-group"] +} + +func (r *TaskDefinitionResource) GetAllCloudWatchLogGroups() []string { + var groups []string + seen := make(map[string]bool) + + for _, c := range r.Item.ContainerDefinitions { + if c.LogConfiguration == nil { + continue + } + if c.LogConfiguration.LogDriver != types.LogDriverAwslogs { + continue + } + if c.LogConfiguration.Options == nil { + continue + } + if group := c.LogConfiguration.Options["awslogs-group"]; group != "" && !seen[group] { + seen[group] = true + groups = append(groups, group) + } + } + + return groups +} diff --git a/custom/ecs/task-definitions/register.go b/custom/ecs/task-definitions/register.go new file mode 100644 index 00000000..664a0846 --- /dev/null +++ b/custom/ecs/task-definitions/register.go @@ -0,0 +1,20 @@ +package taskdefinitions + +import ( + "context" + + "github.com/clawscli/claws/internal/dao" + "github.com/clawscli/claws/internal/registry" + "github.com/clawscli/claws/internal/render" +) + +func init() { + registry.Global.RegisterCustom("ecs", "task-definitions", registry.Entry{ + DAOFactory: func(ctx context.Context) (dao.DAO, error) { + return NewTaskDefinitionDAO(ctx) + }, + RendererFactory: func() render.Renderer { + return NewTaskDefinitionRenderer() + }, + }) +} diff --git a/custom/ecs/task-definitions/render.go b/custom/ecs/task-definitions/render.go new file mode 100644 index 00000000..115625f1 --- /dev/null +++ b/custom/ecs/task-definitions/render.go @@ -0,0 +1,289 @@ +package taskdefinitions + +import ( + "fmt" + "strings" + + "github.com/aws/aws-sdk-go-v2/service/ecs/types" + + appaws "github.com/clawscli/claws/internal/aws" + "github.com/clawscli/claws/internal/dao" + "github.com/clawscli/claws/internal/render" + "github.com/clawscli/claws/internal/ui" +) + +var _ render.Navigator = (*TaskDefinitionRenderer)(nil) + +type TaskDefinitionRenderer struct { + render.BaseRenderer +} + +func NewTaskDefinitionRenderer() render.Renderer { + return &TaskDefinitionRenderer{ + BaseRenderer: render.BaseRenderer{ + Service: "ecs", + Resource: "task-definitions", + Cols: []render.Column{ + {Name: "FAMILY", Width: 35, Getter: func(r dao.Resource) string { return r.GetName() }}, + {Name: "REV", Width: 5, Getter: getRevision}, + {Name: "STATUS", Width: 10, Getter: getStatus}, + {Name: "CPU", Width: 8, Getter: getCPU}, + {Name: "MEMORY", Width: 8, Getter: getMemory}, + {Name: "NETWORK", Width: 10, Getter: getNetworkMode}, + {Name: "CONTAINERS", Width: 10, Getter: getContainerCount}, + }, + }, + } +} + +func getRevision(r dao.Resource) string { + if td, ok := r.(*TaskDefinitionResource); ok { + return fmt.Sprintf("%d", td.Revision()) + } + return "" +} + +func getStatus(r dao.Resource) string { + if td, ok := r.(*TaskDefinitionResource); ok { + status := td.Status() + switch status { + case "ACTIVE": + return "active" + case "INACTIVE": + return "stopped" + case "DELETE_IN_PROGRESS": + return "deleting" + default: + return strings.ToLower(status) + } + } + return "" +} + +func getCPU(r dao.Resource) string { + if td, ok := r.(*TaskDefinitionResource); ok { + if cpu := td.CPU(); cpu != "" { + return cpu + } + } + return "-" +} + +func getMemory(r dao.Resource) string { + if td, ok := r.(*TaskDefinitionResource); ok { + if mem := td.Memory(); mem != "" { + return mem + } + } + return "-" +} + +func getNetworkMode(r dao.Resource) string { + if td, ok := r.(*TaskDefinitionResource); ok { + mode := td.NetworkMode() + if mode == "" { + return "bridge" + } + return mode + } + return "" +} + +func getContainerCount(r dao.Resource) string { + if td, ok := r.(*TaskDefinitionResource); ok { + return fmt.Sprintf("%d", len(td.ContainerDefinitions())) + } + return "" +} + +func (r *TaskDefinitionRenderer) RenderDetail(resource dao.Resource) string { + td, ok := resource.(*TaskDefinitionResource) + if !ok { + return "" + } + + d := render.NewDetailBuilder() + + d.Title("ECS Task Definition", td.GetID()) + + d.Section("Basic Information") + d.Field("Family", td.Family()) + d.Field("Revision", fmt.Sprintf("%d", td.Revision())) + d.Field("ARN", td.GetARN()) + d.FieldStyled("Status", td.Status(), render.StateColorer()(strings.ToLower(td.Status()))) + + d.Section("Task Configuration") + if cpu := td.CPU(); cpu != "" { + d.Field("CPU", cpu+" units") + } + if mem := td.Memory(); mem != "" { + d.Field("Memory", mem+" MiB") + } + d.Field("Network Mode", td.NetworkMode()) + + if compat := td.RequiresCompatibilities(); len(compat) > 0 { + var compatStr []string + for _, c := range compat { + compatStr = append(compatStr, string(c)) + } + d.Field("Compatibilities", strings.Join(compatStr, ", ")) + } + + if rp := td.RuntimePlatform(); rp != nil { + if rp.OperatingSystemFamily != "" { + d.Field("OS Family", string(rp.OperatingSystemFamily)) + } + if rp.CpuArchitecture != "" { + d.Field("CPU Architecture", string(rp.CpuArchitecture)) + } + } + + if role := td.TaskRoleArn(); role != "" { + d.Section("IAM Roles") + d.Field("Task Role", appaws.ExtractResourceName(role)) + } + if execRole := td.ExecutionRoleArn(); execRole != "" { + if td.TaskRoleArn() == "" { + d.Section("IAM Roles") + } + d.Field("Execution Role", appaws.ExtractResourceName(execRole)) + } + + containers := td.ContainerDefinitions() + if len(containers) > 0 { + d.Section(fmt.Sprintf("Containers (%d)", len(containers))) + for _, c := range containers { + containerName := appaws.Str(c.Name) + d.Line("") + d.FieldStyled(containerName, "", ui.TitleStyle()) + + if c.Image != nil { + d.Field(" Image", *c.Image) + } + if c.Essential != nil && *c.Essential { + d.Field(" Essential", "Yes") + } + if c.Cpu != 0 { + d.Field(" CPU", fmt.Sprintf("%d", c.Cpu)) + } + if c.Memory != nil { + d.Field(" Memory", fmt.Sprintf("%d MiB", *c.Memory)) + } + if c.MemoryReservation != nil { + d.Field(" Memory Reservation", fmt.Sprintf("%d MiB", *c.MemoryReservation)) + } + + if len(c.PortMappings) > 0 { + var ports []string + for _, pm := range c.PortMappings { + if pm.ContainerPort != nil { + port := fmt.Sprintf("%d", *pm.ContainerPort) + if pm.HostPort != nil && *pm.HostPort != *pm.ContainerPort { + port = fmt.Sprintf("%d:%d", *pm.HostPort, *pm.ContainerPort) + } + if pm.Protocol != "" { + port += "/" + strings.ToLower(string(pm.Protocol)) + } + ports = append(ports, port) + } + } + d.Field(" Ports", strings.Join(ports, ", ")) + } + + if c.LogConfiguration != nil { + d.Field(" Log Driver", string(c.LogConfiguration.LogDriver)) + if c.LogConfiguration.LogDriver == types.LogDriverAwslogs && c.LogConfiguration.Options != nil { + if group := c.LogConfiguration.Options["awslogs-group"]; group != "" { + d.FieldStyled(" Log Group", group, ui.SuccessStyle()) + } + if prefix := c.LogConfiguration.Options["awslogs-stream-prefix"]; prefix != "" { + d.Field(" Stream Prefix", prefix) + } + } + } + } + } + + if volumes := td.Volumes(); len(volumes) > 0 { + d.Section("Volumes") + for _, v := range volumes { + if v.Name != nil { + d.Field(*v.Name, "") + if v.Host != nil && v.Host.SourcePath != nil { + d.Field(" Source", *v.Host.SourcePath) + } + if v.EfsVolumeConfiguration != nil { + d.Field(" Type", "EFS") + d.Field(" File System ID", appaws.Str(v.EfsVolumeConfiguration.FileSystemId)) + } + } + } + } + + d.Tags(td.GetTags()) + + return d.String() +} + +func (r *TaskDefinitionRenderer) RenderSummary(resource dao.Resource) []render.SummaryField { + td, ok := resource.(*TaskDefinitionResource) + if !ok { + return r.BaseRenderer.RenderSummary(resource) + } + + fields := []render.SummaryField{ + {Label: "Family", Value: td.Family()}, + {Label: "Revision", Value: fmt.Sprintf("%d", td.Revision())}, + {Label: "ARN", Value: td.GetARN()}, + {Label: "Status", Value: td.Status()}, + } + + if cpu := td.CPU(); cpu != "" { + fields = append(fields, render.SummaryField{Label: "CPU", Value: cpu}) + } + if mem := td.Memory(); mem != "" { + fields = append(fields, render.SummaryField{Label: "Memory", Value: mem}) + } + + fields = append(fields, render.SummaryField{Label: "Network Mode", Value: td.NetworkMode()}) + fields = append(fields, render.SummaryField{Label: "Containers", Value: fmt.Sprintf("%d", len(td.ContainerDefinitions()))}) + + if groups := td.GetAllCloudWatchLogGroups(); len(groups) > 0 { + fields = append(fields, render.SummaryField{Label: "Log Groups", Value: strings.Join(groups, ", ")}) + } + + return fields +} + +func (r *TaskDefinitionRenderer) Navigations(resource dao.Resource) []render.Navigation { + td, ok := resource.(*TaskDefinitionResource) + if !ok { + return nil + } + + var navs []render.Navigation + + if groups := td.GetAllCloudWatchLogGroups(); len(groups) > 0 { + navs = append(navs, render.Navigation{ + Key: "l", + Label: "Logs", + Service: "cloudwatch", + Resource: "log-groups", + FilterField: "LogGroupPrefix", + FilterValue: groups[0], + }) + } + + if role := td.TaskRoleArn(); role != "" { + navs = append(navs, render.Navigation{ + Key: "r", + Label: "Task Role", + Service: "iam", + Resource: "roles", + FilterField: "RoleName", + FilterValue: appaws.ExtractResourceName(role), + }) + } + + return navs +} diff --git a/custom/ecs/tasks/render.go b/custom/ecs/tasks/render.go index 1bc11a4a..96c78cf3 100644 --- a/custom/ecs/tasks/render.go +++ b/custom/ecs/tasks/render.go @@ -329,13 +329,21 @@ func (r *TaskRenderer) Navigations(resource dao.Resource) []render.Navigation { }) } - // Add logs navigation - use task definition family as log group prefix if taskDef := task.TaskDefinitionArn(); taskDef != "" { - // Task definition ARN: arn:aws:ecs:region:account:task-definition/family:revision taskDefName := appaws.ExtractResourceName(taskDef) - // Remove revision number (e.g., "my-task:5" -> "my-task") - if idx := strings.LastIndex(taskDefName, ":"); idx > 0 { - taskDefName = taskDefName[:idx] + + navs = append(navs, render.Navigation{ + Key: "D", + Label: "Task Definition", + Service: "ecs", + Resource: "task-definitions", + FilterField: "TaskDefinition", + FilterValue: taskDefName, + }) + + family := taskDefName + if idx := strings.LastIndex(family, ":"); idx > 0 { + family = family[:idx] } navs = append(navs, render.Navigation{ Key: "l", @@ -343,7 +351,7 @@ func (r *TaskRenderer) Navigations(resource dao.Resource) []render.Navigation { Service: "cloudwatch", Resource: "log-groups", FilterField: "LogGroupPrefix", - FilterValue: "/ecs/" + taskDefName, + FilterValue: "/ecs/" + family, }) } diff --git a/custom/emr/clusters/dao.go b/custom/emr/clusters/dao.go index 9cf06f1c..88d1ee00 100644 --- a/custom/emr/clusters/dao.go +++ b/custom/emr/clusters/dao.go @@ -78,8 +78,9 @@ func (d *ClusterDAO) Get(ctx context.Context, id string) (dao.Resource, error) { return &ClusterResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(cluster.Id), - ARN: appaws.Str(cluster.ClusterArn), + ID: appaws.Str(cluster.Id), + ARN: appaws.Str(cluster.ClusterArn), + Data: cluster, }, Cluster: &types.ClusterSummary{ Id: cluster.Id, @@ -135,8 +136,9 @@ type ClusterResource struct { func NewClusterResource(cluster types.ClusterSummary) *ClusterResource { return &ClusterResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(cluster.Id), - ARN: appaws.Str(cluster.ClusterArn), + ID: appaws.Str(cluster.Id), + ARN: appaws.Str(cluster.ClusterArn), + Data: cluster, }, Cluster: &cluster, } diff --git a/custom/emr/steps/dao.go b/custom/emr/steps/dao.go index 366d0f78..e692c7bf 100644 --- a/custom/emr/steps/dao.go +++ b/custom/emr/steps/dao.go @@ -76,8 +76,9 @@ func (d *StepDAO) Get(ctx context.Context, id string) (dao.Resource, error) { step := output.Step return &StepResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(step.Id), - ARN: "", + ID: appaws.Str(step.Id), + ARN: "", + Data: step, }, Step: &types.StepSummary{ Id: step.Id, @@ -118,8 +119,9 @@ type StepResource struct { func NewStepResource(step types.StepSummary, clusterId string) *StepResource { return &StepResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(step.Id), - ARN: "", + ID: appaws.Str(step.Id), + ARN: "", + Data: step, }, Step: &step, clusterId: clusterId, diff --git a/custom/fms/policies/dao.go b/custom/fms/policies/dao.go index acca7526..e4d41e93 100644 --- a/custom/fms/policies/dao.go +++ b/custom/fms/policies/dao.go @@ -84,8 +84,9 @@ type PolicyResource struct { func NewPolicyResource(policy types.PolicySummary) *PolicyResource { return &PolicyResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(policy.PolicyId), - ARN: appaws.Str(policy.PolicyArn), + ID: appaws.Str(policy.PolicyId), + ARN: appaws.Str(policy.PolicyArn), + Data: policy, }, Summary: &policy, } @@ -95,8 +96,9 @@ func NewPolicyResource(policy types.PolicySummary) *PolicyResource { func NewPolicyResourceFromDetail(policy types.Policy, arn *string) *PolicyResource { return &PolicyResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(policy.PolicyId), - ARN: appaws.Str(arn), + ID: appaws.Str(policy.PolicyId), + ARN: appaws.Str(arn), + Data: policy, }, Detail: &policy, } diff --git a/custom/glue/crawlers/dao.go b/custom/glue/crawlers/dao.go index 70cd37e9..432e9779 100644 --- a/custom/glue/crawlers/dao.go +++ b/custom/glue/crawlers/dao.go @@ -84,8 +84,9 @@ type CrawlerResource struct { func NewCrawlerResource(crawler types.Crawler) *CrawlerResource { return &CrawlerResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(crawler.Name), - ARN: "", + ID: appaws.Str(crawler.Name), + ARN: "", + Data: crawler, }, Item: crawler, } diff --git a/custom/glue/databases/dao.go b/custom/glue/databases/dao.go index 6917393f..41d13428 100644 --- a/custom/glue/databases/dao.go +++ b/custom/glue/databases/dao.go @@ -84,8 +84,9 @@ type DatabaseResource struct { func NewDatabaseResource(db types.Database) *DatabaseResource { return &DatabaseResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(db.Name), - ARN: appaws.Str(db.CatalogId), + ID: appaws.Str(db.Name), + ARN: appaws.Str(db.CatalogId), + Data: db, }, Item: db, } diff --git a/custom/glue/job-runs/dao.go b/custom/glue/job-runs/dao.go index 1f5d7973..985a013b 100644 --- a/custom/glue/job-runs/dao.go +++ b/custom/glue/job-runs/dao.go @@ -109,8 +109,9 @@ type JobRunResource struct { func NewJobRunResource(run types.JobRun) *JobRunResource { return &JobRunResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(run.Id), - ARN: "", + ID: appaws.Str(run.Id), + ARN: "", + Data: run, }, Item: run, } diff --git a/custom/glue/jobs/dao.go b/custom/glue/jobs/dao.go index 0491f121..38a16788 100644 --- a/custom/glue/jobs/dao.go +++ b/custom/glue/jobs/dao.go @@ -84,8 +84,9 @@ type JobResource struct { func NewJobResource(job types.Job) *JobResource { return &JobResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(job.Name), - ARN: "", // Glue jobs don't have ARN in the response + ID: appaws.Str(job.Name), + ARN: "", // Glue jobs don't have ARN in the response + Data: job, }, Item: job, } diff --git a/custom/glue/tables/dao.go b/custom/glue/tables/dao.go index 55927805..a25921d7 100644 --- a/custom/glue/tables/dao.go +++ b/custom/glue/tables/dao.go @@ -105,8 +105,9 @@ type TableResource struct { func NewTableResource(table types.Table, databaseName string) *TableResource { return &TableResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(table.Name), - ARN: "", + ID: appaws.Str(table.Name), + ARN: "", + Data: table, }, Item: table, DatabaseName: databaseName, diff --git a/custom/health/events/dao.go b/custom/health/events/dao.go index 3ff44a05..b3f84418 100644 --- a/custom/health/events/dao.go +++ b/custom/health/events/dao.go @@ -121,8 +121,9 @@ type EventResource struct { func NewEventResource(event types.Event) *EventResource { return &EventResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(event.Arn), - ARN: appaws.Str(event.Arn), + ID: appaws.Str(event.Arn), + ARN: appaws.Str(event.Arn), + Data: event, }, Item: event, } diff --git a/custom/license-manager/configurations/dao.go b/custom/license-manager/configurations/dao.go index 3e19fc42..182cfa34 100644 --- a/custom/license-manager/configurations/dao.go +++ b/custom/license-manager/configurations/dao.go @@ -62,8 +62,9 @@ func (d *ConfigurationDAO) Get(ctx context.Context, arn string) (dao.Resource, e return &ConfigurationResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(output.Name), - ARN: appaws.Str(output.LicenseConfigurationArn), + ID: appaws.Str(output.Name), + ARN: appaws.Str(output.LicenseConfigurationArn), + Data: output, }, Config: &types.LicenseConfiguration{ LicenseConfigurationArn: output.LicenseConfigurationArn, @@ -98,8 +99,9 @@ type ConfigurationResource struct { func NewConfigurationResource(config types.LicenseConfiguration) *ConfigurationResource { return &ConfigurationResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(config.Name), - ARN: appaws.Str(config.LicenseConfigurationArn), + ID: appaws.Str(config.Name), + ARN: appaws.Str(config.LicenseConfigurationArn), + Data: config, }, Config: &config, } diff --git a/custom/license-manager/grants/dao.go b/custom/license-manager/grants/dao.go index a0e79a74..6ed8ad6b 100644 --- a/custom/license-manager/grants/dao.go +++ b/custom/license-manager/grants/dao.go @@ -115,8 +115,9 @@ func NewGrantResource(grant types.Grant) *GrantResource { } return &GrantResource{ BaseResource: dao.BaseResource{ - ID: id, - ARN: arn, + ID: id, + ARN: arn, + Data: grant, }, Grant: &grant, } diff --git a/custom/license-manager/licenses/dao.go b/custom/license-manager/licenses/dao.go index a5f729e1..85868678 100644 --- a/custom/license-manager/licenses/dao.go +++ b/custom/license-manager/licenses/dao.go @@ -99,8 +99,9 @@ func NewLicenseResource(license types.License) *LicenseResource { } return &LicenseResource{ BaseResource: dao.BaseResource{ - ID: id, - ARN: arn, + ID: id, + ARN: arn, + Data: license, }, License: &license, } diff --git a/custom/macie2/buckets/dao.go b/custom/macie2/buckets/dao.go index f8c09165..7f8bafde 100644 --- a/custom/macie2/buckets/dao.go +++ b/custom/macie2/buckets/dao.go @@ -81,8 +81,9 @@ type BucketResource struct { func NewBucketResource(bucket types.BucketMetadata) *BucketResource { return &BucketResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(bucket.BucketName), - ARN: appaws.Str(bucket.BucketArn), + ID: appaws.Str(bucket.BucketName), + ARN: appaws.Str(bucket.BucketArn), + Data: bucket, }, Bucket: &bucket, } diff --git a/custom/macie2/classification-jobs/dao.go b/custom/macie2/classification-jobs/dao.go index 6c9f0ac7..16c4748e 100644 --- a/custom/macie2/classification-jobs/dao.go +++ b/custom/macie2/classification-jobs/dao.go @@ -62,8 +62,9 @@ func (d *ClassificationJobDAO) Get(ctx context.Context, id string) (dao.Resource } return &ClassificationJobResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(output.JobId), - ARN: appaws.Str(output.JobArn), + ID: appaws.Str(output.JobId), + ARN: appaws.Str(output.JobArn), + Data: output, }, Job: &types.JobSummary{ JobId: output.JobId, @@ -98,8 +99,9 @@ type ClassificationJobResource struct { func NewClassificationJobResource(job types.JobSummary) *ClassificationJobResource { return &ClassificationJobResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(job.JobId), - ARN: "", + ID: appaws.Str(job.JobId), + ARN: "", + Data: job, }, Job: &job, } diff --git a/custom/macie2/findings/dao.go b/custom/macie2/findings/dao.go index b8c61feb..cbf736d7 100644 --- a/custom/macie2/findings/dao.go +++ b/custom/macie2/findings/dao.go @@ -118,8 +118,9 @@ type FindingResource struct { func NewFindingResource(finding types.Finding) *FindingResource { return &FindingResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(finding.Id), - ARN: "", + ID: appaws.Str(finding.Id), + ARN: "", + Data: finding, }, Finding: &finding, } diff --git a/custom/network-firewall/firewall-policies/dao.go b/custom/network-firewall/firewall-policies/dao.go index 90dfadcf..c1ee0463 100644 --- a/custom/network-firewall/firewall-policies/dao.go +++ b/custom/network-firewall/firewall-policies/dao.go @@ -96,8 +96,9 @@ func NewFirewallPolicyResource(p types.FirewallPolicyMetadata) *FirewallPolicyRe func NewFirewallPolicyResourceFromDetail(resp *types.FirewallPolicyResponse, p *types.FirewallPolicy) *FirewallPolicyResource { return &FirewallPolicyResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(resp.FirewallPolicyName), - ARN: appaws.Str(resp.FirewallPolicyArn), + ID: appaws.Str(resp.FirewallPolicyName), + ARN: appaws.Str(resp.FirewallPolicyArn), + Data: p, }, Response: resp, Detail: p, diff --git a/custom/network-firewall/firewalls/dao.go b/custom/network-firewall/firewalls/dao.go index 9dca7cfb..6a0c0103 100644 --- a/custom/network-firewall/firewalls/dao.go +++ b/custom/network-firewall/firewalls/dao.go @@ -96,8 +96,9 @@ func NewFirewallResource(fw types.FirewallMetadata) *FirewallResource { func NewFirewallResourceFromDetail(fw types.Firewall, status *types.FirewallStatus) *FirewallResource { return &FirewallResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(fw.FirewallName), - ARN: appaws.Str(fw.FirewallArn), + ID: appaws.Str(fw.FirewallName), + ARN: appaws.Str(fw.FirewallArn), + Data: fw, }, Detail: &fw, Status: status, diff --git a/custom/network-firewall/rule-groups/dao.go b/custom/network-firewall/rule-groups/dao.go index 9f39698b..85beec00 100644 --- a/custom/network-firewall/rule-groups/dao.go +++ b/custom/network-firewall/rule-groups/dao.go @@ -85,8 +85,9 @@ type RuleGroupResource struct { func NewRuleGroupResource(rg types.RuleGroupMetadata) *RuleGroupResource { return &RuleGroupResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(rg.Name), - ARN: appaws.Str(rg.Arn), + ID: appaws.Str(rg.Name), + ARN: appaws.Str(rg.Arn), + Data: rg, }, Metadata: &rg, } @@ -96,8 +97,9 @@ func NewRuleGroupResource(rg types.RuleGroupMetadata) *RuleGroupResource { func NewRuleGroupResourceFromDetail(resp *types.RuleGroupResponse, rg *types.RuleGroup) *RuleGroupResource { return &RuleGroupResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(resp.RuleGroupName), - ARN: appaws.Str(resp.RuleGroupArn), + ID: appaws.Str(resp.RuleGroupName), + ARN: appaws.Str(resp.RuleGroupArn), + Data: resp, }, Response: resp, Detail: rg, diff --git a/custom/organizations/accounts/dao.go b/custom/organizations/accounts/dao.go index 28c89f7c..fe9005b2 100644 --- a/custom/organizations/accounts/dao.go +++ b/custom/organizations/accounts/dao.go @@ -84,8 +84,9 @@ type AccountResource struct { func NewAccountResource(account types.Account) *AccountResource { return &AccountResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(account.Id), - ARN: appaws.Str(account.Arn), + ID: appaws.Str(account.Id), + ARN: appaws.Str(account.Arn), + Data: account, }, Account: &account, } diff --git a/custom/organizations/ous/dao.go b/custom/organizations/ous/dao.go index 9face24a..bd0383f2 100644 --- a/custom/organizations/ous/dao.go +++ b/custom/organizations/ous/dao.go @@ -90,8 +90,9 @@ type OUResource struct { func NewOUResource(ou types.OrganizationalUnit) *OUResource { return &OUResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(ou.Id), - ARN: appaws.Str(ou.Arn), + ID: appaws.Str(ou.Id), + ARN: appaws.Str(ou.Arn), + Data: ou, }, OU: &ou, } diff --git a/custom/organizations/policies/dao.go b/custom/organizations/policies/dao.go index dab2dad1..00bb6153 100644 --- a/custom/organizations/policies/dao.go +++ b/custom/organizations/policies/dao.go @@ -75,8 +75,9 @@ func (d *PolicyDAO) Get(ctx context.Context, id string) (dao.Resource, error) { } return &PolicyResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(output.Policy.PolicySummary.Id), - ARN: appaws.Str(output.Policy.PolicySummary.Arn), + ID: appaws.Str(output.Policy.PolicySummary.Id), + ARN: appaws.Str(output.Policy.PolicySummary.Arn), + Data: output, }, Policy: output.Policy.PolicySummary, Content: appaws.Str(output.Policy.Content), @@ -105,8 +106,9 @@ type PolicyResource struct { func NewPolicyResource(policy types.PolicySummary) *PolicyResource { return &PolicyResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(policy.Id), - ARN: appaws.Str(policy.Arn), + ID: appaws.Str(policy.Id), + ARN: appaws.Str(policy.Arn), + Data: policy, }, Policy: &policy, } diff --git a/custom/organizations/roots/dao.go b/custom/organizations/roots/dao.go index 4fa78b76..e814f691 100644 --- a/custom/organizations/roots/dao.go +++ b/custom/organizations/roots/dao.go @@ -81,8 +81,9 @@ type RootResource struct { func NewRootResource(root types.Root) *RootResource { return &RootResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(root.Id), - ARN: appaws.Str(root.Arn), + ID: appaws.Str(root.Id), + ARN: appaws.Str(root.Arn), + Data: root, }, Root: &root, } diff --git a/custom/redshift/clusters/dao.go b/custom/redshift/clusters/dao.go index bee473b2..1ee9c4c2 100644 --- a/custom/redshift/clusters/dao.go +++ b/custom/redshift/clusters/dao.go @@ -90,8 +90,9 @@ type ClusterResource struct { func NewClusterResource(cluster types.Cluster) *ClusterResource { return &ClusterResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(cluster.ClusterIdentifier), - ARN: "", + ID: appaws.Str(cluster.ClusterIdentifier), + ARN: "", + Data: cluster, }, Cluster: &cluster, } diff --git a/custom/redshift/snapshots/dao.go b/custom/redshift/snapshots/dao.go index 357e8e4d..3ca6d431 100644 --- a/custom/redshift/snapshots/dao.go +++ b/custom/redshift/snapshots/dao.go @@ -98,8 +98,9 @@ type SnapshotResource struct { func NewSnapshotResource(snapshot types.Snapshot) *SnapshotResource { return &SnapshotResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(snapshot.SnapshotIdentifier), - ARN: "", + ID: appaws.Str(snapshot.SnapshotIdentifier), + ARN: "", + Data: snapshot, }, Snapshot: &snapshot, } diff --git a/custom/s3/buckets/dao.go b/custom/s3/buckets/dao.go index e3a62336..724cf00d 100644 --- a/custom/s3/buckets/dao.go +++ b/custom/s3/buckets/dao.go @@ -78,6 +78,7 @@ func (d *BucketDAO) Get(ctx context.Context, id string) (dao.Resource, error) { BaseResource: dao.BaseResource{ ID: id, Name: id, + Data: id, }, BucketName: id, Region: region, @@ -273,6 +274,7 @@ func NewBucketResource(bucket types.Bucket) *BucketResource { BaseResource: dao.BaseResource{ ID: name, Name: name, + Data: name, }, BucketName: name, CreationDate: appaws.Time(bucket.CreationDate), diff --git a/custom/sagemaker/endpoints/dao.go b/custom/sagemaker/endpoints/dao.go index 30996292..bf1bea9f 100644 --- a/custom/sagemaker/endpoints/dao.go +++ b/custom/sagemaker/endpoints/dao.go @@ -101,8 +101,9 @@ type EndpointResource struct { func NewEndpointResource(endpoint types.EndpointSummary) *EndpointResource { return &EndpointResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(endpoint.EndpointName), - ARN: appaws.Str(endpoint.EndpointArn), + ID: appaws.Str(endpoint.EndpointName), + ARN: appaws.Str(endpoint.EndpointArn), + Data: endpoint, }, Endpoint: endpoint, } diff --git a/custom/sagemaker/models/dao.go b/custom/sagemaker/models/dao.go index 58023783..8eeb580b 100644 --- a/custom/sagemaker/models/dao.go +++ b/custom/sagemaker/models/dao.go @@ -109,8 +109,9 @@ type ModelResource struct { func NewModelResource(model types.ModelSummary) *ModelResource { return &ModelResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(model.ModelName), - ARN: appaws.Str(model.ModelArn), + ID: appaws.Str(model.ModelName), + ARN: appaws.Str(model.ModelArn), + Data: model, }, Model: model, } diff --git a/custom/sagemaker/notebooks/dao.go b/custom/sagemaker/notebooks/dao.go index 61dc0fdd..b03789b2 100644 --- a/custom/sagemaker/notebooks/dao.go +++ b/custom/sagemaker/notebooks/dao.go @@ -124,8 +124,9 @@ type NotebookResource struct { func NewNotebookResource(notebook types.NotebookInstanceSummary) *NotebookResource { return &NotebookResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(notebook.NotebookInstanceName), - ARN: appaws.Str(notebook.NotebookInstanceArn), + ID: appaws.Str(notebook.NotebookInstanceName), + ARN: appaws.Str(notebook.NotebookInstanceArn), + Data: notebook, }, Notebook: notebook, } diff --git a/custom/sagemaker/training-jobs/dao.go b/custom/sagemaker/training-jobs/dao.go index be4c6264..ae207e24 100644 --- a/custom/sagemaker/training-jobs/dao.go +++ b/custom/sagemaker/training-jobs/dao.go @@ -159,8 +159,9 @@ type TrainingJobResource struct { func NewTrainingJobResource(job types.TrainingJobSummary) *TrainingJobResource { return &TrainingJobResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(job.TrainingJobName), - ARN: appaws.Str(job.TrainingJobArn), + ID: appaws.Str(job.TrainingJobName), + ARN: appaws.Str(job.TrainingJobArn), + Data: job, }, Job: job, } diff --git a/custom/securityhub/findings/dao.go b/custom/securityhub/findings/dao.go index df4fc97a..1ded46bf 100644 --- a/custom/securityhub/findings/dao.go +++ b/custom/securityhub/findings/dao.go @@ -43,7 +43,7 @@ func (d *FindingDAO) List(ctx context.Context) ([]dao.Resource, error) { func (d *FindingDAO) ListPage(ctx context.Context, pageSize int, pageToken string) ([]dao.Resource, string, error) { maxResults := int32(pageSize) if maxResults > 100 { - maxResults = 100 // AWS API max + maxResults = 100 } input := &securityhub.GetFindingsInput{ @@ -53,6 +53,18 @@ func (d *FindingDAO) ListPage(ctx context.Context, pageSize int, pageToken strin input.NextToken = &pageToken } + showResolved := dao.GetFilterFromContext(ctx, "ShowResolved") + if showResolved != "true" { + input.Filters = &types.AwsSecurityFindingFilters{ + RecordState: []types.StringFilter{ + {Value: stringPtr("ACTIVE"), Comparison: types.StringFilterComparisonEquals}, + }, + WorkflowStatus: []types.StringFilter{ + {Value: stringPtr("RESOLVED"), Comparison: types.StringFilterComparisonNotEquals}, + }, + } + } + output, err := d.client.GetFindings(ctx, input) if err != nil { return nil, "", apperrors.Wrap(err, "get security hub findings") @@ -71,6 +83,8 @@ func (d *FindingDAO) ListPage(ctx context.Context, pageSize int, pageToken strin return resources, nextToken, nil } +func stringPtr(s string) *string { return &s } + // Get returns a specific finding by ID. func (d *FindingDAO) Get(ctx context.Context, id string) (dao.Resource, error) { output, err := d.client.GetFindings(ctx, &securityhub.GetFindingsInput{ @@ -107,8 +121,9 @@ type FindingResource struct { func NewFindingResource(finding types.AwsSecurityFinding) *FindingResource { return &FindingResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(finding.Id), - ARN: appaws.Str(finding.Id), + ID: appaws.Str(finding.Id), + ARN: appaws.Str(finding.Id), + Data: finding, }, Item: finding, } diff --git a/custom/securityhub/findings/render.go b/custom/securityhub/findings/render.go index 12d0fd98..ac45bc2b 100644 --- a/custom/securityhub/findings/render.go +++ b/custom/securityhub/findings/render.go @@ -196,3 +196,9 @@ func (r *FindingRenderer) RenderSummary(resource dao.Resource) []render.SummaryF return fields } + +func (r *FindingRenderer) ListToggles() []render.Toggle { + return []render.Toggle{ + {Key: "r", ContextKey: "ShowResolved", LabelOn: "all", LabelOff: "active"}, + } +} diff --git a/custom/transcribe/jobs/dao.go b/custom/transcribe/jobs/dao.go index 12399d29..81da54d8 100644 --- a/custom/transcribe/jobs/dao.go +++ b/custom/transcribe/jobs/dao.go @@ -85,8 +85,9 @@ type JobResource struct { func NewJobResource(job types.TranscriptionJobSummary) *JobResource { return &JobResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(job.TranscriptionJobName), - ARN: "", + ID: appaws.Str(job.TranscriptionJobName), + ARN: "", + Data: job, }, Summary: &job, } @@ -96,8 +97,9 @@ func NewJobResource(job types.TranscriptionJobSummary) *JobResource { func NewJobResourceFromDetail(job types.TranscriptionJob) *JobResource { return &JobResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(job.TranscriptionJobName), - ARN: "", + ID: appaws.Str(job.TranscriptionJobName), + ARN: "", + Data: job, }, Detail: &job, } diff --git a/custom/transfer/servers/dao.go b/custom/transfer/servers/dao.go index 6f6e736b..614d6ba6 100644 --- a/custom/transfer/servers/dao.go +++ b/custom/transfer/servers/dao.go @@ -84,8 +84,9 @@ type ServerResource struct { func NewServerResource(srv types.ListedServer) *ServerResource { return &ServerResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(srv.ServerId), - ARN: appaws.Str(srv.Arn), + ID: appaws.Str(srv.ServerId), + ARN: appaws.Str(srv.Arn), + Data: srv, }, Summary: &srv, } @@ -95,8 +96,9 @@ func NewServerResource(srv types.ListedServer) *ServerResource { func NewServerResourceFromDetail(srv types.DescribedServer) *ServerResource { return &ServerResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(srv.ServerId), - ARN: appaws.Str(srv.Arn), + ID: appaws.Str(srv.ServerId), + ARN: appaws.Str(srv.Arn), + Data: srv, }, Detail: &srv, } diff --git a/custom/transfer/users/dao.go b/custom/transfer/users/dao.go index 55f7b892..1665f6f5 100644 --- a/custom/transfer/users/dao.go +++ b/custom/transfer/users/dao.go @@ -104,8 +104,9 @@ type UserResource struct { func NewUserResource(user types.ListedUser, serverId string) *UserResource { return &UserResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(user.UserName), - ARN: appaws.Str(user.Arn), + ID: appaws.Str(user.UserName), + ARN: appaws.Str(user.Arn), + Data: user, }, Summary: &user, ServerId: serverId, @@ -116,8 +117,9 @@ func NewUserResource(user types.ListedUser, serverId string) *UserResource { func NewUserResourceFromDetail(user types.DescribedUser, serverId string) *UserResource { return &UserResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(user.UserName), - ARN: appaws.Str(user.Arn), + ID: appaws.Str(user.UserName), + ARN: appaws.Str(user.Arn), + Data: user, }, Detail: &user, ServerId: serverId, diff --git a/custom/vpc/endpoints/dao.go b/custom/vpc/endpoints/dao.go index fecccd32..54cc41f0 100644 --- a/custom/vpc/endpoints/dao.go +++ b/custom/vpc/endpoints/dao.go @@ -88,8 +88,9 @@ type VpcEndpointResource struct { func NewVpcEndpointResource(endpoint types.VpcEndpoint) *VpcEndpointResource { return &VpcEndpointResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(endpoint.VpcEndpointId), - ARN: "", + ID: appaws.Str(endpoint.VpcEndpointId), + ARN: "", + Data: endpoint, }, Item: endpoint, } diff --git a/custom/vpc/tgw-attachments/dao.go b/custom/vpc/tgw-attachments/dao.go index 4f0a46e9..cc4961b3 100644 --- a/custom/vpc/tgw-attachments/dao.go +++ b/custom/vpc/tgw-attachments/dao.go @@ -116,8 +116,9 @@ type TGWAttachmentResource struct { func NewTGWAttachmentResource(att types.TransitGatewayAttachment) *TGWAttachmentResource { return &TGWAttachmentResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(att.TransitGatewayAttachmentId), - ARN: "", + ID: appaws.Str(att.TransitGatewayAttachmentId), + ARN: "", + Data: att, }, Item: att, } diff --git a/custom/vpc/transit-gateways/dao.go b/custom/vpc/transit-gateways/dao.go index 1466a002..4c6141b9 100644 --- a/custom/vpc/transit-gateways/dao.go +++ b/custom/vpc/transit-gateways/dao.go @@ -88,8 +88,9 @@ type TransitGatewayResource struct { func NewTransitGatewayResource(tgw types.TransitGateway) *TransitGatewayResource { return &TransitGatewayResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(tgw.TransitGatewayId), - ARN: appaws.Str(tgw.TransitGatewayArn), + ID: appaws.Str(tgw.TransitGatewayId), + ARN: appaws.Str(tgw.TransitGatewayArn), + Data: tgw, }, Item: tgw, } diff --git a/custom/xray/groups/dao.go b/custom/xray/groups/dao.go index 891dc0bd..1b223b19 100644 --- a/custom/xray/groups/dao.go +++ b/custom/xray/groups/dao.go @@ -89,8 +89,9 @@ func NewGroupResource(group types.GroupSummary) *GroupResource { } return &GroupResource{ BaseResource: dao.BaseResource{ - ID: appaws.Str(group.GroupName), - ARN: appaws.Str(group.GroupARN), + ID: appaws.Str(group.GroupName), + ARN: appaws.Str(group.GroupARN), + Data: group, }, Item: &group, InsightsEnabled: insightsEnabled, diff --git a/docs/ai-chat.md b/docs/ai-chat.md new file mode 100644 index 00000000..80080914 --- /dev/null +++ b/docs/ai-chat.md @@ -0,0 +1,147 @@ +# AI Chat + +AI Chat provides an intelligent assistant that helps you analyze AWS resources, compare configurations, identify security risks, and navigate documentation. + +## Overview + +Press `A` in the following views to open AI Chat: +- **Resource Browser** (list view) - Analyzes visible resources +- **Detail View** - Analyzes the selected resource +- **Diff View** - Compares two resources side-by-side + +The assistant has access to: +- Current resource context (what you're viewing) +- Active AWS profile and region +- Tools to query resources, fetch logs, and search AWS documentation + +## Setup + +### 1. IAM Permissions + +The AI Chat feature uses Amazon Bedrock. You need the following permission: + +```json +{ + "Effect": "Allow", + "Action": "bedrock:InvokeModelWithResponseStream", + "Resource": "arn:aws:bedrock:*::foundation-model/*" +} +``` + +See [IAM Permissions](iam-permissions.md#ai-chat-optional) for details. + +### 2. Configuration + +Configure AI Chat in `~/.config/claws/config.yaml`: + +```yaml +ai: + profile: "" # AWS profile for Bedrock (empty = use current profile) + region: "" # AWS region for Bedrock (empty = use current region) + model: "global.anthropic.claude-haiku-4-5-20251001-v1:0" # Bedrock model ID + max_sessions: 100 # Max stored sessions (default: 100) + max_tokens: 16000 # Max response tokens (default: 16000) + thinking_budget: 8000 # Extended thinking token budget (default: 8000) + max_tool_rounds: 15 # Max tool execution rounds per message (default: 15) + max_tool_calls_per_query: 50 # Max tool calls per user query (default: 50) + save_sessions: false # Persist chat sessions to disk (default: false) +``` + +See [Configuration](configuration.md) for all options. + +## Usage + +### Opening Chat + +Press `A` in list/detail/diff views to open the AI Chat overlay. + +### What the AI Can Do + +- List and query AWS resources across services and regions +- Get detailed information about specific resources +- Fetch CloudWatch logs for supported resources (Lambda, ECS, CodeBuild, etc.) +- Search AWS documentation + +The AI automatically uses the current profile, region, and resource context from your view. + +### Context Awareness + +The assistant automatically receives context based on your current view: + +**Resource Browser (List View)**: +``` +Currently viewing: ec2/instances (us-west-2, production profile) +Visible resources: [i-abc123, i-def456, ...] +``` + +**Detail View**: +``` +Currently viewing: ec2/instances/i-abc123 (us-west-2, production profile) +Resource details: {...} +``` + +**Diff View**: +``` +Comparing two resources: +Left: ec2/instances/i-abc123 +Right: ec2/instances/i-def456 +``` + +### Session History + +Press `Ctrl+H` to view and resume previous chat sessions. + +## Keyboard Shortcuts + +| Key | Action | +|-----|--------| +| `A` | Open AI Chat (in list/detail/diff views) | +| `Ctrl+H` | Session history | +| `Enter` | Send message | +| `Esc` | Close chat / Cancel stream | +| `Ctrl+C` | Cancel stream | + +## Extended Thinking + +The assistant supports extended thinking for complex queries. When enabled, you'll see a thinking indicator showing the assistant's reasoning process before the final response. + +Configure thinking budget in config.yaml: +```yaml +ai: + thinking_budget: 8000 # Max tokens for extended thinking (default: 8000) +``` + +## Troubleshooting + +### "Bedrock not available in this region" + +Bedrock is not available in all AWS regions. Configure a supported region in your config: + +```yaml +ai: + region: "us-west-2" # Use a region where Bedrock is available +``` + +### "Access Denied" errors + +Ensure your IAM role/user has the required Bedrock permissions. See [IAM Permissions](iam-permissions.md#ai-chat-optional). + +### Tool call limit reached + +If you see "Tool call limit reached", the assistant made too many tool calls in a single query. Increase the limit: + +```yaml +ai: + max_tool_calls_per_query: 100 # Increase from default 50 +``` + +### Session not persisting + +Enable session persistence in config: + +```yaml +ai: + save_sessions: true # Default: false +``` + +Sessions are stored in `~/.config/claws/sessions/`. diff --git a/docs/configuration.md b/docs/configuration.md index 521b8bfc..612f9241 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -40,6 +40,17 @@ startup: # Applied on launch if present navigation: max_stack_size: 100 # Max navigation history depth (default: 100) +ai: + profile: "" # AWS profile for Bedrock (empty = use current profile) + region: "" # AWS region for Bedrock (empty = use current region) + model: "global.anthropic.claude-haiku-4-5-20251001-v1:0" # Bedrock model ID + max_sessions: 100 # Max stored sessions (default: 100) + max_tokens: 16000 # Max response tokens (default: 16000) + thinking_budget: 8000 # Extended thinking token budget (default: 8000) + max_tool_rounds: 15 # Max tool execution rounds per message (default: 15) + max_tool_calls_per_query: 50 # Max tool calls per user query (default: 50) + save_sessions: false # Persist chat sessions to disk (default: false) + theme: nord # Preset: dark, light, nord, dracula, gruvbox, catppuccin # Or use preset with custom overrides: diff --git a/docs/iam-permissions.md b/docs/iam-permissions.md index 64ce2a41..e8628d02 100644 --- a/docs/iam-permissions.md +++ b/docs/iam-permissions.md @@ -6,6 +6,33 @@ claws requires appropriate IAM permissions to access AWS resources. The permissi For basic read-only browsing, claws needs `Describe*`, `List*`, and `Get*` permissions for the services you want to access. +## AI Chat (Optional) + +The AI Chat feature (`A` key) uses Amazon Bedrock. To enable this feature, you need: + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": "bedrock:InvokeModelWithResponseStream", + "Resource": "arn:aws:bedrock:*::foundation-model/*" + }, + { + "Effect": "Allow", + "Action": [ + "aws-marketplace:Subscribe", + "aws-marketplace:ViewSubscriptions" + ], + "Resource": "*" + } + ] +} +``` + +**Note**: AWS Marketplace permissions are required for first-time model usage in your account. If the model is already enabled, only the `bedrock:InvokeModelWithResponseStream` permission is needed. + ## Inline Metrics (Optional) To display inline CloudWatch metrics (toggle with `M` key), you need: @@ -37,7 +64,7 @@ Some resource actions require additional permissions: ## Recommended Policy -For full read-only access with metrics: +For full read-only access with metrics and AI chat: ```json { @@ -57,6 +84,19 @@ For full read-only access with metrics: "iam:Get*" ], "Resource": "*" + }, + { + "Effect": "Allow", + "Action": "bedrock:InvokeModelWithResponseStream", + "Resource": "arn:aws:bedrock:*::foundation-model/*" + }, + { + "Effect": "Allow", + "Action": [ + "aws-marketplace:Subscribe", + "aws-marketplace:ViewSubscriptions" + ], + "Resource": "*" } ] } diff --git a/docs/images/ai-chat.png b/docs/images/ai-chat.png new file mode 100644 index 0000000000000000000000000000000000000000..5fb35052ce4c52dc1743ed4d759a58317adbeff7 GIT binary patch literal 837181 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ΠOk8l0ttu8i=jFN^!-(rQ)fEul6IwPN; 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 z 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⪙M!9Yx13g!>TlU{_n+tuP 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 N z#fGb;bd6VGVRZRPjLx5`62wAM#&$zBCb_-*s>{wku2%skxBjgmTPaAp5JkxWl#KO= z5f=4YKQB42y#6}4#df0eg?6Ho=5)(>Mw!kMf?fBYF5%tx@!_CZvd%RNoNjl0>V;CD zqHYPUzh|-5cvY%JvOi%T(0b#1na`>ucxDr;n!_}p{{z&tW+W-^0rfsuG1k# zJ=1UX7$S4T$$x7ebZ4iUSLD$`LOEyad~xyOcvQktG_D6Sp``r@AW7ZmkCQMz2CSOA zZhS&QX^sV}Vm2;I+ZlV4@gGS%z&<@`cAGl_PfRO9gC(RsoC`WwvbrTp1Wp*$Ro(4Y zPXteqrkld5caRM`?#3)9AJS#7{{Ej%iuPJ z@m0~uax<)0OWJcFTq7Oy%{Gn8u(rKFp%aVcEc(?f6!!SCe?>*HhC3vsIwv#0rCdlr1k)nD`3AeSX{Akr`(j7 zOg?k1w-@Dmh>RyZlKh*uFh;_E^`tMor(`;L_trjRrp75`i(R2*Lqg0ls?DK6H9;=BjwcC)9ncVR0wlU zs?~d{z!6aHXmWW`?bzm$4n0BDDrl2Qce?$BPV4+x8;s!!VOF<7wY#t z&uSBv8NN!a4Zw_VU$U!l6fAv*aW9=!;9e!Z9mc(E+g_5)jJZZ7xuX%u1|6^C(b{4tix-NLh}>dE z*`+Vf?MPuOe1-XynX;9bLPRQIjL^c@NTy*cSQwM-gQU0bJ3_QHC3QMo)~V^=kM!vZ zgKvn7od@(=lww<{AT5@`vdg;0Hl^4<*sadN4*zBH5q=|V&5aA+}d0v0ge?Tr!!X=?6K>|rd(Fks; z>w$)E3M>WCuM}^u(aiiPg0fj+(bK9Xo{ZZGka^ z<5rp?^qJ`}#5w^#>_@;OTFSp=gx`rVSU~>Tg`#Z7x78+rnoArc_&FX;-px)YjbSNt z`73paoR(Qn)hb<6c;uABRVxQlR0oy12QJ*WOLh3UGOhByF}cAx7I*36!FV+Da)h0} zw54Z7Y!lh%?~6dl4`|iBNS6cJ?{gJpfK~9iFN&6qn4R88kd*aZeDPEkIiGP}GS_%6 zN6J+6WWu^ZU(SB)g7l=?C~!9#OMD9J{luzeE5Eam*P#LZ9pR&{<^hSpr<@g8TE8T?L zUUZg*ROjt(dA;!c z&?5?@R-5sjZUiL+`er*5=dlk~Xr+2-hZUI*E7taYOF#@eifvU{vp0^XkBaoCK18eR z!6*mie0U=AhOWo#)h?(LIlPXI^WKr=5;#_7$pkXAK?@|mXcrvWKDOFAj;{d-EFN^$ zNRsiK2eM{B-cxXqZe^U!&6Iz-(D>CTBIV2R+SqNBF_&q{7tfw8MS-hwh&ow~b?nsU zRiVU)?PKF;GJxr?<08lHllz>Q(8KVhg}|cBP7@`x)Ovr>FRHCgsu0!3HQKLRy|qS~ z4jsDkI^WOd0fq?j<|`ju@A68MVu-@2;m?gif}EKKi-1c!oOfr%cv@wQCS6|T57U=h zN!f4yGPV9E+nmcIQRLl7>a}Q$BFy)v)n-r+k&e{W+8N+WQRg%-cC3y1@-+-<4crwu zbj$|KW}cky`+>)M{)8Ee`pK@VAx3%HaYAjN5`MI-)H({DVwt?#&mq)aTc1U3?7g<| zXWrnc#HBDQk>hZfR@yY!^-yB18J4Aknv!+wrFGYiMtm!?YS750h`CE-i|NeQ$ojEK zXNWiZt;M-~F1r}FagZ2OtmR;;R!`@8qL3{=#OLTUL%LE1vf~TZISXLj*_*z_Q%XN1 z-djNEc0OX!_`%5-&2*Wg7Zjj_Bz3C<%g&yI=CLq!tF2rHp{*I3%y+Ddeh6j?el!H?tixNV=tky0)_VJ1arj<0o05zr;Z zFz}Zq9xy6BlXkhi2yg2Ru$f#LXX*PUu(eX>swH=M1En#W1&sbSjyn)kgIBfw2hylpenE*==0&%k|bp#mJ4e`i%kDYD)%rHLA{B5ajY{w&EfJgzv$5N=a?VSmzi{W_~CQN=bC zkA8YUzv6B1@@eAm>;TDRY-nh3C3B;NTb27pw}WnemMME{WD1ACq68KuCIMSLr%p|K z=`K2(!~JY|HqU*JbnPr=jl>UPavqse#sq>>ozAJmH9brNNlQPe1Oi2#H8kZvGQ5NCU?rP>KinJ5Br%o0D$`Ryy}P#VKMDmDZW2dHg9|0KB{VF8_EinQ?f;F0j>6FLq|en8x>Y8cCp z5F+wIy^A}=C00mm=4hG)oNbDBV-8!zfRnE9%R7-NI+xXv`&HN9Ge(o0A9S+hupNy@ zHp;@^_RZt@UZ;9wsTB381ZnMJ_Tl2Xk<;yYT~wGqVdE6E zWtl^qxsz_sfFDg1&gmHDjGZv4qz~gQMrL_^z(vO7N^Qnp4)Crdcc4e4`<@NPu~b_K zq0#{%-^Xa*z^ll>+ITCE{k*qh6!NK@X+ai{jT6pw^^MaE?U%}d4X+l2c(;q3{_ zxr+MY&p_WQ?q{U*A};n zB<4tQ8aJkNjhRs|sU6OZl;K&Q&BJJqP|KaMU2e+(0!L9E4DdnawXYVx!Ii+hH6vFQ z15ut{&5e;7c{&K(WTn*=eN618`gub!AQy7mQc?wEHAvQ?P%jgNQJr`ESdHwuWqwj4 z{(CbqXIig(HBlr&8B;Omll>f+;Gny0rS=LdjU_TNlAXop@^pOd=1H*T_LyCG#7({s z+}%R^MbEPno%rT@n5HYO#~N zzpiF@J*M9zA0lapjOw`eX5bUnWO7dqpd=4iwM!-WV8y0{kvk&qhncrWP5%H^NG&#x z=dWPrnZ}iwYLcA~z!hDx#2`x$lx#DGy5Ac=cbuKHe6Z`WS|M`;2fXV&;qkeIb7H>N zQQHDG<~}3+=92|lDZK&uMny2e{fiahJ~V7Pna$_Y_0%RiVY22rrom)oQhjSVqN2gM zeO0`$Vwmn^q(PhvRF|cB-9t5Xe$gHBdiPG1*^xS2<5a76*)+MvdJ|2$J(6kY*!e4KWQiq=e1b| z$v1C(n#MnLdJTf2Er(JWS86=6za*Y`Q!S@-pm%jcojnIOb{cXhEpi+MHQOHJsl?lw zQIc(G+wkrW#wuLDtaK35KQSL6s%?R_{e$Zsp?p$Q*M9ykJ_hB|N{xC@l{dxzeQ1hJ; zmZzkSm%0e3ZD(@LXRFqJ?ulEJsByh-wy68TBV>nUFC^awQtDfe9IHvy{`s&RM`n+;>bQxHJtSlBZN;Cv(~U@=2%xB&+QGLy zboM?sJ4TYB!1A24$~Tp%{HG|tP3!e%_75i{8$-j~pDFGtFq>kTRC1mQq3;raro)>X6jfZj}Jq6{>6K<2)E)4j~7GfJStyV zo7nqPT3@D5t9+Tpf8%X?j=29VU3OAXgsk36+VtqhS01)o@*Y>nk5Ez!fkIp z_;{n<`|zh6!otsJ4lJWX|o78-t zdXdLC=qgHvN?S;PTY?R;DMYRtc^a3Wb7Tgj`t)l@0S!#4uU7DM&Ax%>Mlbmkt=CU! ztdT_w&Ik*cqjuNdLVXn=iI&C|ja*PZpp zPE^e7wR%L-emB;iJ%0EM(3Yx2Znb(V+OhtlE?E`f|gT zcd@quVF0?1nTYY>Bh0ph7ciI6We5My38eNS^^RFBUhG^q;k|`9=bCW8Tk&|$Ga6Qo zAzuM|YAXERV8BS8Lm-*yYFIH{>q01YQerN`BLgZmwc7;-i`I#4H1wAOx8pXPs*V@T zUB-t~uE`SMMvywvx2f(%QA_5IRlAZkO@xqxohm-pZZu|$P!Dc*FAccdF%{!BxbNZI z99opE(u2kT)+4!R0O@xDiK&y)Lx~Ca}b@tbsle9U^tOGWs zUz3}-snOm$dR-OlS99k+4{i79Uf2tsmaQb^dTWk8S{#Tmjk z1NtQyq^+*Oej(7v^OCiSwb33zM3vD=a5TasHadkgopq{PkbAUz++#kC2_ES<)0|!u z>NI@zQ>75uL_?q-V!^hO#BH7#WH?vQoOl-#%VVoF^I|eqJ-2UI(T%)!`^e3Eu*8YT zm`u+E@Gs<>fu?@y3l^KZpU;iHxwsM{~c z#7$jZHLG2t_^LxOE(RySE(vE)6r-eHt;|l4&v7yUks_F&42yWNV3A17sS_QIoQL@J zNnEM7!Y&CXp3dDmH%nODz7W3_X05m7)Zz|0Uh8Aa#=F_3Xmma+@=;IO`6yCsZFlc1 zF6w8})#lH#(+E{}%_hJd+TF+1dJ*;z&$DP3S?D4#i&)u`le6e4;S|YDH*R!Il8pp* zsQ8}Bftx3Ed$Lq1KR{ji{m~z+dNn~MsBSgWtzO~{k`0}a45OA=&0^4FVYg%6N|}vR zA(!47Hc&~P&()Lm(1ZS&>URBit(OXh72a~`E0rzxB=*dHzJWGP&?y8c59)K^ zI}(TN`gO9r(Iq4oCA(=2=C!_lW4*UmtI`N)2h2D~#oND|Q7` zU3Y|5Iy-2`Z8P}_#Op+}Gr)nYk*Lx%c8qloSr417^Bb~mXu1a!M!|i~B(S-t(tQxn zZva$175hMf^}9ubrf11#n)QQy$+qXel>`rr$)xo4T3lb@y)u4dyyw}7(CEKE7OoYUTUtZ*h zAuXa6B!_8mLsy|Hg1t7VT4Cs1^hk*A#KjxUNMvCqcH9Y6fL)~6332XhR-c6soXY?n zn-H8(FFMfoMYxN=;~P&RpYd#4sh-Dy6jWFHBo3VyO52u?9Vx%;*Wv!xrf;AbI>+vJ z?-;aI>h{-_<7ONBSrI4uA0$r|z65&TbSS>gDkzApu^$sIOf1aR{65L{5N}s@K))^@ zy2Bq6@=+nSn)~1s#x(Nde#G1;t5X=IZu*+yMsF*yl(GBn;|265K+~_|xtn-TPi68N zO8}Ltr=gtRPSRiU2S}!i0+<`thj6hTS*$LB6poRJoxg#Lbl{;^y1Z#i_S}g{^iHr9 zvnR~wj@?gUJMpqf9Nj;{^-6XjM$DrLrK(D-qSaatJ92}){7Vc{oXl(qOl2SEoHzNr z1Nf|L)vNroth15CwQ2D9KGNX{Hs}_I3FhondBycgLrLoGoC+b!hkD3l?ymVT8xPxi zamt&hZD7|hYeVz0Ixx1%JZ7kY=F#2Q`3SuT;%KbLn^IJZI~;Y{TufJ0Sq*(5t#kD^ z(k1h*8z_#_RVm0YkEwqk`ry`(T*M~IYVmdwWu7!uqW6v`Af}ebGRLRh5&3q}`wY8< zlbxcvP18fDZ*scdvu9bT#_IMRycSknReKHLOcu5#dQ8D5qo?TmEBCU-VSpf|z~ZC0 z4{Hp9d{o~zL$$;ss^&u$Q5$G+H*ExA0)rJ#rL#iU#zZ0nOQU@|wum?OMbS6y9`PI0Pas9&;I z)l5K~{P7>+d?+*h?P)6aH@2eX$l-SSvoC7*d6h z?JQ@SoeF1^=U4l!ByAj@OI$<=Bjyrj>%H`S1G2p<2&t%~v%4bl0h$y&MW+RJTdSY2 zK21Z|pJbW>c#On0W)Xby4r69o^d z1-&nrYj=hN#R+z_x0c@bChQfuK{y71C`s~F;?ed|22HK~Sn@@7#gaP1TH}DiOzW+9 zs7IR8pHn6BbV+;59ep|R#$x6bQYU{@-kuzEdLOjdt!+Q322^{dL?_XwVI}53r>m@a z1eW<{Nl`f!(wl@kZp&VJj;s^CpPbo3U!W0lRsu*TV@5-`|+CH$>TQ7KHGRLZBnarT&B(x19DB)(aifx|{ zOZ6kT6b%QMGCeGM`*Fws+kK8=CF#6xbXm=jeP)H1jGe|1SE!*|`erP9irap7vQiz_ zUBz}0&nUvKHmnN)7`ydvdbQ9s%aDJRX+^clqMgigjN!c<;?!)57ii+#Ik3YBU_py5 zk(llKHCQ`Zm#(AH>_iDsumI1Fi z!2ZIQ@u_iN8-i_1NEz+SXEq)r7TmN~P<%|k;ZZVAfRF!NJSNCVcjU{?&vO=E1R{>k z(mj)%fR31HzK|*3vbe0|`_7XD&SY1zvr`&> zX)ei=Tk4Xs7mrzqdAW;#9{E51{g08tcW<7DJ%2oK#Phy9b*g9hMI#Ei`WWISQdo$c zP7De-A!TMh33JY*HdxePicDht!j=TdG621H(!%bD7p8YlWwPclUUFIdU=9-7Umw_M z>m6|ulXuH~C2isR%KNBI$Z(Cq+EnFKzAVRWe^||wZm*APBwHCatX5P>pT2CD48N>FCdWTUzlhZ@!X}bHdYa{&8d5p zsB=Ss{yVIOp30x7@Oz5uMHuw7b1toiNyR8hZI;Qp;7t!ct7^Blk>g_X54sR|r)?nR ze~a1q{mCU3e*o*RijtYx{3O`s|JsIOxTeRpY8#h?6NR$_Ul>;=yjz4f5%KQ6w#4AEpk<}JMNpjg;$`waln1Aco`im{oApHkU^Zu|x zKtt=TpnsKF4`A5eo?vs`x8p@3e8#rF5gyQ0n4buTLsEzg){nE==SxKn|TS)yP6j+?bFP}eC0 z#ovVgCvFb?eGFi;lksA_`4`C4KmHx2j{=R6rvI0x{onpKSPFPEgFh1|{e6}GQwY+( zKj0JpJ?29j7k{q*=1utbNBs|Epnp7o9tU_9MjVhC&cDaB|LyAm9zXjV6gGf~`2X;w z|NbRW%)b{svraV2|IK|ioBJ(ZJyM*H02*3U!vj%1l$e&o^5~oWe}85F;lTJ$pL{RwPn7!|aI}&D#Qtn{R~U?K6I5EJ(jl3Z*FBm$NXEG05m!>?+1#1Fi(q;4A>nxtQhfZS~+@rMRVim z9>T0d9!sl@vPZy1tWnbOCcU{ru?ZUA;r62e_00_@=xt|q=0Y%#in5cBgEy;?7Ab<~HyuLR|fU_#?%kz1fjPE<2FsU8znK|NaQ+VyA z_4&)s$uSqQIuyvi{A&I;7Qovix=5f^u)S8lEX|{H! zm&H&kO?0dR4B0a|Bx)Q+uCdtUu>$4$00jCuM!n`A=)^?`tt?`%%s# z%mrTS(c$l0o$9|{+;cr@gUnU{?Y}||-+HyZzuHU|DVemXTeRLY9*dF`qa^n@k5F>G z**5>p_POlGZA*51glvSw?l0V^@H`QXHz^--A@Akr>!71SyOX~5f`0`WW;+iV~` z&3Uy~hK$?n-ExdtDi1~?Q(CmY#P8oQJq^aUCsdj66A~TFUYG>U#8Z$%C>BxnqVHw; ztlOqk*NaH~pS8+geZ)d0%NE~4IgRsfuJCMRWiVO27#30G&~*QEGcZt|Zka52DrA@jTb8@r)_-X@jwoXlU+ zo*KIyE(E7P?mw+@MthULT9?Clf}P!r#u)}K_4eey4l`Q-oX$R3on=k?M-ML3tg3nx~u6{Q|ah=674^2c*Dnr7-P}^`7yC`>K9Gm~niLPfVpWW_fE( zx1Dp~mqC;NE-Qq%Zww$%sVzoIRAbk-zAw%L{KXaLowXs~Yi*}C^Nszq(Mv=Rnqi;v zI}!Q4XSXEMkN?Np%fG!q^?$H(?CO2I>h$Bm@n6l%7Ys!N)Kr62aoC^k0>QjpgN6-& zKBhH22Ka0TW1CO!Z+WbmxK$Kx*wH4`$5}G0dVvo{N9eA$dgB!}Gcve=Dfd(`Wz@@0K`RC>G6Zr9@DBaZYj~bm(Uk7 z_j{PYMnFReL7EVu%*Iehdu$tzBr4Tq?Kt|(eDMF*yqp|>(bsnst^a{~%y~bIuJqLN z8fcgn6} zBwAO=sa#}5p=+7FQnVr(>-{yR8xF!=lgDaB1dB7#8|Q!?ybKYDdLc8H4#dsevni{q z<3@Y}0`_MQuyKd~BHy6HHr^?Bz^-0fCE>F&Od}g0iZzY_9(iynm3F`2t(ZmxYEQpF zRkBDGL1|91=XKqVZH+xQbMm=O5_nI##QUHED%81Fy)hHci~Ab~iU7B*@7^UaQ1%kTk|ReozMKOZ<1+YjgjG-*{-&QLx1LJfWvH4>yOyDAKd z4};;)|04FRXkX{K=v^fZT2T}N`73J>PB(VA zz0%m`(iZYHfkj2k6OZwTI*u`nObQEccQV8xgocgMXLv<}m5SV9;NbS=>a?%sNV85# z3n)toCG+{=SVu&xqM=5|BVX# z`q&Au(sAdjT(zj|3H!cfPz=;_@x3^FI&gEsr1#SC>x7C=o5H6(5np=tO>w5#PSo0d z9S6l0l%{J8u-i_^i+Yx=H=FLr{;oasrgj_?#9@Ytx(6X{tIG4tH#{FFYXDTi(Z&fy z>+MywCudf(Ra9bkQet|7ek22zJ|RjCPX}eqKmk^OD+Xu)5;E_LHayCjTkg0X&Gj2; zo*AsC=X|P}49pNxlnH#%)UqNjWIp<+@R1!&p25ymtb!h0*EbdFk?JVnzx z>5(tIPV>-YoM~C0Nix1mY$}QPk8y{g!P3({6~fK7z-k*u=pY53gA!mTWsc)4*SIs< z;~Q(}gZp<^FBB_L9^1@TaU8*BUseBrxodd!xMchMq?INW0~!gt;?jjcLxSAd#_*s{ zT5`5p_dPUV7KO2b|F0+*v;T^MVdB6v0(V7A(+Z3J%T6|o6z$P1?-x#PrwvXQ--^N( zRj2abwCWRWS1^PGEK{*bvX<#oX7O%BF2*UQn6BiNZf(BWpn36e_3)6J_cU~_*jDsYR5Q z{6AsgQL7$AHjV+*+tK}?mhn7$EQ$_YP)XWLhd9lOg*qQ5K$M)3?M)ak%6Bka1Y}Oo z2#^tP_WiieVmnzfDfrvq`Q!jhY};dB47-t^8cZwLn{!khOcg4XEU~(oJJsbq5}Dh0 zVEe%7LyeD@>(0Bq*}6*joq6uzV%|{Bs};tg&?+HI^YxMJuWIlr6_}qcnd>3x&%k5H zWh9SB7GfKPtbwrk2xo9rj@B=GP@e5cMCm~E`?lhB52g>EcdqL@3P$vl87|P>YBT|8 z&aLqJ1ud`W1?Q8sKHIcnBl~GhZ~CQzX1$8{_mTxHzc#cgNIxd8AJ$x&m}9bM{k0w# zecSJXS{f41o8@X{-pS?kWEjHwS8j~}nLXX$I~6#;)Dp%&xn{in?utP&pgmTtH2bH@ ze@3EEW9$6L)xI-1?wGB=qhRDX-|p#};5C$>eEWmegj-l29X<^Y#6gp0f& zb7SoPjzq~7gqSIc_+WW+urosmfGOsqzwSRJ@>%D_46S>@qM@8qE;CcTkptYEtb6TZ zbqS1DyVDh$Jr0zbz;+~{nyi3k;(#`HAskjRDVnpJ7}-B}nAjPa2K#KlLTpy2z4KLW zST^$|;r;9CyE4KJj--KKwR61DxT+@!2dWPic7?c$I$O2&L+@BfRHay}U=mF1mcf-J?sla!3uJo~C^X#D zUKZ}Tq0_R`J<*s%6-ms}cuxMN*%#+$jqAk{wQz$UDVl_nbp!D>Gcheu8PBrC7Ux{; zmd$|v6KiOsJjo#VdMQU?LQfqcz`C7N5#lqOxeYY%N;5imKHD3z=-wUlj582yqN`3( zTE;hY1Ud&FQ5QyhYHe*T;aOBPBBfa>KvEsib^zqKaig|NOI<9PS1FI2NCC)i2bPa% z5b=%<{Nv4KIM6VIOZuGD~yyR0jh!$7T|shx?cQGEYBtLyhGbj4Yk{ ziit#plxGer%<{kYiM;#snV?PymY+hMV~v9(kA=9Fumz@aT||GF&aau72qJig%W%{X z*66GoMMoKZeu2$I?+ZgrR(<|=-uS3+9hD|)ajw(d+j}i{=`z7F97K*Yjp0Sw#R>?- z@=x*~NgD}Hgp5MZ`DYO=p6=PoRI?+%J$P%w*pJk9h0Hn^9S-8i@gvC$5Pio?Gb@6y ze0DXUtqw`#7^;f@1+Xn~ac}RPb}Xp`6YCNFgcKnZ45zD(yKX#3ez#6M%@iD3Dfmq_ z5hNFL7OeFxZuvP#1yJ%tP^KR~*sX_+{$R+J64L+D;@yElh;bh`sEh5WIl`l(nS$A}v0n;xyp0@Kej%PnTEnE6gSD3a$@76JG<& zKY3!-xNO#VHi9VtlxMJff1Q)x=cg3~kit=M2I2lQNoGmh|8$>8-j`)sVgJ{cs~m>8 zp|4^2z<|1ux`CZlk_9`mo_}G3uu1)c4MJ{+@E)09H;5o5G3qI57ID7#W479>n9qJs zxLob(+*7Z7PX!>cDl#DN+Cw3ragDxuhqDjlLhDS{XU+q?t+`dvKAs3*cPIEJit1J9 z**#upb!0k2dn9VMe?X)5kS32;2zh$fyfH8oM&*!mL8K#i=onvO5@`uCUfEvFRy6>2 zvVdlRs?`eiQV*|8cwE1=7)B7w^r@$mC;%sIvm-$!Ld5&>c~iX?)i%MrL!X36i^G zZfoT2;>YAd+8F_imX2792$b1?!DOmnI_OnqHaKUNk8WSI4XXj(iHmTvt^BHgL!C$O})eiHehrHwh#!zd<~f* z;HVyJ;fSyQR%;%t;Y-xy(@$3?ihg%yQ)5%R;+@~FMWed_a5THqBuKb)mHV{I)UZTS z%Zbo+HX~uZaoCwaem=U*w?ny{xc7tGxus@)g(_|DV^B(d`fu)O>%~7zW`G5!%Yr^5aU4aT zkjB{z@3yXwy~ynE2N~R5xI}37);tV}P3B9F?rpLwthYW9*jg>jr)jKlm`D|;xu_Iw zwBATBDp$V;TBhdpC|J$Du%E382mD5$;{wjvM2^#6=M`bT}tIq<`n+hFRJSPe=I_VEGB1=GiG3mHjAHk zqC?!4%05E}sXWytcPE1`ZN)Ay@mY+8CYeTcWmy32p&MIdF&{y$JCr88@S>v$%4wMQ zO)iX)M4rt*@Txz(uZ9@UaCe?8->e7yid)X}s+RoIqkkg2eR>^?&#e8T)H7pbrkW8) zrG_L^r(FDeWYzj$xC_Ra>C}eHh}&s^OElFnAzbbvt?=v4&`x*}v_@Y;mGV zGOunr&mIMM9VzxCUs9N4Cv#E^GaH};1sY7L4<$afo}I)UHeUX$56%4^+EIJJanU^< zjftrhzqc1MXHU1V*8c9uCv#`dr@*kwq0O{pMOtF;PsnBA$aphE1Fdi|WjWvu>QDCl z)h6S)=}1@5vT51`{JVh(Z zcwhPknKGnZUUi+;Wdz;(5{8axIcjV?lgg#*VU5xip^6{@hB5PyS9%l+*!1^*1N zp6s_q*V-u+UU=9v_v&2G=9{yDSo4nohq=X0vcvrJ(E%nM|D5Y815+#RJuWQNTMBBU zxlS9@`+3Ai^@=9P>k6;x=|L5C6VNCSvOi-BPx_&8 zlPvVG=I>tp|17|oN54*bdT2zF?v1$irX5JUq_kgbjGh)Lg~4dt_Ge@Bh78`GpHdm@ zUUS@DOMBjKnVgOj-nYKy`N=}-n{Q{@KOL;uGgUu*xldF;M#&gjZ8a&ry1qV)Xh))Y zCQlfZIF_4hG($g5B&$7>R}o_{Rl@C?**HNX+C6@3ayjayc2YYLqIQ>x&wnKBhYESNI669xbO%p%AEjgsUENcbt=3)77OBxD}o(h=ae3d;YqbWH~-FtqFI? zE`D5VEfU?_iar}g{V3tHUMvG(EtNQ&^BZM#+6F@A%>d=%*qGxsu``@XHdH*>6qXAa z#5lf(C(3>zu6xRPWHg4O&?;Ldq_;-`wJ@_MnsQe|TbvgAgtsUJH>DYyhVN!)b!x;w zdRunieO*h=+b?eLN@^@wXrLaH-DhEGyMKMOk}xF;-W^AF?mkIt489^Asm%VyTzV*8 z!nqWF)y!0JFAmg@n_a;u z#S|=*yzCh(1*ayyFX%aHzIROA-P7Kw1|a<}1`@?rlvo`0Azon5BiKp-xLjmaFu(cI zGx|)>1*n(AF)X|>p&^QF9pHWAT{@>ny)6uoRebm$zN?80ElReELT%eerU;ot$fawDux=;hdJeqn{8oVOhsKJQ-6Er?nWg32tynjnpgoC^ zhfu(HQ7%y}b@;TMeS7GOu0X6=;7C^py*fqw>^xooqvGx)zE3sfUbHW=LEIIYSy&V(HId$OsaDKSMRhDK#UT*^BEYb)q!k!K z$-T*)&s9V#jQ_HdZJjS@`ItC12ph`jxYO-cE5>4hs9&grC;u2Pzf;}%QK5{yTwPf) z9Vk=%0n&Uwz>N9SGPT^WI!KKz$W2cjh{JUxi#JF-;G$%d1UlEOG*%*o$OLKg*m4UXA5f*N!A=$V)l%UeBn`$WMJdCD+(nn6D( zH4UE>|IsXk;|Smh3bp6dmu~Kh$HAd7Dpvh*bP%Y=_kY-X>$oV_c5PS@r6eR3q@|>k zt^oxB>FyGc?jBkYqy$7tYUqxU&H-tpd+6@&0fu;Q*YoaY?`J)0y=(1v|MPv{@B5Dd zemC4R*L_{*d7MYE;OgiTXBZcMG5FR$C-dtpDAu2)VSFkCrgGW!DD)mth1!xQelD&6i7+d19fuSfmXdg3VP8<2ZfV9c#I3U` zquZlkKEqm+;xFo`nH^N=z4*Jh(I$3fp7^eVF;WUc0r93vz%$-vg&8#Lxi9N9SvHew zE#PI&URj?V<~3WJfTABC&Y1De{_W$jyv2>upu&|fN>p|dYC`6WNuzS<3loNo^mPIW$Ob@BLp81j@Z!rQd*0GGn5m`jVletgdDFNww(l&zB?BM%heZBg9Zb;i8p_4}PDo(Qt;7 zoYuODQBVp76JuqVUQm6QV15xcCru+H;P?>q>`h`Og=fIlSfPxeVfxWXtqPjQ`|}st zM?K0Oo3R=xu6o(hCY#L~dQ_`1nd@pUjn$n5XV1E;e;l~6o;_{w?(A0-W}C^*m7LMw|wQ-l?>FrzWQ(8o*aw zb1+6iIYQav`@H1a<^^xvJT-1))Y9y=CxLl$13ar*|1_-w_98{VtrRS*noGL}LY*Lh z7sNJoDYS>iGIGfVqqJ-(2eHlOTUG0i7O(3O*7JX=(6{K%Y#6c{_UsbS_5fTF69th( z2}7kSZks89sBW=B?En<()l%yJyqzF9yaYS;!d4u(IUR|N?`$X%`%F9O7hQ|@M)Mpv zwLy6mfUFImDZ>~!V?oSx|xkL(fodNMg)qL+#iP|t* zXLes|pnl#5NeenWM<+c#H>RUsSVb=I&fidpG&aU&1y}pL2{?#wXE@z;ERfGAAb`72DFl ztDhw8>w9^j%+Hf~j64(EA6kMovBnyYaCDP(T8)~r;$-uO!*KQ|%j1FSlfKc^>$=Zv zv~Yd*2sySl>$x$yIU=68FK2a>Dw@|zF$?Ua;>!l$pUo-=A7*j=2X_mY`_ycrU!6jP*(ojqLO3_WCn;50CHlA|qZ}`@WHt0pi`lqrI=r;1 zK+gO*2$Ua8B_W)#89S(92O& z>dS2@PPCyaoNO;Iiw&`|=wrv<-oQSB1=Ybtn2Q;*oV6jx%6xBkG-zx!q?+3J3f^lQVgxQHS`LCgR- zvw^Ihflw0>Wh2q2hlL59pHmq1XScwxMGweY(ZpW=l;YMmhX$o<(n(2)@z_Tay`Mfh z%9Y#oB*cS}j=WnlMV{Tni-GZCo&9k^k^&eng@Pu+Kx(U5wGF!Uk7FK*~#CMn<9%e7Y{?{4u*;oh)FAl|5!Z_T{UG11D~vmq91szZrz_=gZ2wb|0i^fD4UMvC@*Vq8r7 zjaej)`;~W~lftk1)LM64!~@Q0a4Xm?sDQ=i>Z#ECXKJh`2Re;j_eo!zN72*^fT>=u@mfF4)?(8}C$D$e7xNIp zvEtGEwh`jPteVI8_`Wn&35LSEGrvIgN2g?y3|5fiLMXp#p}rVr(=+@eE-NW*l&Y!4 z)Aw!+5tNP_XD9j35{Oyfe7n+ibwPjl5Fct$T{NZwOh>eG+D|9E&fjJ1G#M_hCAxC4 z5N0v2js}8@g)e%a)s<_AxRi1G&~u7)Fq4FSD1Jh-6vCaq*Tkp(#Ddr%ur4C%Vzz1C z5!;H2&nm=*(%qRQ+}Gj3X|2_VI+@%l{d%bP5M46Vv6`tXFi8Ib(MZ8{kKMu6B zy`4K5>75Z(X*eGzxDMIUn56VLi;8l|HeKFx;egqP2a)KHjM^MlLLH)sx1L3I_SFHU zS7`b++&j% z?Z#>xAMv#z1-Kh5maWlk0xVCaHCIZJ@0^CoGbP13v87IC&_#; zV7=Y8>v^BK6A0fKylrgcIu6&c==z9j&Xe*rj&0=LJru6vU4jc_$Eb!uuGeC7joh~S zN!NklnK;+Lb=$lJkUUR)c1Jf+4CditF&uXLg}(nAzhr3KJouR0UHGv55GbrtX!`7? zbBF>hgTZ86--X`Q+1=g^x~yMfoyX9r9&Q|{vNpUBeA)Otx$Z9QalNrdgLQ$+cH9-M-6B5XPsmx)Die`v5sW8kk(UlgUbiZog`K?2j=rC$CrM)iLCMG28QfcarLrWZU zrz5)61D~#KiwEKCwtHtkaM_$ug0LHuc3Z>)kHLt9Ue{nfgc;Mp_FdZQlor(-u?gk5 zT>}tdGyyEGdQM5xvqFo9`2X&D_0QAyA73~VFcP55!7%Ed-63wQBA~Dt^J_0+(Ovj% zr;sdEa2qmhc(}(8-e4?*>-3vN#*kbPF7@Ir>-mQ7Xn_k_=zM9r1tr?qKDAG_X^^~LzD}et&nmbfIu|Bq+$om>xT8$1 zJOLfPY1)}RJlP}|KJmN+y>tR2!?8y2ZboKqN0n(P~ zMLkp`%7o%_nqVnWV>-P-D;d}XgGcIT=Q`;ezC~3ozCh>27aD^` zdB1Vh4-K zdYc8+_Nv>RQ_THv)_U;64VOcU$cok6m`zY%;P40JUaS-vE~Szg(GxR;@5gKGF(x-J z_bdu@7#g~cTv}Q!Mp6qmdf0{2`+Q4s4(vPk8N_+)KXObKuIg|a(E4z811cx;c1q%O zYEOKV$o5!fz(+vboqR1wfRgee_*78jmfXXj$ur-Hp{F$;BLw+WqMVsc{{}RX?Zn`* zmj9CQsjf`iB5kUZKp4`sgJ1ZE<$E8uz0m0EIrq+8XW!dK9@kPZBHEWdi{Gw4?aRWu zBq8BRwk^k+N*RW&c?87;f+zLZ?UPdEHMfsECnwM*Np!sX$KyyLXnR5C^#SjF&?n+s zyx_W$Ik2TAX?r_E*S?0r?KMU%)D>e0ti339PkWbCD_VQEHZjKyRL;9<+!@hhD#5N8=rIlcLr(?6tWCUo9T!*G z6ZbY|V4fV(UujM7*WQ12)3%J8;hFz6B1F z^qr!xrH1r_QOif4d=4j3>cEE$b%{)e?Ww+RcVGu4jUO4GJ<~-GP8#y_t`9+anokhO zBl&#ySXjM$=|P-6p?I>g#3(J)87YLD4!qB}WXGpVZ3uQF=GQ8)K~ErCh-vm)x!dHE zPbWgiAqEkp3_OMhsOs4g;hCHM?@FD3Y{pZbo^9?hQVj`-=QhMp(0W;~4FKo0V3oDg zGQxF^T8%C*I@`s$7hUK2f$XGcO#_=Lw~QDShCtqeaaD1TnOb?~c=mLapK+8DW7w%8 zsZzH{$0)+~hfleZ1>9;=l;s&zz_aL>SmnXwjaN&wb>QYJ?w_t3r5DNHf^g?O#Y&`z zZ-fp8(WQmmlQmiT{O=Pd`<~Bjw*g5aZJjpRpHH}$;!p9k; z2GuQ%1=a%up08g91fHeX!StMbs@qC(X@d~V;>D7*$DW(Y!rljS4?SG3RH;dy%7bj1 zbc)iSIqaI#oCUP&H~}?YvHodcrHBSikMFIY!2GxhcriR@$k*3z&db|hj=TKqrh8RH z#m&%ZnO=$OHq)+syNO*3X*iHg$qpXz=zm0O)tW=itMJd=;SkszR=6qZ-N{`ZAr}(X z4br0`MHF_J5@DZ_mzOFt{l%lem`7UW#Wn<=d5;0ebxjjQ&M*XZu8ZF6SQGS||}S;81UZG#YoRMC`wHFfe?5qlUfzK{`+( z{N|``K21r5sRhVXMCbkVLy7v14p})w^Xq1v)tL@dC3OyFJ$#;i4`=>% zziCPEz`&_;C4PQm>`8f%3EXaig(6KRDr1t{_lll^RPZg!4-boXM@2?3R5eQ6x^a77>)Le0I3(dgu5<=) zo-LLodc?}M8VT_6vwvLd)q9L2PudN}XwVNwkUK0PeF6wy-qacrxa_+o)rB%SD1^g< z4<)R5$`E5b>^GObTnjfoZF4~Uysk9B|1C8wSM(n4slxU6HnzVndNo&KW95-Ic#kh^ zF$_~;{Jj!@5}UmTH6d@%D(r7`w?+?%rz(7i+HEJLU%yt%cPWf>UhCCzw`oE^>O(pG zW>RR&FImT55dMyn_wO(5!(JsE{OHjSyV*C`ALI=q8FHZhfejFY$9Dyk0i-S_#`$yO z?HXso!0Jvbuz{Oj#+X@5+KIen7iS5u8e1b~nlpL1-@C~^_(?=#N`rGXSsaHs?)oje z5CR=^xB=s`JfG#V>cCPy?}7yLoevs_loSev&ps8yTI6uZ2tg$_+}n&l~HOSg=elQ7M#!uy(dPWh`lei{v= zHX=pQnQ^-oojkica}`I^g2KuLb}lUUrd3v^>av$@lp6r|s3kpOOF=AV4VH9wryL~- z?L@qmvFtPQy0?dHVI%Vp?gXRptG%LvMQ0dK`JLlyw_Gg0Q?@#NZm{hqjFeQ&-D~+iW+pCKk|9 zK9ZTOAD-dM?xllXU-H)OCuqOoG_T{dZ^3Kn+>k#WiJ%XLo3CplDE42U@t9G5F2P2g z>4FfdP1dP%-fb@7Ma(5oR5xh!?d1ZiyWDc+050Q!evRujDGbrCTKDYitpyfPa)3+D zd2~vR#NB6}_lIGb?qn`qejnhigMbXnJs!@rXxWt4I{=eF?sxt?@msyAczzSBe2^F!^2_nJC?m7VDlxPa`NiJstLh@1C{vq{CA{Ol3X+ zpP6qDyvN|xU;89~E^o&P4BSv8CnPXY&_AUHb?G;Nl|o%T5p`k|7uo|$u&B&VWp(wq z;Vc)DNH5fU5^SL&&OD}P)UnTrkK?D?K0M@Oq*iubk$nvJK5O~{EVQl7mrH$ zK2AiqQ+m;XzTRxN^0!fZ3d0oACq68TUvNq=v;Fp=h2VNTPbsY6ff|W^^VclC3Pv_a zFWJcg`UU0|E19u*KS6}gZvVB%Hgxx z&FRkcv~o8GTM^{lPuJ0#ZF9hKpxYn&mIrN#@28945#4GlS7msuWAEPkOh!}edt()T z=71p*a)Dxd%ga3WShY#wx30DQM=Td{KwyxhqP%Stc!B2nQ?aCmwUfF;f{wzBijWIT zxg&X59Ik10ye?gr%U5rdr2fRjr&l|=vC&6W+zZ-AIFGA`;d2xFbABLog0_aa*&qf% zv|1^&Q?H!b1K3um5r>D(Z&(t=)HkDwiize)^linsybM!rW8Ry+ur+N{ZNx2mad95< zthz~obQ2*l<_#3H%6EE~T76}|u{0@+myk>N`1m->f;C;&_wS$80V7%+4|!L5hzk!J z(0nY35g(eGLZat#EiE!NOe=6mEeer?#?iFPWF;Uj1bXC`Rshj=cyU~#&BtJH?)w3 zP@_X}*rX)B9IU)n*>0o`qGsOqKX*X-LkjA)z8P}ff>R-J8HIpr-A)Mi&yN-Qg3g~O zx~|GT&~LUWk;6y}mzeq`#y>?p@+>f*MRs-74fliwZhP#(Jdm83kleL68$^fq3R6;e zZ)NqQ^;u$rGm`;$%ta5c4Y#rO_&^83I6xUtCP&2z(i3`|;M0os6VaS0+NC{yPXxWg z4@dzPF5X9ShPf)xrw#{pcU|cNb~k6cPefdMDiCj*zV_k983$RIX^F_GZmChNx8Fsu9hGbGwGEG@zr%8 zcGC@Wv@N=JOK3WO*|Yzyh5yf&Kc>-N{n%c7Vg+x+8p|g$G``>D?%daHu<2{SS|-(r z@fSmye_05>lU05|5PbLmZ*@9v&&BGRpP#pEuXRd@alCxCy7FFY1n*E7r8LQS6v=zvu3|FjyhZ(A8YxBervQuM{Nvxf_%S}C+c6F~?O*B?{=Fvp&+qt=DcnJs zBO7=M`k%-#XWCGy7GNO^lw$vrf2QW}*HsRChiLx?AN3D8S>T3}z(+5g|K;8N&s+YN zkM&P}=l_du3ox`NtxruAsk9vNOXRYVs%+g>QXYN;#AHU{yV~XY`=j)wS6NJlBuZ2c z_o^`5Tpq)LBePImv>}Gzv5+%iEc1Gv(!g7g+)JzYSjTNceG7WL*$nbZ^I6E(=!hYJ zODSERuRcvf#+4ocgi+OUKB=%=WgmBb^GnHUm6-*Nu6BV%Z@uA4Ynv6cuv=mqdFM%H zHu50i(|_&(|2Ad&!yk1&^y8piz<&5JLrP^$@#81CqUe!EF_I_qv;ktra|t<2Kf-Ot zAba(`!Icr^T?MNmR^x8!6q^u+7Ylwt`Cb=CIG+wYfG6`V7XA#?pl53R-Vq8L($ej(-L$l4*?Ryx(U>W5WLZ7IupB|9B5I`5PnsyE9i?YRp_CMx$E z<~k;4T>_5&bQAcK$Jr$HRRZ3@C6GHE17A~`^@xgr#7S?%i_`9TF6Y}Ib@Fu(bEC@O z?qt3T*2I@K$*rIFrW!-ok_ETF^kbyh?lLu8NGQ3g6R)nXTg1IK+N3);^}Q8u;E5Ro zbQN0@wMmYlz+?0Sn_iKh>I2G0|2(61Y2F$$=k?EbN}~I_sL20f$N%>?UOMjAe>_eA zT{k`4JKYxk)%3(fL1^-Kl3 z7@{LaLY9Lzx5iL%R}0mkQe*8503ia*ET5jpEtY3dP7E-<;^pa5zZk=OuIolFr9ahQ zJAx<}f`Azu>NJWaz0lG)4pfq){XBwP@LA_`EW$%vr+kv3F94BVYP~-tT_yJ`BR5@b zFe&6`+4@sLRlSIK{_=NMvJ!yw%5pSM{PmcnqWbv#KF3ObF{T$Ug4vP>VyxO5sbR_C z6Gc^iaA$A|I#YoH02|4N@jIHxIlwNX!kM_ZlAmi}q$TgLNXyCPWD!V?^$CV=YembG zIlXLMPc(t5Vi(Y_S4*$n5nvielxCuBPE}eZ7C%lg$R2WB?Uw2gny=-bbYy>F1@oIW zzIh7vKDr#i+_PWX#wu6xheDe^KR^r8f}%-Nc=9Xodn@x-@{{V3@r2hp7)dmFKuh=b zXv@cA$-vg3N0(8EG7oG<2Hqr`9R)o2*)iL$chV2$`imd zat1^a6pE)^GWvnN?iZ>6m1!(pE2RAx6p_)00KM_?@eNPfhH75k^FAJH8WS%*6!bjJ zY!;JL-dS^rdEAsDA@owdp}#SM`*yI|8-P5>lxM8uD#A84 zB|2wlJPr%9WH@r(M?%KuvO6&+;yb$i=!P#kZXKv?W&-nbb;pfVs;@w5f2v?%$w4MHd;T6wfG1B2>(@ntz$l0D9KpeLdQLJf? z3n#jfkj|nxWHL#UF_!nuB#N|*U@@V~#f0yV7o)fUC}kD8*QoPz>#8rRBDKFj7=45{1YQ zS(c27`v9Nye*Zuu6Hw&#dF)F{ThO#+%wz+tpAPrdj>fo?5FrTG{nf}DS0L8%%D zH#s>fbjml`J{!(7zKYNc4cC*@2z*floB*4!n%5sqD-5qG6M2|J!bv5Srq5Ec`D$y* zq{(5DfyaTz>-liKYbINy>C|&;`lClYK_Ln+NmlLc~Un?x7qrL7fCNP08^ z+s)B%n!w!Ak4VHIMd{pFGrAcqPdT>OC(vu$VE&R79Vv8;k&wV1H54GiV{0rV@Q^5A zP}m_dlL`XEP)K>1D%xtP;$a!88Ocl+A2afkkWf{usOPOEd`~@8t6mmt+n7O7@_Ab? z8V9>J>Ya(pp3rHd*@xDvas1=ynvgSL4gln$!P@*Je1&@0I6-&TQdLZrx6n;gOJWJn zm$aF3A%=fk9qy_0c)O!lEtXp{Y~|EzIep!yU!3|xBv7;A>=4avXebkHRLw*ecin!` zFbTE?0Y$Tk+`a3k`daA4mqbTt@RfX7j?>N0EaM8Rb<-ptfUuL)F~j$A7+S`2qHlb< zN1M4V-3-jDze4$=b9x_bgQUSZbW`z<6}HD&GK-SKM)z;hho=QS31{mWwvo$t*d&`$dlNVlmrU30fLL|s9`FMTyxxB#&@&X~d(jV~>06q4e zH1wYnROaS9;R%C~4Y^bOT(L(BxJ6{YMF!A9%7*Rmck z-SQ(6U}&OSo{~pgym>3^qY-Kzi^B3CrcX)_)q8Z#pi ztgq9}udx;T$v8~iW>e%AAq&cdLgC0%!^rW!c_>1^@LhE!XQ%xzwk@ecFY2r7BasJkVylv$4z zjF}I9jO^yvhaEq+A)v9KQ-$hB&*^>t)?x`y_Y5RiPxU#6epOp3ZNO6(Ih}dWgg$q* zPTGe?a^k38b>g`ci4TSzLG+H6<12(sYrpQCn7_B-vl$J1$ay)|&=G3274Wk>*}xbZ zkUV@+qY>8*;#|T4;Y3qno*wY(IQ=EK)hv;zOu<#q>{zLPD$The^^~Z6nDNY+Fy~f{0*;pQsex35=l>patMXmUG z{pP?J&ypTn7lefu6I#-n=B{d$i=t1=(`j2q^msR7T7q9|QkCw-hpq82XS@Sou}wcN zH_caT=vimE{5)GBfeF@{v80?}-Va-r90thEj$T_{5RJ1@ZNY|;lQz#+t+W|+SK$#P zgoJ|~A{vvlA#GYDyP*slC z;J}ZB_iH@T*W!OF{KXv5hE#-+^p8xu#QhR^@X%B3dj&2qU`M*@f!HI|V3*;tL_>JA{wlA3tgjn+EkoZRg1M47DQ1xZPt8D2;LRGZ}J<$g4 zAM+v~y5rgH9C3Lioiw2OM7Rv5BY{{6&WlT0hwr+wlZ+Q0)BVXl^*e;2oaZA^4DvvLR zX_nm9N9S#yeQL=oe?$-PGoo0WwQ0)-bB|FA3WjN(bT7Rx{u1M?N`GwCBBQea?>P3F z3i`}RJftxj{OL?9C#AkeYg03_FFZ-2XN&14LyW0)L@wqC8Fy)nidhHAo}Ve}lp-*W z$_LuPEo&n`ihVm`*9o*=W03k12ubJj8vE4xDSe04n8W8nyFI>@=fgXm$RVwLDi?1dPLC?6)p408Q`h;_rMTU|p zNUbp;I4ZxWZ9)NchjM?H(r+RtpF4}^hqS}*eSS!i3pj7l?-{)5jG)MPk>}XSvz1yP_+CeNk(3)!)ZGaqAW%ZlSrx57CHL-qRMgiER(II3QWL1l-{w1F|$2`rYW>XXOrFxsO=vU9}V9 zlS(Kl*~p$|X*DLd3u?S>CX#b<3`Bd514tLBn=I26E6t!jqXlx3L03TY$Wi(rv{nI^?dZq>14sz7 z>UScqDfsD1EoVQxM5`^2s7-)1KP`xJI-_&VQmwGZP#4HOcH+Aq&${_~jO7=94oW_g z%D7f_KRopy@tZGYH=)t4D)k3JrnX6kmXCJ8VYl^s{MX2QDlI8$Ni$(LFcS9B$^IXT z1q-a%*mMAJl~TzWh|aBSrOlc!*XzZcsx&TFZd=^=4ZLopntA&o)4-^~rm` zX?9L$ezUmg6B_XN2t3r)3ZtA7AMWQE8lJwVO-R@rwz7tt`(O{0Ue=T`82bY9uq20Y zQfl?8d<&^d(l@Ms0@(c|a zVAF|9ik1t)-OYBbc8-DyMn+C=InI(Pz2JNH8R)XJC)IrPDfHMx8FRFLwzqxVh}S9% zRJ2sFgqO&$%Xv#Sln4cs=+#T0o$Z!BB9-JZ7u~5PNe82dJFqpn{)`11UxMqF`+c1E zuf-U-DsaXg;>4vA7f%g?t{)72`X;iGi97FT`AVutDmTU5{Zmtuh&YB_=hl}fNP(~m zDFW3j)2emzUFgpP)u#4ufz|}*k72oOn0|ZT7c<+%(2Cn&x%4R_ZtCFhhgm>vpY2kb zWE5I4OomXBn$VrO&f{`q>tCZ`XbhIkv?>eEiBc0q)wC`Mc8esRmn30(AE&6mt5DA3 zwT4^XV1LNM9sK$0XU>7_*M#23EcZSW{;fyJR^T@VeDAYAX22J(Ig)E&K8M&AK2RTH zp`dB2Z4ZP32}w7aH!2S8vI9*+*EsIr&xHGX^GUC;)?oCo(*iW^y}-@U+OYQHbOCaQ zeQNvDEj7RokK`g>vlKt@Qy|umgx=O*wxG-Icv#(OLzoe7=a+HjKKB@24>=E9ElC+b zvoGpo)Ii^c`0HdUC7xnC9d)D^SA{^diJ=^XPwcGIOx&s)NA@gEzYKoA!t zzt;y+Zd(a*K^FkXic>`5=wjS35g+pu-%L#u&i2h?8=wu*2?~5h#WbyA=_BK zZ#P_GlVlQ-N4&cu{T0s{Y|75~iLi64>~Wa)3y!AAKwLuAGx(Y=GoLCZrcQPW8FMz1H$is531@{_?=wShzf9|jo^Gy zqCxfdx9oDARACYQiKjU6yhtX8q~dRzX+r3Jm6Embj@b*9E=duITuC8tU8f1(chzD( zbOr`myw9i3Vvlsc1K=Q`79*zV>+}S}{W-p0LLRje<-iBHPo&haKv!|>27V77`eb#L z;848%a)C`IX%QDjEZS*1i~K?n*_wz=Oz)8~k4f|#Q^If9A2`_yz27a$yt$S*`ww`+#DNajBSGU*QaJS)dN0P)L>jYB3}l@DpmH5%l8+)nTG* zk2WCu&`mMX){X27!lm!NA>*+vv5;&PeGRD1^@8DB(F3v>03fBpptEeo!TG+yF&%v( zk(vfziEiD58OKJgZM^L7y4st!tNp}{mt5Ia6)7YT*U`lH@p6Xyl}*mbucl@5_U4oa zFp>*N{C@!5nkWOHhV}tg3?4v(_yEM`pA34FRkFZ9LBHvQxa0BeATx2^Wz?icduCqb z6%G?Sdu|bS@#tqs!@}=u+@G59{_eT*0?-5F9qh?X&_#Ves7yI9FT`OnHCaRn^7dR> zQMy)`!3lF40AM~X*qo!VXV%l32~NVN_|iZpAj()Hsn=(p+^+7+g4C)S@m+YKeE{7z zzwE^|Z$YaSyj=1)aB~XFX0znL3wKgm!I61#LOhptkNXsNCIq z43Ld@m>@*hTU6Uyp(!Z1olg4Q(CEP<1r^20EotSb&U;AKmi4+*8aiHLnRw?K*WwSC zXOxyiI*zG&^@tqeV%sP^N|57^X4EQ9c6NoL<|AjjTk7#TmWc+tA1|0#2Ks)c-=l|0 zwc~+!C|Pu)fnezPmQ>3?DL{j=rx!$(FZ zb7kP8pT=939D4wLpQojB-6QI_&}A0b5P%@B@vz`5v>*^09A&x{m+YW&KTgyx4N%n; z^s#XC>qj*qFA22P(wjikCGM5v-9VDpiA5M6Yd0^JAG7%X%JoCku)GO~z)#oVP`b3?~!!Dq(gmBxj5JWAfq-w_c87&U0X=j5kC3}ai!T|YIzip;!@N^@tXG{hFR z8(|a_6BQ+$)NVCU%i=`F>6&rie-*DWPbb2Xlj^IhYc04(I8p0D>w>>sD2J4faol>w zZ8w)#fLaEx+6KHk@daY7A2Ae83;7C1-yl|Lm_ncMne(~qISOVc%jX=_Jt8C~W()JV zXqR+8^uMtBN(MJR=6{Jx*@*b08_ja%zL5SX#_Na=;M)U*vaF9N{z&$td8Xv z9?8=5xsjaj0(EWkKdY$H93kHCPHys<#Y7gAffRP`x8o5{xdHK$eHXL%^$8+bT0dCJ2b_`a&=qDm=!Z<2Sk?EVtKaTV6T} zi-Ej}61Ug)J&sQ?c*c6Ckj(M7va^9eZ}w*BDzY(~S>py@xO9CulBB_61PW+VM!qC5 zK(^pr!W?BKKzb8Q{)jYkwjrV}*!!UO7&_S1naXfR*r(s&?G50j>*|)wCBE5A-&_E< zBt&#hpUz;4_g-}C@^Oa07BVkSxZ>y^0u_p&YJ2tR9ov~-B7W$l9QG*f-2(G0t48lErOc*=Jzn!sRbl%!$n2v(T&jQeOX~OngYBH4hpl9T;6*ERf3YwbG0KP2sacxDR7A9J3`c*P*kB9uby8b4u)t9$^&d6 z)YF&Su}A{U<|#L;4H`?UvegXi5*aRMOT7vDQjv)VRmjf=gW57+(p{SMI0N1$`x{x0 z4a0RKGo(Y=1g9Fhg--Q0%l(1a=W1odhfF6qrgAT+0e6m9PV*2^U*9vjUo6WqaY!bs z(T!%_E=v)hltOoUz@m)8idcSh0Pxdr-9nUG;^sq6#Yua3Y-x&e> z7*JEk|4SKv(Gq8Z++*!hXj_w}{T=F=wGPmfRalvf19~e2P*veO_ zyHHb{^yhm77*x6U6=a{p;%NzDNvyd|$BAsF@EYWx1(+EV%K~G4 zeb?3^UxQ1~_1Q5z%ca=K6f4*10$^DtnKr`SddTd#n;C>^9oNedaq^||`Fxv8KJ@o` zmG1MgV`cv~Uee~w!}kA7wd}@6ehVzR`ha4v{k6RnV;{yDyGM4;n?0%Q#I+4+aXRhl zG7DrZ%V=P*AzE+0&v~Zg;61TUrZ)ijt_6DVUx?fD+|0hfsJJ*!nNCFex)Ul6kgxW( zVw)6=52+;ELmN+mzzZ3Shb798eKD_V{n{Od>V44J+0wtKkpDShz?KI^xKC#;qZseh z2!F?rMHhT4Bct#LrA0ShvL$0uyHYgYG4>Ai17Y!t3wJg+cRAJc%l(hgEXiSCZ@!?3 zR(mDCPMus^?@hXM-L(WV`m}>LP285F83nJ$HcvL60n}Oc{S@)T!#8tO)Ftl~Ds*@{ z2E7(v#;#WY(~lz}8q%s+azB%I@4ggyN`UeD`}?1lE`GD%^qegwdMa(kF+4;3mDwqo z7PUTMiTji#*o7NxBv)oLZY8<;+|lQv`Hlu4kY9yMAJ0{P@3yxB0*zDE`4t>^%6KX| z7A+G2r-t9kV~yI0cw=fSFc@`p#dKtnbCcH^G&Q~0U&~PqsmTQc`rVkmz21CpExYo` z9q!HTZArB@#FXse^#$NVEK?F0SYE0K@%#Ls?5Ac{djiD1K(@l%qJTY%q7<#TA;HPux8 z3B}EQSY4Q?y~wjD&d2vmwJKqyg%8}&8A;AD_CSO5_)QZFki8MG)cshxuJ_bYxC~2Q zhsAV^psb7y&(~JiM$e0^0nH`#GyZ%sMX0AKmBg z0Km8*y6U7zSpgXNSBbyV0>0oB{W~<4wDS1g=G3c{_n|g zX*>U5u+5ZslN6}%wsc@FHpGkK%ge`cV?Wxb1@_uc^7Q0%K0tB=DzG4QCR+Y1IpW`Nn16-l@f3mE(2#4r zcL>4|6dGi%W3fO1oSh!|HZXxR`{vq6s(!bM{_c%@5KT+QZw5@v|9nIE>mQ}9V7&UF zbM$%W-hXES{BtGz#}}HwRjkDQ{?eW6KYsrY?=eOQ_)ugZFFk(u2L3l+G6PpBX2iSu zSD(xO>9T%b836b5pPUl7e=yzt`*ZMjba(DTiSE<iTLyG#>jZD$oRouXgiWj&dq1sPs;mWY2hf(@4a4Q*AEEs$HGC0#rHf%ZbC*1!Gl! zkw>0!JcaX~?1N(Mi!g5TM@|^v=_$s|?G+on{Vah;*o+Pc`KhFu9<^QN&Tx8 zt!YN}@(LCev&8q!Y@`>q*i3M~2Il(K_v`Nc&5zQrOe(Dx9enTJNAuwre!y&iU|Y6w ze^9&wfxbA{;Fe>7X`iW6nTy<`G%8+ZDYLv7NIXr6m8@IGcnDrF1UNShAE_HCs1{s) z_@nZobCG1+`hEPQ0+3hqX&1ut=HE_v0i2RZ3@`&sNJlp_X?Q)A^?=mYmIX^|bb4Gu z%78%-eF$uJ$W-Gssd^V$?cnN0o-Xw3xr!l1x$*HKB-U-Ulvh{BSzTGv8F=*f7wDiu znk+ilJHRTB3EGzlw75(l*4S~V&TF)@4wdP%YUS!ZSbp|F48OyX4wvy1ADVJ~m0bqb zdcQ!HlW4AvC#GGa5-_X!b?PUYe9`p4@VcJ&eFVj(#0lNnQdXr0hRpsN$ff z{YaxK&@3Is-g`33Py1mzi<9H+KNzm>Q2g%R;J2+Z-&f5yx{q0;`~*dx1^aJ<2qAw= zr90gDV=CQ)+5Z`pF2)q)?oxC7&W?3t;$te*`llS6?24M^a+v6N>rWCWDlt<2E@1ge zi~3iEKg2rq^WtsryeZNCbAmRbxfDSjrDchs(eFx}Xm_E?^4`_}i5qmf^Zq$wjqH9u zL=>2(XqY9Dn&FmSNxKtM8*~rkendM9rtBI}d~0q~piK9LW59GU5Nkd8Osmn$y?}D^ z&1hs+v{Wb1)3!RFTKbI~jTOMNww9u!qv;xmauxd$sYffcunJbTWdL-yt5hFo;ViuA zT4#M~DI%lA0Q|w<<}Nn@g@aWPJTuJ5t22W9t8wi8(DR_$(J=G3V})Z`PV4=H`6^N| ziC=Z!U=O^Xbi%kdRX#R_S zzl62=^bae62KfNOF+gK2pWFVlwk)Sc|GKM#kd#zDwh&cO-jz5zhhAkkBy(#mrQ;Mp z`on9#Ejsm;X#i4^=wqF1$2%#6+epCP+lcr?-D-n|*!o!>XKG4N&3lHs zxKSolmdyMxR*8W}HpGuvQ3-LHj`&?;EKT{$Wy9=7zVs8ol4omkQBl?D^y?d{FZ8!r z-T*DiYLCX3dC9VN7g^?-^2myi+8$GB(`{TaB+(={A4&@nFP4 z?GE@*os8FQe{gy_k{pe@1}jM@iG4hq!5Z#qSa)uiX#W2*^ZsAP%=;g(@h}DwB2e_* z5K&LAK9`t*c(!4b?qAFGlls2V0*-c{eTF19=@spFpSxI(L`+p$NYyVPX6&ii!(4h| zk00Thu7shXLY4Jz=n8fK{XT4xA1o-fsSo;&^Y^O7A7tn;P$GQcS+a+$-5r3|SP7Tf zyoegLGz3F%V`uUtBUG@@*srHe!&F9TMiOf-t5|1Tkkv|}BP@EpBGhfNXMZZTX{f#E z81Q|->mKOK)^4V*Fhk&M#=;w$3~?jV{XPb%Kp3k};C2?*Q$#=!60-%jlP#WdO}n$0 z&g}yyMp_aQ&3?+zsmd-2BgyYfK@4}C7H$lbhMK2<>&l0ZlC!|bVXz0?~zE3ranE)PsSsE+Loj4=?yGc)FYqC{%LkS_u zxO?+ro^2i)XtV%VxtHhRlQUEh)mqJ0J34AzIR;`j4U9bLu>uVS;tmZwQDhq^!XJD_ zFXYnUg^67#x0%mm1S*Nq{iU2&RKzZ2qs`Z=*I_c<re@lhlHYPa(fSGxQ__% z@ZJ)&-Mg31lpD~w|IA8YJyljfqTbcEY7_pyH)Y!E=T>6--0J(n9CamxLVNlQ7m1t} z-A_yBG6T%CGrU6%fxu#p?(W*R*L#%DVX$(^A(-b<7Q!K$GE+lYJMwJn|{217~xW$6CqBR1v~wMXKM!#??vs zY3OgvMt2KQP0fG`A@1D{&u?R4I}(%0-)a)uLVZad_Y_7o*JQfl++ZXQY3a8 zHF}QTGc<6ipvxo?Oviustp7?E zX?oIwEiS>6?kmKr3qE@OW!HyV=u@MR4O^+4HxieKrcpDYFL;soHycR6qiQvjscrQc{#^%h-d%-52|MZeEPXsWua z2FJYYC@%!@zIubnm3SbPEQ||jxjDR5cpO}KW0-$U^kWn8@VvC%lg%~ zoYxfZacRAuEKn;PN<*WJHJS5VSDT`LGc28_56s%@-@ObRD-lCrviFNz9~jUTb{V@< z7x`?}{#3?mxESD0#AVx9Z2%M+ZIpBe`m3P&l}>gGz|4bga&rhUv7E}odIFfax`z|N zM=_$!_?tRN{}+4j9TnBOt$8b;pdgAMNLElmqU2ZvNlMO{Dv?m+oO3XdCFdYW&J?+b zl4Fr`0g`hricBx}xwr4#=k9Z^r+a*3e51SnW594(wbr}dcg|=2<})XxSn%@21Ivlc zlXgixJOO#0Z9)HQBbH13?n87LTmP<+@#F8GVd3u5TkRD`^Ce0nZ(TaCQBbL{{jKLI zEyPOwLTgB=fo{lSv$1Y0Z_9_zOm1O_?$aePoR4!vOu63k|bcIQO(( z4?#=1F5Sb)Vft3)5{nt+gphtn`wsI+Z<@@riqgWh!@tSvE5lr;x%0=blUx*a?9Gr%X*-EyS_aLd;6>08jl z_gJ!*aLJE~jQ@D8`WKksK#tT-X0m`qJG`b<nz`TA)Ou%rSv2Wxs|&;F5FHq_`XV|h_&_tgL_OqU{Iz0GCY z=F42gA1>ot`bp?FZ*PN=LS|H=&R+W zoh>W1@T^h*`*bU(j|*Vw`GaM~qmI0aqam5$^`7EJ67&VGa_9NRNWjsIy?3_T53%A& z-}(cMWAvlBw`V%P zwy%&$_lf25`T$#xuPK__w{HH0Cp1PQcgpIpkYUo>$hVHPWK+<1ryUk(rU8hX$BK+O zKl@p#B0zhjFvwbuv;scFq>yUtgbEWeN&)9=AneJ71>AD0Yb*xWn??510r2SOPkOTS zp=vfig`7)k-Uq#0RCAfqI5FGr;I!I$rF}F=an96dPyF>hAScA5yV9Kl&;USZh{Emcv-08sdD<$opo|<}1{}3D8ebF=*pk54?ihQAveeHG1 zoS6lE^VE8-!8u;2#E0SPK<=*V>FI&Kz|K72>9wGrFT2r5y*f;-~kv=`J*U_%rdJS`?n^#iRnrl+PF}(R2XA<*6Nu*@wU3RDWUE62yPPK(LXpY+12T!dKj~Xy98f#%FEp;lCOm` zIPf1fA_(CdV}-Rk;Jpd=aF;e;;B+PbSMVyj!D(OW%>M3s#}4Y zz{8@Lh(vyuFUGEXzZI3NZ)ieD3+Wxfcqsc||gQi-O*F z6Lk`dq`YYs#gIAnd6nGad#T5O_cF(=j4_hK@~l9{enDc5_L=gxUj;+T%9Q(tU$SzI zVsmpI;Mx(fp!^cT{1AN)fdUmerzPScHPT+!uTM&{yVor&kEq|#`xjoux zQ$W$F*YCFSiQP#o$)4gE#LAp44i30?@8K7P6W+8+r-&|DyV><&ksKj ze%FOpsI}45lhytZym?2L2Wp&n_kz-l`(AVE5Rp7mGJWLxK0$y_Q;YDjtb+f6G$^XG zllbUIk9ANg^L9*D>j`U3guguJX`o#FIK)I`vpDx0j9An(4u1_B02gSxB|X~53RRv z+38Y+HS1lfb1+$Oi-_zBJXgY63ndqR>#_a>T<7k$KGa5GHpA<)Q0owzAv2SpxJ(b) z7s?A@(?AR5h4hbx)O6!Q+9d1k%L2s=R49o|Cd>Gc&U2s32%h5!3o{p(rGM~Ecfn^)uH{fe&Y6Yjb(tIoC^ zW%gOPcu6nD5;K9*(S=%yj3M4C5sP!sg6f5zeLf?^IZj6u`xaub*iWrW`rQ9c0(~-)?K-uj;HzF|0pG{BrcA-fxK3) zHEN>dd^eyMRn6>M?lqKzIFLlVMTA#Su1~_UO@1b_cHQ$ln`MAShCSv>al)A!iL$R0 z$T6GwS>SAy^J#Y^9e(C$_SIx7!e`d`F4RQG@!)ZV<#_nn5v2&wVf;O%-a34;Kp}=Taz}dpb;pP+vCzuX>^OP>DWTIm~K*r3tAfT#!jA;MBuf zP3p>xl5q0!7KXTXX8DE$To>S4uR1L+%-PWY=ABItNyeE^=JurGdL&GJ9=03=%pW!2 zezj)Lw7Zh}N6uu7b?|u{Au{OK;V!@{k z*gp>0#KdTG924>F>iz0^QQfbPKg!m0#&cR5+DbKoX(}{zp(^Sv6~bOdgh1Oxv7?^# zcNsNLF5IF1uf5|*GPb45h=)D3uZIx?hX_;iXk*bVw~W>AL$Qno{k`(B@blC7vWk}- z%0ic9+z~-q@mWsN^<#8%xGz@`-Hmzz-Wi`7?&WGU>WGg`P;uX^+lh-cSLaiFgso*^BcXLvGRRXzf}7()>L@_fiE1b zSas5WP{6xrC3kCw!6Jk#{>nNyVu)QD(W{i#mm03$*Qs$ar!cc~5ba?PBL3&w(!cLp zO?5#i_I9nS?}9u>#9Eo}r<|hB zZA89>FYo<)7SEiopCQR<){AAP9RG8C8ZLB?8f6o$M=9(iVr4zdq7Ktb!f~Lm*?edB ze6eKZCL<$q(E9T1UXFgo4L(0}GSUX#z2~)R(|MmPXWNB6`!Y=jP^mY$tqZ<=Jf?(y zRllKQs46qLSOcT8aFdsji*dBobL&hs(%`|t!66bJwo1s)eF+^+#M5yy>ZJ>#j<~Ec z9e-<2Vab3w6z#MjDX5Ve$L?>{XpmDoRp;@PM~8){01t!P`T_2*=Y5qA>+Y9T;fd{W zE7~2*$bxE`|LnD&s};QHG)y?V&?WO2x7n0S*rX^pN8cw=eEarJ#qI+cG#+W@WIz3C z{&E(RC*aDHaH*-W5l=qWVP}lgyJSFrW<-alxMo(`9`nVk@pWV{q?&FR?8A!n+*O+5 zYiE;tBr^L7f@6;Eq69QJI%dP7}~M4a~zon zSShH|RciF|B)k6nl8B60{+8=0jpAOK8xOLF!U8p%u)AdYDde$~jL-#ZUp$(MS?kz3 zaCa*4czf2WDhvn{A70nF?>1Bo`1txWPwk)fC)NAB5ax*V^Yde_T-O7(izFl)b6*tc zY09^Z0DqaNAEGLY)R!E$xbG?FPrp9{#Ft(AOL@v&vjG z`!;#ey{o8#UGk7OC@`><;wa1ga|qjR&&@>iJ!(449$ehJYjMr?%B40&y&a6JhOtE; zqj`nl?wXEN3Y=EdGL6fji&0wf5Q(?j=wbTwZKT}&%by7Do05P}IL+fr`ANa$3sRJo z-6W;GG6;vYIFO;Zc+6f5fii+RS&&kS-48v_NWvAQx+=RVeC`%tk*14JH9;#(d3qlz zakbkh6d@;deZ{ul8&p2oN55LmsbX{HGksfth$0JWHIejz@X^kO2Ey|@6r5|C%-i6U_W@IySy5`erl9BsD+#h0dsF8M?^+S(6w^J?bZ{}QWNaH5awq|LDukP zu~L{M$10)2$4c|_hB;D^T*k{Dzz9@Q7%Ww?53q9m9N^6%c)VoKY1f;<={TiZ_=4Dx zR#HfoFf~p(qg_5NJG;MfBCFyDL?rRmy4Jo>!kfrcuebSkHXQDL2!uSY3HOC}+mJKf zth-g&NYre3z_3ZqGjt_!W108l!+#e%{jp2==G`(A@+;lAx;I(Nr(f84KqA!$x5trl z*QYZ2o~^7dNp|bj=WY?6ooD(jWK%~yWQkYmUgvx$Vo_^~2RzPK%KXm9M9Zss1e=!P zS?Vh9)}Yj^-xxJmCb#md-nV7%5<*$)%sb!Q#SMCRvz2mvBK)%TGK2~phirk=#)-*X z^M_^0CO^k`VH|kl2ipSgI{TuPrpt#JYjl5Mx%Zqryu-%GAAWX;SoO|m>~)bdPSHCa zta5mRMaPYALgUpS7|Mbh1cFx$$?;qlO{sME_orDdKoK>}aFCE|Z70~c8>d^K_`>a$ zC%P}tENoHMA0$wz2w`iiMQzEf=dR|eWEYLlr;cu%DFrdr%a!t%aCYmtI@4rS z(SgfOHA$4OO`2z8qSbI+!!#k!aBiejbk>RjXuRtC_qbViXBH3_%7}oIH|6%MQ&pT; z*TUo=c!n@bcJwp;v%ycP8u91$hWguK5S7M6>;R@ZTh4U_#NdTSw$_&MNPVN%wzEV2 zV|zFo#h0i5?gemMTJAFGol{WII&&Y&a`~>y{{?{cFi?5LNKD-4Es zo3a=%&8_5ZOD*eq{5)Z=0~){~VIN})fp|+#!Tf83Rw0%Qzc$~t^Y3EW{SN2z=S%DR zZfb2g=Q(*o<(2F4h#fN6nV?{s7RVSH8_{Fy!KJfB#WDS5NQteYbvaXPXR`n^|^w2_BmX8HPp*_cS<^lq=BYvTb_VaTTDD_@akad0MW;)Cm1FvY$!Z2c_zp= z!6Ak<ZJo2H>}Xt3GS>uP4~?CZltG`(r@C0^fg=(286og4W-P9 zU0i@0S9G%HKx2z-wS^+m(s`Cf_r^JMd-HU3m8F5qfKz)J5LQ%Gbzfh3HMDBEu!3vT zNAVngFFsHV@zKa;sV}D$&z~&iwqC(AP@*}97^~OfJYjNh$)VsRfA~-yQ00l6n{rik z3oc=$_|yY!p9DiCc0HeRM0lNC>9mr5%4hIs=+h~W(DGwl0axqaB-&Z4(tV;6!e-egsD67Pop#j(8FK>lE&7ZLg?WC^v*dYf1K zNEc#F;Lwk5=wg7&&z*1OLalh+K;=_4`4B*V0Af9(`8SlqEuScj-8XCg|=nXrol+k5ek(dxiD8zvoQBLlt}pc z6Ak@vDlcs{Z5yN<{b;*_eM_KBxN3cNn|PAbbdFfc+16b^fdV!?sQdUvlAcHl%U?VQ zle_cY&;diy=YLWq6mhXERH(-MMAs=Xt_AmM?6Z@E@UYbm5T8{lhv8(kEqydWHYS?Y zg2rW-tIezK$GvyNI_5i@X9iiZ(|E}~EOhdXI3xzTfy;1HbSQc^q`P|HvKP)7fdmG? z6cMfXb(VWt2rj3Nq@_qsVF6uU1JHP^e_|ve+3A~xCmbl7AZ7;a+y6aQsWEHw-7Kne z*V{&XxtwvdblpmEp{_CSbt|gK4w+oz(^6KJy;wE*$=bkoxLidum~yT7d$|lC$Jl_= zRS759nHc;EOQ%$w<{ zx3~8pJqx%u^Qin#Rq-pdsKsyf&NC~Sw8O6K0PnlgDuHyNlPVT$^8ZA2`p*~tPv6r= z8~F5Q_js771xu#y)u;C)5WkU;dvSI6eIdZ$_!%Vl(QhbPe|POPoDmh`y*qz@qyA!X z{#=`qsTAj{tob$ z2D2TrKM>}P!!vVW_YpYc-3jLX@T#h+J`ooo;BEeN>ybQ;!$Z+)TFKDtR?9>|;HtH1 zI(#dUbfMMEKF;ky)`3t5?6afA=TwU;t>s_kK$cb`NA_h^IAptYSKTr)_wLQ3IHAy0 zf+?~8x@!O7>KgI%qC>LY8v$i@#t z8$|7)!Upyd=$$FR>wRTn#9m@9@pgbkLn+`ODb>~{kzlE)<$mi5g}5KE`bnAJAuwC$ z@-}_`uf)x&M5LuX7wbpnqu*o2jSCW9?NE2_yQN;g24H zkIrUR=>-_|@+gI(#DOtBQr0XN*p9*5ubZFhN7+B^d4047fdz1h zqp?knoSdA2+B;+BHBAYl5Ut>nx;l!Rb;LB9zBjP0#&7KndN60q)12kJX{Jmj^#r0-AZmicgMzE{VEQVYDP zsUe|f5{w|@x|;@|gS8%~=}4ja`ubcoS@%+JykrF-86c!2x(?g1gKsC^zNPZ@gpt0J za58ZCB`5jH*afpQ5~O<4fEhN>mvXwaQY%Ee z;|y7pfW@^^i)2Xd6X7s+c4hk08B^5HZ5B8?-8NNk^F8pmELu9UDeK^j+iW7PKj_qR z<;WK3y-rwBIs!wM(@-0uk?kJ!xVQ()^-W_%#6nc(_CHU%4i)lw7kd#Tl}dqtX`wFO zy^vSJ_oFP6mIXoEb!BGzxz1La6ukkDXwMu`q?=owh9h-$ejVvA5!9zF=939`?;fsC z3}P-!dUbtspIZ=NDXQMs>J`Ye6(L-x=|OWhnr}z1sauM_gLt0qhI{N&3^Vl4NC7F2 za<;P&P$<%vmD+Ivl*S_6`|jtw9CX{1m?$n*Mb#*U5OVB@MsHfIy|D*8({}piME_)a z`HwTgQSH~tA0zZ-*ZpNX(o~k*vD9Mmy5g<(-{N@Q?8|+9S5Qfe*)m;MZr{xa&=Iqtux~3KL(v1`| zsHsHiPTG3TO9f8-A=pTRSkh_d^0LNy;uy-E8pulcs>ktJKak0YzQ%o*Rpl z0gXf1*P61iEGs$Awwq;BuH(lL)1lN9T48UMJ(~MlO98Y;?fD3EW2Xl9HGhY>O9Og9 zuQ{8>#yJ%g8_ZB7TW3mO8`-p0HnimMccKly4r))UzuOt>YM?Q|!p9*-Vsfo-F$h$m~W>5GaR zH_lRi_0GAY)%tE&fW|dQ8RyU*{TuG7Rom0`$wHSdcZ`g)UdmBkQE+=ugt(eS;MCU1 za$K=1*$YFHKL;AjdhX&R8iY_B((k9%mP{tarYLK483Bc{6(4-{$AheMDn)_@dNpCe zaT@qT6P$}_V=q-8#E2u!L+*a?_29ur)u$=jTza~;PRq$dsvf1UUBl$uSCpj3PY&Jy zMOZh_W~l=Zm*+!mh`!)z%O;v%a+v7g8-*)ccn9tc^{Vu-b!9QDc zKrsH!95d`do7#RxENu9T(qitLa-I4O;qud1$EYNa1nmklsePooib41{$GO?Ga*Q1U^M?l<46IT;=9YuQFCZ8~;HmaTU2;F(%SsWZz%g#-h2hUvrfG)s#$i%13+CcMHCfsi>-}1-)Q?Ho7)ja_1*t{ zLw*ny6lAc%7EFqM40=*j8de;JquWP=uWL0@eTVDbT^ju0Vpfdp`;_JPzN}jOU`6Q3 z{v-&GiVkO4S#(>ENQrsv>)Y2_7yOM1WR?B_GWMmn&uC*fx~HiBZ6YS0l73~aOTbPY zw7L&L&smg@}sH@s2-c+*LaJgcu`F3f>4 z?nV$R9$=yr7(Cat;x_9g1n5ghSYJ8>Snmt($q<$X3Ea7`X1M)yhbPk;64z#R#|t4n&0Ui*p3{x}Bn zruXC?=JrI)6Yn2zS*%Jv?VSebcfOz%`tao_*!&ebC`=b0rzmfZWxqbhz=C9Qsc;;s zjWs>XcjA?B(y9uFOG#Fy>EFKq8(K4`$7TuZvo&B55S@?x2s0Y%3E6DS2F_EiFv95? z#>-28XTt}DLq7hZ+4Zj!uwtBOsOEex;Zbr2!(Y5kMcCN3*RMe!@P+VV&$6vBA*mQ7 zss5{rtk|cJN1`49W#ChV0-P(0;Y$CLfCXh>&@5@t4G2D&`!WhGp=ifLa5N5*u%yT6duJxpU#^CA$|-~CvZ)c!lp2n7p9 z_i6SrNdsR{g!rBd5PWpLUY9^hh^#}5>t0m#ytB9ItuXdSXj8z=4>pWM%%1>Rjafd`u-9q zaS=?eAgiv!oC2SiW@6pCH7p>bt|)G3U#nqgdv(0&&5)RPaYXDpsvrbXxs7Fg?wGMc zLZa&khMh4k2xsoqccZtut%b7t?wCiL!BYW_1nJLT()`7XE46eXp5Jn1^EkDXe}@)Z zK3_7y;gNmP9O;FJ*FG>KELOA#dmiZ;>k7kS}DP$6{QDSN=|T^imcyxZ=t-5UuM1VoOttUGw-gT#4UFa8i>QjuNKgC|I~6QNTLU0o(GWub5{-Af1nnSAN%($UcEf-`Y^ zK07<>dcuqUVRIO*?zS32vcC82+2%^^m`+=H^f}bXSQBg4Zh4HEd@Wp9=sD(wIL>!D zVM_jZ4DYI|+GZM;SaQ1;JIy~Js!1z`bEJ@Mm(_BJvC)j&euZ?XFJw6q4Lsi`P0Cb$B zq`2cbdA%+eEU)8rIhj6PK4QpU;!KCya1O$e-O)6-S=Z0c={`! zXqlRh7^aIrdwb8feG=l4X1K2F{`=70Isr{XEGt&ONlIo~aS@wf7b?BpzD$LyeT_ck z%yI$L$|U8Bf3)OL{%J6>D)Qj)K>X9ujXr2W%6VtA3P&9&f|BL@c&7wscA3Bb)m5kX zFg$qNJiU6MU^V&2t{ndSGHdjQ51)d(ISdmN7=h5+gDpE>cg`5)wJiNNMhUi?F*U!n z71u}D{P7k5DSg~22@=I1jqf^mL($rEGh2EfSM>9BsxTh*E<^kE;o}?s5ncXi|N2w< zyHOxgUu$Qa`TY6OX|{~%PLINH5v7iy(;tqB&OJ{OIIMGVwkXxMbBj)nJl&2Fi)&pS zk^>_z7uTB_K&>4v9tREw`=~-EZzvhoXUWbL3QlvqySTWmh_AaGD z8_y*vV^>}_``qocVcE@o-Rr8P^sS6Rn2&bH*B`!Y_iosjc-o~6*u{7^ZYY3P)3M&z zzZi3)(NxhpI6VB~59Z_5%6~?e8Z&yq@w1|E57B;zDrcD_krjn7r%!8Ivs)mw016VI z(u%~AW`BI%5Ayr5puNJEGB?JW(ZwcW!}E$F_IX7d316$c6MqAa1|1t z+eU~LQv-jr{@GygP zqgx7}c1K~6JYptiXF|=ACmpg4-BRN(pS$8vM|i4^o{lVWFY9ID8>*e<@91Tf4`o#u#6AN({k%wHj7zSF6N*dlwdYvs#^?Y{cO;^8kc3aTOz>hXYATjlOZvZDv zcsmSFsiKf{bp<@m?gkwg)sv`ASXe8Q=J2nDej}PI4C^~C*?Xh4O6wQ49sPQruu=OX zY-Wm4@~Eey5}#tFRwmZYw2e;2HJsNx-_ewqLR#!|NHi&4VwH&m7(C8FI9_Z`x@THn zEHJ*!2Z1}O;paERWpkML**qhAGw_>CD?f~4eU>?c$cjWYE?u9G=@rb20vxT9LG?uk40bob4x_@LOU z?-B8s$@bFan8;{$xo3+~h%yD<(P3;7=bcWKWm zcz6PPwyw)-ftNdAn4>)RfCa97Dhm^;{@;WS*)TFQpGSEP$B&yh$XpIRNAjIwk-wO?2~fNlcXj@p*{6?vw{g}y zv!4atK-w5E9bdCNz`^0`hYhtjh~liX=qKjT6V>p`grf&Hc#yw+kFs=zjGLskcbR%Jq-+%r?3&fx}w;4 zmDT%s!;Q&3^v1Qt0b;W;*83jWiK2@*5pdk6&0bn(>fD|Y#k`G_2$+8OW|OY(l*#%i z>4-vWIy?pB9!T798Zk4Fy<5mGaQY>D(e(44oE#q%?w{9A+3&Xc$?Qa)xi%k?eLJ9? ztbYS z=J)m7^SoTP1H{~#{x2U3f>mAPcIvHY@HJIq2L^D=kXil(c~4kTFKUuJpQ?D-z33y? zni(vPjU{Pw#Ivd#`gaj@#9j( zH`revFA3L0baPg~CQK@T>3q@v;x|;3+&G9WvobuJJZ%^>(GM!njyS0?L(kxLi{!x= z9<9FDJNgsj7kl$#GJHb&xaXsk6X$E-V2$ zvM9T@4dGu!F8AJ)Eeprf?IJQRx@{+)d;j>9{=16Ti1sg0S4UHs9uo|A+RV6v{0EHQ z@o`pMciKCAdd@Nmp(Js@(kbIp2-#o`8)42{qfZd)-7`*)krp*4$BT)ZHT80E%|0hv zngQFULjSJl;FbjJf&vGrM${(H1Ie3qSmWX`>YG1&SqBTJv^C6&4izBwreq_s_U>}3 zmAexk%P}TsGPJvQ`jbKW!8T+BdIC1<_>c#NL>gv!u{L}PNzM7JflBoB!&CTejD8K-;)B*8T zNxKivzCqin_A_9(#d%&@G{V7C5VqV~3qMoaxv#N93p$jxT->r}&$kPeQWF{;=~#e5 z;K#|MYF3b6oK!WJF9Snej*6~^*~5vb_lxeGsOm?5c<29%tCu~A-LR}QQ5=cY^kVG;GL1i8+8gpe1c9%vi4cO$e3*JDam zZS21-kyrjfe(V9AFTK2b6`wmz@>wW)yM1wWLn&k`k%RSz|&`bL;S+Ku^V9})O^K~ZQ;cUk9o-4V1J@?@T&^42V`G=V{ z10yF=*RsBH{!w`h!eNwLw}h%pNNJJ0nzdOy73RyFh7+3hCC|~WJ%!H7t0&;H&Ef3O zNj%>UEmhTLD``AP0gGT)2tHt2c*0An3D#5;>A*{g>=w}_J>2i+n*;O3sweFBs7QL?d#!;x%M;Z^|T?)pSspaiXY|3^=L zdiQ2N3aB}IPR3K}NOlbPFJ95FVuXEev}i&!fTJfx?o&-rWof9>f~#Y&En@9OGGCtM)yMbCZ-5BuUANli(D*qNaiCc!V4AIUj?vpJzQ`Zv_b zP6(Jy5q*U$)d}MqpN(uayMe2k>wE(7<6`&xSsboMmFsuUAu^86= z$(kk?cU0g)E%&`kq4E1D)^EZXK(R>msp26)u$qI_NEVO?OT;wto*|RPOr~%T>1jdH zW$Z)r^t`IKYi)(^kdF;yLB=K=Y7V9mxm7rBi$2vghp|I7uxxta-9SxiTK0+r3^Czz zmpghjc3sAh`bUgHo~gXn(ImereQh&VIO623YNh(eRc6 z`n=gR%Rh(c{5(MUyWPba1I}{=Imf*HoD(nuT-2k(d>uqXREk>)zvG5<2nJb@nCie; z1>;MuHlm~gp_NRTnxkDrY)@Jo0c{81=3S-arhQkU;!D<-FZ7c~hlrTL=$>}OTcK)3 zg9iGdmh;_)7pu>(uLNJ6DM|Br#{g>`T<^v{e{tyyd7dIRW&}i7-TmEB%?ClTo-cPr zQxN(;!{5s%mN6b4_a%;L?J|U|hPsQA;$Tmq;p^Oen{bdVM>4;ws;Sx4h`pzbbl`fu z^XiZaARCMNFPhFnp4Be0EuW6pcbsub+R`L`ZhbsZ`F|wb|DQ=X(eVEdNjU!B zT#EDFa!vUvvEV6!bSkE1()M)mbVbCkIQy>l!ZFZb${CY;H39^`Fl2 zXc-A`6;n{AstY*tZN`&pul_VihM$evQ?JKsgw|?SEsBig!<`&3*=O62qB zZLQv+#8(T691t(Yhuf$3e>24gHpJCi8-_QE5Ak-U7tvvYZ$f0C>7q^3pue5g{pWpF5q;}H)8>l#Oa|23Y?fx~JrRxTOH19xz^ltPyCAoM zf3OMvcPxra1As#zJ?;sgcRV~mqj|y>7TR>B6$BJ+-Ev*wXL3mqS*XO~PxVMR$u0q9 zx-7(j+K3l_uNB}=ghmeV>GQ{IS`L7wN+jS(SYYZ4NW=@m3JfW{#tH{*ntxh1fBL8Y z@S6pSfQ)XY{&rgS_t*J9g5(0&C0XBF|4S(xXASrvKF0*U%H<#JrbSYa8B}wEff%S{CO1D+>@x^5MW&n`QNJN!H5>-?BA#)B zX3d)liR4_DNp+_&)48q==KST}_W=fE{FQ}*J@;0t?UR4u!T9piDnc<_FflMp+Y73I zcFW_HpkbJ^ca4W47Cy6^d!_LWD_su-V5}tDisu&dMcuJL-{p3NRQ>s_Pr`c-T;1KL z@jk3#Vz^jpD*pT+LPu~U{C>$rdZx0=0K)}vA&fQ<0;);V7%tGdlPniW=QIUj@B*8a zye#hI$nc7!oG_hxP};uNLUCRB$iNM-g1wUy)wh3z4--L>P^3Pu8_-| zy1J_B>V60M;VU%eA=ejQU99Lc#j;&ChOK?d0u^vX=5OP2TFk^@U|{BoVy2SMaMZ%+ zd{)1MNJst^@J=Mn_|}H_4QrWwpk265N9m_TF~T#n3^tTG^;7IEAkaAb4(Fsey~3#gveL*a8|rCvd2t&X^Ly^w!k;N8N+cl9y21zMNUZ8$wWJp@hW zPyK+7^LcN*8;GuJ6sgbQ@AZ!SZQHYn{ne}&vNu7$ z_nVE-``Fo+tsg80Ih`K?!V}PH-FC(1Aau%Ykqkm~}QzrN2>Qarms_Fz1GC6P2SVx^o7Yh`{=a@iTm?|T)#pHgyh zAp!!nxVr~HLQMNH0+FmvJ-buJXg{>mOffSLOqea^yko&oYz^pX>veH+Po(Hv73)Dom1+DtKKU$kBh{p--N9a(33!2FRg@ha!{h zTK&Vr{fzmvh(QNPo zA-gIVqk+@1+r)H7JfG?A_2JXe(rW*qbR!hQxjZUhSy0U1xq?{Hp;iJhkzqOE|XEK`VjV z;^-?x8X>R6vv`oPRrRy zp5c7sl&QE{nkUEt=}j~~4srBUpb(bbtv}B*zEPR7nI9az>n;RE)gz{XPFVR=ueuNH z9K%sr_}=*BLZR(~oNux8z*(f;@i{fAuzs;NHelNBhrUwDP}isv1-%K2ei?<_{G~FW z@5o*Rn-Q@8*nO#yQd%@4CE3e}ZEBHVtGF4J%NhiAD{v);7GIm3zRr7qI8IYW($erqo|-`5?rb7i5MJT=u~pS<$os`wC7`+#<-C0NUfm^l zs_Ybc<~YnBe##!L9a!M2|C4|QC!gcF#l@g8q{yZM(tp8YSN`%{ac*Zy52>;DWJv-r zxmoP#Q-YBaeX$LSxyb$`e&a}`DzD44qlIhc?<{)m-NB*fPISue7hcpdV6^y&{V+UU z7%LPQvQG3KqJSw+R_ZZ1?oKM)rqdW#pX?Y_XanYu$T)Q9wa}Z6b5A{OmtA_^PVw~W zbwCFURdtV>lwlE$iqQ&3r0~6Uce1P?eO=suNiS(A4e~mALP>O}r+37=Ny-tX-&@_W zcGEV*)5BAI-i+9G-Y%nEF2_{yf$cjFPXZI~wKPi+K`5b@>(2YTVPC!%1Us-%kITd! zl@r~?vv{#$goj$QB?L%(Nkc0ZmDD0#Cr9}yiq3$$u+cUdhgDFEWs&ul>W1wp^EoNE zBG&uinCtCAN00YCOEqS21)MJ=myL`Lz+Wt8BWh7=bSc>8Mlt57m$pvGSx@V5twcg@ z&Dpu6QMc|9t=499YGBSwvXG~2eZH1H6zmtGJB{Zs7}I&*&=J^ME-rs zl37l~PWM5~c;tVX)us{uN^0{YNHE3zEP62ko9%6Ie}wzd-c3@x9$%kEixXcb3z@IN zKlNTb=(qcFV?au;z60=)RM;#Kb$~M^Pe5;o6GH}IhF2HSVBj@_DuBERkHy57j@I7 z=ComO936&?072L;5af2hI5ib-^hk?~J3hKjp;FK&j_FzSdcppwf!F=%e%vQKaz?+C zheq`WshR{AlUbJ73tK;awG$A`_lO!4*IhN>NbkJx1H(Jbi?%e#g#!_Hp(e=lE#_j^e$ z&~FC4Gjdq)bBcO9u~;FS+WXb5z+a$P0^hmi**rDldVDU-s44CeRBT-n;jCqhzuX%@ zf#tl})O<7sPwm|LHm*Yo8)3*Q6a;!pK1uAcXjmc3nv*~(A4H}cCcZ_!+;iS6CjSLp zJ%yId(3I8MtrJbFd6`{){xcs|<{A{k#tkPOWTocRUo->w;$r&&=)zb?9lgKKvfqaA z-{>%tDOi{`0HQ?#oiks2N{J?Gfv)UPaD_o>U0qV;#Tl&a9Wb(Hy=gYNtVnJ;lz2KA z;PkZCr=x*JPh%DVagmqU?|^HQTrD|1-%~fG<59oL4)5&lUS28{BL-kE=jDUv2^S}r zocllBO%YZ-TLpE<9KNm&yG&Jp4U1kEa$cU$K6eNXi507=8w=himVxSUeBH8qIcASfC(#u|si+0?u?uA9SlB8EB`Wllq1V{#*SKTcK~y zSkSq8YZ~4HFm#)|ExdEITv~s?g-WxD7nu=2cWRIdE*{=PNH1HxbCp&-q~;q?&6fG+BiA&5=S<7Jpz!huA%{ozuam5LZ$K5e)cOJl#!8MJ5-1rnu$%#(i5oc+$#6- z@*3HV5e1k-WdOF%F`LxzI1>V$ui73h8PJ++MC}7Zr>GVT1chArPfyO&;f);k{KRMt z?eP;TSU!HdwE*oz>u4m7PdmT5wQ6J>hoR9&&hQ8Or3|oNW7Dq5-Ya!Lf6jYCXe-FZ zrm6tfVygE&c`-oEQ+M#Uu#5w}KCRU3gy!{bF~jV_!eP8?(f*MY(sl=?qK*3h(+fbW zPnZxqefQcz5TdXd0iXox6<@3j>bpuDp~=yqdZhMnd|Ny8QIvhRK9i6NU}@trbN|N=jNnI;5GTbVx`yNH>#C0V!z^DQTn|=?0~H(lzPs?n!(vd!4-+pQnQ#g7d7rw+821>nYHsL`yR+ffrwy?{r;-Ag3T? z7)!1M{qVyX*9cIZ-#{ZNnK9|TqD!6N{aCjA#NFhWTeEqW@oz3LZv}3A0#~ae<61;M zmYU;a>+wT~idR(Ca!qZNU3Lqq34sJ%wsSh-8Sp)R;f6rL_D2i*BtRqd#o9e5A65W} z!EAQ6%jpN*=V`ad{VoARs8w6oY16pDR1*yCm6zu~!KzV9@lmV)=JwirCiA_PZSk4&l<@$Ex0V};C{V42 z9BSn!U1u#NRo0}_4QTonCw$tpUA7dNiZ6|5nG-8&dSiR<*-_B4zqfVvKK~3fb(1rf z?j!{7Ox8TVR6f|)H}(Kha}#_Oc0%1Wl{#=tNkZujZDuGXQ^hqj^4GUw;uE)@Bqh$| z6CO1=-)xfES@VvLnU@Cb9EjDCtMiIH{h5OIYg&rj?*LFok2QRIuy9y^!c+G?CAOvp zSi;9YoReVJiTmargG8IjN?(m#yyu4+#hwo&R!CL{hlSumq`@;WKO)X8jJQ?NR{yqy z@f+yV#7`~3HrVm8Q^a2Z>DRuHrK{h6Nx$~x+~PGR39I%)*e;4U0NubEUP4v9e2Y(| z$(h=^(vlMsl%xE>zUjAx8w zfy4l_Y8ocGj1fAx4t5v&ALUfn#+FX6V%%w>q8K*=;umlQ)kwr2)FOrwXqrz6x+-m$ z*)9q$ewA0hRT-S|nEf_0xh|8vFeebR-{k!~Ado8;NVC711VrO=Y>t$-afeup9`~pX ze)+*5^BOkvg~fvd`7&~7S-p{|ah@8``_-m#F#E%Ub==BUt!4Z-^j_<0ksPTV7>2gwl+>njf#=dgvx6wIh2P={yuScz;KT#zP zw29ieyw>m8^ZL*iHLt%+$S0m?%x{9WK;_jG z{2u4nj2};4*L{~?&@y&D1}NxJw56m(`$1;}m2lqVvq9AH!xT+NwH7(l7vVZDfYDld zJ^+X`GpqM_6w{iYnBCULj#XSRvL>DNU`f#MHf(=#1x9O@#YEoG*<3gz<#bR=4c6iH z*XuF0^i+=quAB?=(T!nCu-99_j(CjkgZqq|M(NF1P5tn0UFcf#KvIpOT2eeA0B5W% zX9zo_V4`T?=O4eB^OzGQ`jN-<{X6DZy)1%;ijeNMtUU-OoO81(lgL$Go2kdl%p9^~ z)oW3E-c@Sl>vDBdRk9QAm|sw8A~b{lHP%vBTid9@!%p9)XeDiQf^NW9<_s{<%Vt*V zOioB)a=AFM%p>Yt9xc_Us7sACw=gXmw#Y6jicuU)%YN{516c0dCf=GJ+_Dr8^(spH z2Q5ejPobh6(}Wo0Y@qZ;SV+X%dILCH@!wC?g%-!MM%c6D0EE^tcrFpVIpjlGpw}V* znDX!(-R7vw?iGdgB-DN1HEiDq|1X!NEfxbpre3yzYxVQ;cCDHQg&n|AraF|NQTzIo z&C@OGHzT(WWoEUayOqCNxv9o4>=yu9a~p)If9A3N+u8fJ80P`^40s*Dx>#jdBak>m z+yT*c0J&SW3^D7U)lGo&W9Ynxja7g}hR&6n#r=QV)3)Kol`%I02haqO!^5 z*?Eb4n*_hMu^JH6^#uy83>~+^qlP0!nC1DV-NRkv_On;oxn-0D+ya!Bl9XI zqRSU^1;tKP>pb_T9Pv3q1MD@EqQ@xku~wm4scb9*LCS112)&5t*{aZ|g_o=7Ki1v6M`P_FSH;027FL z)ueCjbF9$MbyS02fIZfnx0C90g8BBky$0lr-+k=#xg}$Am9I6aml3P1rxz1yvt_Mp zizW{6@AP+uY~%fJ9t_z$rU00#f^V}t#phNC=4s;He8k0 z*knB!yw0mE7c_L%&W$tYX#rtrUPfyuPlr=Bm5(efXLF$TLTYW#KB#`jE!$L}!LlDY zgBk>YXy>DOp!{)WLCdQ-9*fhb$Y@BT76u9c@hb0v)|~c$1Wm(UM7bjf9vOfA<$|7z zAkLq01txaVV))A_@(22d4i0qEDSAlQADgP#C^X&zn%d6gSm;@KPRJ4w_wiCMk?nwAvwU{}d!dR8=yaU3A?=DFp}cdu04nkC zkDnlDRj!<*fIJ?{dYMqc!@~nAqlV=OFHnP_<_rjQ>hU>(3XMqt89_GO`xJsb#V(m& zaC|bQptR@!T-`^){jx3VwHs9BfOaDe1R$lhX4akBDdOODgMjDuw*d^-WHB)@12p&1 zrnb&+7i*seV4owSebxft;T`)ILHW1QNkBba^x@yuSFP*Uh`NWBMj9mzw|L9&ose745UwT1 znIOxN+Co1B`nH|+qb5b$X0-mpSt=ZGmdf_TJDRU|`$=rTl$ct0)5yx-LN^xZ0xMTI zh)GUn;2}%>_3r%d!4La~J`KGaNs^Q*{StG0q*_jK35lCmGFK95@crU+Q8T;?^_CKU9sbLNgcUK>j&RZz2KgoR*#<+}Bsc zIGyk!8UQ)1uZ_cH2#G&ae4?;Z8{M>ZlsSdbNyFI(`}Xvk1RF%s(=CHlPdqN1?%e)kX(BDN@? zk@wqkf2B?CZ!SRJ$<`s6(r3Nw`8;_N$b)aIAStO{ltzV2n8tHW>_J@`L9z5)RjJvy zSI~Kn1v87$l1$LhQSVwGdBp+OY1@TfVq|tzOQF;LrprZ1ai!}LJvnqAJL)@!s1d;) zb1CEf?9E8WiP@>hxnD{#B-)RkP4t!ZB+0GO&x**%$hzS8?kqFMhvf1A6qDh!RCgcu zvpNlu)Y4&RyIbz}P+g4{o7X^N+4n#(+|BMh+~Y{7z3sK2pjYJA^BKsH)xo!ji3T1M zw4V&#xyk#@E?I~r`=US<_r9!~@*_Z6fT*u{EO5|7Q~4cOW!;xSM-HcRtJXKoPk_JD%|@AnE+?;V%S`*7mia{I5jD$;LPD@0iBDk;iE(o0x3-*NbF*25F=F zzJ6)E$Nm9mX-EhIpZ)!n0LKflH@0**czB@3(kN6nqR@GmunFbu;cZP2+jBLQ%a1_E z)9Z?-di_ndZE!TR!{7=NvN6gxK_eqy^D9@~&j$-W&X!pN4|r9k5?Vt!WDvy?TWW4A z`SL2{6#+>D+v7OkqGi8;Lhfhl<)s^*5)lRXZ#9s8Uvebl9o>3szea!;^l47}i0zCB zW5>)6@Z%~=NFzoi?Ahtldv05~S=T&IzL|XB8Ld*SbkaL~WM1VEUy)k(-H9Y(;_rTD zYk8qgyl6(@At5=wtsjAI2&cC7%rov}4YiVD-g(;lw5F3!kr`(9msrxs7_a=5U-`=s zG)dK@BjRTD&~KxZ;?N%yW~g7%hlId;3YE3Q;kkM0(Q}GEg+uh$6+WQEP?UtD^mz(* zvysr3>fz$iv$v_--(Og`k9hO6h-9Q2Lk{bwYoG0*=fAn31UhjOGn zdfKQf(52IG_T6M)#i|Hn@SXYAGU;Z^$wEzV*Ry{e{r@&p_4i4upY9)0b^A5|yR?Dt zU%p@z;s7Y5BOiUVv~)<3o%)d=oVleUE!)bgBju^P7XW$Zt&hU{zYOtxEBgpemjji# z2rR#_8K9_;m5XFV-1qA4BzG~A6NaTXh5Wx9B4i)UUgWq}e{C@T?8j1ph}Y5${LH6+ zI@|xdgxJ4FlKyxB3}*-&{h$4ozx4j18yv==dn$|cU-^xH{xiQN-~NF6UykZ;f9n5- z!{Wm;QAP0M1rd=jEnte)aNdKB1@&cRMH{-~=cVz4KBHp>LbLQYn40oDg+tZ_oL12n zZUdzusR|PMb6rSb^={3{gF-Wwv0y?hKdGox_7*;30>*>RqT9xaw2t!vgn44ikhPRx14#ga{^UkQ6qRpF7~>vucX0-|}Jq zd|v*3f#qF2weWxNiTjl5wT}wOXHx+!lzGRoSf%NP>h{PU>$cz`oiuJLYp#rELXNyg z_fnY6{EtLAoORxI2hq~LXDHxiB;|(|+-y^h(N`lbNH-e`2beXeuPYC}2?LIEYMfKX%p!(hu=57=?wOE7XNq2RR)t zS5VlkuIqJG3UBq$ve?3Kzs7wA8d4ATJp0#U`PCVWxh6}(qcqQ285!7Ijm~f zJ@MO|tz$hPe2Vh+PjbF3upg7SEdN}2kNEq&X3_F*irWHK|-(=e>!*ea{bcvGHk{lcoZ)2EFEz;$mB5?P7u&@&1#Apme$>8XQ3WV zC7Z9XLZz(iD9xrb8bpJPYAu>8hud9kkQUVdq%Ia!`#>G7D!6G21QXr9mA8ZJvt^I$ z7+0W*%E}Dmg$tjgLqchIVKl}>puEBPt$JST5pxr!eZ*R`I#mKr3kC@I)*Y2bsq#Ps z;bH{P(Q!DixO>IQSdJIG#y`0dP*P+lg*Vx=S-klsh+va8dspd8GC14vhVb=M_<-*5 zpdd3#@AO?m=8dij)>zr3?BQzt=!6JVwqB>2$Z-$igz*x`#WJbhJ&E}~&B~0gY#o)R zF7=yG`Fh7asL|^BXFxeWt3=?>NomvY(fXumLLS~Cc9r+XvHU^vnoo_{iG;%L7e z-x{(%wL5s*4Naaan@%}d+I`7yJ)%K!hwFWA#lm4W6eKLXEds1sYV`9z4xRr*im_-% zjmC!-@NZ{pvv|5VqN^p+_veA>=^td9!A(bvCXK2_28wv@n^C4xW=Q6PMO&2)T_(jw zIqhYOo6Ui#846E}k?yb@_%>`6RMdtyzn=UL3_P4a>am#y`BO|0AN!R)*BEZ9Uh z5=~c^VuGHsdj0orQ^xAOI%3<*#|E%ytOx98%AB46t*#;lJh1wSTfi42qpm{l%d)Oz z)?5hhT6yhGK*B+9a%!SIx@_%z(f0AbE& zd3FjpY%;qzOa7iCNe*|t5dCPoKJnyp75&zr2g&zEj8NAoRA>truj%H-Fflcy6ki~* zq(Pq{n@K~Gv=LSy!Y>|?^49N}9$@T!0q=Qq1>=N;+j1II7_@f}*NhJ7XXbmEJ$W4> z9TH-`)LOa)WfrXJoGz_2hequupT6usB5UwmBG8D_tAFbm zP_y3>d_tJP(*k_RteW|cx=Np{S8wR5kDVY+s(P?S@D7k8xYgxALrwioYWw*3ny_nl zN^2iGqfhDdQjp4mBsc!VHfn=oGQzgoa&-zCd_U%szz-XHvQ1NGQ)$g@v}xdvzjn;A zCP%t6=wu|`V2>Ft!irlpFL;GNoJ*~KQFd`1IiDsLFS@Y5wTNcwvQHb{5s1_4xjP(a zle48(8B4(`ubb%N}3WfK)jm{;w# zm3^=eIW1l?324$E!bg92@+ViwYix8ayJstH~zCc5_-|<-Mz!S4`Gc(Oi4! z5|6y0L@hrOtEMNf#aZ;-snsOok-kIaW$3GF)3bwjM10CQj{9UyOU8Uz5Jb10~_H>BDfEmjMed9b!>=@16psP z3?+sK^4TvFwkl}pcs|=#M24Q%s@sE7oni<$EXZe!`JDFL@w1t&drI72P=y8nalIM*(Rp&$A0|@iHcF6WA6|``)bD8Zl2*-}aR0n) zUspmL8tW?o7VPmHT#;`fpYiZLlzCTa*`=!4>_~xXh3n4`34GzR0qYSoINssl6 z*{bO|SQHni!LnjdY5B|S9Ou@Ts5J4zFg#4mvXLhT(<3kF=y`W?Ieeyne`^1I=WQ1M z)tW9C}D!TWA&AzR}$s$9oh;J(7zL(eQ%g$dQ=Sbdi0dSR3s z*wxOSy92%sJ)C!s1GdzfaL;^UY*=qmWcFlVc4$j(WS0*n=<@vc|fyhoPBP#D$` zh17sUJn^U(24u^|0@$I^$6FfPBV0Q`z5wX)6R)RIlr^sdZB2jGoS01rZYYQerZj-~TXw@M9pZH>o%(qAHB=L;_1ikx?N6;)2a-x}NsIBw&0qTm1dQ%D ztSdOmu+^)&{>@m8zJX3>0=ISBgEW~A{+FdH)A)_;FP%XY@9U6Dd8CiU#~cldNf2|f zZD#4p(d?+|C3Yn|=WhxnCZzP&V-mT|n~jkiw#0Ss86zpTC}+4A*lNAKj>{U_yIv(s z*q796&i3uwdofo~vHw^`Go`qQsZ{ARJFN{Kdj|x}F7f_Xz^rD}pg^q$@$^Qf3slmB zv(KdIfNjIfi3qT<8N_TE{CB1;A2r<_M+3Kt zIJ(Hm=ycC)qA{}{w;-SOl6n{_QK~zb<(a* z#?OecctALZ3 zlS&u!xBhSH{JNetx`XJ&>wn9gl|z&shqJ;xI_5+Hd!YOx%&kasZf-v8?bW|eCFWe# ziQx6zrj)JUp+d$;)n`VGJO5S0X`WVE2vsSw7%mUbhnq)3q8(ws`yj+Pzt-K_`f zUcj6o4DRpP$nPq`LwY!dd6mwzn_kyt z+T{|nr+b8J?ts$U>ip<4{+hjjg%35@;r-K*-QcP?49fx$J3Gv>&Df3}O)`p=^ssgF zR!_#m%A=10dm{5lp__$n+Lkb#9P6F6BJACW0c$9AZ(AfOe}<7^1)nPBLQcx-6!(pZ z5X*ECMbqtRk4NLhgn9F_3_~kBT9%p=z;+|+!^2r%fTLiSveR8Yk|phllX@P!0r7|@ zNo0?h_ujyP=_EoKUbP(MXstF!&U>d^YK|gH*NI;f6@M%YVH@Er=xr#!HHj&l>(Wgp zh9+N>iI-&dZm;xs_BP2d8maUaZtLb`v6VYMg8)r5!OF+32au9{}qv;#cr`SOn-{`;VEhskF=&bwb~NmE7|4`AiD zKT!V!KmEytcz7Vi*WA7$!#Dr*s05shrw)|4e?Q(HUZl&Wce*aB6x4tZ(0~RK)1ce{ zdC6Uwm3yAxWzA|_-A4zxKmsb(fy?XFHIm$EFyFfh6G&!vo|&}drd0v;z)Y3JJD^_) zVwvc$ZdDeBp8gIEMyQIYf}jUVR^+*GoFc`?p;&E`eCZb?Hw}WWgDAa3Qx7KJj}4EZZGTvDEhKA zoJ4uglx+jqpvw^h;e?-iERqGig{f4wkk;P=S_6ARdQ(|(HpMz~@Lcrja@rrnvp-P$r;_>#m@W|vV~B-^KiH@qHh8AFNo4s6Fc!bwE;ouW0&KBX#rHGh1iN6 zDVWcx8u&da_-`+pPB*cb6?2*R%`Q>7Mw3cq9Y8f~uR9*{zqV(ZB9r$&?!-AUk!lem zQIKbLV7JL~gwpYQ#0{#DV~_H6cSy>|^c9T@(^S#2uncSko9beX#!5WY;DDL%UKTDv zSB-_lZXMot3%FRjl4~234lDw}MNJa)&uJ zq$Z}||8>g$^Ys7s*NbgWEs8#==?@CeFR0zn!8ZaB1>GX<%pI*wzE;uzcW1;DQ)?vrNTOTq2^L&2(Eh<;^J~3lM4d#Q8%3x(4P%aKok!I za3O))u0=5P$EkyxH606mE%i=MfLncBMrM*yq`;Fry?grn`^7?9YgsyTS@XfFqJ7RS zK_g;D{*RZB1`@AgW``>#A8n<4Z0|m*lsn{iJ`GxVT<{WX%Q?fTyuPD(rdn4YCTdWF z__8VYN8tJs*v0##YT`W8(z{C6VYuyy%(PRJCIDLO?JLyAH~>x)E~|5iK^qQ9yD!6> zTQ+svXn(Se{2}V~v*D?QTaBxOw6|mLb9epqh6ml;!1G8`<6c+e04HY3#h z5bp|e9IvvT;5RNlZ0-AQ3hwKoX!3uVDl$X&u5tDUYy)^6HP+{*gU-1NTR9rQ%6L5W zyRG`)=juPdvsm$P#k-K1#;0}N8>`Eu+C(!*v_xyTuyU9peThvODJxl-qL_*`QKFNy zOfUM2frg!JceXBoNw*Q`_nyoJWLfXqkkMPJEGNGcO3Z1RD|lRF_1zXjsmyZTVGDQN zJ^B2Fmn4vbttjYJyv}KR3@1!D8y+tpUKRGUHNG~^dN-4!#%-C4wU~95aUVGOxkur@gRwL~U4E^KE-E9nl8A+K@8}mXy&ZT<5YCT7sba608)(5!i zWabbT(?8vk=5ZpoDRf!5=hm@XvvsO;x)l{l)h<;dgR9=hpa-bP?c+j&*!bvdIi|_* zoj@GjL;^X7L)8gP^Xs7{!=R4W(jSJ$E};jR z;^A#EdD8J7&RYJE0j>6THVkp+j^|0E zfcJ|ruIY@ASkqM>p%Q#Vojaa{5j_=OT8a`517Kn=_LDEyZ|e5LIrBNmUo9$TZ>SY6 zys`I6U~{m-#FNkLx|OYluMQOCTx>4&2;dsp+e#V((%uGjqgrkt608H?T4u?v8(#*V zn%E8YmIsZ(&LiA#Ft3m;3ju&+n>%fBDi!8qrnvm%7xF$)08ufIS??OUCPQgpqg}JToR0 z6tA1sz{VvZ3$W3a+x{8=cnk{%lH+{@a!N5WxNzHLz0H}3K_5Sbgvy2PO|&W%ioLv^ z?sC{X-HJx*A_)I>u?20PM=2%Y{7Oz?81Sj~$=-BTcx@xg%D!#lZBOgRL>L6$@qRTo z;DbQ{21W$V6;V8i3q@wenjPU}X^yqlB=1053h_MMg?F0w+k4j=_${~d6u@DPSU7p= zRV7)Kd6H!A(@1#*c}4EikDIPx#!*Ki$EuV2apmfN#Y1 zoGAt|gh#q;V15AF)s`QC?(Vxi5-@5KYVp! zJYhfOu+`w8TW)g_0&IfOQG=-en9Q!gWS;FDonXinSmvp{U&=LWT_B-4#~#q_^z#)q z>&tN(6Fc>8#_q4r%?gG|!6Kv_JHG4~ow+uYf0%Gm*1*?6~PVU_`Zz+#O8;EJ_U=mHWnGki}r zAfpxXf^RdsDo<}0;6NbXiH^wdvAc;@H0!7&YXZ5E0AJD?@& z3$LZ`Rr!bPGZLvMzhDjXpI)PqBzP}Ib3y_Yb4(tL@s17CZ%|o zImhJ|9_`Qc!}T9e_H89v0#{Tgi4Y(`NIHOe3GgcH;q2K^p#Op4HZvyWxG@(Qw6)3G zS6zhYGCg{F?bL!5nlu=LZAr}3?l)hDJN2z%LwYM*EjRcCR(p0+KJd;P7QBuURaNm} z^`->K^VRj(lB$<-g44Z|zx z1$2?zVLcb~?g=67PW{5FcW-@Q^_j~n{#<$yw);Lu88GyVO*{Dlk9Ar5Hp% zjkPzUlFzQ?DzoN501)85{z0FuCnDP5h5*Hl~ZX}u$ zwqGNPNBm{u^DQS8zO3XKxKgTmV8&&vkQu`4{c_O)I36ughCzBTTvfOFnb^W}^X!uc zsh3D0NlB+$p*96TDLAju;lUgkjKagiGXc^x#+$rmW+tLUb0*DE`T6?hH@lWFVn6~*{1?BtW4|C0-o>1a#ULL+2tL&$IoAP^nEx5g{s-?b zLS%jpuj!+v{~CQl3bKFeBCD?4Ga9Irx-TNjpI&-MPK1g7`lR3XJBju}Ti|(ug&%!_ zTc$Q{dM%x>T{c>(oI}3qCk9_AZm7l_l9*U-(0u{@H+?pbuY&`i{kR_S!7{EHO0|(0 zms$lwijDY=0elIxdWC{Ak;aZ`N$49z!@uvAv&J2*PA>aR`*Eh7cJn+nRtKbSLrKD&nG)5Vcp&kPUeF}>^dbdfDzFrnr- z(SDk#e3#z6`LeFbYXG?v(VcIL(Vr*J$}E-kC~Om7;KG|AIsiP z4U#$(*p9lC^hu{$pv-g+A~ksRmq`D~efXb#vGdbci&am2W&e|V(7!!{LrNdb)P5qj z|NXfBZGfj9RBCYu)qgOF|FFLP!#96s0-xxNM%U;+su2EZM1I5msvpK>&4=;-Zd_;{ zBf9d3;3fQD4a_G-;D51p`+)MFU=RN`Hhj{6%rSCVZ2e!b&42sc|HF4byi1SRqx$cj zkOiF&pd=yqIBv`1S^g!C@hW&Qg}yLfMXc$6c&tBjk;Z5qgKcdQvmN(^_GYM+WUNqn zySd-OE_TyTtqJoQ8?{eF9rs5%i1vi(2n{T24o6rgnQcT286QO%EPpFVyqzP1l82 zTkC);77*!^!hS5V0I^JKaMe~a(R)%l>Ki41eO;H)z{!rBhYb-f(54lU9t}@zc9a1> zk>Re#8MM3%9SNSbqHR}le*V&tLoD=SxdWvoJDM6L#*9HIy%W>clp(KEXh`F`mQ9*y z)AjQ&5@oLOVvQWMjxw8~v^wsJ6T=jMw-XpmWk>J>Vh4L<<|*Tj`9+B}8f-57eJawV=$KrHkIv$T6y?rDlDx~~7{`kMX{Iu{Qj|FaiXh{6@ zaN zD&GG2OT_#VbtP}}RH{I&rP=Qj7f*d%W4s2sZFxEPKn~$?=?K`27|1gaPxEAlZk^}@ zy#+dST-M{Pz_*S|qUw#l=X|QMjBI8%JLEVODV_m{K#HQu?O^T`B$|zQhlo&1ky6OV zk(@7Ct2P<3k;8l~`_-mi&j$Ji$}kUgL961@(p$nB!WgBvOQ`tnSXJgwA9d}BM{pdm zpBfB>IAQ|}&*+$VtYtN|9ee9_$3t2gN2_+eFdWPgP85$|AD+J;6>vP5j+chSe4|@b z`P*|&FK~#sUB?OnkEe=L4Ff4+$wNJy_80#IzBOeQ@_oYHlkgdmp0C7&y`d5WyHn>gd zlqezK@A^cIgpD*jw53Fy?{FcIg?s3HFdccmH!UaIQ8^zQpcD}BP_S^z+K=>9@RQm~ zy0+@nUrM-H-9AIj7lBc3&z!KBctVrwJPO%(qN~$3*Mg+TRH+2X1;a9(^D{_^f@$v}*V(9zLUc_ij~z6u%jXxpn)hpn%+ zP-aC-UL_ z8>bzwgm2TY#QK{jQ&JP|q_-c68SgNnm&`j|M3C#Ngs|w_$O3Y1~RZb z_bwi8vtuhV=<(3Xi~d1kEq~LD1GDuaZ-qdABV;gv)Rlp>j?-fPab*sWE&rR|^fK|D zq3L*e#qoR>|4d)~;t3p}Jm~F|zAGby(klx^5bI0H7e)}wVqH!c4dno`Vs7+niJ)kSrir%Zm2{3#q;K+ zSQmSyfbeV+zzs~IW7D2hn&R#r!>p7Y83H)1C<6`wAQ?bKiBQ+&8Auh2ezYKKm6f*uuTU&5w<0vQN zcg;>b)uPn|9dkN2`Ki=Pr+oj(AQ#WRN5_%R-%0V3|?;Z?C- z>0{%N^MUzW0R7KP2pv8tG}0_oW$gI|xk&hG;KumI(&foU*A2+F!C{?W4&8KL?_2l_ zUti%>3;)BSyhfDy1@@j8YX>8g&H{j4+m&=Z5EjjCaHM8zfT!ArJ&QbO)Buy|xOfU!MR9ix` zs{zZjlE1;Q!ailE?!Y6df|s+wK_Hnp6ZjZ?ZKTV7?I%OHEGlnMTpNNmY;eb%_lD&Y zoAu`oPUE3eJsXGH1%1cHEkj?va|L@95m(`5e6n6nX;t$rEjz|xYBuSLleIJlh{&;; zdzE_lM)w_iatQgN@j?A=PvQdVrNGC_Azj+VwLPWf+mN(9vC3Lf;~hr0Euo)a#BUt9 ztAh>^a8d^z3Bw!CE@>)&I2@>y;zFi5-;tp3xRmr$$xXjGmYYl$$~$Btpy+yn7Ro|u z`YU$B)U?Z;9B|JtNSei`KNQvZl6!!er&Uv8%Nj-AB{sd)9yDB|)PNRjpF@s20Yw1G zQ3$ky$OVX$;CS=Qrhb&2{wQ@;{x#`Je8f~BS4gxn2us)SEHdx|HoM*Y%SE3igRmBU zKqZN}vRMtNYD)B~)xZlSW5d&5Pfo^JxJ)@INdDx8oqmn@AgpI(;qD=ml*!G;nRj1t zD>fUbZO>vl1>KTB+dm{zN%m7pn;wQ*p|B4EqHUd4;ZaY)fapj;&nMfgCv<4}}AP zCtIOz2czgHmAQx{PA8%S&T}u3aYt|Id?NKuM<-7^ZqQ?BlGbq4&~g>HJ4A_8#geK(aM7GKxqK}HQWag%-Nd0N?gj^st4$40L?!A)lkZV&1=8= zlz{z`;CiWnc{3EUP%{no2i6)6g?I=l$!9b|SwOTzqwt_!Lc#NrUGTcf=NR%eA^o2?g6sY^WL0*Rs5`yBsQG%4=tN1Q+fG2YGV_fui^OhLHk}yu_22u31v9Pn#~cC5{C+jE_ZS zAD044`MH-k^kMA*DikVH7i-QHqPkJ40h-AqHzK}T2>}WkxOozOn1XmG*^!-MYFmY_ z(^P`nE3J;m-WmSDk6D4b9hec`j>?KDsLn2=bUA_<}QTTuGKrZ*}9yPcIL6KDtRRvsjP|y+`~YPF$?h7_^?ZgDSrMM?`lYGZ z2A5=d?0dw^FP>K0efGBJV?Dw%O@=7OB)r1J?R9dJ+cH3@mHrHsU?g<({=0bvPwBQU zy7f@dKF0Fk0O7=YRn^bQxTS^$rXcS7i>}n%1hyQ+$evWp+O4&sA9j=LWh?vXgc9NL z(?3jqq--V8uw*A;V2X}xmU(B_3!B}yFbgP#lrf2j5Dp9G^ke9@w$gAV@tc%l!W}aV zQyJzr9CmhP{G?Sf9l?s6TY8htVQL_rR7sHOkgad_a?b7`v(oL>BI%@W`4oXl9?N$O z=CrQvWPj)5wz>&5Y8}XcbocYgSo^yiMFgj;qH`-5B#kO5?Ngnb?O1#$onUyrad?CP zmSkqK7de9v54tV$?N3+DCBA#d(m{qY?UrcX95=p{mlx>yW`=Y3#K8#~Wfv*%&jarJ63wj|kpu3KU{7}!XqKnI zUA0bws`XYtjmN=V`?8By#QlDfx022h*%0B9l}0{8T!(lw?#J-h(xW4I57)TxaeD7) z$mK19_S&%d4-`6+;}7T#L>c6UYxWltJ9#CzXUkKdh!in0n4MTT=jlhqxb8OPl}}Y0^xf zSE~aEp_-8z_U+cDW$wIF-DjcoIvRg)#fZo`6dk;(INPG}v+qa~Zr(6Kf=0Dpx6{z5 zx9HtK$IHR#uWaq}g|R=`64((@vx%1zl@KTn{KR4E9caMHyPBh=?O?@PU_$*>p_tR~ zPX2VSnP+{SiGm2>Ptd~;0zN$JrW&`P^7z>~-qCDosHuOAkMTRNEGJ9FHSGBfNSe!Z zy5;AHO7~%+*X>rj@E3hoqzYbMC4FwSiElvJ4JwVW-cTj;o%N~-kx~NtC$fzm6YiX^k9OT%#FPCWHK_*H`mE-?WwW$vHI{8c=dOnfuP%d*{yo{Z)VcRd1C= zb#{U4=7r8CA zsSI27tJrsMp}v@!1@L}B3(bb+-4fz?3b`D;b)Q4$p7-m6lofF5behdSzd5Xru$M=6a-Tf4G86o+kT*U%)*=-d@H_ zH+86g4O^j9sE=6_e(9Q`;v7v%t)S=+Z%~(Y3(aMH;GV5jxrFm%Zj0%(?Xm|Fv)R5{ z4PTn;LhBhi3QVdArbfp)F!$^6LAZ_$a<)q`6-KdK5@cxkYhE<5BobS{XTlP~Qw#rm z8BQtc3Kw7&xlKY-|5}nS1`~8K#wzsCyAnJumQ;I~>Uy+36vxG&wA6iKXhgl>$Me92 zhI_DG6Q77>JEZEsSw%`pI8LR+`rSaGq%JwF(WAgSG)0%S))-qe6>>U3^A1R2e>9dXkmG``2N^Xy_w%?IczCbQw!?wjVZ!`yyOFg22@gM(#q}>e3a4Qe)ip46F}el z`JI%1hn!Cin15d9-8{8 z()dd2_1us5M&QG=nA>I4x1^H7cKki|0 z2<_Iov48Csy;&_tYOuuLPP}92x~8@Q{Q{aCUgIm!VmNJA9^`r)xHIg9ezVQh-PQG2 z&dqlxpub|p@UZhtm-3?4ac41Lp}<8IrS4VnzThV}2I;p-({?CAJ3|=43MDC}$DCPq z>Y2M$npb;QGL$+x;jF%iPTqMI`4u#88Uw4d{TyOS7xTuohVFulYSy|cRHbVrvu<$< zhQ`k#6h$juz>Y3r4y)=e<%22whY~f0{J3yepnbXBqtNYuhUu90slyU+%|FNiG2p+{ ze(gRqT^=Z`3?3Q-B4=J7VMEwBIK+gKF+t2c?Cb8V#8ul0g%`#;mbkwbK%dvc z0Jhcl)u3MX9_;qzvN8imd4`TcY>CBw zSF9|B2gi;`m9wvXLo+VFdimer_E2M@VC#a14RV4q0uFaccdS>x4#&A3Q_sy&`D3}d zx$9P2i@V2a^X1D47;JucrBaaSf@A*-;&1Q^*#71+UZ@129J%C9N;LJIHJWXE+L-#~etL%1*1Z)gvB-%;fW_A5phJGA;k&RhIbDShQ( zU8z;VDLKxeCQ9>Od`ix9!gN`=D_`N5Gg93+j9+kO>ZdlD!D4{cgfZs!FDaB_2+szr zhw5VFq+lV$z3Z7!UxqV+%KqdjgC_ep3kOaz)~A%4LU+2lIXcEYoZD!QXxbs1?*J!e z+VHOLrEdx$9oyh(2i;ZMDFIzB)^yvha$>#CGyrfb(a&D-b3R)((a$v%A7e>n9S7a-ch+B zt)G$TDIBraPy~Ah^j2n}Y2Ot!!q4S<+icAnq6X@Kft$^k99LlymHdU<` zThMlLu)>|M%k4&K?OT9{%SSdvU3UJ?Cx%h$LRKoi05Eo`#A*iC8x z`4rnN!`6N}J zc4Zjk%|xEzpt%El=6XQ(JylFAJN5i|_n5`=Jps;cBd2Z2nPq_a;+8y(4|s8%;c1u6 zPH&R*H+%|ZagXHI3{mR6#Q0d2WU4H=q$PV#x#@X={P?aPY8w@L=R1QDONQ>nsV6D0 zm-ktWGIM%Buq};~9L2p73X(hYLYJB%KMZWFVk}9E!{-;y14KWvunH+^?2X}I@_hO@ zQ%Xw@=Qpa4A;Z$0hBHfS>QpW<)o!;XAb*mG9_$LIwA)t~FO`&iU?ihAd@dN>PV5}i zFLI{eg*Ob#E|h$a=9Z{Tf@MF}7dHwec~DByu1e#T5ouXgqC+BoJ2KqKa`sK3W>xm9 z(pgZVrx1&>^Qs|X9VuZwK$sd|ENgAg6!OsyeBNE~{E9*4ZEIT=X@MLJ+QaXYvZ689 z_cim`vT>+)&#(5FO+H9FGTG+C#;1Nm><5S&Q>W0);{04xfO?a;*NFz##I@p|{CrfpP6`XYC0UK}`Q!bMINhr%?@ z$P(eW*Zh;_g?_SQm@w+$JGoTOJ{6j+ntL2Vf^qDPAD-(#n$^F2F_W0Tb7!C9NhTgX zl}NaU!gDE)_d2nfS}0L=-`S|@lpriQ_e7}g$7G)P-CQ{ zBDv}MsrWAB_XfnjRHJ-{03fDWKW*gQh&m~sLDg(kvseJbl3Y@g2js!FMP18p6xgkmzpNqn%T@aTtQ-vcTW2RWEc@ z@S$9Um3u0z!SCm}$6TuUYMs?`fJhmOpP#=n$dcd*7bh7TPP@;?V&eB+?Eas#6hH80 z?5Wx{w>mT7G_Y?r*Vq{@2z1`X1O@s}SN|N{*LRY4;yx%L+N})tf5jIV0gcfys3_KX zYUlP6a1i4!Q%en9NqfgK3(Yxzz6^kzd_ex}RF#~^@*(V~?LhOjGh5$m+f+UFtv_fM zUBewC$|=PJWkw5?{6v4z&HPvel;asDkjS+(WllB-HW$}pfHSfnsa8nzq@_m14+t|E zpz{G%>6D$-okoVhknB%preR!3%k_JSJ{^nSB$G2|+*y+e<%(H33rI0HsW$&X8F?d% zMahK4nM^# z0)~E(Kl{@+$6SG28IN*YXAYV-cn#;;^((pzp;}McHq;~if&OliSWc{5p`i?QOv5W6 z@rw35b_gLx^Xn+Ks+X5tjt$&i(-Q?4vCm^o7-q#?XhP?AW62;{NSpE_bJfTGW30l@ z8s^P_8R4^8bB8k;&|v`vN~i9TN=QTLx|P0VgE+qG9|iw#-c4NUBu*bt>CPQ`zOCdJD+Uu)a8_Mt(mhhUV!8<7CjB1Kg%r{@c&m%wm_z6I+o5bg z&_XUl1tw%@;k{$RyDr=IlZq@!gBLsGAN9sV(M8|anut%cuE zTquQs=AT?%Z{e2_e#QG}0KG}7cJ^7B<5hwkpw;r^@$mjc+L$7{#X@e($rZaLgDC?z zb-Et_Pj}F`N7}DvR`Ih`u9{MGJu_m>|2y>0(Dx2>KK0P_*oFV#u2o7=ZDnt5b^!^< zng4JTeBzuDL)2_1rDYfpL*aONwkNP(*?ZA5zI`Y-%?)IKY}W*}8JkdV3F0IPs@;NE zZgw-z(EW%v5heb_Ll83_X(~iq8!kHdliCZcHQN285b~MuwlF}lH}6Sye{J%0N1D<>ymF5!?if~!Fe<+hS0-*YEo#By5VcrP&B#7DFcOGL?Fyn;gi>6ds7mF8ZTao*T zC5~GY_jKuQ8Hs<3(_86dvlSR}J=z$`DJ9O)^YfpbsMzQaPPZ&9&w)V(kr$(2V(uY^ zTi^6AFF|@ zNe3xLHViEQm!#1JocK%mrduGIZt-sGu!|5|oj?gwNQ;4t7ShXA;f9mNOrw)OcV-0< zo{8UR;bgZ7Ly`b~)kxirXJS#cZMDEetw$QILJ=aL2t&*1GYJ4JbvRKe?dtASVv5<2 z=-b(zjut71n(G12icGmqK8imZvnRJb3O=smt{NX2@ZW*GF*GzBE^@~NHa%~+k~9)I zBGt}1?*3)2&W1j<%@r!WP@W|D2WF1`2PQyP{24=rebl7+P;~yCv4R8#s&Rw$AD_aG zH`x-R%ua}nTa92c00&sLMm((;3E2R^K9B2j4K+c9_HVQS*p?2U&Opw8>S%j>CoDWV z=KF$vI2~e2iZ8fU?m$B_X%vCDQe5&T60shG+2!XhGt=BsPa!qrn{(yK1Wk z39USE;o#b+nbyiy-Vsdtuma?$rbydBb*HI9)MuOI(}m;&d4BSpo~~yzn(@Cl^{%SB zqU8~hlIH}RWPDlO zwwQ$`@~#JJXjC`pA&%K>b1$k4(_8CS02rGlY-Zo%7?A!hMk&8CxEFPg1n*mF%~0t6 z$Ax0N#)vF!(GQfpE;}-=j%`}pD@m71Z?dV&9Tqh!;&?Y8E$?zR!Kr{W$*|`fQG&bn zZ0-;!Q<_jN^C_LgQ!eLp?c>{FQHEqW)B~iD(gn&dmw8r>f z5s(g2`KvmYb?gGS-E|3!hKbj|c$9`7b17t-sR^dB|bs8Q~%t zQ&p#5as{J2qR=cdmk!cNFa!danU?}{yHNomvBj`E^j5^EtoP64Tm(p#v_ziw=jCW1 z!c=FXfNeD#fNST5irT1JjMf43v{@7c=4@2GI3sBSXc?LArw>(xkF^9paA5&z-JHR- zKuPSmOMI_LA^ycR@Q)fked}ehPCmp{|o{;dJzNax`->$q&+Yqfbx9gXFD$-ATbI9R z>ZbX+KbBxiMVi^HGx)K!}0y z8036Z5_XS_jO9iPl?l8E+=2`ybMhbBJdvEJxl9yCDR^k&GG=}G-d*}IWpQxP(%@aW zkmcg(NI5uvQ7mgnR_BUleTh23ISg&r{hDk%d$OuELDv!>WHOlrewcKZl^3in2bA^d zyU3T2hC}4fw)nFREuG}zAJ)@q33=~-$7@KIoOS0a?s7YPG*)g&a&&hq->wQ|f2Pjp zusIg6?$1l4p`F&+$VzKiqLZ`&Y}5~D;j=Mmu~)K(J0d>5YL)5tCWS)MTJ0k!MJ_j~ z1l+sMcspI{^zFVqC$tKDa;@g?v?^jqp;4(l+cP-LWz+6)0$j!mBjy`{R#gEpsM$6{ z-Q!qLx5NDFq#b}PGZ`Y?>8kg9?N>#8R61i8@dcl?e8+v9w)dcp7mRg<0g^V;A?I%d zaQPqNTh|VomW+K(tcCC??SoBTk^$f@rlOmHRm)UsS|*)VqUzmthRB$d#;h5496XNt z0N@P4(0&=XsF_`?M&kX;eJXq+UZ}d+vEU`~hX_t{{>_@hX?_lkDwkHN$&yZ~l}}Y& zJhoJ1rfOzFu4rk?MN{)=Wf~i*smonu?s+Rh;JNfT7RANs$_fSP%Rvv6Q z&v0*lRQsXBq0F^jN8{;+KwiP^LU)M?XqAO3qFMKOK6tdwQZ=ERLrqxuWni`H3o&vN zl9TalB)9^#<7}g=CoTfH>p(hDe$u?*!9v%fH!lK-%M0l*&=7JztFaq^d%4%G`Wasq znZ6;M|F$_z*cgR=;foTzFbQq`<^x$n-p}fAKzp8nns#o!c^gu5)}^=5;8<~24lZO< z>~>Kq@j1%doA_{AE#(&pG*74gCYGX1?Pm_Q?_{|aOk4QfE_+v^Jg(6a&!_0#8DkRB z1kM76S1Bs0Ol-+R`ighUDS%xu8ns>agq}mxU-PrEH38F{mzr%;W4z4Yn;-N$hDO3w z8^ZZ0(VvfwR=N6@pI&_RYsTZ6jpBOsL{Kg3;?3ph`{*)L=rCG7TO?N0z__@MidcDx z^{Hu859pgnQ7xkPx#V^b$vY=uMbOcmxzM5XRwxamcNS)sVD!DtDKl{6YfGKI9e*8` z=u2k0jo8*waaoJR2Q)oI@WT}(yJbEA!ThjUSYdx7Rfy(s``%y&IyFpWG-&S_^x>@1 z9Mw=JfKg%INoX8Zd%i!!S8hTqkZt~@G1HA9; zjiMfbl@^ZM-^MkPi`weUx$vHL>vF`*Ab~BYlnt8}9>;rQ(?+5J>x89)91``r%cVEZ*hK8YMu+#B||I#8SA?;$U#r zBkkmc!4~TS{>BYNQ3e9!8g6+vIauRq)ZqAnsh`H>*^t`dqSNI7Tk1l?s!vVvV368u z%uG0r?z14JmR9oP{kNx(*|U%{CdCdy%xZnYPY{B1+x>~-g;aWoLyKeeX?<}ub( zB2xH91Ge(K$qRHe9}yLBrM}za9$Vta8o5)arCHR=t=vN?x{_E zrKUQ|ty^n6+5xDF%mKTlZFR3=n~%cFL{e`mokswn3nCJdq`{u@j0~wDJ({nm-B)23 zD3ZX!wKp>G!GL;gGx9rYbA~Zg*X^aGNNJpCM%}3S_C-uGLV5J-lZ+Yjm9vwhUWQnY z+E~w8P9!&kDe)xQKfW3KOKzL#hR!SwsTrRP0RK^MX6Fv>YV4Gb7TvmV!tIzI>b4Jl zs7A^?eMSZPb+h;XgUTl)wjSK%Sid*wwib2>tJ2NgmvsM?#rImtKD~4L)D^(D zSwhdYw%X3n(|M;LZfw^IcfIK&tebc66&$ixDE&V1hPNjZ zQTql41|G(KV=0W|g~p}=9lD=hbdptHwfWl_UX*CjX5|`-7vz7Z0EdI2$5cn_-3R&3 zFKt>a0qUBkI?{ah2eX1&ooy?xvFR`THEY%TY7766ZTAaMB~PXnQFXq-%&#m9 z*-^RYt6kxQkD8&7&&zd1DQnzPNo%zi zpQUIX4MOj~yn(>wwir_vLD-0{1(1noLosu&NHVELcjwRzg8JgJ%^h4mSKD-2Z++ithRI+QByjL8%?_Pgw=RX;}e|)sddd0EpzF+enp8Oxa{<|+e z;$Ly>tlwY%0|fBD(dmAd%qAoQaO~jwueX0EGX6T#?mci~35bI7iv93^@eh7Qri8kM z3I0yloq0k~7{;$1cse_+aJObkMHlt|txzIw(x{3@N1Ji_EdTBo@zcPCmx-xcwQ zaVMcgJ*_QAI_T15^C52&rqqs{0BebihC1okd5Up#O-~^tdkO}PnLTlvlNVm;aq3GH zblS0l(|){$iTme&Ufm>WqG>q+OPzWn*PIo@^4=ji@b(WuSnVR zZy)^UpZcnSf$7Y(Ap?jm{O70pmrMTlVgH)R{&!^mnsbRoilt)IySTV0#%;*fb}u;E zm(q%gUOXMtSx$Sv`}!Kj4I;6>|DnNLn@MMy=#wnn{30fC<8H-}eT-qo3xh9B(u`lX zkD^WLB7(j&zrNopR@r*3Fgicr^MBC%=Kvcrp51wr6CG>L)FReJ9`?EUY@)YGsJe}x}&yDDoMVVS_=ju@P7tP{_X7btu_!lfl5#W>9`NxZr4_1ka!YA3PhSeSTo z;K@p#dwf<;z!T*$3#4p?>45UT|GlOAZ(`Vm3ru5r*+GYEJ6M0uYdltvs3~4tD3)u>*dFRtEz;qg}PqAgseviI;zEi@(75 zlWP(yrnV1Lcii&xJ0@qChibu>l(sj1HOc=llYtU#$+a061SKW6^^9NJU5bEO=>3Qb ze7md`A?#2b6@^o9R6E@OLwZpnBq>JUI5<>nvT?*r*Ewps6lp=HTy^Rk{f=t$E$S*j zRg>j5I<&~}o4++o1_v;7r>YiY9vBmLKjC+HdcYV(-={D!RvseB1=JrwSDDY5LZ${Q~EG56~qj1AO(!z`~TTg=q=?KQ~6kS?>Lbcb?CrKvL zwZTuIngmn=4?6Sov)Rk++ABNU4whR7Go-^dC)Ms~v;jv^{d3~wPP~TN!Z((aj=P^K zY&;Wtmq?1Ci6{1rzXRY?jDSx)O~POI{vXEr`=>e6I|MI4BzX90;ozRnaH~-sjiqSd&Wh%b{L^M7x&r6ORgY9N;Uod4w@i(T^~ z0$haRN5qS$|L~ZY3*r9_+rK*N{eQOdJ}xIL)KCl%HU%9} z4#__J?F9T6S5&cDOqCImCwzuCW+H_OUj5fggcvO#x9;q7^oZ}jo}V#pC|#}L;r>y7 z)y}`U?#5NJQ&`SXRci;I`KQSLaXHa27NZM?{G6Z+ZnDR(}80i+gVXhqN+qJT-yHUH>!b?l&-xNanv(f=(4mczAd{6IIjXv0}qR z)W-~52M4WGJsaguXL=^R+?!BYGVykEy}rJF>~#K4FQLEkMHib_+aRNz zjIf~O&{x!LkC3mtYm%YvcG4fs>0(cSP$@EcqaJ64k@zb7i9h>)|F(Ob1j6sEZ;A)nEyuiif=P zg~waGQkn_pujFr-T%eQMGb<~3O{w+x8?y=cm1`YW8CXO#AG7r2H0LU*uP5e(=xL%l z0#Tg>48{JrZ}`<>fTv&~*bL*i*f0BhNk*U5Wh`<$(&1Jl&;7*Yp>mt{qt;$cP5|;vC^IH7qqAf`0=rm2|1RIONNL!|D&guKE^YhIF&P8rD-e}&q z<@pkgTNoUu+fT~!om8@R__9*=o^*}F@uqP<$I`?V=Z!^6;WSF9fB3b|~Y;;T*`f>K+ z3olu+9hp`{2)M38ZfNH#H5FPyb!!S*GjnZ*mu<2OQGEK%-Vz~PHrwH`Sg^KbbO}kR zm0>1`lpN(}I@{dnX@{31c-K&xyUNDLM~rjF$=|h}I6!z>&fBnvMd+|xuxv)O8*IWh zu7M_?pA*pe&+TdO%2bL%nfbSESz_9e%{x4q81rn?M^cRseBCAs-pn@QZEY8XR6) z@_TfU*-n>hWoRhcjT?DeCh(dvZ)rx2YWa~GWz0+#UzFNgOsb56wVuFX2fmw+XBm@Z zpYAdk+4nc-U~6u8?vGK8O0D(XKEJZo&fl%2Wp(VtbY(4_?Ex*m;q$g3E1?g`^wtZ~ znCEqbVFTA0U#T(n@=w^szqJ@m#RQ>OunQmg{ipP}Nty$g^`zp(jsPtd>n$9*T)Azv z#o=$pZ}dFAS|mgHLLCw1wnLmQbE2rxI`>WN;Jq{6Hi7n9t`sfI^3L5P z_>**wujYNQ&b+b2OfIBV@r87Q#}VPstvRYG^9+*ux^%bvse_gz+m@i)MW%=usxq@7 zr5dzdW5wu(FD_7$vC2q{vj$%ZM3olrdEpSFKd!-O}ceG!GfiS8gC(LdqZrgfk^q8fzeA(N8#?~#y7BaVgd7ERVdBHqyy%2yZZNib;>p_HhLlaK(cye=oBPmlg>3=v5pH!zo_a*YC*X9HJ`JVI)w)_e zo1qWD5ilf^1&Gs&16L0oU3XTu1k$aH{auVF`D1#R^Mg*68<`04(7yAGx*F1}d6_36 zMhiTcNDGOmMpz&;(7~W(*LAZZ^g6x2VPP&5WS0pRkPggZGlY9hmlc;iESjmMt%X0~ z-Krc^Nr`wmbep}rSf%P*!Xm2NbI@F`qI4o6_43Q~OtRXLC6%jmfT=l2n>xBPDJxW- zsb)?8{7_BvelfVr@IZC&n}cNTv-2Mk2Nz+inL~&Z7tuGWmh5a+x`T=YG{US3@lDej zrpMXGd*+iPhsQtE9)7Po7QkP2hYaNJ2SAHrFsA+6uy#+!5$w&~`M*p=)}teG2^N+T zuSX$wVeR8k&zRdA!d>-^_q)O!4hm4cs;z~FuTq|s)oi9mBHrk4=qG{1W$R$Z{hY94 zIKE5c{0O7g)_9TC+nsNY9+n{3ly2+tPA2##8$zyezMy#4e70BEN2dsJuu zeAw7Rl9wNJ@THeKnaxh0fiwLrrYoc!W8ru_VfigH>n-M+#$%&w6H=F!FEcuX;`Ay` z^i{m;6b)WI=uRzkZ` z3ZG@I%MB-;_jyIdyx+ySmW7$%^+$-Y-t=WSg;TDq=wf`1yMs?F<6C4WnJ4e}uaoJO zh`8KE_At{$ju?$V-3Uq0sr%HajC`X6Bfh94$m3FX|1cp`q7cE+ap<7=wyJtisAO?0EhC<>84eI`?lL$xAb*qrcfb; zA^}w3U`DF+BTwN*!uSDGo>gw%6PwC|yl3K5S4l-tk|Xpe#}QIPy>cCX&b$`mee8Ig zj~#=0ddatLa4a%QK^~BLIv8e`X)Ptpmq3Jx3(uo+{RPj zG%dUH_J;6s_(?Y&Ds$8B!$J39eTql32H!d7IV1(GV=ypH=st2|!SYKFQ+)r&NCi1` zcu*;IgvY|bTJ6@^h~1L(qK-|f&_K~BXO(+F%EcSJ!wE*7_f6ZnJFas^19w3B%9E;F za=IvU=yQc_k5!hNXaUh3$xq)} zrCoLYNk_L4(J%?YW<$VF@E0_94>X$dp<7$aJf9n;+2L{tx6`zXQE46%pWF*NLD@;i zHZ^BG=5lk}pp)d96oQ9XCpDA3{I;Xh-8?2co~0*s-X0zZ8pR1|Pwmswsml&KXiEA0 z*DwLm=Jk~?8$GkW@jRx~<3`h!D*Ka-o(cpiG(oIG0knl_oJQDxmXVn}!t}!4tu&j_ zv~1XHEW88mKDLZ`y&>{-n$e^ZMi$`ou5qZ~x!8@tCLtNWdZF05=F zI-I=Y?)>F}hT{g~1WEgi{VbzLHB7o_yD5)au|{r~^8!<$Lm;9zimgs0d?pE}<0Eym z!K>L)MxuhivyL0%sw!#>Q?oli0-1E!1egc#7}cw)HA;iXoNIZMiaf}wGc91Ont~j8 zxKvb5z0pX;?$d+;3!Ye3l?kx7eiGVYXKeEHgGKhuw?m4aNz(X<($~#nJjFvrCf2TyV6Q(d7wR?Ir z*nYZJd>9La2^E9I#&)n#2*K=NA4g1oY|7|7k zRzgQyb|OcToM*)}<>VxuF0ZIBe|VT#4=8cw@p8Z|MA!_^np9_iQtuuODLng|T6mx`>UopmAvO0KUF6uvMVRxTE%twsXeCA5VMB+ zBdYwHaVL=d-$jk>4}FraEK7zx^0+5bCo7!Q4!VH3)Ss9iKO}xoFV?WXS69$9K(~Cx zK3Vd#j6LUOA&R*8T-wOvXId1}(@4}1%Voyp9HzRvyDO4z*m+8*OsZyr*(T4^Ui4(- znc35dkVA@uQzt6cQx0B@bW#cTJ8N5K0ta%N_5IIhs(Q%mkm%D;#}t~*{Q||6?rb>P)SpahAwYI{TK-()~^s4u064CKvWJge~U5=1fyjI{eE5za~ zu+d$RSxgsAv?YTb2QJ?#o}oNn#4L6%n7!yyNJy;v5KY!8;Bsxpgewo4MW|X&Y>Zqv zf6NZW2Bn4yUdEHzsL*IwWS9EDzv{-L5+*d!^K-}XyRh}uPBb%%kV+4{VU2XPLhcks zdREz)zFd2m?E*(+V5GY?Rt(qcu0O6nwMsOl7Zy0?`OZ(3w%u4Z-ELGToDG2!O?pfn z!u6_WKNotsAB{7V6u>{$t1%Xa71k}!uEVBP+()T68s$HAa71{d4kty6#VM)V!x_)mT398 z8akO^!LCa6#}@y-9satoH}=kYgxSSzPpDz%#K~yryQB6S&FJ7~7}c|-qi_vQu6bhi z@&=#YPzz!YBR++O2UB(#s~yJ_J;wKqZ*_IWF%A7`mZ*ORm-}PYYbfccP4RTuL*}pY z_t!s%I-TtpGl+`L-Uy}+J{VA><$+b5_2$go(WwLu;BYJ1WHWyyj!!f^K$2wok<3qJ z$c$?uhs#+lMul>W-cI)E`gl5fq{k?3RW0|=4DmL{-J<0pUDsY7-5X}^M}Z|J8Gek` znlywHc`gA#o1&oQ!bGZEO%7t;sTv(8ehY8Wm4vw}gSC!QdQPX-fOGpghK>8NlKneX z61$rd-_#^9okK?{qfpT>GL^zDNM&ppsd{>$Bj0z)Bdpz!RlYt-i1~oz$E;hgVD0B? z%;xt_fz7jOQ!X@!Hp}PdRN;{|B;-un=hT2g4o~(e^Z=)WN6EaGL;buch)2|WV-oy! z@Y+lUBJ9L1PeE!8?D6&iP@;;AQgOLDAL5UT#^iBV*ckGbT+{cVddsCqV%euN^*Qa1 z=GG~R&YB8ho?2)`KT=yzZNP-P>d+?mjVkH#a1iNI}wM z9x99ecJgEUE7?tc4NXJurmNgivoQ3#HXz$X#Un zKI(*)8lRi*h7A&)v*bAD_tp#zv~ot(57+2O>mO~S+m>ZEIT28 z)fgtfTST4iaZ-F4p4rP7t8m>}i!`PMeD{+GZ;{dwYyZb3IYES$7nwe`d=Pn!Tlr1u z4leioHY)RUiHDWY!vhb4m9RntX|EOotHAiaFNLwGUd-`yVR)VW>RK(f$FYNsI;mra zYC?}AO+vvmqZajHt~4r{PNQb+m-$jk^-`9NFi#j~k2-$)8MeD;l>B6WEihGvLQ~8| zb0D8S2{k$j?(Sj=0-!;{H0wi9!91lBbtx@&59sLEBeF<5`6 zZTrtg?+aEUlWWBxsWnC$x6I2M6S6$JDIE*RI>A*Wv=xRZ>;v337imeO3ZJ&V2QEdo zx44)?3d%E}6$LrcL)8v)f$Py!BiH6{tnG$1+y0a~{2XzU_n>0+y{uTiQC29%CA!DI zz^(akbss3)f56%$*bsg*QEA43T&7&p&ub?X8Oqz(Y~?CI7+k7Q70FiTO*}W|WMHYv z%CbD1^se`gY~9F7Xhw&WR>r}89z{-m(labu963&8MJ7moHyix8{%Y-gxLu5i7DqSW z2Teg;g=;3>VKoje8GRKM0u)ieIvm4(Ua7INYUc?4+R9ks5qHy>z}#%r<7F|*G#g}e zx=J@#^OA6*whA1-`nX+M>A~sPBNdq%bxFjQ9`%r*0Pjbq{#WLWHPRB~r~{A4vKROz zN}dL(={bCCLVKXxgN@4MpW-jBziU>$LkQcY;BUR5#_^QCwN4K34J}pPO8DP=qxX1f zY@4iXfE^Z1_6}m%f$F!I*8LYh(2@t7XdU{A#<*@OVr^stmNV30KDj#4;P1A$PNSva zV2KzxYpQHI)!RtBA==^)y<$AEvtr3%h_1obqPnEBETgHV7S=?eqDNc75vqj^8#%i_ zS4PHem+6sc;Vl0aX7#TVikAiMqtLN^^#n+Ay>cHVze*pKQ=GlUkj6yC`ulT=DLY?T zD))dM4PNDo{U7eqc0gT9dLot<`YLz5gN{GEEq#G_Dd_q|DtbyigStb;53e|>O^a3Tx;7!45 zwTsiP%Q*enh<)|u65EX;xwOmrN1|xgCmdR6gLF9~XlSnyWI37cpiqc-i(h%?TO6W8 z`z9y}>aqo76bC0q>xrA{b~Y}IJFkaKDSE%echeqZ&>By`s+j9kh=*>iN_Go#^=y_d2nTozw? zxV++lwDq0K%3y6bytePwj1J5tZt8NA!=RXqZYs{Q_Ar6daqiJdnuKT}J#Nu#gXXC{ zHElYM?AJJ&hR5lrDP~yv8~PhgBgEwc>a(Gw1unXWy4}l#OHJR-gD-?^uQA;y8(@S2 z$z{H<9o#x?+R%)d0*63EsaC|^eGFU8O4>krrkn({3c5C# z+K%w^bzu<~KNVe#XP9(^pfMB$R@M#9rjkNr(!eEeGJgCS0;a>ktJWA{y!tr5m!8z& zosc`_aWnjKRH*ObdVU6m@Tc}DlTeSL@|@?EI_`J`ltS;pgHu6y5sxdZbj-Y~5(f2kE?lb zSHd6BbMZa%T8PgJ9T6Vn8jxO_u4%Lf&kh9%xysJy9809h*?t%g(;peNxkpOMAe79@ zMjUFHWW9*3sjgbu%WV3p(MI)J5p;}zfhyA}+ruGne?Kw*uDl>R(Lec8E(1ko_+?&&1GwYZt!#?RAb;9F zQ@D=*;+GkWPs*4N;i2DP3@l>wz-w?>DR=_~;{LoS{6U0edtA-vJb{15Wg~#wZ}|ds zir4T^vr#tl)5X-*3OQ^3nB_BW7PIKD=^8_|$YaL<-n<=wJOL#XD&`fMw_GJnpyW?6 zdL|}bV%MD=?jbffe&)&YhC&r0k159t+1M?~X)@~Cg!*)pQ`oN;BE~MU6C{&Ucn?|E zOsNm36GkmSW^C)zE~!g0XE9c4@+b9fCuNt>P)Xefb>;@;N#cy3RcH#gWeel0D!sKF z#w&FwlJZ}0x%UJ)7v$zhE2}vSay;F7Kdq4H00WvYbOM~lc^r${Bt+0s;k+=Kl?xpl z>B;D=54NOh!$ox!7HX{_)egs)kVi0vjpgo!*f7a0u&cZ;Mkjt;>qvT*V4Uor7XgIQS-`R^NVPgiRPh$+W;x9p4n-9+Pfr^Tc zIXTb*2c4tkljd|<;C+KFeV=M$sVdJtRf-_Cs2;g9(gfZ$&W(;`kDb@_WLVta6cSxFk+a?N z>He8kG2)yKn+zC8$`m=Gl)C9edQrq&Z6QnH_0^;$D^6ICG8^~!qz2cs%Mm9k@JuPY zQ7AD~Ts^5H_{kq<%0yzRHn=y)o3!3b6vc6GQl7Mor^GlK4Qm;j8B0|EpT^EKsHv-s z<1Q4WieOz@K|qT_0Rh<+gh;DI_D#TmfXEUc7?2ncFhR76bs=a3WC=lbFd+g7lqD>p zVpuCnfP_U$AS4h$LWm&=d9QunnNFuO&U-(d4|nF?dFI^nJkS64zvtkNv7%w`=yDw| zU@kyGlwO6g#2+KTt&>xfrlV-LS3c zX2nU|`R3fL6mokKa--pm9MLD|9TK^U#DU3goG~)@`n35YT^=Hd5f>P& zypgbps`)A0u32^v`fRI!gYOw=l@#va*f{PZ{i1xb<1X@SIR;6m zxuwkZcJ-0vuaJy8wgKcj?wR7Y7)D7B!9toS+L&~x`veqHW?nw86bWPGF+YoU6P6Fu z&2*!v{cb1GMlYg5Cgb~+zA$wi{~2_*efTH6>+-Tw}m_N1N@YM($*JK;Mz09moWP_tK|d$vP1p$}jcC6VNI5H?bK z%w5O}wx6K1Is5-Uhl12^bpDP~&g(j6yF5W+y>WtPZ`ZkSXm^_1BVMy5V9P=ERWuO^ zJm06A3LDZD2v(+JF#8bH91%R5i-@UR8!3FIo?P$9UYjfl$up#v$*#==k0->E5SG(J zsz6?HpVXS<^_sOi4<*xg@{(D1>NsteyL4bm^3b6K|{3w2l4iddVuG85^9L3RpQUhsoq zn@MV$KFIIz>#~$ZX4s8+LF2VP1rzu((CtcAYx;LguKC(gXo>n$S-_g#1IXMl-e9)Z zm$X{B()Svf?bhm3mY%19j5sp~-kn=59rI;*v8N9}gv}|}gM8qAsyWl4_7{g>5^iNs z2GD$Y*)m$~<9(vBgY^Ml5`B2HU)=_J{C5N@R5SVIKx@24VBoDN>_0w>%d*8%1BO^` zL_2{uscUtqu~m5<^%>eLSZMhk72FRxEj!})u+&DUD!HjgG7)s5Czfd;d9qMGy^P=~ zAOW?`32Jp0*7293!8zbt)^LTXEgkL}pjP1Hym;0jQwTb{3_a&yG>Tf>ClYu~yHFb0 zNVIK2vke$s-EnVga__YFN8=P<(H&+<3wJ0CCRu)`gVyQEL_>($XKJKUx@I9qS8@ zgS=P|*nL#&`)=3e-KCBC;SMe?56wLzVodc2RFZsPRBU_ClC9!WM#Bi2v5MK84N$R;UmtM@^yBur|Nlsqn%Yw`ypw0Bvj;+ z$Q3AfrEgNDy_!9pnplGJu=99Hy=rz5dJmUSJ=Cr(E&5bzN}}Avi+E8afBg9f(CD+22PDq@Al{5aU2ix}U|bL2>KN$| z_?3oryjdI({AP!1tTYPG$9BQ90c>>S3O;20b_!K+e$ZA&*Iw)VGdpeZPM@<2e_GIC zRgdloV)x~4ngrR1w~JUO@Y(eS)^G2&&Xmws%V7<~$7#J79kQ}RCNRt6TjAp8g^ZGo z;~%hIa20ik7v=;`Q~z@!s1_p(y=HK%_=63_f%7}c{eW?Fk-c}h>VKt+Jv^q?D?NwD`iTKvz-da`ns9nF89N_J{rWmGUmoc zODReluO2uPwB)7lACYKtIJHZbwDCkGpT$u7$Mgl##4J)}-yWM$P%FocUNMDfxrXmG zDfTFLL<9YxsLW5ugQWyNydmXIr1aD(ZT~TkD<}y>gll_Vo>`C|0qf)LSwN3htc!vlY; zHzotyTfN2TV?_bc_6s8($?V)ylVZHIbRIcOmjsl)mGs$x>dM8I``dhI+^)E(oXCY=WSCV`LTzm3sjz7O*?jpNuZ;LCAp5u z2}?}f=N5*Me=x*X=teTLb}O7&@Yo*xYY9XBNFDFxYDUCGM2PZv4bZBy{xx3=G(>fj zjJE~%EMv`1dCA5(=`t~4k?1_RnaNB-jT8o`8@#)`vcLkWF#pkG(MxO6iZgtlxP`q5 zY0tP1t9Am$S(~;5@pyyBA$1agX|aFUc%5f`vkF|c^1sMdhHOW)eu=Eh{}EX<;|V^l zjzXFqm_&c!p6+<`IiH%&y?IivSZX@pB12F}@Ws(?Wa{GAmiTR1eH*?*;kyDA)YPY$iW>EjvU#w@r??Xj^l5qybyy%TrS@?=(cW_kBEZ#C^{_Q7< zxw-iuu)18izIIDl{r=Nl-PCxZQ>kxxYfATwVp8U4=Ys=%m!u;QxON=t~ZZs4Yo?7YqsparLUh(fo iesvVFY4gKLsjOy~r;gc>=}()0%i)ac>FS>^-uw@71( 0 { + input.ToolConfig = c.buildToolConfig() + } + + if c.maxTokens > 0 { + input.InferenceConfig = &types.InferenceConfiguration{ + MaxTokens: aws.Int32(c.maxTokens), + } + } + + if c.thinkingBudget > 0 && strings.Contains(c.modelID, "anthropic.claude") { + log.Debug("applying thinking config", "budget", c.thinkingBudget) + thinkingConfig := map[string]any{ + "thinking": map[string]any{ + "type": "enabled", + "budget_tokens": c.thinkingBudget, + }, + "anthropic_beta": []string{"interleaved-thinking-2025-05-14"}, + } + input.AdditionalModelRequestFields = document.NewLazyDocument(thinkingConfig) + if input.InferenceConfig == nil { + input.InferenceConfig = &types.InferenceConfiguration{} + } + input.InferenceConfig.Temperature = aws.Float32(1.0) + } + + return input +} + +// convertMessages converts our Message type to Bedrock API types. +func convertMessages(messages []Message) []types.Message { + result := make([]types.Message, len(messages)) + for i, msg := range messages { + result[i] = types.Message{ + Role: types.ConversationRole(msg.Role), + Content: convertContentBlocks(msg.Content), + } + } + return result +} + +// convertContentBlocks converts our ContentBlock to Bedrock API types. +// Based on dt's implementation. +func convertContentBlocks(blocks []ContentBlock) []types.ContentBlock { + result := make([]types.ContentBlock, 0, len(blocks)) + for _, block := range blocks { + if block.Text != "" { + result = append(result, &types.ContentBlockMemberText{Value: block.Text}) + } + if block.ToolUse != nil { + result = append(result, &types.ContentBlockMemberToolUse{ + Value: types.ToolUseBlock{ + ToolUseId: aws.String(block.ToolUse.ID), + Name: aws.String(block.ToolUse.Name), + Input: document.NewLazyDocument(block.ToolUse.Input), + }, + }) + } + if block.ToolResult != nil { + status := types.ToolResultStatusSuccess + if block.ToolResult.IsError { + status = types.ToolResultStatusError + } + result = append(result, &types.ContentBlockMemberToolResult{ + Value: types.ToolResultBlock{ + ToolUseId: aws.String(block.ToolResult.ToolUseID), + Status: status, + Content: []types.ToolResultContentBlock{ + &types.ToolResultContentBlockMemberText{Value: block.ToolResult.Content}, + }, + }, + }) + } + if block.Reasoning != "" { + reasoningBlock := types.ReasoningTextBlock{ + Text: aws.String(block.Reasoning), + } + if block.ReasoningSignature != "" { + reasoningBlock.Signature = aws.String(block.ReasoningSignature) + } + result = append(result, &types.ContentBlockMemberReasoningContent{ + Value: &types.ReasoningContentBlockMemberReasoningText{ + Value: reasoningBlock, + }, + }) + } + } + return result +} + +func (c *Client) buildToolConfig() *types.ToolConfiguration { + toolDefs := make([]types.Tool, 0, len(c.tools)) + + for _, t := range c.tools { + toolDefs = append(toolDefs, &types.ToolMemberToolSpec{ + Value: types.ToolSpecification{ + Name: aws.String(t.Name), + Description: aws.String(t.Description), + InputSchema: &types.ToolInputSchemaMemberJson{ + Value: document.NewLazyDocument(t.InputSchema), + }, + }, + }) + } + + return &types.ToolConfiguration{ + Tools: toolDefs, + } +} + +// processStream processes the streaming response from Bedrock. +// Based on dt's implementation. +func (c *Client) processStream(ctx context.Context, output *bedrockruntime.ConverseStreamOutput, events chan<- StreamEvent) { + defer close(events) + + stream := output.GetStream() + defer func() { + if err := stream.Close(); err != nil { + log.Debug("stream close error", "error", err) + } + }() + + // Track current content block state + var currentToolUse *ToolUseContent + var toolInputBuffer string + + var thinkingText string + var thinkingSignature string + var isThinkingBlock bool + + for event := range stream.Events() { + select { + case <-ctx.Done(): + events <- StreamEvent{Type: "error", Error: ctx.Err()} + return + default: + } + + switch e := event.(type) { + case *types.ConverseStreamOutputMemberContentBlockStart: + // Start of a new content block + start := e.Value.Start + switch s := start.(type) { + case *types.ContentBlockStartMemberToolUse: + // Initialize tool use tracking + currentToolUse = &ToolUseContent{ + ID: aws.ToString(s.Value.ToolUseId), + Name: aws.ToString(s.Value.Name), + } + toolInputBuffer = "" + } + + case *types.ConverseStreamOutputMemberContentBlockDelta: + switch delta := e.Value.Delta.(type) { + case *types.ContentBlockDeltaMemberText: + events <- StreamEvent{Type: "text", Text: delta.Value} + case *types.ContentBlockDeltaMemberReasoningContent: + // Mark that we're processing a thinking block + isThinkingBlock = true + + // Process reasoning delta types + switch reasoningDelta := delta.Value.(type) { + case *types.ReasoningContentBlockDeltaMemberText: + thinkingText += reasoningDelta.Value + // Stream text chunks as they arrive + events <- StreamEvent{ + Type: "thinking", + Thinking: &ThinkingContent{Text: reasoningDelta.Value}, + } + case *types.ReasoningContentBlockDeltaMemberSignature: + thinkingSignature = reasoningDelta.Value + case *types.ReasoningContentBlockDeltaMemberRedactedContent: + // Redacted content - ignore + } + case *types.ContentBlockDeltaMemberToolUse: + // Accumulate tool use input + if currentToolUse != nil { + toolInputBuffer += aws.ToString(delta.Value.Input) + } + } + + case *types.ConverseStreamOutputMemberContentBlockStop: + if currentToolUse != nil { + var input map[string]any + if err := json.Unmarshal([]byte(toolInputBuffer), &input); err != nil { + log.Debug("failed to parse tool input JSON", "error", err) + input = make(map[string]any) + currentToolUse.InputError = err.Error() + } + currentToolUse.Input = input + + events <- StreamEvent{ + Type: "tool_use", + ToolUse: currentToolUse, + } + + currentToolUse = nil + toolInputBuffer = "" + } + + // If we were processing a thinking block, send the complete version with signature + if isThinkingBlock { + // Send complete thinking event with both text and signature + events <- StreamEvent{ + Type: "thinking_complete", + Thinking: &ThinkingContent{ + Text: thinkingText, + Signature: thinkingSignature, + }, + } + + // Reset thinking state + thinkingText = "" + thinkingSignature = "" + isThinkingBlock = false + } + + case *types.ConverseStreamOutputMemberMessageStop: + events <- StreamEvent{ + Type: "done", + StopReason: convertStopReason(e.Value.StopReason), + } + return + } + } + + if err := stream.Err(); err != nil { + events <- StreamEvent{Type: "error", Error: err} + } +} + +func convertStopReason(reason types.StopReason) StopReason { + switch reason { + case types.StopReasonEndTurn: + return StopReasonEndTurn + case types.StopReasonToolUse: + return StopReasonToolUse + case types.StopReasonMaxTokens: + return StopReasonMaxTokens + default: + return StopReasonEndTurn + } +} + +// Helper functions for building messages + +// NewUserMessage creates a user message with text content. +func NewUserMessage(text string) Message { + return Message{ + Role: RoleUser, + Content: []ContentBlock{{Text: text}}, + } +} + +// NewAssistantMessage creates an assistant message with content blocks. +func NewAssistantMessage(blocks ...ContentBlock) Message { + return Message{ + Role: RoleAssistant, + Content: blocks, + } +} + +// NewToolResultMessage creates a user message with tool results. +func NewToolResultMessage(results ...ToolResultContent) Message { + blocks := make([]ContentBlock, len(results)) + for i, r := range results { + blocks[i] = ContentBlock{ToolResult: &r} + } + return Message{ + Role: RoleUser, + Content: blocks, + } +} diff --git a/internal/ai/bedrock_test.go b/internal/ai/bedrock_test.go new file mode 100644 index 00000000..9859d7a3 --- /dev/null +++ b/internal/ai/bedrock_test.go @@ -0,0 +1,287 @@ +package ai + +import ( + "testing" +) + +func TestNewUserMessage(t *testing.T) { + msg := NewUserMessage("hello world") + + if msg.Role != RoleUser { + t.Errorf("expected role %q, got %q", RoleUser, msg.Role) + } + if len(msg.Content) != 1 { + t.Fatalf("expected 1 content block, got %d", len(msg.Content)) + } + if msg.Content[0].Text != "hello world" { + t.Errorf("expected text %q, got %q", "hello world", msg.Content[0].Text) + } +} + +func TestNewAssistantMessage(t *testing.T) { + blocks := []ContentBlock{ + {Text: "response text"}, + {ToolUse: &ToolUseContent{ID: "123", Name: "test_tool", Input: map[string]any{"key": "value"}}}, + } + msg := NewAssistantMessage(blocks...) + + if msg.Role != RoleAssistant { + t.Errorf("expected role %q, got %q", RoleAssistant, msg.Role) + } + if len(msg.Content) != 2 { + t.Fatalf("expected 2 content blocks, got %d", len(msg.Content)) + } + if msg.Content[0].Text != "response text" { + t.Errorf("expected text %q, got %q", "response text", msg.Content[0].Text) + } + if msg.Content[1].ToolUse == nil { + t.Fatal("expected tool use block") + } + if msg.Content[1].ToolUse.Name != "test_tool" { + t.Errorf("expected tool name %q, got %q", "test_tool", msg.Content[1].ToolUse.Name) + } +} + +func TestNewToolResultMessage(t *testing.T) { + results := []ToolResultContent{ + {ToolUseID: "123", Content: "success result", IsError: false}, + {ToolUseID: "456", Content: "error message", IsError: true}, + } + msg := NewToolResultMessage(results...) + + if msg.Role != RoleUser { + t.Errorf("expected role %q, got %q", RoleUser, msg.Role) + } + if len(msg.Content) != 2 { + t.Fatalf("expected 2 content blocks, got %d", len(msg.Content)) + } + if msg.Content[0].ToolResult == nil { + t.Fatal("expected tool result block") + } + if msg.Content[0].ToolResult.ToolUseID != "123" { + t.Errorf("expected tool use ID %q, got %q", "123", msg.Content[0].ToolResult.ToolUseID) + } + if msg.Content[0].ToolResult.IsError { + t.Error("expected IsError to be false for first result") + } + if !msg.Content[1].ToolResult.IsError { + t.Error("expected IsError to be true for second result") + } +} + +func TestConvertStopReason(t *testing.T) { + tests := []struct { + name string + input string + expected StopReason + }{ + {"end_turn", "end_turn", StopReasonEndTurn}, + {"tool_use", "tool_use", StopReasonToolUse}, + {"max_tokens", "max_tokens", StopReasonMaxTokens}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if string(tt.expected) != tt.input { + t.Errorf("StopReason constant mismatch: expected %q, got %q", tt.input, tt.expected) + } + }) + } +} + +func TestContentBlockTypes(t *testing.T) { + t.Run("text block", func(t *testing.T) { + block := ContentBlock{Text: "hello"} + if block.Text != "hello" { + t.Errorf("expected text %q, got %q", "hello", block.Text) + } + if block.ToolUse != nil || block.ToolResult != nil { + t.Error("other fields should be nil for text block") + } + }) + + t.Run("tool use block", func(t *testing.T) { + block := ContentBlock{ + ToolUse: &ToolUseContent{ + ID: "tool-123", + Name: "query_resources", + Input: map[string]any{"service": "ec2", "region": "us-east-1"}, + }, + } + if block.ToolUse == nil { + t.Fatal("expected tool use") + } + if block.ToolUse.ID != "tool-123" { + t.Errorf("expected ID %q, got %q", "tool-123", block.ToolUse.ID) + } + if block.ToolUse.Input["service"] != "ec2" { + t.Errorf("expected service %q, got %v", "ec2", block.ToolUse.Input["service"]) + } + }) + + t.Run("tool result block", func(t *testing.T) { + block := ContentBlock{ + ToolResult: &ToolResultContent{ + ToolUseID: "tool-123", + Content: "Found 5 instances", + IsError: false, + }, + } + if block.ToolResult == nil { + t.Fatal("expected tool result") + } + if block.ToolResult.Content != "Found 5 instances" { + t.Errorf("expected content %q, got %q", "Found 5 instances", block.ToolResult.Content) + } + }) + + t.Run("reasoning block", func(t *testing.T) { + block := ContentBlock{ + Reasoning: "Let me think about this...", + ReasoningSignature: "sig123", + } + if block.Reasoning != "Let me think about this..." { + t.Errorf("expected reasoning %q, got %q", "Let me think about this...", block.Reasoning) + } + if block.ReasoningSignature != "sig123" { + t.Errorf("expected signature %q, got %q", "sig123", block.ReasoningSignature) + } + }) +} + +func TestToolDefinition(t *testing.T) { + tool := Tool{ + Name: "test_tool", + Description: "A test tool", + InputSchema: map[string]any{ + "type": "object", + "properties": map[string]any{ + "param1": map[string]any{ + "type": "string", + "description": "First parameter", + }, + }, + "required": []string{"param1"}, + }, + } + + if tool.Name != "test_tool" { + t.Errorf("expected name %q, got %q", "test_tool", tool.Name) + } + if tool.Description != "A test tool" { + t.Errorf("expected description %q, got %q", "A test tool", tool.Description) + } + + props, ok := tool.InputSchema["properties"].(map[string]any) + if !ok { + t.Fatal("expected properties to be map") + } + if _, ok := props["param1"]; !ok { + t.Error("expected param1 in properties") + } +} + +func TestClientOptions(t *testing.T) { + t.Run("WithModel", func(t *testing.T) { + c := &Client{} + opt := WithModel("test-model") + opt(c) + if c.modelID != "test-model" { + t.Errorf("expected model %q, got %q", "test-model", c.modelID) + } + }) + + t.Run("WithMaxTokens", func(t *testing.T) { + c := &Client{} + opt := WithMaxTokens(1000) + opt(c) + if c.maxTokens != 1000 { + t.Errorf("expected maxTokens %d, got %d", 1000, c.maxTokens) + } + }) + + t.Run("WithThinkingBudget", func(t *testing.T) { + c := &Client{} + opt := WithThinkingBudget(5000) + opt(c) + if c.thinkingBudget != 5000 { + t.Errorf("expected thinkingBudget %d, got %d", 5000, c.thinkingBudget) + } + }) + + t.Run("WithTools", func(t *testing.T) { + c := &Client{} + tools := []Tool{ + {Name: "tool1"}, + {Name: "tool2"}, + } + opt := WithTools(tools) + opt(c) + if len(c.tools) != 2 { + t.Errorf("expected 2 tools, got %d", len(c.tools)) + } + }) +} + +func TestStreamEvent(t *testing.T) { + t.Run("text event", func(t *testing.T) { + event := StreamEvent{Type: "text", Text: "hello"} + if event.Type != "text" { + t.Errorf("expected type %q, got %q", "text", event.Type) + } + if event.Text != "hello" { + t.Errorf("expected text %q, got %q", "hello", event.Text) + } + }) + + t.Run("thinking event", func(t *testing.T) { + event := StreamEvent{ + Type: "thinking", + Thinking: &ThinkingContent{Text: "reasoning..."}, + } + if event.Thinking == nil { + t.Fatal("expected thinking content") + } + if event.Thinking.Text != "reasoning..." { + t.Errorf("expected thinking text %q, got %q", "reasoning...", event.Thinking.Text) + } + }) + + t.Run("tool_use event", func(t *testing.T) { + event := StreamEvent{ + Type: "tool_use", + ToolUse: &ToolUseContent{ID: "123", Name: "test"}, + } + if event.ToolUse == nil { + t.Fatal("expected tool use") + } + if event.ToolUse.Name != "test" { + t.Errorf("expected tool name %q, got %q", "test", event.ToolUse.Name) + } + }) + + t.Run("done event", func(t *testing.T) { + event := StreamEvent{Type: "done", StopReason: StopReasonEndTurn} + if event.StopReason != StopReasonEndTurn { + t.Errorf("expected stop reason %q, got %q", StopReasonEndTurn, event.StopReason) + } + }) + + t.Run("error event", func(t *testing.T) { + event := StreamEvent{Type: "error", Error: &testError{"test error"}} + if event.Error == nil { + t.Fatal("expected error") + } + if event.Error.Error() != "test error" { + t.Errorf("expected error %q, got %q", "test error", event.Error.Error()) + } + }) +} + +type testError struct { + msg string +} + +func (e *testError) Error() string { + return e.msg +} diff --git a/internal/ai/session.go b/internal/ai/session.go new file mode 100644 index 00000000..538779fb --- /dev/null +++ b/internal/ai/session.go @@ -0,0 +1,350 @@ +package ai + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" + "sort" + "time" + + "github.com/google/uuid" + + "github.com/clawscli/claws/internal/config" + "github.com/clawscli/claws/internal/log" +) + +const ( + DefaultMaxSessions = 100 + sessionDir = "chat/sessions" + currentSessionFile = "chat/current.json" +) + +type Session struct { + ID string `json:"id"` + StartedAt time.Time `json:"started_at"` + UpdatedAt time.Time `json:"updated_at"` + Messages []Message `json:"messages"` + Context *Context `json:"context,omitempty"` +} + +type ContextMode string + +const ( + ContextModeSingle ContextMode = "single" + ContextModeList ContextMode = "list" + ContextModeDiff ContextMode = "diff" +) + +type ResourceRef struct { + ID string `json:"id"` + Name string `json:"name,omitempty"` + Region string `json:"region,omitempty"` + Profile string `json:"profile,omitempty"` + Cluster string `json:"cluster,omitempty"` +} + +type Context struct { + Service string `json:"service,omitempty"` + ResourceType string `json:"resource_type,omitempty"` + UserRegions []string `json:"user_regions,omitempty"` + UserProfiles []string `json:"user_profiles,omitempty"` + Mode ContextMode `json:"mode,omitempty"` + + ResourceID string `json:"resource_id,omitempty"` + ResourceName string `json:"resource_name,omitempty"` + ResourceRegion string `json:"resource_region,omitempty"` + ResourceProfile string `json:"resource_profile,omitempty"` + Cluster string `json:"cluster,omitempty"` + LogGroup string `json:"log_group,omitempty"` + + ResourceCount int `json:"resource_count,omitempty"` + FilterText string `json:"filter_text,omitempty"` + Toggles map[string]bool `json:"toggles,omitempty"` + + DiffLeft *ResourceRef `json:"diff_left,omitempty"` + DiffRight *ResourceRef `json:"diff_right,omitempty"` +} + +type SessionManager struct { + maxSessions int + saveEnabled bool + currentID string +} + +func NewSessionManager(maxSessions int, saveEnabled bool) *SessionManager { + if maxSessions <= 0 { + maxSessions = DefaultMaxSessions + } + return &SessionManager{ + maxSessions: maxSessions, + saveEnabled: saveEnabled, + } +} + +func (m *SessionManager) sessionsDir() (string, error) { + dir, err := config.ConfigDir() + if err != nil { + return "", err + } + return filepath.Join(dir, sessionDir), nil +} + +func (m *SessionManager) currentPath() (string, error) { + dir, err := config.ConfigDir() + if err != nil { + return "", err + } + return filepath.Join(dir, currentSessionFile), nil +} + +func (m *SessionManager) NewSession(ctx *Context) (*Session, error) { + session := &Session{ + ID: generateSessionID(), + StartedAt: time.Now(), + UpdatedAt: time.Now(), + Messages: []Message{}, + Context: ctx, + } + + m.currentID = session.ID + return session, nil +} + +func (m *SessionManager) CurrentSession() (*Session, error) { + if m.currentID == "" { + path, err := m.currentPath() + if err != nil { + return nil, err + } + + data, err := os.ReadFile(path) + if err != nil { + if os.IsNotExist(err) { + return nil, nil + } + return nil, err + } + + var current struct { + ID string `json:"id"` + } + if err := json.Unmarshal(data, ¤t); err != nil { + return nil, err + } + m.currentID = current.ID + } + + if m.currentID == "" { + return nil, nil + } + + return m.LoadSession(m.currentID) +} + +func (m *SessionManager) LoadSession(id string) (*Session, error) { + dir, err := m.sessionsDir() + if err != nil { + return nil, err + } + + path := filepath.Join(dir, id+".json") + data, err := os.ReadFile(path) + if err != nil { + return nil, err + } + + var session Session + if err := json.Unmarshal(data, &session); err != nil { + return nil, err + } + + return &session, nil +} + +func (m *SessionManager) SaveMessages(session *Session) error { + session.UpdatedAt = time.Now() + return m.saveSession(session) +} + +func (m *SessionManager) AddMessage(session *Session, msg Message) error { + session.Messages = append(session.Messages, msg) + err := m.SaveMessages(session) + if err != nil { + return err + } + if len(session.Messages) == 1 { + // Check if pruning is needed before loading all sessions + shouldPrune, checkErr := m.shouldPrune() + if checkErr != nil { + log.Debug("failed to check prune status", "error", checkErr) + } else if shouldPrune { + if pruneErr := m.pruneOldSessions(); pruneErr != nil { + log.Debug("failed to prune old sessions", "error", pruneErr) + } + } + } + return nil +} + +func (m *SessionManager) shouldPrune() (bool, error) { + dir, err := m.sessionsDir() + if err != nil { + return false, err + } + + entries, err := os.ReadDir(dir) + if err != nil { + if os.IsNotExist(err) { + return false, nil + } + return false, err + } + + // Count only .json files + count := 0 + for _, entry := range entries { + if !entry.IsDir() && filepath.Ext(entry.Name()) == ".json" { + count++ + } + } + + return count > m.maxSessions, nil +} + +func (m *SessionManager) ListSessions() ([]Session, error) { + dir, err := m.sessionsDir() + if err != nil { + return nil, err + } + + entries, err := os.ReadDir(dir) + if err != nil { + if os.IsNotExist(err) { + return nil, nil + } + return nil, err + } + + var sessions []Session + for _, entry := range entries { + if entry.IsDir() || filepath.Ext(entry.Name()) != ".json" { + continue + } + + id := entry.Name()[:len(entry.Name())-5] + session, err := m.LoadSession(id) + if err != nil { + log.Debug("failed to load session", "id", id, "error", err) + continue + } + sessions = append(sessions, *session) + } + + sort.Slice(sessions, func(i, j int) bool { + return sessions[i].UpdatedAt.After(sessions[j].UpdatedAt) + }) + + return sessions, nil +} + +func (m *SessionManager) saveSession(session *Session) error { + if !m.saveEnabled { + return nil + } + + dir, err := m.sessionsDir() + if err != nil { + return err + } + + if err := os.MkdirAll(dir, 0700); err != nil { + return err + } + + path := filepath.Join(dir, session.ID+".json") + data, err := json.MarshalIndent(session, "", " ") + if err != nil { + return err + } + + if err := os.WriteFile(path, data, 0600); err != nil { + return err + } + + return m.saveCurrentID(session.ID) +} + +func (m *SessionManager) saveCurrentID(id string) error { + path, err := m.currentPath() + if err != nil { + return err + } + + dir := filepath.Dir(path) + if err := os.MkdirAll(dir, 0700); err != nil { + return err + } + + data, _ := json.Marshal(struct { + ID string `json:"id"` + }{ID: id}) + + return os.WriteFile(path, data, 0600) +} + +func (m *SessionManager) pruneOldSessions() error { + dir, err := m.sessionsDir() + if err != nil { + return err + } + + entries, err := os.ReadDir(dir) + if err != nil { + if os.IsNotExist(err) { + return nil + } + return err + } + + // Collect .json files with modification times + type sessionFile struct { + name string + modTime time.Time + } + var files []sessionFile + for _, entry := range entries { + if !entry.IsDir() && filepath.Ext(entry.Name()) == ".json" { + info, err := entry.Info() + if err != nil { + continue + } + files = append(files, sessionFile{ + name: entry.Name(), + modTime: info.ModTime(), + }) + } + } + + if len(files) <= m.maxSessions { + return nil + } + + // Sort by modification time (oldest first) + sort.Slice(files, func(i, j int) bool { + return files[i].modTime.Before(files[j].modTime) + }) + + // Delete oldest sessions + deleteCount := len(files) - m.maxSessions + for i := 0; i < deleteCount; i++ { + _ = os.Remove(filepath.Join(dir, files[i].name)) + } + + return nil +} + +func generateSessionID() string { + now := time.Now() + return fmt.Sprintf("%s-%s", now.Format("20060102-150405"), uuid.New().String()[:8]) +} diff --git a/internal/ai/session_test.go b/internal/ai/session_test.go new file mode 100644 index 00000000..d9ec4e72 --- /dev/null +++ b/internal/ai/session_test.go @@ -0,0 +1,385 @@ +package ai + +import ( + "os" + "path/filepath" + "testing" + "time" +) + +func TestNewSessionManager(t *testing.T) { + t.Run("default max sessions", func(t *testing.T) { + sm := NewSessionManager(0, false) + if sm.maxSessions != DefaultMaxSessions { + t.Errorf("expected maxSessions %d, got %d", DefaultMaxSessions, sm.maxSessions) + } + }) + + t.Run("custom max sessions", func(t *testing.T) { + sm := NewSessionManager(50, false) + if sm.maxSessions != 50 { + t.Errorf("expected maxSessions %d, got %d", 50, sm.maxSessions) + } + }) + + t.Run("save disabled", func(t *testing.T) { + sm := NewSessionManager(10, false) + if sm.saveEnabled { + t.Error("expected saveEnabled to be false") + } + }) + + t.Run("save enabled", func(t *testing.T) { + sm := NewSessionManager(10, true) + if !sm.saveEnabled { + t.Error("expected saveEnabled to be true") + } + }) +} + +func TestGenerateSessionID(t *testing.T) { + id1 := generateSessionID() + time.Sleep(time.Millisecond) + id2 := generateSessionID() + + if id1 == "" { + t.Error("expected non-empty session ID") + } + if id1 == id2 { + t.Error("expected unique session IDs") + } + + // Check format: YYYY-MM-DD-xxxxxx + if len(id1) < 10 { + t.Errorf("session ID too short: %q", id1) + } +} + +func TestSessionManagerNewSession(t *testing.T) { + sm := NewSessionManager(10, false) // save disabled + + ctx := &Context{ + Service: "ec2", + ResourceType: "instances", + } + + session, err := sm.NewSession(ctx) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + if session.ID == "" { + t.Error("expected non-empty session ID") + } + if session.Context == nil { + t.Error("expected context") + } + if session.Context.Service != "ec2" { + t.Errorf("expected service %q, got %q", "ec2", session.Context.Service) + } + if len(session.Messages) != 0 { + t.Errorf("expected 0 messages, got %d", len(session.Messages)) + } + if session.StartedAt.IsZero() { + t.Error("expected StartedAt to be set") + } + if session.UpdatedAt.IsZero() { + t.Error("expected UpdatedAt to be set") + } +} + +func TestSessionManagerAddMessage(t *testing.T) { + sm := NewSessionManager(10, false) + + session, err := sm.NewSession(nil) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + msg := NewUserMessage("test message") + err = sm.AddMessage(session, msg) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + if len(session.Messages) != 1 { + t.Fatalf("expected 1 message, got %d", len(session.Messages)) + } + if session.Messages[0].Content[0].Text != "test message" { + t.Errorf("expected message %q, got %q", "test message", session.Messages[0].Content[0].Text) + } +} + +func TestSessionManagerWithPersistence(t *testing.T) { + // Create temp directory for test + tmpDir, err := os.MkdirTemp("", "claws-session-test") + if err != nil { + t.Fatalf("failed to create temp dir: %v", err) + } + defer os.RemoveAll(tmpDir) + + t.Setenv("HOME", tmpDir) + + sm := NewSessionManager(10, true) // save enabled + + session, err := sm.NewSession(&Context{Service: "lambda"}) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + // Add a message + err = sm.AddMessage(session, NewUserMessage("hello")) + if err != nil { + t.Fatalf("failed to add message: %v", err) + } + + // Verify file was created + sessionsDir := filepath.Join(tmpDir, ".config", "claws", "chat", "sessions") + sessionFile := filepath.Join(sessionsDir, session.ID+".json") + if _, err := os.Stat(sessionFile); os.IsNotExist(err) { + t.Error("expected session file to be created") + } + + // Load session + loaded, err := sm.LoadSession(session.ID) + if err != nil { + t.Fatalf("failed to load session: %v", err) + } + + if loaded.ID != session.ID { + t.Errorf("expected ID %q, got %q", session.ID, loaded.ID) + } + if len(loaded.Messages) != 1 { + t.Fatalf("expected 1 message, got %d", len(loaded.Messages)) + } + if loaded.Context.Service != "lambda" { + t.Errorf("expected service %q, got %q", "lambda", loaded.Context.Service) + } +} + +func TestContext(t *testing.T) { + t.Run("single mode", func(t *testing.T) { + ctx := &Context{ + Mode: ContextModeSingle, + Service: "ec2", + ResourceType: "instances", + ResourceID: "i-12345", + ResourceName: "my-instance", + ResourceRegion: "us-east-1", + } + if ctx.Mode != ContextModeSingle { + t.Errorf("expected mode %q, got %q", ContextModeSingle, ctx.Mode) + } + if ctx.ResourceID != "i-12345" { + t.Errorf("expected resource ID %q, got %q", "i-12345", ctx.ResourceID) + } + }) + + t.Run("list mode", func(t *testing.T) { + ctx := &Context{ + Mode: ContextModeList, + Service: "lambda", + ResourceType: "functions", + ResourceCount: 25, + FilterText: "prod", + } + if ctx.Mode != ContextModeList { + t.Errorf("expected mode %q, got %q", ContextModeList, ctx.Mode) + } + if ctx.ResourceCount != 25 { + t.Errorf("expected count %d, got %d", 25, ctx.ResourceCount) + } + }) + + t.Run("diff mode", func(t *testing.T) { + ctx := &Context{ + Mode: ContextModeDiff, + Service: "rds", + ResourceType: "instances", + DiffLeft: &ResourceRef{ + ID: "db-1", + Name: "prod-db", + Region: "us-east-1", + }, + DiffRight: &ResourceRef{ + ID: "db-2", + Name: "staging-db", + Region: "us-west-2", + }, + } + if ctx.Mode != ContextModeDiff { + t.Errorf("expected mode %q, got %q", ContextModeDiff, ctx.Mode) + } + if ctx.DiffLeft.ID != "db-1" { + t.Errorf("expected left ID %q, got %q", "db-1", ctx.DiffLeft.ID) + } + if ctx.DiffRight.Region != "us-west-2" { + t.Errorf("expected right region %q, got %q", "us-west-2", ctx.DiffRight.Region) + } + }) +} + +func TestResourceRef(t *testing.T) { + ref := ResourceRef{ + ID: "i-12345", + Name: "my-instance", + Region: "us-east-1", + Profile: "prod", + Cluster: "my-cluster", + } + + if ref.ID != "i-12345" { + t.Errorf("expected ID %q, got %q", "i-12345", ref.ID) + } + if ref.Cluster != "my-cluster" { + t.Errorf("expected cluster %q, got %q", "my-cluster", ref.Cluster) + } +} + +func TestSessionListEmpty(t *testing.T) { + tmpDir, err := os.MkdirTemp("", "claws-session-test") + if err != nil { + t.Fatalf("failed to create temp dir: %v", err) + } + defer os.RemoveAll(tmpDir) + + t.Setenv("HOME", tmpDir) + + sm := NewSessionManager(10, true) + + sessions, err := sm.ListSessions() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if len(sessions) != 0 { + t.Errorf("expected 0 sessions, got %d", len(sessions)) + } +} + +func TestSessionPruning(t *testing.T) { + tmpDir, err := os.MkdirTemp("", "claws-session-test") + if err != nil { + t.Fatalf("failed to create temp dir: %v", err) + } + defer os.RemoveAll(tmpDir) + + t.Setenv("HOME", tmpDir) + + sm := NewSessionManager(3, true) // Max 3 sessions + + // Create 5 sessions with messages (files only saved on AddMessage) + for i := 0; i < 5; i++ { + sess, err := sm.NewSession(nil) + if err != nil { + t.Fatalf("failed to create session %d: %v", i, err) + } + // AddMessage triggers file save and pruning (on first message) + if err := sm.AddMessage(sess, NewUserMessage("test")); err != nil { + t.Fatalf("failed to add message to session %d: %v", i, err) + } + time.Sleep(time.Millisecond) // Ensure different timestamps + } + + // List sessions - should be pruned to 3 + sessions, err := sm.ListSessions() + if err != nil { + t.Fatalf("failed to list sessions: %v", err) + } + + if len(sessions) != 3 { + t.Errorf("expected 3 sessions after pruning, got %d", len(sessions)) + } +} + +func TestShouldPrune(t *testing.T) { + tmpDir := t.TempDir() + origHome := os.Getenv("HOME") + defer os.Setenv("HOME", origHome) + os.Setenv("HOME", tmpDir) + + sm := NewSessionManager(5, true) + + t.Run("no sessions", func(t *testing.T) { + should, err := sm.shouldPrune() + if err != nil { + t.Fatalf("shouldPrune failed: %v", err) + } + if should { + t.Error("empty dir should not need pruning") + } + }) + + t.Run("under limit", func(t *testing.T) { + // Create 4 sessions (below limit of 5) + for i := 0; i < 4; i++ { + sess, err := sm.NewSession(nil) + if err != nil { + t.Fatalf("NewSession failed: %v", err) + } + if err := sm.SaveMessages(sess); err != nil { + t.Fatalf("SaveMessages failed: %v", err) + } + } + + should, err := sm.shouldPrune() + if err != nil { + t.Fatalf("shouldPrune failed: %v", err) + } + if should { + t.Error("under limit should not need pruning") + } + }) + + t.Run("at limit", func(t *testing.T) { + // Add one more session (total 5, at limit) + sess, err := sm.NewSession(nil) + if err != nil { + t.Fatalf("NewSession failed: %v", err) + } + if err := sm.SaveMessages(sess); err != nil { + t.Fatalf("SaveMessages failed: %v", err) + } + + should, err := sm.shouldPrune() + if err != nil { + t.Fatalf("shouldPrune failed: %v", err) + } + if should { + t.Error("at limit should not need pruning") + } + }) + + t.Run("over limit", func(t *testing.T) { + // Add one more session (total 6, over limit) + sess, err := sm.NewSession(nil) + if err != nil { + t.Fatalf("NewSession failed: %v", err) + } + if err := sm.SaveMessages(sess); err != nil { + t.Fatalf("SaveMessages failed: %v", err) + } + + should, err := sm.shouldPrune() + if err != nil { + t.Fatalf("shouldPrune failed: %v", err) + } + if !should { + t.Error("over limit should need pruning") + } + }) + + t.Run("non-existent directory", func(t *testing.T) { + tmpDir2 := t.TempDir() + os.Setenv("HOME", tmpDir2) + defer os.Setenv("HOME", tmpDir) + + sm2 := NewSessionManager(5, true) + should, err := sm2.shouldPrune() + if err != nil { + t.Fatalf("shouldPrune failed: %v", err) + } + if should { + t.Error("non-existent dir should not need pruning") + } + }) +} diff --git a/internal/ai/tools.go b/internal/ai/tools.go new file mode 100644 index 00000000..19ac28a1 --- /dev/null +++ b/internal/ai/tools.go @@ -0,0 +1,769 @@ +package ai + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "net/http" + "strings" + "time" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs" + + appaws "github.com/clawscli/claws/internal/aws" + appconfig "github.com/clawscli/claws/internal/config" + "github.com/clawscli/claws/internal/dao" + "github.com/clawscli/claws/internal/log" + "github.com/clawscli/claws/internal/registry" + + apigatewayStages "github.com/clawscli/claws/custom/apigateway/stages" + apigatewayStagesV2 "github.com/clawscli/claws/custom/apigateway/stages-v2" + cloudtrailtrails "github.com/clawscli/claws/custom/cloudtrail/trails" + codebuildbuilds "github.com/clawscli/claws/custom/codebuild/builds" + codebuildprojects "github.com/clawscli/claws/custom/codebuild/projects" + ecsservices "github.com/clawscli/claws/custom/ecs/services" + taskdefinitions "github.com/clawscli/claws/custom/ecs/task-definitions" + ecstasks "github.com/clawscli/claws/custom/ecs/tasks" + sfnStateMachines "github.com/clawscli/claws/custom/stepfunctions/state-machines" +) + +type ToolExecutor struct { + registry *registry.Registry +} + +func NewToolExecutor(_ context.Context, reg *registry.Registry) (*ToolExecutor, error) { + return &ToolExecutor{ + registry: reg, + }, nil +} + +func (e *ToolExecutor) Tools() []Tool { + return []Tool{ + { + Name: "list_resources", + Description: "List resource types available for a specific AWS service", + InputSchema: map[string]any{ + "type": "object", + "properties": map[string]any{ + "service": map[string]any{ + "type": "string", + "description": "AWS service name (e.g., ec2, lambda, s3)", + }, + }, + "required": []string{"service"}, + }, + }, + { + Name: "query_resources", + Description: "List AWS resources. You MUST provide service, resource_type, and region parameters.", + InputSchema: map[string]any{ + "type": "object", + "properties": map[string]any{ + "service": map[string]any{ + "type": "string", + "description": "AWS service name. Examples: ec2, lambda, s3, rds, ecs, dynamodb", + }, + "resource_type": map[string]any{ + "type": "string", + "description": "Resource type. Examples: instances (for ec2), functions (for lambda), buckets (for s3), tables (for dynamodb)", + }, + "region": map[string]any{ + "type": "string", + "description": "AWS region. Examples: us-east-1, us-west-2, ap-northeast-1", + }, + "profile": map[string]any{ + "type": "string", + "description": "AWS profile name (optional, uses current profile if not specified)", + }, + "include_resolved": map[string]any{ + "type": "boolean", + "description": "Include resolved/archived items (securityhub/findings only, default: false)", + }, + "limit": map[string]any{ + "type": "integer", + "description": "Maximum resources to return (default: 100, max: 2000)", + }, + "offset": map[string]any{ + "type": "integer", + "description": "Skip first N resources for pagination (default: 0)", + }, + }, + "required": []string{"service", "resource_type", "region"}, + }, + }, + { + Name: "get_resource_detail", + Description: "Get detailed information about a specific AWS resource. NOTE: For ecs/services and ecs/tasks, cluster parameter is required.", + InputSchema: map[string]any{ + "type": "object", + "properties": map[string]any{ + "service": map[string]any{ + "type": "string", + "description": "AWS service name", + }, + "resource_type": map[string]any{ + "type": "string", + "description": "Resource type", + }, + "region": map[string]any{ + "type": "string", + "description": "AWS region (e.g., us-east-1, us-west-2)", + }, + "id": map[string]any{ + "type": "string", + "description": "Resource ID", + }, + "cluster": map[string]any{ + "type": "string", + "description": "ECS cluster name (required for ecs/services and ecs/tasks)", + }, + "profile": map[string]any{ + "type": "string", + "description": "AWS profile name (optional, uses current profile if not specified)", + }, + }, + "required": []string{"service", "resource_type", "region", "id"}, + }, + }, + { + Name: "tail_logs", + Description: "Fetch recent CloudWatch logs for an AWS resource. Automatically extracts log group from resource configuration. Supported: lambda/functions, ecs/services, ecs/tasks, ecs/task-definitions, codebuild/projects, codebuild/builds, cloudtrail/trails, apigateway/stages, apigateway/stages-v2, stepfunctions/state-machines. NOTE: For ecs/services and ecs/tasks, cluster parameter is required.", + InputSchema: map[string]any{ + "type": "object", + "properties": map[string]any{ + "service": map[string]any{ + "type": "string", + "description": "AWS service name (e.g., lambda, ecs, codebuild)", + }, + "resource_type": map[string]any{ + "type": "string", + "description": "Resource type (e.g., functions, services, tasks, task-definitions)", + }, + "region": map[string]any{ + "type": "string", + "description": "AWS region (e.g., us-east-1, ap-northeast-1)", + }, + "id": map[string]any{ + "type": "string", + "description": "Resource ID", + }, + "cluster": map[string]any{ + "type": "string", + "description": "ECS cluster name (required for ecs/services and ecs/tasks)", + }, + "profile": map[string]any{ + "type": "string", + "description": "AWS profile name (optional, uses current profile if not specified)", + }, + "filter": map[string]any{ + "type": "string", + "description": "Optional filter pattern for log messages", + }, + "since": map[string]any{ + "type": "string", + "description": "Time range (e.g., 5m, 1h, 24h). Default: 15m", + }, + "limit": map[string]any{ + "type": "integer", + "description": "Maximum number of log events. Default: 100", + }, + }, + "required": []string{"service", "resource_type", "region", "id"}, + }, + }, + { + Name: "search_aws_docs", + Description: "Search AWS documentation for information", + InputSchema: map[string]any{ + "type": "object", + "properties": map[string]any{ + "query": map[string]any{ + "type": "string", + "description": "Search query for AWS documentation", + }, + }, + "required": []string{"query"}, + }, + }, + } +} + +func (e *ToolExecutor) Execute(ctx context.Context, call *ToolUseContent) ToolResultContent { + if call.InputError != "" { + return ToolResultContent{ + ToolUseID: call.ID, + Content: fmt.Sprintf("Error: malformed tool input: %s", call.InputError), + IsError: true, + } + } + + var content string + var isError bool + + switch call.Name { + case "list_resources": + service, _ := call.Input["service"].(string) + content = e.listResources(service) + case "query_resources": + service, _ := call.Input["service"].(string) + resourceType, _ := call.Input["resource_type"].(string) + region, _ := call.Input["region"].(string) + profile, _ := call.Input["profile"].(string) + includeResolved, _ := call.Input["include_resolved"].(bool) + limit, _ := call.Input["limit"].(float64) + offset, _ := call.Input["offset"].(float64) + content, isError = e.queryResources(ctx, service, resourceType, region, profile, includeResolved, int(limit), int(offset)) + case "get_resource_detail": + service, _ := call.Input["service"].(string) + resourceType, _ := call.Input["resource_type"].(string) + region, _ := call.Input["region"].(string) + id, _ := call.Input["id"].(string) + cluster, _ := call.Input["cluster"].(string) + profile, _ := call.Input["profile"].(string) + content, isError = e.getResourceDetail(ctx, service, resourceType, region, id, cluster, profile) + case "tail_logs": + service, _ := call.Input["service"].(string) + resourceType, _ := call.Input["resource_type"].(string) + region, _ := call.Input["region"].(string) + id, _ := call.Input["id"].(string) + cluster, _ := call.Input["cluster"].(string) + profile, _ := call.Input["profile"].(string) + filter, _ := call.Input["filter"].(string) + since, _ := call.Input["since"].(string) + limit, _ := call.Input["limit"].(float64) + content, isError = e.tailLogs(ctx, service, resourceType, region, id, cluster, profile, filter, since, int(limit)) + case "search_aws_docs": + query, _ := call.Input["query"].(string) + content = e.searchDocs(ctx, query) + default: + content = fmt.Sprintf("Unknown tool: %s", call.Name) + isError = true + } + + return ToolResultContent{ + ToolUseID: call.ID, + Content: content, + IsError: isError, + } +} + +func (e *ToolExecutor) listResources(service string) string { + resources := e.registry.ListResources(service) + if len(resources) == 0 { + return fmt.Sprintf("No resources found for service: %s", service) + } + + displayName := e.registry.GetDisplayName(service) + result := fmt.Sprintf("Resource types for %s (%s):\n", displayName, service) + for _, r := range resources { + result += fmt.Sprintf("- %s\n", r) + } + return result +} + +func (e *ToolExecutor) queryResources(ctx context.Context, service, resourceType, region, profile string, includeResolved bool, limit, offset int) (string, bool) { + if service == "" { + return "Error: service parameter is required", true + } + if resourceType == "" { + return "Error: resource_type parameter is required", true + } + if region == "" { + return "Error: region parameter is required", true + } + + // Validate and apply limit + if limit <= 0 { + limit = 100 // default changed from 50 + } + if limit > 2000 { + limit = 2000 // max 2000 + } + + // Validate offset + if offset < 0 { + offset = 0 + } + + if profile != "" { + ctx = appaws.WithSelectionOverride(ctx, appconfig.ProfileSelectionFromID(profile)) + } + ctx = appaws.WithRegionOverride(ctx, region) + if includeResolved { + ctx = dao.WithFilter(ctx, "ShowResolved", "true") + } + d, err := e.registry.GetDAO(ctx, service, resourceType) + if err != nil { + return fmt.Sprintf("Error: %s/%s not found. Use list_resources(service=\"%s\") to see available types.", service, resourceType, service), true + } + + resources, err := d.List(ctx) + if err != nil { + return fmt.Sprintf("Error listing %s/%s: %v", service, resourceType, err), true + } + + if len(resources) == 0 { + return fmt.Sprintf("No %s/%s resources found in %s", service, resourceType, region), false + } + + filterNote := "" + if service == "securityhub" && resourceType == "findings" { + if includeResolved { + filterNote = " (including resolved)" + } else { + filterNote = " (active only, use include_resolved=true for all)" + } + } + + // Apply offset + start := offset + if start >= len(resources) { + return fmt.Sprintf("Offset %d exceeds total count %d", offset, len(resources)), true + } + + end := start + limit + if end > len(resources) { + end = len(resources) + } + + viewResources := resources[start:end] + + result := fmt.Sprintf("Found %d %s/%s resources in %s%s (showing %d-%d):\n\n", + len(resources), service, resourceType, region, filterNote, start+1, end) + + for _, r := range viewResources { + result += formatResourceSummary(r) + } + + if end < len(resources) { + result += fmt.Sprintf("\n... and %d more (use offset=%d to see next page)\n", len(resources)-end, end) + } + + return result, false +} + +func (e *ToolExecutor) getResourceDetail(ctx context.Context, service, resourceType, region, id, cluster, profile string) (string, bool) { + if region == "" { + return "Error: region parameter is required", true + } + + if profile != "" { + ctx = appaws.WithSelectionOverride(ctx, appconfig.ProfileSelectionFromID(profile)) + } + ctx = appaws.WithRegionOverride(ctx, region) + + if service == "ecs" && (resourceType == "services" || resourceType == "tasks") { + if cluster == "" { + err := "Error: cluster parameter is required for ecs/services and ecs/tasks" + log.Warn("getResourceDetail failed", "error", err) + return err, true + } + ctx = dao.WithFilter(ctx, "ClusterName", cluster) + } + + d, err := e.registry.GetDAO(ctx, service, resourceType) + if err != nil { + log.Warn("getResourceDetail GetDAO failed", "error", err) + return fmt.Sprintf("Error getting DAO: %v", err), true + } + + resource, err := d.Get(ctx, id) + if err != nil { + log.Warn("getResourceDetail Get failed", "service", service, "resourceType", resourceType, "id", id, "error", err) + return fmt.Sprintf("Error getting resource: %v", err), true + } + + return formatResourceDetail(resource), false +} + +func (e *ToolExecutor) tailLogs(ctx context.Context, service, resourceType, region, id, cluster, profile, filter, since string, limit int) (string, bool) { + if region == "" { + return "Error: region parameter is required", true + } + if limit <= 0 { + limit = 100 + } + if limit > 500 { + limit = 500 + } + + if profile != "" { + ctx = appaws.WithSelectionOverride(ctx, appconfig.ProfileSelectionFromID(profile)) + } + ctx = appaws.WithRegionOverride(ctx, region) + + logGroup, err := e.extractLogGroup(ctx, service, resourceType, id, cluster) + if err != nil { + log.Warn("tailLogs extractLogGroup failed", "service", service, "resourceType", resourceType, "id", id, "error", err) + return fmt.Sprintf("Error extracting log group for %s/%s/%s: %v", service, resourceType, id, err), true + } + + cfg, err := appaws.NewConfigWithRegion(ctx, region) + if err != nil { + return fmt.Sprintf("Error creating config for region %s: %v", region, err), true + } + cwClient := cloudwatchlogs.NewFromConfig(cfg) + + startTime := time.Now().Add(-15 * time.Minute) + if since != "" { + if d, err := time.ParseDuration(since); err == nil { + startTime = time.Now().Add(-d) + } + } + + input := &cloudwatchlogs.FilterLogEventsInput{ + LogGroupName: aws.String(logGroup), + StartTime: aws.Int64(startTime.UnixMilli()), + Limit: aws.Int32(int32(limit)), + } + + if filter != "" { + input.FilterPattern = aws.String(filter) + } + + output, err := cwClient.FilterLogEvents(ctx, input) + if err != nil { + return fmt.Sprintf("Error fetching logs from %s: %v", logGroup, err), true + } + + if len(output.Events) == 0 { + sinceStr := "15m" + if since != "" { + sinceStr = since + } + return fmt.Sprintf("No logs found in %s (since %s)", logGroup, sinceStr), false + } + + result := fmt.Sprintf("Logs from %s (%d events):\n\n", logGroup, len(output.Events)) + for _, event := range output.Events { + ts := time.UnixMilli(aws.ToInt64(event.Timestamp)) + result += fmt.Sprintf("[%s] %s\n", ts.Format("15:04:05"), aws.ToString(event.Message)) + } + + return result, false +} + +func (e *ToolExecutor) extractLogGroup(ctx context.Context, service, resourceType, id, cluster string) (string, error) { + key := service + "/" + resourceType + + switch key { + case "lambda/functions": + return "/aws/lambda/" + id, nil + + case "ecs/task-definitions": + resource, err := e.getResource(ctx, service, resourceType, id) + if err != nil { + return "", err + } + td, ok := resource.(*taskdefinitions.TaskDefinitionResource) + if !ok { + return "", fmt.Errorf("unexpected resource type for task-definitions") + } + if logGroup := td.GetCloudWatchLogGroup(""); logGroup != "" { + return logGroup, nil + } + return "", fmt.Errorf("no CloudWatch logs configured for task definition %s", id) + + case "ecs/services": + if cluster == "" { + return "", fmt.Errorf("cluster parameter is required for ecs/services") + } + ctxWithCluster := dao.WithFilter(ctx, "ClusterName", cluster) + resource, err := e.getResource(ctxWithCluster, service, resourceType, id) + if err != nil { + return "", err + } + svc, ok := resource.(*ecsservices.ServiceResource) + if !ok { + return "", fmt.Errorf("unexpected resource type for ecs services") + } + taskDefArn := svc.TaskDefinition() + if taskDefArn == "" { + return "", fmt.Errorf("no task definition found for service %s", id) + } + return e.extractLogGroupFromTaskDef(ctx, taskDefArn) + + case "ecs/tasks": + if cluster == "" { + return "", fmt.Errorf("cluster parameter is required for ecs/tasks") + } + ctxWithCluster := dao.WithFilter(ctx, "ClusterName", cluster) + resource, err := e.getResource(ctxWithCluster, service, resourceType, id) + if err != nil { + return "", err + } + task, ok := resource.(*ecstasks.TaskResource) + if !ok { + return "", fmt.Errorf("unexpected resource type for ecs tasks") + } + taskDefArn := task.TaskDefinitionArn() + if taskDefArn == "" { + return "", fmt.Errorf("no task definition found for task %s", id) + } + return e.extractLogGroupFromTaskDef(ctx, taskDefArn) + + case "codebuild/projects": + resource, err := e.getResource(ctx, service, resourceType, id) + if err != nil { + return "", err + } + proj, ok := resource.(*codebuildprojects.ProjectResource) + if !ok { + return "", fmt.Errorf("unexpected resource type for codebuild projects") + } + if proj.Project.LogsConfig != nil && + proj.Project.LogsConfig.CloudWatchLogs != nil && + proj.Project.LogsConfig.CloudWatchLogs.GroupName != nil { + return *proj.Project.LogsConfig.CloudWatchLogs.GroupName, nil + } + return "/aws/codebuild/" + id, nil + + case "codebuild/builds": + resource, err := e.getResource(ctx, service, resourceType, id) + if err != nil { + return "", err + } + build, ok := resource.(*codebuildbuilds.BuildResource) + if !ok { + return "", fmt.Errorf("unexpected resource type for codebuild builds") + } + if build.LogsGroupName() != "" { + return build.LogsGroupName(), nil + } + return "", fmt.Errorf("no CloudWatch logs configured for build %s", id) + + case "cloudtrail/trails": + resource, err := e.getResource(ctx, service, resourceType, id) + if err != nil { + return "", err + } + trail, ok := resource.(*cloudtrailtrails.TrailResource) + if !ok { + return "", fmt.Errorf("unexpected resource type for cloudtrail trails") + } + logGroupArn := trail.CloudWatchLogsLogGroupArn() + if logGroupArn == "" { + return "", fmt.Errorf("no CloudWatch logs configured for trail %s", id) + } + return extractLogGroupNameFromArn(logGroupArn), nil + + case "apigateway/stages": + resource, err := e.getResource(ctx, service, resourceType, id) + if err != nil { + return "", err + } + stage, ok := resource.(*apigatewayStages.StageResource) + if !ok { + return "", fmt.Errorf("unexpected resource type for apigateway stages") + } + destArn := stage.AccessLogDestination() + if destArn == "" { + return "", fmt.Errorf("no access logs configured for stage %s", id) + } + return extractLogGroupNameFromArn(destArn), nil + + case "apigateway/stages-v2": + resource, err := e.getResource(ctx, service, resourceType, id) + if err != nil { + return "", err + } + stage, ok := resource.(*apigatewayStagesV2.StageV2Resource) + if !ok { + return "", fmt.Errorf("unexpected resource type for apigateway stages-v2") + } + destArn := stage.AccessLogDestination() + if destArn == "" { + return "", fmt.Errorf("no access logs configured for stage %s", id) + } + return extractLogGroupNameFromArn(destArn), nil + + case "stepfunctions/state-machines": + resource, err := e.getResource(ctx, service, resourceType, id) + if err != nil { + return "", err + } + sm, ok := resource.(*sfnStateMachines.StateMachineResource) + if !ok { + return "", fmt.Errorf("unexpected resource type for stepfunctions state-machines") + } + if sm.Detail != nil && sm.Detail.LoggingConfiguration != nil { + for _, dest := range sm.Detail.LoggingConfiguration.Destinations { + if dest.CloudWatchLogsLogGroup != nil && dest.CloudWatchLogsLogGroup.LogGroupArn != nil { + return extractLogGroupNameFromArn(*dest.CloudWatchLogsLogGroup.LogGroupArn), nil + } + } + } + return "", fmt.Errorf("no CloudWatch logs configured for state machine %s", id) + + default: + return "", fmt.Errorf("log extraction not supported for %s/%s. Supported: lambda/functions, ecs/services, ecs/tasks, ecs/task-definitions, codebuild/projects, codebuild/builds, cloudtrail/trails, apigateway/stages, apigateway/stages-v2, stepfunctions/state-machines", service, resourceType) + } +} + +func (e *ToolExecutor) extractLogGroupFromTaskDef(ctx context.Context, taskDefArn string) (string, error) { + taskDefID := appaws.ExtractResourceName(taskDefArn) + resource, err := e.getResource(ctx, "ecs", "task-definitions", taskDefID) + if err != nil { + return "", fmt.Errorf("failed to get task definition %s: %w", taskDefArn, err) + } + + td, ok := resource.(*taskdefinitions.TaskDefinitionResource) + if !ok { + return "", fmt.Errorf("unexpected resource type") + } + + if logGroup := td.GetCloudWatchLogGroup(""); logGroup != "" { + return logGroup, nil + } + + return "", fmt.Errorf("no CloudWatch logs configured in task definition %s", taskDefArn) +} + +func extractLogGroupNameFromArn(arn string) string { + parts := strings.Split(arn, ":") + if len(parts) >= 7 { + logGroupPart := parts[6] + if strings.HasPrefix(logGroupPart, "log-group:") { + return strings.TrimPrefix(logGroupPart, "log-group:") + } + return logGroupPart + } + return arn +} + +func (e *ToolExecutor) searchDocs(ctx context.Context, query string) string { + if query == "" { + return "Error: query parameter is required" + } + + reqBody := map[string]any{ + "textQuery": map[string]string{ + "input": query, + }, + "contextAttributes": []map[string]string{ + {"key": "domain", "value": "docs.aws.amazon.com"}, + }, + "acceptSuggestionBody": "RawText", + "locales": []string{"en_us"}, + } + + jsonBody, err := json.Marshal(reqBody) + if err != nil { + return fmt.Sprintf("Error creating request: %v", err) + } + + reqCtx, cancel := context.WithTimeout(ctx, appconfig.File().DocsSearchTimeout()) + defer cancel() + + req, err := http.NewRequestWithContext(reqCtx, "POST", "https://proxy.search.docs.aws.amazon.com/search", bytes.NewBuffer(jsonBody)) + if err != nil { + return fmt.Sprintf("Error creating request: %v", err) + } + req.Header.Set("Content-Type", "application/json") + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return fmt.Sprintf("Error searching documentation: %v", err) + } + defer func() { _ = resp.Body.Close() }() + + if resp.StatusCode >= 400 { + return fmt.Sprintf("Error: received status %d from AWS documentation search", resp.StatusCode) + } + + var result struct { + Suggestions []struct { + TextExcerptSuggestion struct { + Link string `json:"link"` + Title string `json:"title"` + Metadata struct { + SeoAbstract string `json:"seo_abstract"` + Abstract string `json:"abstract"` + } `json:"metadata"` + Summary string `json:"summary"` + } `json:"textExcerptSuggestion"` + } `json:"suggestions"` + } + + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return fmt.Sprintf("Error parsing response: %v", err) + } + + if len(result.Suggestions) == 0 { + return fmt.Sprintf("No documentation found for: %s", query) + } + + var sb strings.Builder + sb.WriteString(fmt.Sprintf("AWS Documentation results for '%s':\n\n", query)) + for i, s := range result.Suggestions { + if i >= 5 { + break + } + suggestion := s.TextExcerptSuggestion + sb.WriteString(fmt.Sprintf("%d. %s\n", i+1, suggestion.Title)) + sb.WriteString(fmt.Sprintf(" URL: %s\n", suggestion.Link)) + context := suggestion.Metadata.SeoAbstract + if context == "" { + context = suggestion.Metadata.Abstract + } + if context == "" { + context = suggestion.Summary + } + if context != "" { + sb.WriteString(fmt.Sprintf(" %s\n", context)) + } + sb.WriteString("\n") + } + return sb.String() +} + +func (e *ToolExecutor) getResource(ctx context.Context, service, resourceType, id string) (dao.Resource, error) { + d, err := e.registry.GetDAO(ctx, service, resourceType) + if err != nil { + return nil, err + } + resource, err := d.Get(ctx, id) + if err != nil { + return nil, err + } + return dao.UnwrapResource(resource), nil +} + +func formatResourceSummary(r dao.Resource) string { + result := fmt.Sprintf("- ID: %s", r.GetID()) + if name := r.GetName(); name != "" && name != r.GetID() { + result += fmt.Sprintf(", Name: %s", name) + } + result += "\n" + return result +} + +func formatResourceDetail(r dao.Resource) string { + result := fmt.Sprintf("ID: %s\n", r.GetID()) + + if name := r.GetName(); name != "" { + result += fmt.Sprintf("Name: %s\n", name) + } + + if arn := r.GetARN(); arn != "" { + result += fmt.Sprintf("ARN: %s\n", arn) + } + + if tags := r.GetTags(); len(tags) > 0 { + result += "\nTags:\n" + for k, v := range tags { + result += fmt.Sprintf(" %s: %s\n", k, v) + } + } + + if raw := r.Raw(); raw != nil { + data, err := json.MarshalIndent(raw, "", " ") + if err == nil { + result += fmt.Sprintf("\nRaw Data:\n%s\n", string(data)) + } + } + + return result +} diff --git a/internal/ai/tools_test.go b/internal/ai/tools_test.go new file mode 100644 index 00000000..3cf0c34e --- /dev/null +++ b/internal/ai/tools_test.go @@ -0,0 +1,326 @@ +package ai + +import ( + "context" + "strings" + "testing" +) + +func TestToolExecutorTools(t *testing.T) { + executor := &ToolExecutor{} + tools := executor.Tools() + + expectedTools := []string{ + "list_resources", + "query_resources", + "get_resource_detail", + "tail_logs", + "search_aws_docs", + } + + if len(tools) != len(expectedTools) { + t.Errorf("expected %d tools, got %d", len(expectedTools), len(tools)) + } + + toolNames := make(map[string]bool) + for _, tool := range tools { + toolNames[tool.Name] = true + } + + for _, name := range expectedTools { + if !toolNames[name] { + t.Errorf("missing tool: %s", name) + } + } +} + +func TestToolSchemas(t *testing.T) { + executor := &ToolExecutor{} + tools := executor.Tools() + + for _, tool := range tools { + t.Run(tool.Name, func(t *testing.T) { + if tool.Name == "" { + t.Error("tool name is empty") + } + if tool.Description == "" { + t.Error("tool description is empty") + } + if tool.InputSchema == nil { + t.Error("tool input schema is nil") + } + + schemaType, ok := tool.InputSchema["type"].(string) + if !ok || schemaType != "object" { + t.Errorf("expected schema type 'object', got %v", tool.InputSchema["type"]) + } + + props, ok := tool.InputSchema["properties"].(map[string]any) + if !ok { + t.Error("schema properties is not a map") + } + + if len(props) == 0 { + t.Error("schema has no properties") + } + }) + } +} + +func TestQueryResourcesRequiredParams(t *testing.T) { + executor := &ToolExecutor{} + tools := executor.Tools() + + var queryTool *Tool + for i := range tools { + if tools[i].Name == "query_resources" { + queryTool = &tools[i] + break + } + } + + if queryTool == nil { + t.Fatal("query_resources tool not found") + } + + required, ok := queryTool.InputSchema["required"].([]string) + if !ok { + t.Fatal("required field is not []string") + } + + expectedRequired := map[string]bool{ + "service": true, + "resource_type": true, + "region": true, + } + + for _, r := range required { + if !expectedRequired[r] { + t.Errorf("unexpected required field: %s", r) + } + delete(expectedRequired, r) + } + + for missing := range expectedRequired { + t.Errorf("missing required field: %s", missing) + } +} + +func TestToolExecuteUnknownTool(t *testing.T) { + executor := &ToolExecutor{registry: nil} + + result := executor.Execute(context.TODO(), &ToolUseContent{ + ID: "test-123", + Name: "unknown_tool", + Input: map[string]any{}, + }) + + if result.ToolUseID != "test-123" { + t.Errorf("expected tool use ID %q, got %q", "test-123", result.ToolUseID) + } + if !result.IsError { + t.Error("expected IsError to be true") + } + if !strings.Contains(result.Content, "Unknown tool") { + t.Errorf("expected error message about unknown tool, got %q", result.Content) + } +} + +func TestToolExecuteQueryResourcesMissingParams(t *testing.T) { + executor := &ToolExecutor{registry: nil} + + tests := []struct { + name string + input map[string]any + expectedError string + }{ + { + name: "missing service", + input: map[string]any{"resource_type": "instances", "region": "us-east-1"}, + expectedError: "service parameter is required", + }, + { + name: "missing resource_type", + input: map[string]any{"service": "ec2", "region": "us-east-1"}, + expectedError: "resource_type parameter is required", + }, + { + name: "missing region", + input: map[string]any{"service": "ec2", "resource_type": "instances"}, + expectedError: "region parameter is required", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := executor.Execute(context.TODO(), &ToolUseContent{ + ID: "test-123", + Name: "query_resources", + Input: tt.input, + }) + + if !result.IsError { + t.Error("expected IsError to be true") + } + if !strings.Contains(result.Content, tt.expectedError) { + t.Errorf("expected error %q, got %q", tt.expectedError, result.Content) + } + }) + } +} + +func TestToolExecuteGetResourceDetailMissingRegion(t *testing.T) { + executor := &ToolExecutor{registry: nil} + + result := executor.Execute(context.TODO(), &ToolUseContent{ + ID: "test-123", + Name: "get_resource_detail", + Input: map[string]any{ + "service": "ec2", + "resource_type": "instances", + "id": "i-12345", + }, + }) + + if !result.IsError { + t.Error("expected IsError to be true") + } + if !strings.Contains(result.Content, "region parameter is required") { + t.Errorf("expected region error, got %q", result.Content) + } +} + +func TestToolExecuteTailLogsMissingRegion(t *testing.T) { + executor := &ToolExecutor{registry: nil} + + result := executor.Execute(context.TODO(), &ToolUseContent{ + ID: "test-123", + Name: "tail_logs", + Input: map[string]any{ + "service": "lambda", + "resource_type": "functions", + "id": "my-function", + }, + }) + + if !result.IsError { + t.Error("expected IsError to be true") + } + if !strings.Contains(result.Content, "region parameter is required") { + t.Errorf("expected region error, got %q", result.Content) + } +} + +func TestToolExecuteSearchDocsEmptyQuery(t *testing.T) { + executor := &ToolExecutor{registry: nil} + + result := executor.Execute(context.TODO(), &ToolUseContent{ + ID: "test-123", + Name: "search_aws_docs", + Input: map[string]any{}, + }) + + if !strings.Contains(result.Content, "query parameter is required") { + t.Errorf("expected query error, got %q", result.Content) + } +} + +func TestExtractLogGroupNameFromArn(t *testing.T) { + tests := []struct { + arn string + expected string + }{ + { + arn: "arn:aws:logs:us-east-1:123456789012:log-group:/aws/lambda/my-function", + expected: "/aws/lambda/my-function", + }, + { + arn: "arn:aws:logs:us-west-2:123456789012:log-group:/ecs/my-service", + expected: "/ecs/my-service", + }, + { + arn: "/aws/lambda/simple", + expected: "/aws/lambda/simple", + }, + } + + for _, tt := range tests { + t.Run(tt.arn, func(t *testing.T) { + result := extractLogGroupNameFromArn(tt.arn) + if result != tt.expected { + t.Errorf("expected %q, got %q", tt.expected, result) + } + }) + } +} + +func TestFormatResourceSummary(t *testing.T) { + resource := &mockResource{ + id: "i-12345", + name: "my-instance", + } + + result := formatResourceSummary(resource) + + if !strings.Contains(result, "i-12345") { + t.Errorf("expected ID in summary, got %q", result) + } + if !strings.Contains(result, "my-instance") { + t.Errorf("expected name in summary, got %q", result) + } +} + +func TestFormatResourceSummarySameIDAndName(t *testing.T) { + resource := &mockResource{ + id: "my-bucket", + name: "my-bucket", + } + + result := formatResourceSummary(resource) + + if strings.Count(result, "my-bucket") != 1 { + t.Errorf("expected ID only once when same as name, got %q", result) + } +} + +func TestFormatResourceDetail(t *testing.T) { + resource := &mockResource{ + id: "i-12345", + name: "my-instance", + arn: "arn:aws:ec2:us-east-1:123456789012:instance/i-12345", + tags: map[string]string{"Environment": "prod", "Team": "platform"}, + raw: map[string]string{"InstanceType": "t3.micro"}, + } + + result := formatResourceDetail(resource) + + if !strings.Contains(result, "i-12345") { + t.Errorf("expected ID in detail, got %q", result) + } + if !strings.Contains(result, "my-instance") { + t.Errorf("expected name in detail, got %q", result) + } + if !strings.Contains(result, "arn:aws:ec2") { + t.Errorf("expected ARN in detail, got %q", result) + } + if !strings.Contains(result, "Environment") { + t.Errorf("expected tags in detail, got %q", result) + } + if !strings.Contains(result, "InstanceType") { + t.Errorf("expected raw data in detail, got %q", result) + } +} + +type mockResource struct { + id string + name string + arn string + tags map[string]string + raw any +} + +func (m *mockResource) GetID() string { return m.id } +func (m *mockResource) GetName() string { return m.name } +func (m *mockResource) GetARN() string { return m.arn } +func (m *mockResource) GetTags() map[string]string { return m.tags } +func (m *mockResource) Raw() any { return m.raw } diff --git a/internal/app/app.go b/internal/app/app.go index 67cc2a8a..19b75006 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -11,6 +11,7 @@ import ( tea "charm.land/bubbletea/v2" "charm.land/lipgloss/v2" + "github.com/clawscli/claws/internal/ai" "github.com/clawscli/claws/internal/aws" "github.com/clawscli/claws/internal/clipboard" "github.com/clawscli/claws/internal/config" @@ -331,6 +332,15 @@ func (a *App) Update(msg tea.Msg) (tea.Model, tea.Cmd) { profileSelector.Init(), a.modal.SetSize(a.width, a.height), ) + + case key.Matches(msg, a.keys.AI): + aiCtx := a.buildAIContext() + chatOverlay := view.NewChatOverlay(a.ctx, a.registry, aiCtx) + a.modal = &view.Modal{Content: chatOverlay, Width: view.ModalWidthChat} + return a, tea.Batch( + chatOverlay.Init(), + a.modal.SetSize(a.width, a.height), + ) } case view.ShowModalMsg: @@ -773,6 +783,7 @@ type keyMap struct { Command key.Binding Region key.Binding Profile key.Binding + AI key.Binding Help key.Binding Quit key.Binding } @@ -811,6 +822,10 @@ func defaultKeyMap() keyMap { key.WithKeys("P"), key.WithHelp("P", "profile"), ), + AI: key.NewBinding( + key.WithKeys("A"), + key.WithHelp("A", "ai chat"), + ), Help: key.NewBinding( key.WithKeys("?"), key.WithHelp("?", "help"), @@ -854,3 +869,88 @@ func (a *App) resolveStartupView(viewName string) view.View { return view.NewResourceBrowserWithType(a.ctx, a.registry, service, resourceType) } } + +func (a *App) buildAIContext() *ai.Context { + regions := config.Global().Regions() + selections := config.Global().Selections() + var profiles []string + for _, sel := range selections { + if id := sel.ID(); id != "" { + profiles = append(profiles, id) + } + } + + switch v := a.currentView.(type) { + case *view.ResourceBrowser: + return &ai.Context{ + Mode: ai.ContextModeList, + Service: v.Service(), + ResourceType: v.ResourceType(), + ResourceCount: v.ResourceCount(), + FilterText: v.FilterText(), + Toggles: v.ToggleStates(), + UserRegions: regions, + UserProfiles: profiles, + } + + case *view.DiffView: + return &ai.Context{ + Mode: ai.ContextModeDiff, + Service: v.Service(), + ResourceType: v.ResourceType(), + DiffLeft: buildResourceRef(v.Left()), + DiffRight: buildResourceRef(v.Right()), + UserRegions: regions, + UserProfiles: profiles, + } + + case *view.DetailView: + r := v.Resource() + if r != nil { + unwrapped := dao.UnwrapResource(r) + resourceRegion := dao.GetResourceRegion(r) + log.Debug("buildAIContext DetailView", "service", v.Service(), "resourceType", v.ResourceType(), + "id", unwrapped.GetID(), "resourceRegion", resourceRegion, "regions", regions) + ctx := &ai.Context{ + Mode: ai.ContextModeSingle, + Service: v.Service(), + ResourceType: v.ResourceType(), + ResourceID: unwrapped.GetID(), + ResourceName: unwrapped.GetName(), + ResourceRegion: resourceRegion, + ResourceProfile: dao.GetResourceProfile(r), + UserRegions: regions, + UserProfiles: profiles, + } + if v.Service() == "lambda" && v.ResourceType() == "functions" { + ctx.LogGroup = "/aws/lambda/" + unwrapped.GetName() + } + if clusterArn := dao.GetResourceClusterArn(r); clusterArn != "" { + ctx.Cluster = aws.ExtractResourceName(clusterArn) + } + return ctx + } + + case *view.LogView: + return &ai.Context{ + LogGroup: v.LogGroupName(), + UserRegions: regions, + UserProfiles: profiles, + } + } + return &ai.Context{UserRegions: regions, UserProfiles: profiles} +} + +func buildResourceRef(r dao.Resource) *ai.ResourceRef { + unwrapped := dao.UnwrapResource(r) + ref := &ai.ResourceRef{ + ID: unwrapped.GetID(), + Name: unwrapped.GetName(), + Region: dao.GetResourceRegion(r), + Profile: dao.GetResourceProfile(r), + } + if clusterArn := dao.GetResourceClusterArn(r); clusterArn != "" { + ref.Cluster = aws.ExtractResourceName(clusterArn) + } + return ref +} diff --git a/internal/config/file.go b/internal/config/file.go index d2ca68d1..bccb5a38 100644 --- a/internal/config/file.go +++ b/internal/config/file.go @@ -10,6 +10,8 @@ import ( "time" "gopkg.in/yaml.v3" + + "github.com/clawscli/claws/internal/log" ) const ( @@ -18,9 +20,11 @@ const ( DefaultTagSearchTimeout = 30 * time.Second DefaultMetricsLoadTimeout = 30 * time.Second DefaultLogFetchTimeout = 10 * time.Second + DefaultDocsSearchTimeout = 10 * time.Second DefaultMetricsWindow = 15 * time.Minute DefaultMaxConcurrentFetches = 50 DefaultMaxStackSize = 100 + DefaultAIMaxToolCallsPerQuery = 50 ) func ConfigDir() (string, error) { @@ -45,6 +49,7 @@ type TimeoutConfig struct { TagSearch Duration `yaml:"tag_search,omitempty"` MetricsLoad Duration `yaml:"metrics_load,omitempty"` LogFetch Duration `yaml:"log_fetch,omitempty"` + DocsSearch Duration `yaml:"docs_search,omitempty"` } type CloudWatchConfig struct { @@ -78,11 +83,22 @@ func (s StartupConfig) GetProfiles() []string { return nil } -// NavigationConfig controls navigation behavior. type NavigationConfig struct { MaxStackSize int `yaml:"max_stack_size,omitempty"` } +type AIConfig struct { + Profile string `yaml:"profile,omitempty"` + Region string `yaml:"region,omitempty"` + Model string `yaml:"model,omitempty"` + MaxSessions int `yaml:"max_sessions,omitempty"` + MaxTokens int `yaml:"max_tokens,omitempty"` + ThinkingBudget *int `yaml:"thinking_budget,omitempty"` + MaxToolRounds int `yaml:"max_tool_rounds,omitempty"` + MaxToolCallsPerQuery int `yaml:"max_tool_calls_per_query,omitempty"` + SaveSessions *bool `yaml:"save_sessions,omitempty"` +} + // ThemeConfig holds theme configuration. // Can be specified as: // - A preset name string: "dark", "light", "nord", "dracula", "gruvbox", "catppuccin" @@ -134,6 +150,7 @@ type FileConfig struct { Startup StartupConfig `yaml:"startup,omitempty"` Theme ThemeConfig `yaml:"theme,omitempty"` Navigation NavigationConfig `yaml:"navigation,omitempty"` + AI AIConfig `yaml:"ai,omitempty"` } // Duration wraps time.Duration for YAML marshal/unmarshal as string (e.g., "5s", "30s") @@ -172,6 +189,7 @@ func DefaultFileConfig() *FileConfig { TagSearch: Duration(DefaultTagSearchTimeout), MetricsLoad: Duration(DefaultMetricsLoadTimeout), LogFetch: Duration(DefaultLogFetchTimeout), + DocsSearch: Duration(DefaultDocsSearchTimeout), }, Concurrency: ConcurrencyConfig{ MaxFetches: DefaultMaxConcurrentFetches, @@ -240,6 +258,9 @@ func (c *FileConfig) applyDefaults() { if c.Timeouts.LogFetch <= 0 { c.Timeouts.LogFetch = Duration(DefaultLogFetchTimeout) } + if c.Timeouts.DocsSearch <= 0 { + c.Timeouts.DocsSearch = Duration(DefaultDocsSearchTimeout) + } if c.CloudWatch.Window <= 0 { c.CloudWatch.Window = Duration(DefaultMetricsWindow) } @@ -296,6 +317,15 @@ func (c *FileConfig) LogFetchTimeout() time.Duration { }) } +func (c *FileConfig) DocsSearchTimeout() time.Duration { + return withRLock(&c.mu, func() time.Duration { + if c.Timeouts.DocsSearch == 0 { + return DefaultDocsSearchTimeout + } + return c.Timeouts.DocsSearch.Duration() + }) +} + func (c *FileConfig) MaxConcurrentFetches() int { return withRLock(&c.mu, func() int { if c.Concurrency.MaxFetches == 0 { @@ -362,6 +392,92 @@ func (c *FileConfig) GetTheme() ThemeConfig { return withRLock(&c.mu, func() ThemeConfig { return c.Theme }) } +const DefaultAIModel = "global.anthropic.claude-haiku-4-5-20251001-v1:0" +const DefaultAIMaxSessions = 100 +const DefaultAIMaxTokens = 16000 +const DefaultAIThinkingBudget = 8000 +const DefaultAIMaxToolRounds = 15 + +func (c *FileConfig) GetAIProfile() string { + return withRLock(&c.mu, func() string { + return c.AI.Profile + }) +} + +func (c *FileConfig) GetAIRegion() string { + return withRLock(&c.mu, func() string { + return c.AI.Region + }) +} + +func (c *FileConfig) GetAIModel() string { + return withRLock(&c.mu, func() string { + if c.AI.Model == "" { + return DefaultAIModel + } + return c.AI.Model + }) +} + +func (c *FileConfig) GetAIMaxSessions() int { + return withRLock(&c.mu, func() int { + if c.AI.MaxSessions <= 0 { + return DefaultAIMaxSessions + } + return c.AI.MaxSessions + }) +} + +func (c *FileConfig) GetAIMaxTokens() int { + return withRLock(&c.mu, func() int { + if c.AI.MaxTokens <= 0 { + return DefaultAIMaxTokens + } + return c.AI.MaxTokens + }) +} + +func (c *FileConfig) GetAIThinkingBudget() int { + return withRLock(&c.mu, func() int { + if c.AI.ThinkingBudget == nil { + return DefaultAIThinkingBudget + } + v := *c.AI.ThinkingBudget + if v < 0 { + log.Warn("ai.thinking_budget is negative, treating as disabled", "value", v) + return 0 + } + return v + }) +} + +func (c *FileConfig) GetAIMaxToolRounds() int { + return withRLock(&c.mu, func() int { + if c.AI.MaxToolRounds <= 0 { + return DefaultAIMaxToolRounds + } + return c.AI.MaxToolRounds + }) +} + +func (c *FileConfig) GetAIMaxToolCallsPerQuery() int { + return withRLock(&c.mu, func() int { + if c.AI.MaxToolCallsPerQuery <= 0 { + return DefaultAIMaxToolCallsPerQuery + } + return c.AI.MaxToolCallsPerQuery + }) +} + +func (c *FileConfig) GetAISaveSessions() bool { + return withRLock(&c.mu, func() bool { + if c.AI.SaveSessions == nil { + return false + } + return *c.AI.SaveSessions + }) +} + func (c *FileConfig) SaveRegions(regions []string) error { if len(regions) == 0 { return nil diff --git a/internal/config/file_test.go b/internal/config/file_test.go index 9a28f417..b77f068c 100644 --- a/internal/config/file_test.go +++ b/internal/config/file_test.go @@ -667,6 +667,27 @@ func TestConcurrentSaves(t *testing.T) { } } +func TestGetAIMaxToolCallsPerQuery(t *testing.T) { + tests := []struct { + name string + config AIConfig + want int + }{ + {"default", AIConfig{}, DefaultAIMaxToolCallsPerQuery}, + {"custom", AIConfig{MaxToolCallsPerQuery: 25}, 25}, + {"zero defaults", AIConfig{MaxToolCallsPerQuery: 0}, DefaultAIMaxToolCallsPerQuery}, + {"negative defaults", AIConfig{MaxToolCallsPerQuery: -1}, DefaultAIMaxToolCallsPerQuery}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cfg := &FileConfig{AI: tt.config} + if got := cfg.GetAIMaxToolCallsPerQuery(); got != tt.want { + t.Errorf("GetAIMaxToolCallsPerQuery() = %d, want %d", got, tt.want) + } + }) + } +} + func contains(s, substr string) bool { return len(s) >= len(substr) && (s == substr || len(s) > 0 && containsHelper(s, substr)) } diff --git a/internal/dao/dao.go b/internal/dao/dao.go index 7556c7a8..06c478aa 100644 --- a/internal/dao/dao.go +++ b/internal/dao/dao.go @@ -158,6 +158,10 @@ type profiledResource interface { GetAccountID() string } +type clusterAwareResource interface { + ClusterArn() string +} + func GetResourceRegion(res Resource) string { if rr, ok := res.(regionalResource); ok { return rr.GetRegion() @@ -179,6 +183,14 @@ func GetResourceAccountID(res Resource) string { return "" } +func GetResourceClusterArn(res Resource) string { + unwrapped := UnwrapResource(res) + if cr, ok := unwrapped.(clusterAwareResource); ok { + return cr.ClusterArn() + } + return "" +} + func UnwrapResource(res Resource) Resource { if pr, ok := res.(*ProfiledResource); ok { return pr.Resource diff --git a/internal/render/render.go b/internal/render/render.go index 5194ce37..43ae68b7 100644 --- a/internal/render/render.go +++ b/internal/render/render.go @@ -71,6 +71,19 @@ type Navigator interface { Navigations(resource dao.Resource) []Navigation } +// Toggle defines a list-level toggle for filtering or view modes +type Toggle struct { + Key string // Key to press (e.g., "r") + ContextKey string // Context key for DAO filtering (e.g., "ShowResolved") + LabelOn string // Label when toggle is ON (e.g., "all") + LabelOff string // Label when toggle is OFF (e.g., "active only") +} + +// Toggler is an optional interface for renderers that support list-level toggles +type Toggler interface { + ListToggles() []Toggle +} + // MetricSpecProvider is an optional interface for renderers that support inline metrics. type MetricSpecProvider interface { MetricSpec() *MetricSpec diff --git a/internal/ui/theme.go b/internal/ui/theme.go index 098b24e9..60022708 100644 --- a/internal/ui/theme.go +++ b/internal/ui/theme.go @@ -409,6 +409,24 @@ func FaintStyle() lipgloss.Style { return lipgloss.NewStyle().Faint(true) } +// DimItalicStyle returns a dim italic style (for AI context/thinking) +func DimItalicStyle() lipgloss.Style { + return lipgloss.NewStyle().Foreground(Current().TextDim).Italic(true) +} + +// ItalicStyle returns an italic style +func ItalicStyle() lipgloss.Style { + return lipgloss.NewStyle().Italic(true) +} + +// ChatInputStyle returns a style for chat input with rounded border +func ChatInputStyle() lipgloss.Style { + return lipgloss.NewStyle(). + Border(lipgloss.RoundedBorder()). + BorderForeground(Current().Border). + Padding(0, 1) +} + func BoxStyle() lipgloss.Style { return lipgloss.NewStyle(). Border(lipgloss.RoundedBorder()). diff --git a/internal/view/chat_overlay.go b/internal/view/chat_overlay.go new file mode 100644 index 00000000..e2210016 --- /dev/null +++ b/internal/view/chat_overlay.go @@ -0,0 +1,775 @@ +package view + +import ( + "context" + "errors" + "fmt" + "strings" + "sync" + "time" + + "charm.land/bubbles/v2/textinput" + tea "charm.land/bubbletea/v2" + "charm.land/lipgloss/v2" + + "github.com/clawscli/claws/internal/ai" + "github.com/clawscli/claws/internal/config" + apperrors "github.com/clawscli/claws/internal/errors" + "github.com/clawscli/claws/internal/log" + "github.com/clawscli/claws/internal/registry" + "github.com/clawscli/claws/internal/ui" +) + +type chatStyles struct { + title lipgloss.Style + context lipgloss.Style + userMsg lipgloss.Style + assistantMsg lipgloss.Style + toolCall lipgloss.Style + toolError lipgloss.Style + thinking lipgloss.Style + input lipgloss.Style + errorMsg lipgloss.Style + mdBold lipgloss.Style + mdCode lipgloss.Style + mdItalic lipgloss.Style +} + +func newChatStyles() chatStyles { + return chatStyles{ + title: ui.TitleStyle(), + context: ui.DimItalicStyle(), + userMsg: ui.TextStyle(), + assistantMsg: ui.SecondaryStyle(), + toolCall: ui.DimStyle(), + toolError: ui.DangerStyle(), + thinking: ui.DimItalicStyle(), + input: ui.ChatInputStyle(), + errorMsg: ui.DangerStyle(), + mdBold: ui.TitleStyle(), + mdCode: ui.SuccessStyle(), + mdItalic: ui.ItalicStyle(), + } +} + +type ChatOverlay struct { + ctx context.Context + registry *registry.Registry + aiCtx *ai.Context + styles chatStyles + + client *ai.Client + executor *ai.ToolExecutor + session *ai.Session + sessMgr *ai.SessionManager + + input textinput.Model + vp ViewportState + + messages []chatMessage + streamingMsg string + streamingThinking string + collapsedThinking map[int]bool + collapsedToolCalls map[int]bool + thinkingLineRanges map[int][2]int + toolCallLineRanges map[int][2]int + isStreaming bool + err error + + // Streaming state - accumulates ContentBlocks for the current assistant turn + pendingToolUses []*ai.ToolUseContent + currentReasoning string + reasoningSignature string + streamMessages []ai.Message + toolRound int + toolCallCount int // Counts tool calls within current query (reset per query) + + width int + height int + + showingHistory bool + sessionHistory *SessionHistory + + statusMsg string + statusMsgTime time.Time + + contextExpanded bool + + // Stream cancellation - prevents goroutine leaks when overlay closes mid-stream + streamCancel context.CancelFunc + streamCancelMu sync.Mutex +} + +// chatMessage is a UI-level message for display purposes. +// It stores extracted text/thinking for rendering. +type chatMessage struct { + role ai.Role + content string + thinkingContent string + toolUse *ai.ToolUseContent + toolResult *ai.ToolResultContent + toolError bool +} + +type chatStreamMsg struct { + event ai.StreamEvent + eventCh <-chan ai.StreamEvent +} + +type chatToolExecuteMsg struct { + // The assistant message with ToolUse blocks that triggered this execution + assistantBlocks []ai.ContentBlock + toolUses []*ai.ToolUseContent + messages []ai.Message + toolRound int +} + +type chatInitMsg struct { + client *ai.Client + executor *ai.ToolExecutor + session *ai.Session + err error +} + +func NewChatOverlay(ctx context.Context, reg *registry.Registry, aiCtx *ai.Context) *ChatOverlay { + cfg := config.File() + + ti := textinput.New() + ti.Placeholder = "Ask about AWS resources..." + ti.Focus() + ti.CharLimit = 500 + + return &ChatOverlay{ + ctx: ctx, + registry: reg, + aiCtx: aiCtx, + styles: newChatStyles(), + input: ti, + sessMgr: ai.NewSessionManager(cfg.GetAIMaxSessions(), cfg.GetAISaveSessions()), + messages: []chatMessage{}, + collapsedThinking: make(map[int]bool), + collapsedToolCalls: make(map[int]bool), + } +} + +func (c *ChatOverlay) Init() tea.Cmd { + return tea.Batch( + textinput.Blink, + c.initClient, + ) +} + +func (c *ChatOverlay) initClient() tea.Msg { + executor, err := ai.NewToolExecutor(c.ctx, c.registry) + if err != nil { + return chatInitMsg{err: apperrors.Wrap(err, "init tool executor")} + } + + client, err := ai.NewClient( + c.ctx, + ai.WithModel(config.File().GetAIModel()), + ai.WithTools(executor.Tools()), + ai.WithMaxTokens(config.File().GetAIMaxTokens()), + ai.WithThinkingBudget(config.File().GetAIThinkingBudget()), + ) + if err != nil { + return chatInitMsg{err: apperrors.Wrap(err, "init ai client")} + } + + session, err := c.sessMgr.NewSession(c.aiCtx) + if err != nil { + return chatInitMsg{err: apperrors.Wrap(err, "create session")} + } + + return chatInitMsg{client: client, executor: executor, session: session} +} + +func (c *ChatOverlay) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + if c.showingHistory { + return c.handleHistoryUpdate(msg) + } + + switch msg := msg.(type) { + case chatInitMsg: + if msg.err != nil { + c.err = msg.err + } else { + c.client = msg.client + c.executor = msg.executor + c.session = msg.session + } + return c, nil + + case tea.KeyPressMsg: + return c.handleKeyPress(msg) + + case chatStreamMsg: + return c.handleStreamEvent(msg) + + case chatToolExecuteMsg: + return c.handleToolExecute(msg) + + case tea.MouseClickMsg: + return c.handleMouseClick(msg) + } + + var cmds []tea.Cmd + + if c.vp.Ready { + var vpCmd tea.Cmd + c.vp.Model, vpCmd = c.vp.Model.Update(msg) + cmds = append(cmds, vpCmd) + } + + var inputCmd tea.Cmd + c.input, inputCmd = c.input.Update(msg) + cmds = append(cmds, inputCmd) + + return c, tea.Batch(cmds...) +} + +func (c *ChatOverlay) cancelStream() { + c.streamCancelMu.Lock() + defer c.streamCancelMu.Unlock() + if c.streamCancel != nil { + c.streamCancel() + c.streamCancel = nil + } +} + +func (c *ChatOverlay) handleKeyPress(msg tea.KeyPressMsg) (tea.Model, tea.Cmd) { + if IsEscKey(msg) { + c.cancelStream() + return c, func() tea.Msg { return HideModalMsg{} } + } + + switch msg.String() { + case "ctrl+c": + c.cancelStream() + return c, func() tea.Msg { return HideModalMsg{} } + case "ctrl+h": + return c.showHistory() + case "enter": + if c.isStreaming { + return c, nil + } + + text := strings.TrimSpace(c.input.Value()) + if text == "" { + return c, nil + } + + c.input.SetValue("") + c.messages = append(c.messages, chatMessage{role: ai.RoleUser, content: text}) + c.isStreaming = true + c.streamingMsg = "" + c.streamingThinking = "" + c.pendingToolUses = nil + c.currentReasoning = "" + c.reasoningSignature = "" + c.toolRound = 0 + c.toolCallCount = 0 // Reset per-query tool call counter + c.err = nil + c.updateViewport() + + userMsg := ai.NewUserMessage(text) + c.streamMessages = append(c.streamMessages, userMsg) + if c.session != nil { + if err := c.sessMgr.AddMessage(c.session, userMsg); err != nil { + log.Warn("failed to save user message", "error", err) + c.statusMsg = "Failed to save message" + c.statusMsgTime = time.Now() + } + } + return c, c.startStream(c.streamMessages) + } + + var kpCmd tea.Cmd + c.input, kpCmd = c.input.Update(msg) + return c, kpCmd +} + +func (c *ChatOverlay) handleMouseClick(msg tea.MouseClickMsg) (tea.Model, tea.Cmd) { + if c.aiCtx != nil && c.aiCtx.Service != "" && msg.Y == 1 { + c.contextExpanded = !c.contextExpanded + c.updateViewport() + return c, nil + } + + if !c.vp.Ready { + return c, nil + } + + headerHeight := c.headerHeight() + + contentLine := msg.Y - headerHeight + c.vp.Model.YOffset() + if contentLine < 0 { + return c, nil + } + + for msgIdx, lineRange := range c.thinkingLineRanges { + if contentLine >= lineRange[0] && contentLine < lineRange[1] { + wasCollapsed := c.collapsedThinking[msgIdx] + c.collapsedThinking[msgIdx] = !wasCollapsed + c.scrollToCollapsible(lineRange[0], wasCollapsed) + return c, nil + } + } + + for msgIdx, lineRange := range c.toolCallLineRanges { + if contentLine >= lineRange[0] && contentLine < lineRange[1] { + wasCollapsed := c.collapsedToolCalls[msgIdx] + c.collapsedToolCalls[msgIdx] = !wasCollapsed + c.scrollToCollapsible(lineRange[0], wasCollapsed) + return c, nil + } + } + + return c, nil +} + +func (c *ChatOverlay) startStream(messages []ai.Message) tea.Cmd { + c.cancelStream() + streamCtx, cancel := context.WithCancel(c.ctx) + + c.streamCancelMu.Lock() + c.streamCancel = cancel + c.streamCancelMu.Unlock() + + return func() tea.Msg { + if c.client == nil || c.executor == nil { + return chatStreamMsg{event: ai.StreamEvent{Type: "error", Error: errors.New("client not initialized")}} + } + + systemPrompt := c.buildSystemPrompt() + + eventCh, err := c.client.ConverseStream(streamCtx, messages, systemPrompt) + if err != nil { + return chatStreamMsg{event: ai.StreamEvent{Type: "error", Error: err}} + } + + event, ok := <-eventCh + if !ok { + return chatStreamMsg{event: ai.StreamEvent{Type: "done"}} + } + return chatStreamMsg{event: event, eventCh: eventCh} + } +} + +func (c *ChatOverlay) waitForStream(eventCh <-chan ai.StreamEvent) tea.Cmd { + return func() tea.Msg { + event, ok := <-eventCh + if !ok { + return chatStreamMsg{event: ai.StreamEvent{Type: "done"}} + } + return chatStreamMsg{event: event, eventCh: eventCh} + } +} + +func (c *ChatOverlay) handleStreamEvent(msg chatStreamMsg) (tea.Model, tea.Cmd) { + event := msg.event + + switch event.Type { + case "text": + c.streamingMsg += event.Text + c.updateViewport() + return c, c.waitForStream(msg.eventCh) + + case "thinking": + if event.Thinking != nil { + c.streamingThinking += event.Thinking.Text + } + c.updateViewport() + return c, c.waitForStream(msg.eventCh) + + case "thinking_complete": + // Capture the complete thinking with signature for API replay + if event.Thinking != nil { + c.currentReasoning = event.Thinking.Text + c.reasoningSignature = event.Thinking.Signature + } + return c, c.waitForStream(msg.eventCh) + + case "tool_use": + if event.ToolUse != nil { + c.pendingToolUses = append(c.pendingToolUses, event.ToolUse) + } + return c, c.waitForStream(msg.eventCh) + + case "done": + return c.handleStreamDone(msg.eventCh) + + case "error": + c.err = event.Error + c.isStreaming = false + c.updateViewport() + return c, nil + } + + return c, c.waitForStream(msg.eventCh) +} + +func (c *ChatOverlay) handleStreamDone(_ <-chan ai.StreamEvent) (tea.Model, tea.Cmd) { + // Build the assistant's ContentBlocks from accumulated state + var assistantBlocks []ai.ContentBlock + + // Add reasoning block if present + if c.currentReasoning != "" { + assistantBlocks = append(assistantBlocks, ai.ContentBlock{ + Reasoning: c.currentReasoning, + ReasoningSignature: c.reasoningSignature, + }) + } + + // Add text block if present + if c.streamingMsg != "" { + assistantBlocks = append(assistantBlocks, ai.ContentBlock{Text: c.streamingMsg}) + } + + // Add tool use blocks + for _, tu := range c.pendingToolUses { + assistantBlocks = append(assistantBlocks, ai.ContentBlock{ToolUse: tu}) + } + + // Save to UI messages for display + if c.streamingMsg != "" || c.streamingThinking != "" { + c.messages = append(c.messages, chatMessage{ + role: ai.RoleAssistant, + content: c.streamingMsg, + thinkingContent: c.streamingThinking, + }) + if c.streamingThinking != "" { + c.collapsedThinking[len(c.messages)-1] = true + } + } + + // If there are tool uses, execute them + if len(c.pendingToolUses) > 0 && c.toolRound < config.File().GetAIMaxToolRounds() { + c.updateViewport() + + // Save assistant message with tool uses to session + if c.session != nil && len(assistantBlocks) > 0 { + assistantMsg := ai.Message{ + Role: ai.RoleAssistant, + Content: assistantBlocks, + } + c.streamMessages = append(c.streamMessages, assistantMsg) + if err := c.sessMgr.AddMessage(c.session, assistantMsg); err != nil { + log.Warn("failed to save assistant message with tool uses", "error", err) + } + } + + // Clear streaming state before tool execution + toolUses := c.pendingToolUses + c.pendingToolUses = nil + c.streamingMsg = "" + c.streamingThinking = "" + c.currentReasoning = "" + c.reasoningSignature = "" + c.toolRound++ + + return c, func() tea.Msg { + return chatToolExecuteMsg{ + assistantBlocks: assistantBlocks, + toolUses: toolUses, + messages: c.streamMessages, + toolRound: c.toolRound, + } + } + } + + // No tool uses or max rounds reached - done + if len(assistantBlocks) > 0 { + assistantMsg := ai.Message{ + Role: ai.RoleAssistant, + Content: assistantBlocks, + } + c.streamMessages = append(c.streamMessages, assistantMsg) + if c.session != nil { + if err := c.sessMgr.AddMessage(c.session, assistantMsg); err != nil { + log.Warn("failed to save assistant message", "error", err) + c.statusMsg = "Failed to save message" + c.statusMsgTime = time.Now() + } + } + } + + if len(c.pendingToolUses) > 0 && c.toolRound >= config.File().GetAIMaxToolRounds() { + c.messages = append(c.messages, chatMessage{ + role: ai.RoleAssistant, + content: "(tool limit reached)", + }) + } + + c.streamingMsg = "" + c.streamingThinking = "" + c.currentReasoning = "" + c.reasoningSignature = "" + c.pendingToolUses = nil + c.isStreaming = false + c.updateViewport() + return c, nil +} + +func (c *ChatOverlay) handleToolExecute(msg chatToolExecuteMsg) (tea.Model, tea.Cmd) { + maxCalls := config.File().GetAIMaxToolCallsPerQuery() + + // Execute each tool and collect results + var toolResults []ai.ToolResultContent + for _, tu := range msg.toolUses { + // Check tool call limit before executing each tool + if c.toolCallCount >= maxCalls { + c.err = fmt.Errorf("Tool call limit reached (%d calls). Start new query to continue.", maxCalls) + c.isStreaming = false + c.updateViewport() + return c, nil + } + + result := c.executor.Execute(c.ctx, tu) + toolResults = append(toolResults, result) + c.toolCallCount++ + + c.messages = append(c.messages, chatMessage{ + content: result.Content, + toolUse: tu, + toolResult: &result, + toolError: result.IsError, + }) + c.collapsedToolCalls[len(c.messages)-1] = true + } + c.updateViewport() + + // Build the new messages to send to API: + // 1. Previous messages (including assistant message with tool uses from handleStreamDone) + // 2. User message with tool results + + messages := make([]ai.Message, len(msg.messages), len(msg.messages)+1) + copy(messages, msg.messages) + + // Add user message with tool results + var resultBlocks []ai.ContentBlock + for _, tr := range toolResults { + resultBlocks = append(resultBlocks, ai.ContentBlock{ToolResult: &tr}) + } + messages = append(messages, ai.Message{ + Role: ai.RoleUser, + Content: resultBlocks, + }) + + c.streamMessages = messages + c.isStreaming = true + + // Save tool result message to session + if c.session != nil { + toolResultMsg := messages[len(messages)-1] // Last message with tool results + if err := c.sessMgr.AddMessage(c.session, toolResultMsg); err != nil { + log.Warn("failed to save tool result message", "error", err) + } + } + + return c, c.startStream(messages) +} + +func (c *ChatOverlay) View() tea.View { + return tea.NewView(c.ViewString()) +} + +func (c *ChatOverlay) ViewString() string { + if c.showingHistory && c.sessionHistory != nil { + return c.sessionHistory.ViewString() + } + + var sb strings.Builder + + title := c.styles.title.Render("AI Chat") + hint := c.styles.context.Render("Ctrl+h: history") + titleWidth := lipgloss.Width(title) + hintWidth := lipgloss.Width(hint) + padding := c.width - titleWidth - hintWidth + if padding < 1 { + padding = 1 + } + sb.WriteString(title + strings.Repeat(" ", padding) + hint) + sb.WriteString("\n") + + if c.aiCtx != nil && c.aiCtx.Service != "" { + indicator := "▶" + if c.contextExpanded { + indicator = "▼" + } + ctx := fmt.Sprintf("Context: %s", c.aiCtx.Service) + if c.aiCtx.ResourceType != "" { + ctx += "/" + c.aiCtx.ResourceType + } + if c.aiCtx.ResourceName != "" { + ctx += " - " + c.aiCtx.ResourceName + } + ctx += " [" + indicator + "]" + sb.WriteString(c.styles.context.Render(ctx)) + sb.WriteString("\n") + } + sb.WriteString("\n") + + if c.vp.Ready { + sb.WriteString(c.vp.Model.View()) + } else { + sb.WriteString(c.renderMessages()) + } + + sb.WriteString("\n") + sb.WriteString(c.styles.input.Render(c.input.View())) + + return sb.String() +} + +func (c *ChatOverlay) SetSize(width, height int) tea.Cmd { + c.width = width + c.height = height + + vpHeight := height - 8 + if vpHeight < 5 { + vpHeight = 5 + } + + c.vp.SetSize(width, vpHeight) + c.updateViewport() + + return nil +} + +func (c *ChatOverlay) StatusLine() string { + if c.statusMsg != "" && time.Since(c.statusMsgTime) < 3*time.Second { + return c.statusMsg + } + return "AI Chat | Enter: send | Esc: close" +} + +func (c *ChatOverlay) headerHeight() int { + lines := 2 + if c.aiCtx != nil && c.aiCtx.Service != "" { + ctx := fmt.Sprintf("Context: %s", c.aiCtx.Service) + if c.aiCtx.ResourceType != "" { + ctx += "/" + c.aiCtx.ResourceType + } + if c.aiCtx.ResourceName != "" { + ctx += " - " + c.aiCtx.ResourceName + } + rendered := c.styles.context.Render(ctx) + lines += strings.Count(rendered, "\n") + 1 + } + return lines +} + +func (c *ChatOverlay) HasActiveInput() bool { + return true +} + +func (c *ChatOverlay) scrollToCollapsible(startLine int, wasCollapsed bool) { + if !c.vp.Ready { + return + } + content := c.renderMessages() + c.vp.Model.SetContent(content) + if wasCollapsed { + c.vp.Model.SetYOffset(startLine) + } +} + +func (c *ChatOverlay) showHistory() (tea.Model, tea.Cmd) { + sessions, _ := c.sessMgr.ListSessions() + currentID := "" + if c.session != nil { + currentID = c.session.ID + } + c.sessionHistory = NewSessionHistory(sessions, currentID) + c.sessionHistory.SetSize(c.width, c.height) + c.showingHistory = true + return c, nil +} + +func (c *ChatOverlay) handleHistoryUpdate(msg tea.Msg) (tea.Model, tea.Cmd) { + switch msg := msg.(type) { + case SessionSelectedMsg: + c.showingHistory = false + c.sessionHistory = nil + if msg.Session != nil { + return c.loadSession(msg.Session) + } + return c, nil + + case NewSessionMsg: + c.showingHistory = false + c.sessionHistory = nil + return c.newSession() + + case CloseHistoryMsg: + c.showingHistory = false + c.sessionHistory = nil + return c, nil + } + + if c.sessionHistory != nil { + model, cmd := c.sessionHistory.Update(msg) + if sh, ok := model.(*SessionHistory); ok { + c.sessionHistory = sh + } + return c, cmd + } + return c, nil +} + +func (c *ChatOverlay) loadSession(sess *ai.Session) (tea.Model, tea.Cmd) { + if sess == nil { + return c, nil + } + + c.cancelStream() + if c.isStreaming { + c.isStreaming = false + c.streamingMsg = "" + c.streamingThinking = "" + c.pendingToolUses = nil + c.currentReasoning = "" + c.reasoningSignature = "" + } + + c.session = sess + c.messages = []chatMessage{} + c.streamMessages = []ai.Message{} + c.collapsedThinking = make(map[int]bool) + c.collapsedToolCalls = make(map[int]bool) + c.toolCallCount = 0 // Reset per-query counter + + for _, msg := range sess.Messages { + cm := chatMessage{role: msg.Role} + for _, block := range msg.Content { + if block.Text != "" { + cm.content = block.Text + } + if block.Reasoning != "" { + cm.thinkingContent = block.Reasoning + } + } + c.messages = append(c.messages, cm) + c.streamMessages = append(c.streamMessages, msg) + } + + c.updateViewport() + return c, nil +} + +func (c *ChatOverlay) newSession() (tea.Model, tea.Cmd) { + session, err := c.sessMgr.NewSession(c.aiCtx) + if err != nil { + c.err = err + return c, nil + } + c.session = session + c.messages = []chatMessage{} + c.streamMessages = []ai.Message{} + c.collapsedThinking = make(map[int]bool) + c.collapsedToolCalls = make(map[int]bool) + c.toolCallCount = 0 // Reset per-query counter + c.updateViewport() + return c, nil +} diff --git a/internal/view/chat_overlay_prompt.go b/internal/view/chat_overlay_prompt.go new file mode 100644 index 00000000..2e7d1eb9 --- /dev/null +++ b/internal/view/chat_overlay_prompt.go @@ -0,0 +1,230 @@ +package view + +import ( + "fmt" + "strings" + + "github.com/clawscli/claws/internal/ai" + "github.com/clawscli/claws/internal/config" +) + +// formatProfileName converts internal profile ID to display name +func formatProfileName(profileID string) string { + sel := config.ProfileSelectionFromID(profileID) + if sel.Mode == config.ModeNamedProfile { + return sel.ProfileName + } + return sel.Mode.String() +} + +func (c *ChatOverlay) buildSystemPrompt() string { + services := c.registry.ListServices() + serviceList := strings.Join(services, ", ") + + prompt := fmt.Sprintf(`You are an AWS resource assistant in claws TUI. + + +%s + + + +When a user asks about AWS resources, you MUST call the appropriate tool. Do not just describe what you would do - actually call the tool. +Use ONLY the service names listed in available_services above. Do not guess or use similar names. +All resource tools require a region parameter. Use profile parameter when querying cross-profile resources. + +Available tools: +- list_resources(service): Lists resource types for a service +- query_resources(service, resource_type, region, profile?, limit?, offset?): Lists resources (default: 100, max: 2000, supports pagination) +- get_resource_detail(service, resource_type, region, id, cluster?, profile?): Gets resource details +- tail_logs(service, resource_type, region, id, cluster?, profile?): Fetches CloudWatch logs for a resource + - Supported: lambda/functions, ecs/services, ecs/tasks, ecs/task-definitions, codebuild/projects, codebuild/builds, cloudtrail/trails, apigateway/stages, apigateway/stages-v2, stepfunctions/state-machines + - cluster parameter required for ecs/services and ecs/tasks +- search_aws_docs(query): Search AWS documentation + + + +Be concise. Use markdown for formatting. +`, serviceList) + + if c.aiCtx != nil { + if len(c.aiCtx.UserRegions) > 0 { + prompt += "\n\n" + prompt += strings.Join(c.aiCtx.UserRegions, ", ") + prompt += "\nThese are ALL regions the user is currently browsing." + prompt += "\nIn list mode, query resources across ALL these regions (call query_resources for each)." + prompt += "\nFor specific resources (detail/diff mode), use the region from current_context instead." + prompt += "\n" + } + + if len(c.aiCtx.UserProfiles) > 0 { + prompt += "\n\n" + prompt += strings.Join(c.aiCtx.UserProfiles, ", ") + prompt += "\nThese are ALL profile IDs the user is currently browsing." + prompt += "\nIn list mode, query resources across ALL these profiles (call query_resources for each)." + prompt += "\nFor specific resources (detail/diff mode), use the profile from current_context instead." + prompt += "\n" + } + + switch c.aiCtx.Mode { + case ai.ContextModeList: + prompt += c.buildListContextPrompt() + case ai.ContextModeDiff: + prompt += c.buildDiffContextPrompt() + default: + prompt += c.buildSingleContextPrompt() + } + } + + return prompt +} + +func (c *ChatOverlay) buildListContextPrompt() string { + ctx := c.aiCtx + if ctx.Service == "" { + return "" + } + + prompt := fmt.Sprintf("\n\nservice=%s, resource_type=%s", ctx.Service, ctx.ResourceType) + prompt += fmt.Sprintf(", count=%d", ctx.ResourceCount) + if ctx.FilterText != "" { + prompt += fmt.Sprintf(", filter=\"%s\"", ctx.FilterText) + } + if ctx.Service == "securityhub" && ctx.ResourceType == "findings" { + if ctx.Toggles["ShowResolved"] { + prompt += ", show_resolved=true" + } else { + prompt += ", show_resolved=false (use include_resolved=true in query_resources for all)" + } + } + prompt += "\n" + prompt += "\nIMPORTANT: When the user asks to list or analyze resources, call query_resources for EACH combination of user_selected_regions and user_selected_profiles to get the complete view across all selected contexts." + return prompt +} + +func (c *ChatOverlay) buildDiffContextPrompt() string { + ctx := c.aiCtx + if ctx.DiffLeft == nil || ctx.DiffRight == nil { + return "" + } + + prompt := fmt.Sprintf("\n\nservice=%s, resource_type=%s", ctx.Service, ctx.ResourceType) + prompt += fmt.Sprintf("\nleft: id=%s, name=%s", ctx.DiffLeft.ID, ctx.DiffLeft.Name) + if ctx.DiffLeft.Region != "" { + prompt += fmt.Sprintf(", region=%s", ctx.DiffLeft.Region) + } + if ctx.DiffLeft.Profile != "" { + prompt += fmt.Sprintf(", profile=%s", ctx.DiffLeft.Profile) + } + if ctx.DiffLeft.Cluster != "" { + prompt += fmt.Sprintf(", cluster=%s", ctx.DiffLeft.Cluster) + } + prompt += fmt.Sprintf("\nright: id=%s, name=%s", ctx.DiffRight.ID, ctx.DiffRight.Name) + if ctx.DiffRight.Region != "" { + prompt += fmt.Sprintf(", region=%s", ctx.DiffRight.Region) + } + if ctx.DiffRight.Profile != "" { + prompt += fmt.Sprintf(", profile=%s", ctx.DiffRight.Profile) + } + if ctx.DiffRight.Cluster != "" { + prompt += fmt.Sprintf(", cluster=%s", ctx.DiffRight.Cluster) + } + prompt += "\n" + prompt += "\nIMPORTANT: Call get_resource_detail twice (once for left, once for right) using each resource's specific region and profile." + return prompt +} + +func (c *ChatOverlay) buildSingleContextPrompt() string { + ctx := c.aiCtx + if ctx.Service == "" { + return "" + } + + prompt := fmt.Sprintf("\nservice=%s", ctx.Service) + if ctx.ResourceType != "" { + prompt += ", resource_type=" + ctx.ResourceType + } + if ctx.ResourceRegion != "" { + prompt += ", region=" + ctx.ResourceRegion + } + if ctx.ResourceID != "" { + prompt += ", id=" + ctx.ResourceID + } + if ctx.ResourceProfile != "" { + prompt += ", profile=" + ctx.ResourceProfile + } + if ctx.Cluster != "" { + prompt += ", cluster=" + ctx.Cluster + } + prompt += "" + prompt += "\nIMPORTANT: Use the region and profile from current_context when querying this resource." + return prompt +} + +func (c *ChatOverlay) renderContextParams() string { + ctx := c.aiCtx + if ctx == nil { + return "" + } + + var lines []string + lines = append(lines, fmt.Sprintf(" mode: %s", ctx.Mode)) + + if len(ctx.UserRegions) > 0 { + lines = append(lines, fmt.Sprintf(" regions: %s", strings.Join(ctx.UserRegions, ", "))) + } + if len(ctx.UserProfiles) > 0 { + var profileNames []string + for _, pid := range ctx.UserProfiles { + profileNames = append(profileNames, formatProfileName(pid)) + } + lines = append(lines, fmt.Sprintf(" profiles: %s", strings.Join(profileNames, ", "))) + } + if ctx.ResourceCount > 0 { + lines = append(lines, fmt.Sprintf(" count: %d", ctx.ResourceCount)) + } + if ctx.FilterText != "" { + lines = append(lines, fmt.Sprintf(" filter: %s", ctx.FilterText)) + } + if ctx.ResourceID != "" { + lines = append(lines, fmt.Sprintf(" id: %s", ctx.ResourceID)) + } + if ctx.ResourceRegion != "" { + lines = append(lines, fmt.Sprintf(" region: %s", ctx.ResourceRegion)) + } + if ctx.ResourceProfile != "" { + profileName := formatProfileName(ctx.ResourceProfile) + lines = append(lines, fmt.Sprintf(" profile: %s", profileName)) + } + if ctx.Cluster != "" { + lines = append(lines, fmt.Sprintf(" cluster: %s", ctx.Cluster)) + } + if ctx.DiffLeft != nil && ctx.DiffRight != nil { + left := fmt.Sprintf("%s/%s", ctx.DiffLeft.ID, ctx.DiffLeft.Name) + if ctx.DiffLeft.Profile != "" { + profileName := formatProfileName(ctx.DiffLeft.Profile) + left += fmt.Sprintf(" [%s]", profileName) + } + if ctx.DiffLeft.Region != "" { + left += fmt.Sprintf(" (%s)", ctx.DiffLeft.Region) + } + right := fmt.Sprintf("%s/%s", ctx.DiffRight.ID, ctx.DiffRight.Name) + if ctx.DiffRight.Profile != "" { + profileName := formatProfileName(ctx.DiffRight.Profile) + right += fmt.Sprintf(" [%s]", profileName) + } + if ctx.DiffRight.Region != "" { + right += fmt.Sprintf(" (%s)", ctx.DiffRight.Region) + } + lines = append(lines, fmt.Sprintf(" left: %s", left)) + lines = append(lines, fmt.Sprintf(" right: %s", right)) + } + if ctx.Service == "securityhub" && ctx.ResourceType == "findings" { + showResolved := "false" + if ctx.Toggles["ShowResolved"] { + showResolved = "true" + } + lines = append(lines, fmt.Sprintf(" show_resolved: %s", showResolved)) + } + + return strings.Join(lines, "\n") + "\n" +} diff --git a/internal/view/chat_overlay_render.go b/internal/view/chat_overlay_render.go new file mode 100644 index 00000000..2ef3a588 --- /dev/null +++ b/internal/view/chat_overlay_render.go @@ -0,0 +1,237 @@ +package view + +import ( + "fmt" + "regexp" + "sort" + "strings" + + "github.com/mattn/go-runewidth" + + "github.com/clawscli/claws/internal/ai" +) + +func (c *ChatOverlay) updateViewport() { + if !c.vp.Ready { + return + } + content := c.renderMessages() + c.vp.Model.SetContent(content) + c.vp.Model.GotoBottom() +} + +func (c *ChatOverlay) renderMessages() string { + var sb strings.Builder + w := c.wrapWidth() + lineNum := 0 + c.thinkingLineRanges = make(map[int][2]int) + c.toolCallLineRanges = make(map[int][2]int) + + if c.contextExpanded && c.aiCtx != nil { + params := c.renderContextParams() + for _, line := range strings.Split(strings.TrimSuffix(params, "\n"), "\n") { + sb.WriteString(c.styles.context.Render(line)) + sb.WriteString("\n") + lineNum++ + } + sb.WriteString("\n") + lineNum++ + } + + for i, msg := range c.messages { + if msg.toolUse != nil { + startLine := lineNum + toolStr := c.renderToolCall(i, msg.toolUse, msg.toolError, w) + sb.WriteString(toolStr) + lineNum += strings.Count(toolStr, "\n") + c.toolCallLineRanges[i] = [2]int{startLine, lineNum} + } else { + switch msg.role { + case ai.RoleUser: + userText := c.styles.userMsg.Render(wrapText("You: "+msg.content, w)) + sb.WriteString(userText) + sb.WriteString("\n") + lineNum += strings.Count(userText, "\n") + 1 + case ai.RoleAssistant: + if msg.thinkingContent != "" { + startLine := lineNum + thinkingStr := c.renderThinking(i, msg.thinkingContent, w) + sb.WriteString(thinkingStr) + lineNum += strings.Count(thinkingStr, "\n") + c.thinkingLineRanges[i] = [2]int{startLine, lineNum} + if msg.content != "" { + sb.WriteString("\n") + lineNum++ + } + } + if msg.content != "" { + rendered := c.renderMarkdown(msg.content, w) + contentStr := c.styles.assistantMsg.Render("AI: ") + "\n" + rendered + sb.WriteString(contentStr) + sb.WriteString("\n") + lineNum += strings.Count(contentStr, "\n") + 1 + } + } + } + sb.WriteString("\n") + lineNum++ + } + + if c.streamingThinking != "" { + sb.WriteString(c.styles.thinking.Render("💭 ▶ Thinking...")) + sb.WriteString("\n") + if c.streamingMsg != "" { + sb.WriteString("\n") + } + } + if c.streamingMsg != "" { + sb.WriteString(c.styles.assistantMsg.Render("AI: ")) + sb.WriteString("\n") + sb.WriteString(wrapText(c.streamingMsg, w)) + sb.WriteString("\n") + } else if c.isStreaming && c.streamingThinking == "" { + sb.WriteString(c.styles.thinking.Render("⏳ Waiting...")) + sb.WriteString("\n") + } + + if c.err != nil { + sb.WriteString(c.styles.errorMsg.Render(wrapText("Error: "+c.err.Error(), w))) + sb.WriteString("\n") + } + + return sb.String() +} + +func (c *ChatOverlay) renderThinking(idx int, content string, width int) string { + collapsed := c.collapsedThinking[idx] + var sb strings.Builder + + if collapsed { + sb.WriteString(c.styles.thinking.Render("💭 ▶ [click to expand]")) + sb.WriteString("\n") + } else { + sb.WriteString(c.styles.thinking.Render("💭 ▼ Thinking:")) + sb.WriteString("\n") + wrapped := wrapText(content, width-2) + for _, line := range strings.Split(wrapped, "\n") { + sb.WriteString(c.styles.thinking.Render(" " + line)) + sb.WriteString("\n") + } + } + return sb.String() +} + +func (c *ChatOverlay) renderToolCall(idx int, tu *ai.ToolUseContent, isError bool, width int) string { + collapsed := c.collapsedToolCalls[idx] + style := c.styles.toolCall + if isError { + style = c.styles.toolError + } + + var sb strings.Builder + paramCount := len(tu.Input) + + if collapsed { + summary := fmt.Sprintf("🔧 %s ▶ [%d params]", tu.Name, paramCount) + sb.WriteString(style.Render(wrapText(summary, width))) + sb.WriteString("\n") + } else { + header := fmt.Sprintf("🔧 %s ▼", tu.Name) + sb.WriteString(style.Render(header)) + sb.WriteString("\n") + + keys := make([]string, 0, len(tu.Input)) + for k := range tu.Input { + keys = append(keys, k) + } + sort.Strings(keys) + + for _, k := range keys { + v := tu.Input[k] + line := fmt.Sprintf(" %s: %v", k, v) + sb.WriteString(style.Render(wrapText(line, width))) + sb.WriteString("\n") + } + } + return sb.String() +} + +func (c *ChatOverlay) wrapWidth() int { + if c.width > 4 { + return c.width - 4 + } + return 76 +} + +var ( + mdBold = regexp.MustCompile(`\*\*([^*]+)\*\*`) + mdItalic = regexp.MustCompile(`\*([^*]+)\*`) + mdCode = regexp.MustCompile("`([^`]+)`") +) + +func (c *ChatOverlay) renderMarkdown(text string, width int) string { + wrapped := wrapText(text, width) + + wrapped = mdBold.ReplaceAllStringFunc(wrapped, func(m string) string { + inner := mdBold.FindStringSubmatch(m)[1] + return c.styles.mdBold.Render(inner) + }) + wrapped = mdCode.ReplaceAllStringFunc(wrapped, func(m string) string { + inner := mdCode.FindStringSubmatch(m)[1] + return c.styles.mdCode.Render(inner) + }) + wrapped = mdItalic.ReplaceAllStringFunc(wrapped, func(m string) string { + inner := mdItalic.FindStringSubmatch(m)[1] + return c.styles.mdItalic.Render(inner) + }) + + return wrapped +} + +func wrapText(text string, width int) string { + if width <= 0 { + width = 76 + } + var lines []string + for _, line := range strings.Split(text, "\n") { + lines = append(lines, wrapLine(line, width)...) + } + return strings.Join(lines, "\n") +} + +func wrapLine(line string, width int) []string { + if len(line) == 0 { + return []string{""} + } + runes := []rune(line) + lineWidth := 0 + for _, r := range runes { + lineWidth += runeWidth(r) + } + if lineWidth <= width { + return []string{line} + } + + var lines []string + var current []rune + currentWidth := 0 + + for _, r := range runes { + rw := runeWidth(r) + if currentWidth+rw > width && len(current) > 0 { + lines = append(lines, string(current)) + current = nil + currentWidth = 0 + } + current = append(current, r) + currentWidth += rw + } + if len(current) > 0 { + lines = append(lines, string(current)) + } + return lines +} + +func runeWidth(r rune) int { + return runewidth.RuneWidth(r) +} diff --git a/internal/view/detail_view.go b/internal/view/detail_view.go index b7694368..2b1d1a65 100644 --- a/internal/view/detail_view.go +++ b/internal/view/detail_view.go @@ -225,7 +225,6 @@ func (d *DetailView) SetSize(width, height int) tea.Cmd { return nil } -// StatusLine implements View func (d *DetailView) StatusLine() string { parts := []string{d.resource.GetID()} @@ -251,6 +250,18 @@ func (d *DetailView) StatusLine() string { return strings.Join(parts, " • ") } +func (d *DetailView) Resource() dao.Resource { + return d.resource +} + +func (d *DetailView) Service() string { + return d.service +} + +func (d *DetailView) ResourceType() string { + return d.resType +} + // getNavigationShortcuts returns a string of navigation shortcuts for the current resource func (d *DetailView) getNavigationShortcuts() string { if d.renderer == nil { @@ -309,19 +320,3 @@ func (d *DetailView) renderGenericDetail() string { return out } - -// mergeResources merges the refreshed resource with the original to preserve -// fields that are only available from List() but not from Get(). -func mergeResources(original, refreshed dao.Resource) dao.Resource { - if original == nil { - return refreshed - } - if refreshed == nil { - return original - } - // If refreshed resource implements Mergeable, let it copy fields from original - if m, ok := refreshed.(dao.Mergeable); ok { - m.MergeFrom(original) - } - return refreshed -} diff --git a/internal/view/diff_view.go b/internal/view/diff_view.go index 90abe26b..99f66273 100644 --- a/internal/view/diff_view.go +++ b/internal/view/diff_view.go @@ -108,7 +108,7 @@ func (d *DiffView) SetSize(width, height int) tea.Cmd { // StatusLine implements View func (d *DiffView) StatusLine() string { - return d.left.GetName() + " vs " + d.right.GetName() + " • ↑/↓:scroll • q/esc:back" + return dao.UnwrapResource(d.left).GetName() + " vs " + dao.UnwrapResource(d.right).GetName() + " • ↑/↓:scroll • q/esc:back" } // renderSideBySide generates the side-by-side view @@ -124,8 +124,8 @@ func (d *DiffView) renderSideBySide() string { leftDetail := "" rightDetail := "" if d.renderer != nil { - leftDetail = d.renderer.RenderDetail(d.left) - rightDetail = d.renderer.RenderDetail(d.right) + leftDetail = d.renderer.RenderDetail(dao.UnwrapResource(d.left)) + rightDetail = d.renderer.RenderDetail(dao.UnwrapResource(d.right)) } // Split into lines @@ -136,8 +136,8 @@ func (d *DiffView) renderSideBySide() string { colWidth := (d.width - 3) / 2 // Column headers - leftHeader := TruncateOrPadString("◀ "+d.left.GetName(), colWidth) - rightHeader := TruncateOrPadString(d.right.GetName()+" ▶", colWidth) + leftHeader := TruncateOrPadString("◀ "+dao.UnwrapResource(d.left).GetName(), colWidth) + rightHeader := TruncateOrPadString(dao.UnwrapResource(d.right).GetName()+" ▶", colWidth) out.WriteString(s.header.Render(leftHeader)) out.WriteString(s.separator.Render(" │ ")) out.WriteString(s.header.Render(rightHeader)) @@ -169,3 +169,8 @@ func (d *DiffView) renderSideBySide() string { return out.String() } + +func (d *DiffView) Left() dao.Resource { return d.left } +func (d *DiffView) Right() dao.Resource { return d.right } +func (d *DiffView) Service() string { return d.service } +func (d *DiffView) ResourceType() string { return d.resourceType } diff --git a/internal/view/log_view.go b/internal/view/log_view.go index b4d69a9b..90253f93 100644 --- a/internal/view/log_view.go +++ b/internal/view/log_view.go @@ -351,7 +351,7 @@ func (v *LogView) Update(msg tea.Msg) (tea.Model, tea.Cmd) { v.updateViewportContent() v.SetSize(v.width, v.height) // Recalculate viewport height } - return v, nil + return v, tea.ClearScreen } v.logs = v.logs[:0] v.oldestEventTime = 0 @@ -437,7 +437,7 @@ func (v *LogView) handleFilterInput(msg tea.KeyPressMsg) (tea.Model, tea.Cmd) { v.updateViewportContent() } - return v, cmd + return v, tea.Batch(cmd, tea.ClearScreen) } } @@ -566,3 +566,7 @@ func (v *LogView) StatusLine() string { func (v *LogView) HasActiveInput() bool { return v.filterActive } + +func (v *LogView) LogGroupName() string { + return v.logGroupName +} diff --git a/internal/view/modal.go b/internal/view/modal.go index 16b27966..3700b802 100644 --- a/internal/view/modal.go +++ b/internal/view/modal.go @@ -23,6 +23,7 @@ const ( ModalWidthProfile = 55 ModalWidthProfileDetail = 65 ModalWidthActionMenu = 60 + ModalWidthChat = 80 ) type Modal struct { diff --git a/internal/view/resource_browser.go b/internal/view/resource_browser.go index d7d71143..30810545 100644 --- a/internal/view/resource_browser.go +++ b/internal/view/resource_browser.go @@ -122,6 +122,9 @@ type ResourceBrowser struct { // Partial region errors (for multi-region queries) partialErrors []string + + // List-level toggles (e.g., show resolved findings) + toggleStates map[string]bool } // NewResourceBrowser creates a new ResourceBrowser @@ -175,8 +178,9 @@ func newResourceBrowser(ctx context.Context, reg *registry.Registry, service, re spinner: ui.NewSpinner(), styles: newResourceBrowserStyles(), pageSize: 100, - sortColumn: -1, // -1 = no sort + sortColumn: -1, sortAscending: true, + toggleStates: make(map[string]bool), } } @@ -350,7 +354,7 @@ func (r *ResourceBrowser) contextForResource(res dao.Resource) (context.Context, if region := dao.GetResourceRegion(res); region != "" { ctx = aws.WithRegionOverride(ctx, region) } - return ctx, dao.UnwrapResource(res) + return ctx, res } func (r *ResourceBrowser) renderTabs() string { diff --git a/internal/view/resource_browser_fetch.go b/internal/view/resource_browser_fetch.go index c0131f57..b5295a5f 100644 --- a/internal/view/resource_browser_fetch.go +++ b/internal/view/resource_browser_fetch.go @@ -25,7 +25,12 @@ type listResourcesResult struct { func (r *ResourceBrowser) listResourcesWithContext(ctx context.Context, d dao.DAO) listResourcesResult { listCtx := ctx if r.fieldFilter != "" && r.fieldFilterValue != "" { - listCtx = dao.WithFilter(ctx, r.fieldFilter, r.fieldFilterValue) + listCtx = dao.WithFilter(listCtx, r.fieldFilter, r.fieldFilterValue) + } + for key, val := range r.toggleStates { + if val { + listCtx = dao.WithFilter(listCtx, key, "true") + } } var resources []dao.Resource @@ -203,7 +208,12 @@ func (r *ResourceBrowser) fetchWithDAO(ctx context.Context, d dao.DAO, token str if pagDAO, ok := d.(dao.PaginatedDAO); ok { listCtx := ctx if r.fieldFilter != "" && r.fieldFilterValue != "" { - listCtx = dao.WithFilter(ctx, r.fieldFilter, r.fieldFilterValue) + listCtx = dao.WithFilter(listCtx, r.fieldFilter, r.fieldFilterValue) + } + for key, val := range r.toggleStates { + if val { + listCtx = dao.WithFilter(listCtx, key, "true") + } } resources, nextToken, err := pagDAO.ListPage(listCtx, r.pageSize, token) return listResourcesResult{resources: resources, nextToken: nextToken, err: err} @@ -411,7 +421,12 @@ func (r *ResourceBrowser) loadNextPage() tea.Msg { listCtx := r.ctx if r.fieldFilter != "" && r.fieldFilterValue != "" { - listCtx = dao.WithFilter(r.ctx, r.fieldFilter, r.fieldFilterValue) + listCtx = dao.WithFilter(listCtx, r.fieldFilter, r.fieldFilterValue) + } + for key, val := range r.toggleStates { + if val { + listCtx = dao.WithFilter(listCtx, key, "true") + } } resources, nextToken, err := pagDAO.ListPage(listCtx, r.pageSize, r.nextPageToken) diff --git a/internal/view/resource_browser_input.go b/internal/view/resource_browser_input.go index eade01bb..5cedc3f6 100644 --- a/internal/view/resource_browser_input.go +++ b/internal/view/resource_browser_input.go @@ -7,6 +7,7 @@ import ( "github.com/clawscli/claws/internal/action" "github.com/clawscli/claws/internal/clipboard" "github.com/clawscli/claws/internal/dao" + "github.com/clawscli/claws/internal/render" ) func (r *ResourceBrowser) handleKeyPress(msg tea.KeyPressMsg) (tea.Model, tea.Cmd) { @@ -20,6 +21,10 @@ func (r *ResourceBrowser) handleKeyPress(msg tea.KeyPressMsg) (tea.Model, tea.Cm } } + if model, cmd := r.handleToggleKey(msg.String()); cmd != nil { + return model, cmd + } + switch msg.String() { case "/": r.filterActive = true @@ -173,7 +178,7 @@ func (r *ResourceBrowser) handleEnter() (tea.Model, tea.Cmd) { if len(r.filtered) > 0 && cursor >= 0 && cursor < len(r.filtered) { ctx, resource := r.contextForResource(r.filtered[cursor]) if r.markedResource != nil && r.markedResource.GetID() != resource.GetID() { - diffView := NewDiffView(ctx, dao.UnwrapResource(r.markedResource), resource, r.renderer, r.service, r.resourceType) + diffView := NewDiffView(ctx, r.markedResource, resource, r.renderer, r.service, r.resourceType) return r, func() tea.Msg { return NavigateMsg{View: diffView} } @@ -343,3 +348,21 @@ func (r *ResourceBrowser) handleCopyARN() (tea.Model, tea.Cmd) { } return r, nil } + +func (r *ResourceBrowser) handleToggleKey(key string) (tea.Model, tea.Cmd) { + if r.renderer == nil { + return nil, nil + } + toggler, ok := r.renderer.(render.Toggler) + if !ok { + return nil, nil + } + for _, toggle := range toggler.ListToggles() { + if toggle.Key == key { + r.toggleStates[toggle.ContextKey] = !r.toggleStates[toggle.ContextKey] + r.loading = true + return r, tea.Batch(r.loadResources, r.spinner.Tick) + } + } + return nil, nil +} diff --git a/internal/view/resource_browser_nav.go b/internal/view/resource_browser_nav.go index f60f63f1..adc300b8 100644 --- a/internal/view/resource_browser_nav.go +++ b/internal/view/resource_browser_nav.go @@ -3,11 +3,13 @@ package view import ( "fmt" "slices" + "strings" tea "charm.land/bubbletea/v2" "github.com/clawscli/claws/internal/action" "github.com/clawscli/claws/internal/dao" + "github.com/clawscli/claws/internal/render" ) // handleNavigation processes navigation key shortcuts @@ -90,6 +92,7 @@ func (r *ResourceBrowser) StatusLine() string { } navInfo := r.getNavigationShortcuts() + toggleInfo := r.getToggleInfo() dHint := "d:describe" if r.markedResource != nil && markInFiltered { @@ -113,7 +116,7 @@ func (r *ResourceBrowser) StatusLine() string { } if r.filterText != "" || filterInfo != "" { - base := fmt.Sprintf("%s/%s%s%s%s%s%s • %d/%d items • c:clear", r.service, r.resourceType, filterInfo, sortInfo, markInfo, autoReloadInfo, partialWarn, shown, total) + base := fmt.Sprintf("%s/%s%s%s%s%s%s%s • %d/%d items • c:clear", r.service, r.resourceType, filterInfo, sortInfo, markInfo, toggleInfo, autoReloadInfo, partialWarn, shown, total) if hasActions { base += " a:actions" } @@ -124,7 +127,7 @@ func (r *ResourceBrowser) StatusLine() string { return base } - base := fmt.Sprintf("%s/%s%s%s%s%s • %d items • /:filter %s", r.service, r.resourceType, sortInfo, markInfo, autoReloadInfo, partialWarn, total, dHint) + base := fmt.Sprintf("%s/%s%s%s%s%s%s • %d items • /:filter %s", r.service, r.resourceType, sortInfo, markInfo, toggleInfo, autoReloadInfo, partialWarn, total, dHint) if hasActions { base += " a:actions" } @@ -160,12 +163,29 @@ func (r *ResourceBrowser) CanRefresh() bool { return true } -// Service returns the service name for this browser func (r *ResourceBrowser) Service() string { return r.service } -// getNavigationShortcuts returns a string of navigation shortcuts for the current resource +func (r *ResourceBrowser) ResourceType() string { + return r.resourceType +} + +func (r *ResourceBrowser) SelectedResource() dao.Resource { + if len(r.filtered) == 0 { + return nil + } + cursor := r.tc.Cursor() + if cursor < 0 || cursor >= len(r.filtered) { + return nil + } + return r.filtered[cursor] +} + +func (r *ResourceBrowser) ResourceCount() int { return len(r.filtered) } +func (r *ResourceBrowser) FilterText() string { return r.filterText } +func (r *ResourceBrowser) ToggleStates() map[string]bool { return r.toggleStates } + func (r *ResourceBrowser) getNavigationShortcuts() string { if r.renderer == nil || len(r.filtered) == 0 { return "" @@ -175,3 +195,26 @@ func (r *ResourceBrowser) getNavigationShortcuts() string { resource := dao.UnwrapResource(r.filtered[r.tc.Cursor()]) return helper.FormatShortcuts(resource) } + +func (r *ResourceBrowser) getToggleInfo() string { + if r.renderer == nil { + return "" + } + toggler, ok := r.renderer.(render.Toggler) + if !ok { + return "" + } + toggles := toggler.ListToggles() + if len(toggles) == 0 { + return "" + } + var parts []string + for _, t := range toggles { + label := t.LabelOff + if r.toggleStates[t.ContextKey] { + label = t.LabelOn + } + parts = append(parts, fmt.Sprintf("%s:%s", t.Key, label)) + } + return " [" + strings.Join(parts, " ") + "]" +} diff --git a/internal/view/service_browser.go b/internal/view/service_browser.go index 0b86c1fe..22ca865f 100644 --- a/internal/view/service_browser.go +++ b/internal/view/service_browser.go @@ -263,14 +263,13 @@ func (s *ServiceBrowser) handleFilterInput(msg tea.KeyPressMsg) (tea.Model, tea. s.rebuildFlatItems() s.cursor = 0 s.updateViewport() - return s, cmd + return s, tea.Batch(cmd, tea.ClearScreen) } func (s *ServiceBrowser) handleNavigation(msg tea.KeyPressMsg) (tea.Model, tea.Cmd) { // Handle special keys that work regardless of flatItems state switch msg.String() { case "~": - // Toggle to Dashboard dashboard := NewDashboardView(s.ctx, s.registry) return s, func() tea.Msg { return NavigateMsg{View: dashboard, ClearStack: false} @@ -279,6 +278,24 @@ func (s *ServiceBrowser) handleNavigation(msg tea.KeyPressMsg) (tea.Model, tea.C s.filterActive = true s.filterInput.Focus() return s, textinput.Blink + case "c": + if s.filterText != "" { + s.filterText = "" + s.filterInput.SetValue("") + s.rebuildFlatItems() + s.cursor = 0 + s.updateViewport() + return s, tea.ClearScreen + } + } + + if IsEscKey(msg) && s.filterText != "" { + s.filterText = "" + s.filterInput.SetValue("") + s.rebuildFlatItems() + s.cursor = 0 + s.updateViewport() + return s, tea.ClearScreen } // Navigation requires loaded services @@ -315,25 +332,8 @@ func (s *ServiceBrowser) handleNavigation(msg tea.KeyPressMsg) (tea.Model, tea.C case "enter": return s.selectCurrentService() - - case "c": - if s.filterText != "" { - s.filterText = "" - s.filterInput.SetValue("") - s.rebuildFlatItems() - s.cursor = 0 - } } - // Also allow esc to clear filter (handles various escape sequences) - if IsEscKey(msg) && s.filterText != "" { - s.filterText = "" - s.filterInput.SetValue("") - s.rebuildFlatItems() - s.cursor = 0 - } - - // Update viewport content and scroll to cursor s.updateViewport() return s, nil @@ -344,28 +344,17 @@ func (s *ServiceBrowser) updateViewport() { return } content := s.renderContent() - s.vp.Model.SetContent(content) + vpWidth := s.vp.Model.Width() + vpHeight := s.vp.Model.Height() lines := strings.Split(content, "\n") - totalLines := len(lines) - - if len(s.flatItems) == 0 { - return + emptyLine := strings.Repeat(" ", vpWidth) + for len(lines) < vpHeight { + lines = append(lines, emptyLine) } - cursorRatio := float64(s.cursor) / float64(len(s.flatItems)) - targetLine := int(cursorRatio * float64(totalLines)) - - vpHeight := s.vp.Model.Height() - currentTop := s.vp.Model.YOffset() - - if targetLine < currentTop { - s.vp.Model.SetYOffset(max(0, targetLine-2)) - } else if targetLine > currentTop+vpHeight-cellHeight { - newOffset := targetLine - vpHeight + cellHeight + 2 - newOffset = max(0, min(newOffset, totalLines-vpHeight)) - s.vp.Model.SetYOffset(newOffset) - } + s.vp.Model.SetContent(strings.Join(lines, "\n")) + s.vp.Model.GotoTop() } func (s *ServiceBrowser) moveToNextCategory() { diff --git a/internal/view/session_history.go b/internal/view/session_history.go new file mode 100644 index 00000000..441e9489 --- /dev/null +++ b/internal/view/session_history.go @@ -0,0 +1,147 @@ +package view + +import ( + "fmt" + "strings" + + tea "charm.land/bubbletea/v2" + "charm.land/lipgloss/v2" + + "github.com/clawscli/claws/internal/ai" + "github.com/clawscli/claws/internal/ui" +) + +const ModalWidthSessionHistory = 50 + +type sessionHistoryStyles struct { + title lipgloss.Style + item lipgloss.Style + selected lipgloss.Style + hint lipgloss.Style + current lipgloss.Style +} + +func newSessionHistoryStyles() sessionHistoryStyles { + return sessionHistoryStyles{ + title: ui.TableHeaderStyle().Padding(0, 1), + item: ui.TextStyle().PaddingLeft(2), + selected: ui.SelectedStyle().PaddingLeft(2), + hint: ui.DimStyle(), + current: ui.AccentStyle(), + } +} + +type SessionSelectedMsg struct { + Session *ai.Session +} + +type NewSessionMsg struct{} + +type CloseHistoryMsg struct{} + +type SessionHistory struct { + sessions []ai.Session + currentID string + cursor int + styles sessionHistoryStyles + width int + height int +} + +func NewSessionHistory(sessions []ai.Session, currentID string) *SessionHistory { + return &SessionHistory{ + sessions: sessions, + currentID: currentID, + cursor: 0, + styles: newSessionHistoryStyles(), + } +} + +func (s *SessionHistory) Init() tea.Cmd { + return nil +} + +func (s *SessionHistory) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + switch msg := msg.(type) { + case tea.KeyPressMsg: + switch msg.String() { + case "up", "k": + if s.cursor > 0 { + s.cursor-- + } + return s, nil + case "down", "j": + if s.cursor < len(s.sessions)-1 { + s.cursor++ + } + return s, nil + case "enter": + if s.cursor >= 0 && s.cursor < len(s.sessions) { + return s, func() tea.Msg { + return SessionSelectedMsg{Session: &s.sessions[s.cursor]} + } + } + return s, nil + case "n": + return s, func() tea.Msg { + return NewSessionMsg{} + } + case "esc", "q", "ctrl+c", "ctrl+h": + return s, func() tea.Msg { + return CloseHistoryMsg{} + } + } + } + return s, nil +} + +func (s *SessionHistory) View() tea.View { + return tea.NewView(s.ViewString()) +} + +func (s *SessionHistory) ViewString() string { + var b strings.Builder + + b.WriteString(s.styles.title.Render("Chat History")) + b.WriteString("\n\n") + + if len(s.sessions) == 0 { + b.WriteString(s.styles.hint.Render(" No saved sessions")) + b.WriteString("\n") + } else { + for i, sess := range s.sessions { + style := s.styles.item + prefix := " " + if i == s.cursor { + style = s.styles.selected + prefix = "> " + } + + dateStr := sess.UpdatedAt.Format("2006-01-02 15:04") + msgCount := len(sess.Messages) + line := fmt.Sprintf("%s%s (%d msgs)", prefix, dateStr, msgCount) + + if sess.ID == s.currentID { + line += " " + s.styles.current.Render("*") + } + + b.WriteString(style.Render(line)) + b.WriteString("\n") + } + } + + b.WriteString("\n") + b.WriteString(s.styles.hint.Render("j/k:select enter:load n:new esc:close")) + + return b.String() +} + +func (s *SessionHistory) SetSize(width, height int) tea.Cmd { + s.width = width + s.height = height + return nil +} + +func (s *SessionHistory) StatusLine() string { + return "" +} diff --git a/internal/view/view.go b/internal/view/view.go index 2a7bd99e..b6b910f8 100644 --- a/internal/view/view.go +++ b/internal/view/view.go @@ -230,3 +230,28 @@ func (h *NavigationHelper) createLogView(resource dao.Resource) tea.Cmd { return NavigateMsg{View: logView} } } + +// mergeResources merges the refreshed resource with the original to preserve +// fields that are only available from List() but not from Get(). +func mergeResources(original, refreshed dao.Resource) dao.Resource { + if original == nil { + return refreshed + } + if refreshed == nil { + return original + } + // If refreshed resource implements Mergeable, let it copy fields from original + if m, ok := refreshed.(dao.Mergeable); ok { + m.MergeFrom(original) + } + + // Preserve wrapping from original + if rr, ok := original.(*dao.RegionalResource); ok { + return dao.WrapWithRegion(refreshed, rr.Region) + } + if pr, ok := original.(*dao.ProfiledResource); ok { + return dao.WrapWithProfile(refreshed, pr.Profile, pr.AccountID, pr.Region) + } + + return refreshed +}