From 0e7dc282c40a6845aba4992585f2bf3cc335d3b9 Mon Sep 17 00:00:00 2001 From: Rodrigo Borba Date: Wed, 25 Feb 2026 15:01:48 -0300 Subject: [PATCH] chore(style): fix rubocop offenses and update typing indicators --- .env.example | 352 ++------ .github/workflows/deploy_ghcr.yml | 120 +++ .gitignore | 25 + .husky/pre-commit | 14 +- .rubocop.yml | 9 +- .rubocop_todo.yml | 158 ++++ AGENTS.md | 259 ++++-- Procfile.dev | 4 +- .../v1/accounts/captain/units_controller.rb | 101 +++ .../v1/accounts/inboxes/wuzapi_controller.rb | 133 +++ .../api/v1/accounts/inboxes_controller.rb | 30 +- .../v1/captain/inter_webhooks_controller.rb | 92 ++ .../api/v1/captain/payments_controller.rb | 13 + app/controllers/webhooks/wuzapi_controller.rb | 37 + .../dashboard/api/captain/galleryItems.js | 35 + .../dashboard/api/captain/reservations.js | 26 + app/javascript/dashboard/api/captain/units.js | 29 + app/javascript/dashboard/api/inbox/jasmine.js | 96 ++ .../captain/assistant/InboxCard.vue | 7 + .../pageComponents/customTool/AuthConfig.vue | 100 ++- .../customTool/CustomToolForm.vue | 4 + .../components-next/sidebar/Sidebar.vue | 41 +- .../widgets/conversation/ConversationCard.vue | 6 + .../conversation/ConversationHeader.vue | 18 + .../conversation/ReservationMarker.vue | 76 ++ .../dashboard/composables/useUISettings.js | 1 + app/javascript/dashboard/featureFlags.js | 7 +- .../dashboard/i18n/locale/en/captain.json | 281 ++++++ .../i18n/locale/en/conversation.json | 1 + .../dashboard/i18n/locale/en/inboxMgmt.json | 291 +++--- .../dashboard/i18n/locale/en/index.js | 4 + .../i18n/locale/en/integrations.json | 12 +- .../dashboard/i18n/locale/en/jasmine.json | 81 ++ .../dashboard/i18n/locale/en/settings.json | 81 ++ .../dashboard/i18n/locale/pt_BR/captain.json | 281 ++++++ .../i18n/locale/pt_BR/conversation.json | 1 + .../i18n/locale/pt_BR/inboxMgmt.json | 267 +++--- .../dashboard/i18n/locale/pt_BR/index.js | 4 + .../dashboard/i18n/locale/pt_BR/jasmine.json | 84 ++ .../dashboard/i18n/locale/pt_BR/settings.json | 3 + .../dashboard/captain/captain.routes.js | 7 + .../dashboard/captain/reservations/Index.vue | 626 +++++++++++++ .../dashboard/conversation/ContactPanel.vue | 16 + .../reservation/ReservationSummary.vue | 163 ++++ .../settings/captain/captain.routes.js | 36 + .../settings/captain/gallery/Edit.vue | 303 +++++++ .../settings/captain/gallery/Index.vue | 196 ++++ .../dashboard/settings/captain/units/Edit.vue | 433 +++++++++ .../settings/captain/units/Index.vue | 234 +++++ .../settings/captain/units/TestIndex.vue | 29 + .../settings/inbox/JasmineConfiguration.vue | 214 +++++ .../dashboard/settings/inbox/Settings.vue | 113 ++- .../settings/inbox/channels/EvolutionGo.vue | 448 +++++++++ .../settings/inbox/channels/Whatsapp.vue | 95 +- .../settings/inbox/channels/Wuzapi.vue | 159 ++++ .../evolution_go/EvolutionGoConfiguration.vue | 211 +++++ .../channels/wuzapi/WuzapiConfiguration.vue | 296 ++++++ .../inbox/components/InboxAutoResolve.vue | 86 ++ .../inbox/components/JasmineKnowledgeBase.vue | 363 ++++++++ .../inbox/settingsPage/CollaboratorsPage.vue | 759 +++------------- .../inbox/settingsPage/ConfigurationPage.vue | 417 +-------- .../settingsPage/CustomerSatisfactionPage.vue | 585 +----------- .../dashboard/store/captain/reservations.js | 29 + app/javascript/dashboard/store/index.js | 6 + .../store/modules/captainGalleryItems.js | 85 ++ .../dashboard/store/modules/captainUnits.js | 83 ++ .../dashboard/store/mutation-types.js | 14 + app/jobs/webhooks/whatsapp_events_job.rb | 2 + app/models/account.rb | 3 + app/models/campaign.rb | 2 +- app/models/captain/unit.rb | 69 ++ app/models/channel/whatsapp.rb | 353 ++++++-- app/models/concerns/featurable.rb | 6 +- app/models/conversation.rb | 58 +- app/models/csat_survey_response.rb | 34 +- app/models/inbox.rb | 3 + app/models/message.rb | 22 +- app/models/reporting_event.rb | 15 +- app/models/super_admin.rb | 2 +- app/models/user.rb | 2 +- app/services/evolution_api/client.rb | 156 ++++ app/services/whatsapp/decryption_service.rb | 145 +++ .../incoming_message_wuzapi_service.rb | 322 +++++++ .../providers/evolution_api/payload_parser.rb | 161 ++++ .../whatsapp/providers/evolution_service.rb | 103 +++ .../providers/wuzapi/payload_parser.rb | 276 ++++++ .../whatsapp/providers/wuzapi_service.rb | 187 ++++ app/services/wuzapi/client.rb | 286 ++++++ app/services/wuzapi/provisioning_service.rb | 34 + app/views/api/v1/models/_inbox.json.jbuilder | 3 + .../api/v1/captain/payments/show.html.erb | 104 +++ config/agents/tools.yml | 15 + config/application.yml | 15 + config/routes.rb | 28 + config/schedule.yml | 7 + .../20250213190934_add_name_to_webhooks.rb | 5 - ...ypted_wuzapi_tokens_to_channel_whatsapp.rb | 8 + .../20251227230000_rename_wuzapi_tokens.rb | 8 + ...30832_add_cert_content_to_captain_units.rb | 6 + ..._webhook_configured_at_to_captain_units.rb | 5 + ...60223142442_add_typing_delay_to_inboxes.rb | 5 + ..._proactive_pix_polling_to_captain_units.rb | 5 + ...0225160000_create_captain_gallery_items.rb | 19 + ...cope_and_inbox_to_captain_gallery_items.rb | 69 ++ db/schema.rb | 848 ++++++++++++++++-- docker-compose.production.yaml | 2 +- .../captain/gallery_items_controller.rb | 128 +++ .../v1/accounts/captain/inboxes_controller.rb | 25 +- .../captain/reservations_controller.rb | 156 ++++ .../conversation/response_builder_job.rb | 83 +- .../app/jobs/captain/documents/crawl_job.rb | 14 +- .../captain/documents/response_builder_job.rb | 14 +- .../payments/poll_pix_charge_status_job.rb | 92 ++ .../proactive_pix_polling_scheduler_job.rb | 28 + enterprise/app/models/captain/assistant.rb | 26 +- enterprise/app/models/captain/brand.rb | 44 + enterprise/app/models/captain/custom_tool.rb | 2 +- enterprise/app/models/captain/document.rb | 4 +- enterprise/app/models/captain/gallery_item.rb | 114 +++ enterprise/app/models/captain/pix_charge.rb | 68 ++ enterprise/app/models/captain/reservation.rb | 128 +++ enterprise/app/models/captain/scenario.rb | 36 +- enterprise/app/models/captain/unit.rb | 109 +++ enterprise/app/models/captain_inbox.rb | 18 +- enterprise/app/models/company.rb | 2 +- enterprise/app/models/concerns/toolable.rb | 7 + .../enterprise/concerns/conversation.rb | 11 + .../app/policies/captain/assistant_policy.rb | 8 + .../captain/assistant/agent_runner_service.rb | 205 ++++- .../services/captain/inter/auth_service.rb | 60 ++ .../app/services/captain/inter/cob_service.rb | 89 ++ .../captain/inter/cob_status_service.rb | 89 ++ .../captain/inter/webhook_setup_service.rb | 70 ++ .../captain/llm/system_prompts_service.rb | 4 +- .../open_ai_message_builder_service.rb | 37 +- .../captain/payments/confirmation_service.rb | 80 ++ .../conversation_marker_sync_service.rb | 27 + .../captain/reservations/marker_builder.rb | 84 ++ .../reservations/revenue_summary_service.rb | 72 ++ .../captain/tools/check_pix_payment_tool.rb | 238 +++++ .../captain/tools/generate_pix_tool.rb | 722 +++++++++++++++ .../captain/tools/send_suite_images_tool.rb | 252 ++++++ .../hook_execution_service.rb | 11 +- .../captain/inboxes/index.json.jbuilder | 8 +- .../captain/reservations/index.json.jbuilder | 10 + .../captain/reservations/show.json.jbuilder | 1 + .../models/captain/_reservation.json.jbuilder | 36 + .../partials/_conversation.json.jbuilder | 2 + .../captain/conversation/reaction_policy.rb | 52 ++ .../lib/captain/prompts/assistant.liquid | 33 +- .../lib/captain/prompts/scenario.liquid | 28 + enterprise/lib/captain/response_schema.rb | 4 + .../lib/captain/tools/faq_lookup_tool.rb | 4 + get_error.js | 27 + informacoes/inter_api | 95 ++ lib/integrations/llm_instrumentation.rb | 12 +- package.json | 4 +- ..._migracao_dos_canais_wuzapi_e_evolution.md | 59 ++ ...2026-02-24_faq_guardrail_e_orquestracao.md | 74 ++ progresso/como-criar-pagina-settings.md | 204 +++++ progresso/leitura-de-imagem-llm-v2.md | 21 + progresso/plano-novo-projeto-ia.md | 35 + proposito/PROPOSITO.md | 208 +++++ reference/chatwoot-develop | 1 + scripts/create_fake_pix.rb | 45 + .../captain/gallery_items_controller_spec.rb | 79 ++ .../captain/inboxes_controller_spec.rb | 48 + .../captain/reservations_controller_spec.rb | 236 +++++ .../accounts/captain/units_controller_spec.rb | 57 ++ .../conversation/response_builder_job_spec.rb | 47 + .../poll_pix_charge_status_job_spec.rb | 126 +++ ...roactive_pix_polling_scheduler_job_spec.rb | 124 +++ .../models/captain/reservation_spec.rb | 108 +++ .../models/captain/scenario_spec.rb | 43 + .../assistant/agent_runner_service_spec.rb | 159 +++- .../captain/inter/cob_status_service_spec.rb | 97 ++ .../reservations/marker_builder_spec.rb | 93 ++ .../tools/check_pix_payment_tool_spec.rb | 131 +++ .../captain/tools/generate_pix_tool_spec.rb | 269 ++++++ .../tools/send_suite_images_tool_spec.rb | 109 +++ spec/factories/captain/brand.rb | 6 + spec/factories/captain/gallery_item.rb | 37 + spec/factories/captain/unit.rb | 9 + 183 files changed, 15842 insertions(+), 2639 deletions(-) create mode 100644 .github/workflows/deploy_ghcr.yml create mode 100644 .rubocop_todo.yml create mode 100644 app/controllers/api/v1/accounts/captain/units_controller.rb create mode 100644 app/controllers/api/v1/accounts/inboxes/wuzapi_controller.rb create mode 100644 app/controllers/public/api/v1/captain/inter_webhooks_controller.rb create mode 100644 app/controllers/public/api/v1/captain/payments_controller.rb create mode 100644 app/controllers/webhooks/wuzapi_controller.rb create mode 100644 app/javascript/dashboard/api/captain/galleryItems.js create mode 100644 app/javascript/dashboard/api/captain/reservations.js create mode 100644 app/javascript/dashboard/api/captain/units.js create mode 100644 app/javascript/dashboard/api/inbox/jasmine.js create mode 100644 app/javascript/dashboard/components/widgets/conversation/ReservationMarker.vue create mode 100644 app/javascript/dashboard/i18n/locale/en/captain.json create mode 100644 app/javascript/dashboard/i18n/locale/en/jasmine.json create mode 100644 app/javascript/dashboard/i18n/locale/pt_BR/captain.json create mode 100644 app/javascript/dashboard/i18n/locale/pt_BR/jasmine.json create mode 100644 app/javascript/dashboard/routes/dashboard/captain/reservations/Index.vue create mode 100644 app/javascript/dashboard/routes/dashboard/conversation/reservation/ReservationSummary.vue create mode 100644 app/javascript/dashboard/routes/dashboard/settings/captain/gallery/Edit.vue create mode 100644 app/javascript/dashboard/routes/dashboard/settings/captain/gallery/Index.vue create mode 100644 app/javascript/dashboard/routes/dashboard/settings/captain/units/Edit.vue create mode 100644 app/javascript/dashboard/routes/dashboard/settings/captain/units/Index.vue create mode 100644 app/javascript/dashboard/routes/dashboard/settings/captain/units/TestIndex.vue create mode 100644 app/javascript/dashboard/routes/dashboard/settings/inbox/JasmineConfiguration.vue create mode 100644 app/javascript/dashboard/routes/dashboard/settings/inbox/channels/EvolutionGo.vue create mode 100644 app/javascript/dashboard/routes/dashboard/settings/inbox/channels/Wuzapi.vue create mode 100644 app/javascript/dashboard/routes/dashboard/settings/inbox/channels/evolution_go/EvolutionGoConfiguration.vue create mode 100644 app/javascript/dashboard/routes/dashboard/settings/inbox/channels/wuzapi/WuzapiConfiguration.vue create mode 100644 app/javascript/dashboard/routes/dashboard/settings/inbox/components/InboxAutoResolve.vue create mode 100644 app/javascript/dashboard/routes/dashboard/settings/inbox/components/JasmineKnowledgeBase.vue create mode 100644 app/javascript/dashboard/store/captain/reservations.js create mode 100644 app/javascript/dashboard/store/modules/captainGalleryItems.js create mode 100644 app/javascript/dashboard/store/modules/captainUnits.js create mode 100644 app/models/captain/unit.rb create mode 100644 app/services/evolution_api/client.rb create mode 100644 app/services/whatsapp/decryption_service.rb create mode 100644 app/services/whatsapp/incoming_message_wuzapi_service.rb create mode 100644 app/services/whatsapp/providers/evolution_api/payload_parser.rb create mode 100644 app/services/whatsapp/providers/evolution_service.rb create mode 100644 app/services/whatsapp/providers/wuzapi/payload_parser.rb create mode 100644 app/services/whatsapp/providers/wuzapi_service.rb create mode 100644 app/services/wuzapi/client.rb create mode 100644 app/services/wuzapi/provisioning_service.rb create mode 100644 app/views/public/api/v1/captain/payments/show.html.erb create mode 100644 config/application.yml delete mode 100644 db/migrate/20250213190934_add_name_to_webhooks.rb create mode 100644 db/migrate/20251227223543_add_encrypted_wuzapi_tokens_to_channel_whatsapp.rb create mode 100644 db/migrate/20251227230000_rename_wuzapi_tokens.rb create mode 100644 db/migrate/20260222230832_add_cert_content_to_captain_units.rb create mode 100644 db/migrate/20260223035055_add_webhook_configured_at_to_captain_units.rb create mode 100644 db/migrate/20260223142442_add_typing_delay_to_inboxes.rb create mode 100644 db/migrate/20260225003000_add_proactive_pix_polling_to_captain_units.rb create mode 100644 db/migrate/20260225160000_create_captain_gallery_items.rb create mode 100644 db/migrate/20260226002000_add_scope_and_inbox_to_captain_gallery_items.rb create mode 100644 enterprise/app/controllers/api/v1/accounts/captain/gallery_items_controller.rb create mode 100644 enterprise/app/controllers/api/v1/accounts/captain/reservations_controller.rb create mode 100644 enterprise/app/jobs/captain/payments/poll_pix_charge_status_job.rb create mode 100644 enterprise/app/jobs/captain/payments/proactive_pix_polling_scheduler_job.rb create mode 100644 enterprise/app/models/captain/brand.rb create mode 100644 enterprise/app/models/captain/gallery_item.rb create mode 100644 enterprise/app/models/captain/pix_charge.rb create mode 100644 enterprise/app/models/captain/reservation.rb create mode 100644 enterprise/app/models/captain/unit.rb create mode 100644 enterprise/app/services/captain/inter/auth_service.rb create mode 100644 enterprise/app/services/captain/inter/cob_service.rb create mode 100644 enterprise/app/services/captain/inter/cob_status_service.rb create mode 100644 enterprise/app/services/captain/inter/webhook_setup_service.rb create mode 100644 enterprise/app/services/captain/payments/confirmation_service.rb create mode 100644 enterprise/app/services/captain/reservations/conversation_marker_sync_service.rb create mode 100644 enterprise/app/services/captain/reservations/marker_builder.rb create mode 100644 enterprise/app/services/captain/reservations/revenue_summary_service.rb create mode 100644 enterprise/app/services/captain/tools/check_pix_payment_tool.rb create mode 100644 enterprise/app/services/captain/tools/generate_pix_tool.rb create mode 100644 enterprise/app/services/captain/tools/send_suite_images_tool.rb create mode 100644 enterprise/app/views/api/v1/accounts/captain/reservations/index.json.jbuilder create mode 100644 enterprise/app/views/api/v1/accounts/captain/reservations/show.json.jbuilder create mode 100644 enterprise/app/views/api/v1/models/captain/_reservation.json.jbuilder create mode 100644 enterprise/lib/captain/conversation/reaction_policy.rb create mode 100644 get_error.js create mode 100644 informacoes/inter_api create mode 100644 progresso/2026-02-22_migracao_dos_canais_wuzapi_e_evolution.md create mode 100644 progresso/2026-02-24_faq_guardrail_e_orquestracao.md create mode 100644 progresso/como-criar-pagina-settings.md create mode 100644 progresso/leitura-de-imagem-llm-v2.md create mode 100644 progresso/plano-novo-projeto-ia.md create mode 100644 proposito/PROPOSITO.md create mode 160000 reference/chatwoot-develop create mode 100755 scripts/create_fake_pix.rb create mode 100644 spec/enterprise/controllers/api/v1/accounts/captain/gallery_items_controller_spec.rb create mode 100644 spec/enterprise/controllers/api/v1/accounts/captain/reservations_controller_spec.rb create mode 100644 spec/enterprise/controllers/api/v1/accounts/captain/units_controller_spec.rb create mode 100644 spec/enterprise/jobs/captain/payments/poll_pix_charge_status_job_spec.rb create mode 100644 spec/enterprise/jobs/captain/payments/proactive_pix_polling_scheduler_job_spec.rb create mode 100644 spec/enterprise/models/captain/reservation_spec.rb create mode 100644 spec/enterprise/services/captain/inter/cob_status_service_spec.rb create mode 100644 spec/enterprise/services/captain/reservations/marker_builder_spec.rb create mode 100644 spec/enterprise/services/captain/tools/check_pix_payment_tool_spec.rb create mode 100644 spec/enterprise/services/captain/tools/generate_pix_tool_spec.rb create mode 100644 spec/enterprise/services/captain/tools/send_suite_images_tool_spec.rb create mode 100644 spec/factories/captain/brand.rb create mode 100644 spec/factories/captain/gallery_item.rb create mode 100644 spec/factories/captain/unit.rb diff --git a/.env.example b/.env.example index 0158c1f0f..adeda3705 100644 --- a/.env.example +++ b/.env.example @@ -1,297 +1,101 @@ -# Learn about the various environment variables at -# https://www.chatwoot.com/docs/self-hosted/configuration/environment-variables/#rails-production-variables +# ============================================ +# Synkra AIOS Environment Configuration +# ============================================ +# Copy this file to .env and fill in your actual values +# DO NOT commit .env with real credentials +# ============================================ -# Used to verify the integrity of signed cookies. so ensure a secure value is set -# SECRET_KEY_BASE should be alphanumeric. Avoid special characters or symbols. -# Use `rake secret` to generate this variable -SECRET_KEY_BASE=replace_with_lengthy_secure_hex +# -------------------------------------------- +# LLM Providers +# -------------------------------------------- -# Active Record Encryption keys (required for MFA/2FA functionality) -# Generate these keys by running: rails db:encryption:init -# IMPORTANT: Use different keys for each environment (development, staging, production) -# ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY= -# ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY= -# ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT= +# DeepSeek API (for claude-free command) +# Get your key at: https://platform.deepseek.com/api_keys +# Cost: ~$0.14/M tokens with tool calling support +DEEPSEEK_API_KEY= -# Replace with the URL you are planning to use for your app -FRONTEND_URL=http://0.0.0.0:3000 -# To use a dedicated URL for help center pages -# HELPCENTER_URL=http://0.0.0.0:3000 +# OpenRouter API (for multi-model routing) +# Get your key at: https://openrouter.ai/keys +OPENROUTER_API_KEY= -# If the variable is set, all non-authenticated pages would fallback to the default locale. -# Whenever a new account is created, the default language will be DEFAULT_LOCALE instead of en -# DEFAULT_LOCALE=en +# Anthropic API (direct, if not using Claude Max subscription) +# Get your key at: https://console.anthropic.com/ +ANTHROPIC_API_KEY= -# If you plan to use CDN for your assets, set Asset CDN Host -ASSET_CDN_HOST= +# OpenAI API Key - Get yours at: https://platform.openai.com/api-keys +OPENAI_API_KEY= -# Force all access to the app over SSL, default is set to false -FORCE_SSL=false +# -------------------------------------------- +# Search & Research Tools +# -------------------------------------------- -# This lets you control new sign ups on your chatwoot installation -# true : default option, allows sign ups -# false : disables all the end points related to sign ups -# api_only: disables the UI for signup, but you can create sign ups via the account apis -ENABLE_ACCOUNT_SIGNUP=false +# Exa Search API (web search for agents) +# Get your key at: https://exa.ai/ +EXA_API_KEY= -# Redis config -# specify the configs via single URL or individual variables -# ref: https://www.iana.org/assignments/uri-schemes/prov/redis -# You can also use the following format for the URL: redis://:password@host:port/db_number -REDIS_URL=redis://redis:6379 -# If you are using docker-compose, set this variable's value to be any string, -# which will be the password for the redis service running inside the docker-compose -# to make it secure -REDIS_PASSWORD= -# Redis Sentinel can be used by passing list of sentinel host and ports e,g. sentinel_host1:port1,sentinel_host2:port2 -REDIS_SENTINELS= -# Redis sentinel master name is required when using sentinel, default value is "mymaster". -# You can find list of master using "SENTINEL masters" command -REDIS_SENTINEL_MASTER_NAME= +# Context7 (library documentation lookup) +# Usually free, no key required for basic usage +CONTEXT7_API_KEY= -# By default Chatwoot will pass REDIS_PASSWORD as the password value for sentinels -# Use the following environment variable to customize passwords for sentinels. -# Use empty string if sentinels are configured with out passwords -# REDIS_SENTINEL_PASSWORD= +# -------------------------------------------- +# Database & Backend +# -------------------------------------------- -# Redis premium breakage in heroku fix -# enable the following configuration -# ref: https://github.com/chatwoot/chatwoot/issues/2420 -# REDIS_OPENSSL_VERIFY_MODE=none +# Supabase (database, auth, storage) +# Get from your Supabase project settings +SUPABASE_URL= +SUPABASE_ANON_KEY= +SUPABASE_SERVICE_ROLE_KEY= -# Postgres Database config variables -# You can leave POSTGRES_DATABASE blank. The default name of -# the database in the production environment is chatwoot_production -# POSTGRES_DATABASE= -POSTGRES_HOST=postgres -POSTGRES_USERNAME=postgres -POSTGRES_PASSWORD= -RAILS_ENV=development -# Changes the Postgres query timeout limit. The default is 14 seconds. Modify only when required. -# POSTGRES_STATEMENT_TIMEOUT=14s -RAILS_MAX_THREADS=5 +# -------------------------------------------- +# Version Control & CI/CD +# -------------------------------------------- -# The email from which all outgoing emails are sent -# could user either `email@yourdomain.com` or `BrandName ` -MAILER_SENDER_EMAIL=Chatwoot +# GitHub Token (for GitHub CLI and API access) +# Create at: https://github.com/settings/tokens +GITHUB_TOKEN= -#SMTP domain key is set up for HELO checking -SMTP_DOMAIN=chatwoot.com -# Set the value to "mailhog" if using docker-compose for development environments, -# Set the value as "localhost" or your SMTP address in other environments -# If SMTP_ADDRESS is empty, Chatwoot would try to use sendmail(postfix) -SMTP_ADDRESS= -SMTP_PORT=1025 -SMTP_USERNAME= -SMTP_PASSWORD= -# plain,login,cram_md5 -SMTP_AUTHENTICATION= -SMTP_ENABLE_STARTTLS_AUTO=true -# Can be: 'none', 'peer', 'client_once', 'fail_if_no_peer_cert', see http://api.rubyonrails.org/classes/ActionMailer/Base.html -SMTP_OPENSSL_VERIFY_MODE=peer -# Comment out the following environment variables if required by your SMTP server -# SMTP_TLS= -# SMTP_SSL= -# SMTP_OPEN_TIMEOUT -# SMTP_READ_TIMEOUT +# -------------------------------------------- +# Project Management +# -------------------------------------------- -# Mail Incoming -# This is the domain set for the reply emails when conversation continuity is enabled -MAILER_INBOUND_EMAIL_DOMAIN= -# Set this to the appropriate ingress channel with regards to incoming emails -# Possible values are : -# relay for Exim, Postfix, Qmail -# mailgun for Mailgun -# mandrill for Mandrill -# postmark for Postmark -# sendgrid for Sendgrid -# ses for Amazon SES -RAILS_INBOUND_EMAIL_SERVICE= -# Use one of the following based on the email ingress service -# Ref: https://edgeguides.rubyonrails.org/action_mailbox_basics.html -# Set this to a password of your choice and use it in the Inbound webhook -RAILS_INBOUND_EMAIL_PASSWORD= +# ClickUp API (if using ClickUp integration) +# Get from: ClickUp Settings > Apps > API Token +CLICKUP_API_KEY= -MAILGUN_INGRESS_SIGNING_KEY= -MANDRILL_INGRESS_API_KEY= +# -------------------------------------------- +# Automation & Workflows +# -------------------------------------------- -# SNS topic ARN for ActionMailbox (format: arn:aws:sns:region:account-id:topic-name) -# Configure only if the rails_inbound_email_service = ses -ACTION_MAILBOX_SES_SNS_TOPIC= +# N8N (workflow automation) +# From your N8N instance settings +N8N_API_KEY= +N8N_WEBHOOK_URL= -# Creating Your Inbound Webhook Instructions for Postmark and Sendgrid: -# Inbound webhook URL format: -# https://actionmailbox:[YOUR_RAILS_INBOUND_EMAIL_PASSWORD]@[YOUR_CHATWOOT_DOMAIN.COM]/rails/action_mailbox/[RAILS_INBOUND_EMAIL_SERVICE]/inbound_emails -# Note: Replace the values inside the brackets; do not include the brackets themselves. -# Example: https://actionmailbox:mYRandomPassword3@chatwoot.example.com/rails/action_mailbox/postmark/inbound_emails -# For Postmark -# Ensure the 'Include raw email content in JSON payload' checkbox is selected in the inbound webhook section. +# -------------------------------------------- +# Monitoring & Analytics +# -------------------------------------------- -# Storage -ACTIVE_STORAGE_SERVICE=local +# Sentry (error tracking) +SENTRY_DSN= -# Amazon S3 -# documentation: https://www.chatwoot.com/docs/configuring-s3-bucket-as-cloud-storage -S3_BUCKET_NAME= -AWS_ACCESS_KEY_ID= -AWS_SECRET_ACCESS_KEY= -AWS_REGION= +# -------------------------------------------- +# Cloud Providers +# -------------------------------------------- -# S3-compatible storage (e.g., Cloudflare R2, MinIO, DigitalOcean Spaces) -# Set ACTIVE_STORAGE_SERVICE=s3_compatible to use this -# STORAGE_ACCESS_KEY_ID= -# STORAGE_SECRET_ACCESS_KEY= -# STORAGE_REGION= -# STORAGE_BUCKET_NAME= -# STORAGE_ENDPOINT= -# STORAGE_FORCE_PATH_STYLE=true -# STORAGE_REQUEST_CHECKSUM_CALCULATION=when_required -# STORAGE_RESPONSE_CHECKSUM_VALIDATION=when_required +# Railway (deployment) +RAILWAY_TOKEN= -# Log settings -# Disable if you want to write logs to a file -RAILS_LOG_TO_STDOUT=true -LOG_LEVEL=info -LOG_SIZE=500 -# Configure this environment variable if you want to use lograge instead of rails logger -#LOGRAGE_ENABLED=true +# Vercel (deployment) +VERCEL_TOKEN= -### This environment variables are only required if you are setting up social media channels +# -------------------------------------------- +# AIOS Core Configuration +# -------------------------------------------- +NODE_ENV=development +AIOS_VERSION=2.2.0 -# Facebook -# documentation: https://www.chatwoot.com/docs/facebook-setup -FB_VERIFY_TOKEN= -FB_APP_SECRET= -FB_APP_ID= - -# https://developers.facebook.com/docs/messenger-platform/instagram/get-started#app-dashboard -IG_VERIFY_TOKEN= - -# Twitter -# documentation: https://www.chatwoot.com/docs/twitter-app-setup -TWITTER_APP_ID= -TWITTER_CONSUMER_KEY= -TWITTER_CONSUMER_SECRET= -TWITTER_ENVIRONMENT= - -#slack integration -SLACK_CLIENT_ID= -SLACK_CLIENT_SECRET= - -# Google OAuth -GOOGLE_OAUTH_CLIENT_ID= -GOOGLE_OAUTH_CLIENT_SECRET= -GOOGLE_OAUTH_CALLBACK_URL= - -### Change this env variable only if you are using a custom build mobile app -## Mobile app env variables -IOS_APP_ID=L7YLMN4634.com.chatwoot.app -ANDROID_BUNDLE_ID=com.chatwoot.app - -# https://developers.google.com/android/guides/client-auth (use keytool to print the fingerprint in the first section) -ANDROID_SHA256_CERT_FINGERPRINT=AC:73:8E:DE:EB:56:EA:CC:10:87:02:A7:65:37:7B:38:D4:5D:D4:53:F8:3B:FB:D3:C6:28:64:1D:AA:08:1E:D8 - -### Smart App Banner -# https://developer.apple.com/library/archive/documentation/AppleApplications/Reference/SafariWebContent/PromotingAppswithAppBanners/PromotingAppswithAppBanners.html -# You can find your app-id in https://itunesconnect.apple.com -#IOS_APP_IDENTIFIER=1495796682 - -## Push Notification -## generate a new key value here : https://d3v.one/vapid-key-generator/ -# VAPID_PUBLIC_KEY= -# VAPID_PRIVATE_KEY= -# -# for mobile apps -# FCM_SERVER_KEY= - -### APM and Error Monitoring configurations -## Elastic APM -## https://www.elastic.co/guide/en/apm/agent/ruby/current/getting-started-rails.html -# ELASTIC_APM_SERVER_URL= -# ELASTIC_APM_SECRET_TOKEN= - -## Sentry -# SENTRY_DSN= - - -## Scout -## https://scoutapm.com/docs/ruby/configuration -# SCOUT_KEY=YOURKEY -# SCOUT_NAME=YOURAPPNAME (Production) -# SCOUT_MONITOR=true - -## NewRelic -# https://docs.newrelic.com/docs/agents/ruby-agent/configuration/ruby-agent-configuration/ -# NEW_RELIC_LICENSE_KEY= -# Set this to true to allow newrelic apm to send logs. -# This is turned off by default. -# NEW_RELIC_APPLICATION_LOGGING_ENABLED= - -## Datadog -## https://github.com/DataDog/dd-trace-rb/blob/master/docs/GettingStarted.md#environment-variables -# DD_TRACE_AGENT_URL= - - -# MaxMindDB API key to download GeoLite2 City database -# IP_LOOKUP_API_KEY= - -## Rack Attack configuration -## To prevent and throttle abusive requests -# ENABLE_RACK_ATTACK=true -# RACK_ATTACK_LIMIT=300 -# ENABLE_RACK_ATTACK_WIDGET_API=true -# Comma-separated list of trusted IPs that bypass Rack Attack throttling rules -# RACK_ATTACK_ALLOWED_IPS=127.0.0.1,::1,192.168.0.10 - -## Running chatwoot as an API only server -## setting this value to true will disable the frontend dashboard endpoints -# CW_API_ONLY_SERVER=false - -## Development Only Config -# if you want to use letter_opener for local emails -# LETTER_OPENER=true -# meant to be used in github codespaces -# WEBPACKER_DEV_SERVER_PUBLIC= - -# If you want to use official mobile app, -# the notifications would be relayed via a Chatwoot server -ENABLE_PUSH_RELAY_SERVER=true - -# Stripe API key -STRIPE_SECRET_KEY= -STRIPE_WEBHOOK_SECRET= - -# Set to true if you want to upload files to cloud storage using the signed url -# Make sure to follow https://edgeguides.rubyonrails.org/active_storage_overview.html#cross-origin-resource-sharing-cors-configuration on the cloud storage after setting this to true. -DIRECT_UPLOADS_ENABLED= - -#MS OAUTH creds -AZURE_APP_ID= -AZURE_APP_SECRET= - -## Advanced configurations -## Change these values to fine tune performance -# control the concurrency setting of sidekiq -# SIDEKIQ_CONCURRENCY=10 -# Enable verbose logging each time a job is dequeued in Sidekiq -# ENABLE_SIDEKIQ_DEQUEUE_LOGGER=false - - -# AI powered features -## OpenAI key -# OPENAI_API_KEY= - -# Housekeeping/Performance related configurations -# Set to true if you want to remove stale contact inboxes -# contact_inboxes with no conversation older than 90 days will be removed -# REMOVE_STALE_CONTACT_INBOX_JOB_STATUS=false - -# REDIS_ALFRED_SIZE=10 -# REDIS_VELMA_SIZE=10 - -# Baileys API Whatsapp provider -BAILEYS_PROVIDER_DEFAULT_CLIENT_NAME=Chatwoot -BAILEYS_PROVIDER_DEFAULT_URL=http://localhost:3025 -BAILEYS_PROVIDER_DEFAULT_API_KEY= - -RESEND_API_KEY= +# -------------------------------------------- +# Custom Configuration +# -------------------------------------------- +# Add your custom API keys below diff --git a/.github/workflows/deploy_ghcr.yml b/.github/workflows/deploy_ghcr.yml new file mode 100644 index 000000000..0cd1f64d3 --- /dev/null +++ b/.github/workflows/deploy_ghcr.yml @@ -0,0 +1,120 @@ +name: Build and Push to GHCR (multi-arch) + +on: + push: + branches: + - chatwoot-jasmine + - main + workflow_dispatch: + +env: + IMAGE_NAME: ghcr.io/${{ github.repository }} + +jobs: + build: + strategy: + fail-fast: false + matrix: + include: + - platform: linux/amd64 + runner: ubuntu-latest + - platform: linux/arm64 + runner: ubuntu-22.04-arm + runs-on: ${{ matrix.runner }} + permissions: + contents: read + packages: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Prepare + run: | + platform="${{ matrix.platform }}" + echo "PLATFORM_PAIR=${platform//\//-}" >> "$GITHUB_ENV" + echo "CACHE_SCOPE=${platform//\//-}-${GITHUB_REF_NAME}" >> "$GITHUB_ENV" + echo "CACHE_SCOPE_FALLBACK=${platform//\//-}-main" >> "$GITHUB_ENV" + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Login to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push by digest + id: build + uses: docker/build-push-action@v6 + with: + context: . + file: ./docker/Dockerfile + platforms: ${{ matrix.platform }} + push: true + build-args: | + RAILS_ENV=production + NODE_ENV=production + outputs: type=image,name=${{ env.IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true + cache-from: | + type=gha,scope=${{ env.CACHE_SCOPE }} + type=gha,scope=${{ env.CACHE_SCOPE_FALLBACK }} + type=registry,ref=${{ env.IMAGE_NAME }}:buildcache-${{ env.PLATFORM_PAIR }} + cache-to: | + type=gha,mode=max,scope=${{ env.CACHE_SCOPE }} + type=registry,ref=${{ env.IMAGE_NAME }}:buildcache-${{ env.PLATFORM_PAIR }},mode=max + + - name: Export digest + run: | + mkdir -p "${{ runner.temp }}/digests" + digest="${{ steps.build.outputs.digest }}" + touch "${{ runner.temp }}/digests/${digest#sha256:}" + + - name: Upload digest + uses: actions/upload-artifact@v4 + with: + name: digests-${{ env.PLATFORM_PAIR }} + path: ${{ runner.temp }}/digests/* + if-no-files-found: error + retention-days: 1 + + merge: + runs-on: ubuntu-latest + needs: [build] + permissions: + packages: write + + steps: + - name: Download digests + uses: actions/download-artifact@v4 + with: + path: ${{ runner.temp }}/digests + pattern: digests-* + merge-multiple: true + + - name: Login to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Create manifest list and push + working-directory: ${{ runner.temp }}/digests + run: | + docker buildx imagetools create \ + -t "${{ env.IMAGE_NAME }}:latest" \ + -t "${{ env.IMAGE_NAME }}:v${{ github.run_number }}" \ + $(printf '${{ env.IMAGE_NAME }}@sha256:%s ' *) + + - name: Inspect image + run: | + docker buildx imagetools inspect "${{ env.IMAGE_NAME }}:latest" diff --git a/.gitignore b/.gitignore index dbdd35bcd..0ecc74c91 100644 --- a/.gitignore +++ b/.gitignore @@ -103,3 +103,28 @@ CLAUDE.local.md .pnpm-store/* local/ Procfile.worktree + +# Environment & Secrets (AIOS) +.env.local +.env.*.local +*.key +*.pem + +# Dependencies (AIOS) +node_modules/ + +# Build & Logs (AIOS) +dist/ +build/ +logs/ + +# IDE & OS (AIOS) +Thumbs.db +.idea/ + +# AIOS Local (AIOS) +.aios-core/ +.aios/ +.claude/ +.env.aios +.env.backup* diff --git a/.husky/pre-commit b/.husky/pre-commit index 9c350db88..a9f6aec56 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,11 +1,23 @@ #!/bin/sh . "$(dirname "$0")/_/husky.sh" +# Ensure hooks always use project Ruby (rbenv + .ruby-version), not system Ruby. +export PATH="$HOME/.rbenv/bin:$PATH" +if command -v rbenv >/dev/null 2>&1; then + eval "$(rbenv init -)" + if [ -f .ruby-version ]; then + rbenv shell "$(cat .ruby-version)" + fi +fi + # lint js and vue files npx --no-install lint-staged # lint only staged ruby files -git diff --name-only --cached | xargs ls -1 2>/dev/null | grep '\.rb$' | xargs bundle exec rubocop --force-exclusion +STAGED_RB_FILES=$(git diff --name-only --cached --diff-filter=ACMR -- '*.rb') +if [ -n "$STAGED_RB_FILES" ]; then + echo "$STAGED_RB_FILES" | xargs bundle exec rubocop --force-exclusion +fi # stage rubocop changes to files # git diff --name-only --cached | xargs git add diff --git a/.rubocop.yml b/.rubocop.yml index 246f503bb..dc8de3cbe 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,3 +1,5 @@ +inherit_from: .rubocop_todo.yml + plugins: - rubocop-performance - rubocop-rails @@ -18,11 +20,13 @@ Metrics/ClassLength: Exclude: - 'app/models/message.rb' - 'app/models/conversation.rb' + - 'enterprise/app/services/captain/tools/generate_pix_tool.rb' Metrics/MethodLength: Max: 19 Exclude: - 'enterprise/lib/captain/agent.rb' + - 'enterprise/app/services/captain/tools/generate_pix_tool.rb' RSpec/ExampleLength: Max: 50 @@ -177,10 +181,10 @@ Metrics/AbcSize: Max: 26 Exclude: - 'app/controllers/concerns/auth_helper.rb' - - 'app/models/integrations/hook.rb' - 'app/models/canned_response.rb' - 'app/models/telegram_bot.rb' + - 'enterprise/app/services/captain/tools/generate_pix_tool.rb' Rails/RenderInline: Exclude: @@ -231,6 +235,9 @@ AllCops: - 'tmp/**/*' - 'storage/**/*' - 'db/migrate/20230426130150_init_schema.rb' + - 'reference/**/*' + - '.aios-core/**/*' + - '.claude/**/*' FactoryBot/SyntaxMethods: Enabled: false diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml new file mode 100644 index 000000000..b6c68f115 --- /dev/null +++ b/.rubocop_todo.yml @@ -0,0 +1,158 @@ +# This configuration was generated by +# `rubocop --auto-gen-config` +# on 2026-02-25 17:56:28 UTC using RuboCop version 1.75.6. +# The point is for the user to remove these configuration records +# one by one as the offenses are removed from the code base. +# Note that changes in the inspected code, or installation of new +# versions of RuboCop, may require this file to be generated again. + +# Offense count: 2 +Chatwoot/AttachmentDownload: + Exclude: + - 'app/services/whatsapp/providers/evolution_service.rb' + - 'app/services/whatsapp/providers/wuzapi_service.rb' + +# Offense count: 8 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: Max, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings. +# URISchemes: http, https +Layout/LineLength: + Exclude: + - 'app/controllers/public/api/v1/captain/inter_webhooks_controller.rb' + - 'app/models/channel/whatsapp.rb' + - 'app/services/whatsapp/providers/wuzapi_service.rb' + - 'app/services/wuzapi/client.rb' + - 'enterprise/app/services/captain/assistant/agent_runner_service.rb' + +# Offense count: 2 +# Configuration parameters: IgnoreLiteralBranches, IgnoreConstantBranches, IgnoreDuplicateElseBranch. +Lint/DuplicateBranch: + Exclude: + - 'app/services/whatsapp/providers/wuzapi/payload_parser.rb' + - 'app/services/whatsapp/providers/wuzapi_service.rb' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AutoCorrect, AllowUnusedKeywordArguments, IgnoreEmptyMethods, IgnoreNotImplementedMethods, NotImplementedExceptions. +# NotImplementedExceptions: NotImplementedError +Lint/UnusedMethodArgument: + Exclude: + - 'app/services/whatsapp/providers/evolution_service.rb' + +# Offense count: 29 +# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max. +Metrics/AbcSize: + Exclude: + - 'app/models/channel/whatsapp.rb' + - 'app/services/evolution_api/client.rb' + - 'app/services/whatsapp/decryption_service.rb' + - 'app/services/whatsapp/incoming_message_wuzapi_service.rb' + - 'app/services/whatsapp/providers/evolution_api/payload_parser.rb' + - 'app/services/whatsapp/providers/wuzapi/payload_parser.rb' + - 'app/services/whatsapp/providers/wuzapi_service.rb' + - 'app/services/wuzapi/client.rb' + - 'enterprise/app/services/captain/inter/auth_service.rb' + - 'enterprise/app/services/captain/inter/cob_service.rb' + - 'enterprise/app/services/captain/inter/webhook_setup_service.rb' + - 'enterprise/app/services/captain/reservations/marker_builder.rb' + - 'enterprise/app/services/captain/tools/generate_pix_tool.rb' + +# Offense count: 6 +# Configuration parameters: CountComments, Max, CountAsOne. +Metrics/ClassLength: + Exclude: + - 'app/models/channel/whatsapp.rb' + - 'app/services/whatsapp/incoming_message_wuzapi_service.rb' + - 'app/services/whatsapp/providers/wuzapi/payload_parser.rb' + - 'app/services/wuzapi/client.rb' + - 'enterprise/app/services/captain/assistant/agent_runner_service.rb' + - 'enterprise/app/services/captain/tools/generate_pix_tool.rb' + +# Offense count: 41 +# Configuration parameters: AllowedMethods, AllowedPatterns. +Metrics/CyclomaticComplexity: + Max: 23 + +# Offense count: 29 +# Configuration parameters: CountComments, Max, CountAsOne, AllowedMethods, AllowedPatterns. +Metrics/MethodLength: + Exclude: + - 'app/controllers/public/api/v1/captain/inter_webhooks_controller.rb' + - 'app/models/channel/whatsapp.rb' + - 'app/services/evolution_api/client.rb' + - 'app/services/whatsapp/decryption_service.rb' + - 'app/services/whatsapp/incoming_message_wuzapi_service.rb' + - 'app/services/whatsapp/providers/evolution_api/payload_parser.rb' + - 'app/services/whatsapp/providers/wuzapi/payload_parser.rb' + - 'app/services/whatsapp/providers/wuzapi_service.rb' + - 'app/services/wuzapi/client.rb' + - 'enterprise/app/services/captain/assistant/agent_runner_service.rb' + - 'enterprise/app/services/captain/inter/webhook_setup_service.rb' + - 'enterprise/app/services/captain/reservations/marker_builder.rb' + - 'enterprise/app/services/captain/tools/generate_pix_tool.rb' + +# Offense count: 1 +# Configuration parameters: CountKeywordArgs, MaxOptionalParameters. +Metrics/ParameterLists: + Max: 6 + +# Offense count: 29 +# Configuration parameters: AllowedMethods, AllowedPatterns. +Metrics/PerceivedComplexity: + Max: 25 + +# Offense count: 2 +# Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames. +# AllowedNames: as, at, by, cc, db, id, if, in, io, ip, of, on, os, pp, to +Naming/MethodParameterName: + Exclude: + - 'app/services/whatsapp/decryption_service.rb' + +# Offense count: 13 +# Configuration parameters: IgnoreNameless, IgnoreSymbolicNames. +RSpec/VerifiedDoubles: + Exclude: + - 'spec/enterprise/services/captain/assistant/agent_runner_service_spec.rb' + +# Offense count: 1 +Rails/AfterCommitOverride: + Exclude: + - 'app/models/channel/whatsapp.rb' + +# Offense count: 1 +# Configuration parameters: Include. +# Include: **/app/models/**/*.rb +Rails/HasManyOrHasOneDependent: + Exclude: + - 'enterprise/app/models/captain/brand.rb' + +# Offense count: 5 +# Configuration parameters: IgnoreScopes, Include. +# Include: **/app/models/**/*.rb +Rails/InverseOf: + Exclude: + - 'enterprise/app/models/captain/brand.rb' + - 'enterprise/app/models/captain/reservation.rb' + +# Offense count: 5 +# Configuration parameters: ForbiddenMethods, AllowedMethods. +# ForbiddenMethods: decrement!, decrement_counter, increment!, increment_counter, insert, insert!, insert_all, insert_all!, toggle!, touch, touch_all, update_all, update_attribute, update_column, update_columns, update_counters, upsert, upsert_all +Rails/SkipsModelValidations: + Exclude: + - 'app/controllers/api/v1/accounts/inboxes_controller.rb' + - 'enterprise/app/controllers/api/v1/accounts/captain/inboxes_controller.rb' + - 'enterprise/app/services/captain/payments/confirmation_service.rb' + - 'enterprise/app/services/captain/tools/generate_pix_tool.rb' + +# Offense count: 3 +# Configuration parameters: TransactionMethods. +Rails/TransactionExitStatement: + Exclude: + - 'app/services/whatsapp/incoming_message_wuzapi_service.rb' + +# Offense count: 2 +# Configuration parameters: MinBranchesCount. +Style/HashLikeCase: + Exclude: + - 'app/services/whatsapp/providers/evolution_api/payload_parser.rb' + - 'app/services/whatsapp/providers/wuzapi/payload_parser.rb' diff --git a/AGENTS.md b/AGENTS.md index 301633d7f..92a16bdd3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,104 +1,197 @@ -# Chatwoot Development Guidelines +# CLAUDE.md -## Build / Test / Lint +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. -- **Setup**: `bundle install && pnpm install` -- **Run Dev**: `pnpm dev` or `overmind start -f ./Procfile.dev` -- **Seed Local Test Data**: `bundle exec rails db:seed` (quickly populates minimal data for standard feature verification) -- **Seed Search Test Data**: `bundle exec rails search:setup_test_data` (bulk fixture generation for search/performance/manual load scenarios) -- **Seed Account Sample Data (richer test data)**: `Seeders::AccountSeeder` is available as an internal utility and is exposed through Super Admin `Accounts#seed`, but can be used directly in dev workflows too: - - UI path: Super Admin → Accounts → Seed (enqueues `Internal::SeedAccountJob`). - - CLI path: `bundle exec rails runner "Internal::SeedAccountJob.perform_now(Account.find())"` (or call `Seeders::AccountSeeder.new(account: Account.find()).perform!` directly). -- **Lint JS/Vue**: `pnpm eslint` / `pnpm eslint:fix` -- **Lint Ruby**: `bundle exec rubocop -a` -- **Test JS**: `pnpm test` or `pnpm test:watch` -- **Test Ruby**: `bundle exec rspec spec/path/to/file_spec.rb` -- **Single Test**: `bundle exec rspec spec/path/to/file_spec.rb:LINE_NUMBER` -- **Run Project**: `overmind start -f Procfile.dev` -- **Ruby Version**: Manage Ruby via `rbenv` and install the version listed in `.ruby-version` (e.g., `rbenv install $(cat .ruby-version)`) -- **rbenv setup**: Before running any `bundle` or `rspec` commands, init rbenv in your shell (`eval "$(rbenv init -)"`) so the correct Ruby/Bundler versions are used -- Always prefer `bundle exec` for Ruby CLI tasks (rspec, rake, rubocop, etc.) +## Project Overview -## Code Style +This is a **Chatwoot** customer engagement platform (open-source alternative to Intercom/Zendesk), customized for **fazer.ai**. It includes the **Synkra AIOS** framework overlay for AI-orchestrated development workflows. -- **Ruby**: Follow RuboCop rules (150 character max line length) -- **Vue/JS**: Use ESLint (Airbnb base + Vue 3 recommended) -- **Vue Components**: Use PascalCase -- **Events**: Use camelCase -- **I18n**: No bare strings in templates; use i18n -- **Error Handling**: Use custom exceptions (`lib/custom_exceptions/`) -- **Models**: Validate presence/uniqueness, add proper indexes -- **Type Safety**: Use PropTypes in Vue, strong params in Rails -- **Naming**: Use clear, descriptive names with consistent casing -- **Vue API**: Always use Composition API with ` diff --git a/app/javascript/dashboard/components-next/captain/pageComponents/customTool/CustomToolForm.vue b/app/javascript/dashboard/components-next/captain/pageComponents/customTool/CustomToolForm.vue index 5c4ea9d5b..b7079ca36 100644 --- a/app/javascript/dashboard/components-next/captain/pageComponents/customTool/CustomToolForm.vue +++ b/app/javascript/dashboard/components-next/captain/pageComponents/customTool/CustomToolForm.vue @@ -92,6 +92,10 @@ const authTypeOptions = computed(() => [ value: 'api_key', label: t('CAPTAIN.CUSTOM_TOOLS.FORM.AUTH_TYPES.API_KEY'), }, + { + value: 'custom_headers', + label: t('CAPTAIN.CUSTOM_TOOLS.FORM.AUTH_TYPES.CUSTOM_HEADERS'), + }, ]); const v$ = useVuelidate(validationRules, state); diff --git a/app/javascript/dashboard/components-next/sidebar/Sidebar.vue b/app/javascript/dashboard/components-next/sidebar/Sidebar.vue index a4a63ea18..6822182c6 100644 --- a/app/javascript/dashboard/components-next/sidebar/Sidebar.vue +++ b/app/javascript/dashboard/components-next/sidebar/Sidebar.vue @@ -288,7 +288,7 @@ const menuItems = computed(() => { children: sortedInboxes.value.map(inbox => ({ name: `${inbox.name}-${inbox.id}`, label: inbox.name, - icon: h(ChannelIcon, { inbox, class: 'size-[12px]' }), + icon: () => h(ChannelIcon, { inbox, class: 'size-[12px]' }), to: accountScopedRoute('inbox_dashboard', { inbox_id: inbox.id }), component: leafProps => h(ChannelLeaf, { @@ -306,10 +306,11 @@ const menuItems = computed(() => { children: labels.value.map(label => ({ name: `${label.title}-${label.id}`, label: label.title, - icon: h('span', { - class: `size-[8px] rounded-sm`, - style: { backgroundColor: label.color }, - }), + icon: () => + h('span', { + class: `size-[8px] rounded-sm`, + style: { backgroundColor: label.color }, + }), to: accountScopedRoute('label_conversations', { label: label.title, }), @@ -386,6 +387,27 @@ const menuItems = computed(() => { navigationPath: 'captain_assistants_settings_index', }), }, + { + name: 'Pix Units', + label: t('SIDEBAR.CAPTAIN_PIX_UNITS'), + activeOn: ['captain_settings_units', 'captain_settings_units_edit'], + to: accountScopedRoute('captain_settings_units'), + }, + { + name: 'Gallery', + label: t('SIDEBAR.CAPTAIN_GALLERY'), + activeOn: [ + 'captain_settings_gallery', + 'captain_settings_gallery_edit', + ], + to: accountScopedRoute('captain_settings_gallery'), + }, + { + name: 'Reservations', + label: t('SIDEBAR.CAPTAIN_RESERVATIONS'), + activeOn: ['captain_reservations_index'], + to: accountScopedRoute('captain_reservations_index'), + }, ], }, { @@ -434,10 +456,11 @@ const menuItems = computed(() => { children: labels.value.map(label => ({ name: `${label.title}-${label.id}`, label: label.title, - icon: h('span', { - class: `size-[8px] rounded-sm`, - style: { backgroundColor: label.color }, - }), + icon: () => + h('span', { + class: `size-[8px] rounded-sm`, + style: { backgroundColor: label.color }, + }), to: accountScopedRoute( 'contacts_dashboard_labels_index', { label: label.title }, diff --git a/app/javascript/dashboard/components/widgets/conversation/ConversationCard.vue b/app/javascript/dashboard/components/widgets/conversation/ConversationCard.vue index 9f7805b4e..ded0841fd 100644 --- a/app/javascript/dashboard/components/widgets/conversation/ConversationCard.vue +++ b/app/javascript/dashboard/components/widgets/conversation/ConversationCard.vue @@ -14,6 +14,7 @@ import PriorityMark from './PriorityMark.vue'; import SLACardLabel from './components/SLACardLabel.vue'; import ContextMenu from 'dashboard/components/ui/ContextMenu.vue'; import VoiceCallStatus from './VoiceCallStatus.vue'; +import ReservationMarker from './ReservationMarker.vue'; const props = defineProps({ activeLabel: { type: String, default: '' }, @@ -112,6 +113,8 @@ const showMetaSection = computed(() => { }); const hasSlaPolicyId = computed(() => props.chat?.sla_policy_id); +const reservationMarker = computed(() => props.chat?.reservation_marker || {}); +const hasReservationMarker = computed(() => reservationMarker.value?.visible); const showLabelsSection = computed(() => { return props.chat.labels?.length > 0 || hasSlaPolicyId.value; @@ -370,6 +373,9 @@ const deleteConversation = () => { +
+ +
store.getters.getSelectedChat); const accountId = computed(() => store.getters.getCurrentAccountId); @@ -90,6 +93,15 @@ const hasMultipleInboxes = computed( ); const hasSlaPolicyId = computed(() => props.chat?.sla_policy_id); +const reservationMarker = computed(() => props.chat?.reservation_marker || {}); +const hasReservationMarker = computed(() => reservationMarker.value?.visible); + +const openReservationSummary = () => { + updateUISettings({ + is_contact_sidebar_open: true, + is_reservation_summary_open: true, + }); +};