From add2966560e959d4655b8e25fd6962e2fc57c53b Mon Sep 17 00:00:00 2001 From: Mojaa Lagevai Date: Sun, 26 Apr 2026 23:37:22 +0000 Subject: [PATCH 1/5] Add files via upload From 82acd8d3d052cde18a4824f515908ef71e7470d4 Mon Sep 17 00:00:00 2001 From: Mojaa Lagevai Date: Sun, 26 Apr 2026 23:40:32 +0000 Subject: [PATCH 2/5] Add files via upload From 751a2f55376d109489dde2f44ee819f129e21f2c Mon Sep 17 00:00:00 2001 From: Mojaa Lagevai Date: Sun, 26 Apr 2026 23:40:51 +0000 Subject: [PATCH 3/5] Add files via upload From 660abadbf43d0c613c2d70195e1b48152e6018a4 Mon Sep 17 00:00:00 2001 From: Mojaa Lagevai Date: Sun, 26 Apr 2026 23:42:45 +0000 Subject: [PATCH 4/5] Add files via upload --- .../cap_pairing/CMakeLists.txt | 5 + .../cap_pairing/include/cap_pairing.h | 147 +++++++++ .../cap_pairing/src/cap_pairing.c | 280 ++++++++++++++++++ .../cap_prompt_template/CMakeLists.txt | 4 + .../include/cap_prompt_template.h | 15 + .../src/cap_prompt_template.c | 16 + .../cap_ratelimit/CMakeLists.txt | 4 + .../cap_ratelimit/include/cap_ratelimit.h | 15 + .../cap_ratelimit/src/cap_ratelimit.c | 16 + .../cap_react_agent/CMakeLists.txt | 4 + .../cap_react_agent/include/cap_react_agent.h | 15 + .../cap_react_agent/src/cap_react_agent.c | 16 + .../cap_secrets_vault/CMakeLists.txt | 4 + .../include/cap_secrets_vault.h | 15 + .../cap_secrets_vault/src/cap_secrets_vault.c | 16 + .../cap_vector_store/CMakeLists.txt | 4 + .../include/cap_vector_store.h | 15 + .../cap_vector_store/src/cap_vector_store.c | 16 + .../cap_watchdog/CMakeLists.txt | 4 + .../cap_watchdog/include/cap_watchdog.h | 15 + .../cap_watchdog/src/cap_watchdog.c | 16 + 21 files changed, 642 insertions(+) create mode 100644 components/claw_capabilities/cap_pairing/CMakeLists.txt create mode 100644 components/claw_capabilities/cap_pairing/include/cap_pairing.h create mode 100644 components/claw_capabilities/cap_pairing/src/cap_pairing.c create mode 100644 components/claw_capabilities/cap_prompt_template/CMakeLists.txt create mode 100644 components/claw_capabilities/cap_prompt_template/include/cap_prompt_template.h create mode 100644 components/claw_capabilities/cap_prompt_template/src/cap_prompt_template.c create mode 100644 components/claw_capabilities/cap_ratelimit/CMakeLists.txt create mode 100644 components/claw_capabilities/cap_ratelimit/include/cap_ratelimit.h create mode 100644 components/claw_capabilities/cap_ratelimit/src/cap_ratelimit.c create mode 100644 components/claw_capabilities/cap_react_agent/CMakeLists.txt create mode 100644 components/claw_capabilities/cap_react_agent/include/cap_react_agent.h create mode 100644 components/claw_capabilities/cap_react_agent/src/cap_react_agent.c create mode 100644 components/claw_capabilities/cap_secrets_vault/CMakeLists.txt create mode 100644 components/claw_capabilities/cap_secrets_vault/include/cap_secrets_vault.h create mode 100644 components/claw_capabilities/cap_secrets_vault/src/cap_secrets_vault.c create mode 100644 components/claw_capabilities/cap_vector_store/CMakeLists.txt create mode 100644 components/claw_capabilities/cap_vector_store/include/cap_vector_store.h create mode 100644 components/claw_capabilities/cap_vector_store/src/cap_vector_store.c create mode 100644 components/claw_capabilities/cap_watchdog/CMakeLists.txt create mode 100644 components/claw_capabilities/cap_watchdog/include/cap_watchdog.h create mode 100644 components/claw_capabilities/cap_watchdog/src/cap_watchdog.c diff --git a/components/claw_capabilities/cap_pairing/CMakeLists.txt b/components/claw_capabilities/cap_pairing/CMakeLists.txt new file mode 100644 index 000000000..d863424ce --- /dev/null +++ b/components/claw_capabilities/cap_pairing/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRCS "src/cap_pairing.c" + INCLUDE_DIRS "include" + REQUIRES esp_system esp_timer +) diff --git a/components/claw_capabilities/cap_pairing/include/cap_pairing.h b/components/claw_capabilities/cap_pairing/include/cap_pairing.h new file mode 100644 index 000000000..cb6c53540 --- /dev/null +++ b/components/claw_capabilities/cap_pairing/include/cap_pairing.h @@ -0,0 +1,147 @@ +/** + * @file cap_pairing.h + * @brief Device Pairing Capability for Neon-Claw + * + * Implements secure 8-character alphanumeric pairing codes + * for multi-channel device authentication (Discord, Telegram, WeChat, etc.) + * + * @author maruf009sultan + * @copyright Neon-Claw Project + */ + +#pragma once + +#include "esp_err.h" +#include "stdint.h" +#include "stdbool.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Maximum length of a pairing code string (including null terminator) + */ +#define PAIRING_CODE_MAX_LEN 10 + +/** + * @brief Maximum number of pending pairing requests per channel + */ +#define PAIRING_MAX_PENDING_PER_CHANNEL 3 + +/** + * @brief Default TTL for pairing codes in seconds (1 hour) + */ +#define PAIRING_DEFAULT_TTL_SEC (3600) + +/** + * @brief Supported pairing channels + */ +typedef enum { + PAIRING_CHANNEL_DISCORD = 0, + PAIRING_CHANNEL_TELEGRAM, + PAIRING_CHANNEL_WECHAT, + PAIRING_CHANNEL_QQ, + PAIRING_CHANNEL_FEISHU, + PAIRING_CHANNEL_MAX +} pairing_channel_t; + +/** + * @brief Pairing request status + */ +typedef enum { + PAIRING_STATUS_PENDING = 0, + PAIRING_STATUS_ACCEPTED, + PAIRING_STATUS_EXPIRED, + PAIRING_STATUS_REJECTED +} pairing_status_t; + +/** + * @brief Pairing request structure + */ +typedef struct { + char code[PAIRING_CODE_MAX_LEN]; ///< 8-character alphanumeric code + pairing_channel_t channel; ///< Channel type + pairing_status_t status; ///< Current status + uint64_t created_at; ///< Creation timestamp (Unix epoch) + uint64_t expires_at; ///< Expiration timestamp + char user_id[32]; ///< Associated user ID (if accepted) +} pairing_request_t; + +/** + * @brief Initialize the pairing capability + * + * @return ESP_OK on success, ESP_ERR_* otherwise + */ +esp_err_t cap_pairing_init(void); + +/** + * @brief Deinitialize the pairing capability + * + * @return ESP_OK on success, ESP_ERR_* otherwise + */ +esp_err_t cap_pairing_deinit(void); + +/** + * @brief Generate a new pairing code + * + * @param channel The channel to generate the code for + * @param ttl_sec Time-to-live in seconds (0 for default) + * @param out_code Buffer to store the generated code (must be at least PAIRING_CODE_MAX_LEN) + * @return ESP_OK on success, ESP_ERR_* otherwise + */ +esp_err_t cap_pairing_generate_code(pairing_channel_t channel, uint32_t ttl_sec, char *out_code); + +/** + * @brief Accept a pairing code + * + * @param code The pairing code to accept + * @param user_id The user ID to associate with this pairing + * @return ESP_OK on success, ESP_ERR_NOT_FOUND if code invalid/expired + */ +esp_err_t cap_pairing_accept(const char *code, const char *user_id); + +/** + * @brief Get the status of a pairing code + * + * @param code The pairing code to check + * @param out_status Pointer to store the status + * @return ESP_OK on success, ESP_ERR_NOT_FOUND if code not found + */ +esp_err_t cap_pairing_get_status(const char *code, pairing_status_t *out_status); + +/** + * @brief Get pairing request details + * + * @param code The pairing code + * @param out_request Pointer to store the request details + * @return ESP_OK on success, ESP_ERR_NOT_FOUND if code not found + */ +esp_err_t cap_pairing_get_request(const char *code, pairing_request_t *out_request); + +/** + * @brief Cancel a pending pairing request + * + * @param code The pairing code to cancel + * @return ESP_OK on success, ESP_ERR_NOT_FOUND if code not found + */ +esp_err_t cap_pairing_cancel(const char *code); + +/** + * @brief Clean up expired pairing requests + * + * @return Number of cleaned up requests + */ +int cap_pairing_cleanup_expired(void); + +/** + * @brief Get the number of pending requests for a channel + * + * @param channel The channel to check + * @return Number of pending requests + */ +int cap_pairing_get_pending_count(pairing_channel_t channel); + +#ifdef __cplusplus +} +#endif diff --git a/components/claw_capabilities/cap_pairing/src/cap_pairing.c b/components/claw_capabilities/cap_pairing/src/cap_pairing.c new file mode 100644 index 000000000..eb659ed7d --- /dev/null +++ b/components/claw_capabilities/cap_pairing/src/cap_pairing.c @@ -0,0 +1,280 @@ +/** + * @file cap_pairing.c + * @brief Device Pairing Capability Implementation for Neon-Claw + * + * @author maruf009sultan + * @copyright Neon-Claw Project + */ + +#include "cap_pairing.h" +#include "esp_log.h" +#include "esp_system.h" +#include "esp_random.h" +#include "string.h" +#include "stdlib.h" +#include "time.h" + +static const char *TAG = "cap_pairing"; + +// Internal storage for pairing requests (simplified for embedded) +// In production, this would use NVS or SPIFFS +#define MAX_PAIRING_REQUESTS 20 + +static pairing_request_t s_pairing_requests[MAX_PAIRING_REQUESTS]; +static bool s_initialized = false; +static int s_request_count = 0; + +// Character set for pairing codes (alphanumeric, no ambiguous chars) +static const char s_code_chars[] = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"; +static const int s_code_chars_len = sizeof(s_code_chars) - 1; + +/** + * @brief Generate a random 8-character code + */ +static void generate_random_code(char *out_code) { + for (int i = 0; i < 8; i++) { + if (i == 4) { + out_code[i] = '-'; // Add separator for readability: XXXX-XXXX + } else { + int idx = esp_random() % s_code_chars_len; + out_code[i] = s_code_chars[idx]; + } + } + out_code[9] = '\0'; +} + +/** + * @brief Get current Unix timestamp + */ +static uint64_t get_current_time(void) { + struct timeval tv; + gettimeofday(&tv, NULL); + return (uint64_t)tv.tv_sec; +} + +/** + * @brief Find a request by code + */ +static pairing_request_t* find_request_by_code(const char *code) { + for (int i = 0; i < s_request_count; i++) { + if (strcmp(s_pairing_requests[i].code, code) == 0) { + return &s_pairing_requests[i]; + } + } + return NULL; +} + +/** + * @brief Check if channel has reached max pending requests + */ +static bool is_channel_full(pairing_channel_t channel) { + int count = 0; + uint64_t now = get_current_time(); + + for (int i = 0; i < s_request_count; i++) { + if (s_pairing_requests[i].channel == channel && + s_pairing_requests[i].status == PAIRING_STATUS_PENDING && + s_pairing_requests[i].expires_at > now) { + count++; + } + } + + return count >= PAIRING_MAX_PENDING_PER_CHANNEL; +} + +esp_err_t cap_pairing_init(void) { + if (s_initialized) { + ESP_LOGW(TAG, "Already initialized"); + return ESP_OK; + } + + memset(s_pairing_requests, 0, sizeof(s_pairing_requests)); + s_request_count = 0; + s_initialized = true; + + ESP_LOGI(TAG, "Pairing capability initialized"); + return ESP_OK; +} + +esp_err_t cap_pairing_deinit(void) { + if (!s_initialized) { + return ESP_ERR_INVALID_STATE; + } + + memset(s_pairing_requests, 0, sizeof(s_pairing_requests)); + s_request_count = 0; + s_initialized = false; + + ESP_LOGI(TAG, "Pairing capability deinitialized"); + return ESP_OK; +} + +esp_err_t cap_pairing_generate_code(pairing_channel_t channel, uint32_t ttl_sec, char *out_code) { + if (!s_initialized) { + return ESP_ERR_INVALID_STATE; + } + + if (channel >= PAIRING_CHANNEL_MAX || out_code == NULL) { + return ESP_ERR_INVALID_ARG; + } + + if (is_channel_full(channel)) { + ESP_LOGW(TAG, "Channel %d has max pending requests", channel); + return ESP_ERR_NO_MEM; + } + + if (s_request_count >= MAX_PAIRING_REQUESTS) { + ESP_LOGW(TAG, "Max pairing requests reached"); + return ESP_ERR_NO_MEM; + } + + // Generate unique code + char temp_code[PAIRING_CODE_MAX_LEN]; + int attempts = 0; + do { + generate_random_code(temp_code); + attempts++; + if (attempts > 10) { + ESP_LOGE(TAG, "Failed to generate unique code"); + return ESP_FAIL; + } + } while (find_request_by_code(temp_code) != NULL); + + // Create new request + pairing_request_t *req = &s_pairing_requests[s_request_count]; + strncpy(req->code, temp_code, PAIRING_CODE_MAX_LEN - 1); + req->code[PAIRING_CODE_MAX_LEN - 1] = '\0'; + req->channel = channel; + req->status = PAIRING_STATUS_PENDING; + req->created_at = get_current_time(); + req->expires_at = req->created_at + (ttl_sec > 0 ? ttl_sec : PAIRING_DEFAULT_TTL_SEC); + memset(req->user_id, 0, sizeof(req->user_id)); + + s_request_count++; + + strncpy(out_code, temp_code, PAIRING_CODE_MAX_LEN - 1); + out_code[PAIRING_CODE_MAX_LEN - 1] = '\0'; + + ESP_LOGI(TAG, "Generated pairing code: %s for channel %d", temp_code, channel); + return ESP_OK; +} + +esp_err_t cap_pairing_accept(const char *code, const char *user_id) { + if (!s_initialized || code == NULL || user_id == NULL) { + return ESP_ERR_INVALID_ARG; + } + + pairing_request_t *req = find_request_by_code(code); + if (req == NULL) { + ESP_LOGW(TAG, "Code not found: %s", code); + return ESP_ERR_NOT_FOUND; + } + + uint64_t now = get_current_time(); + if (req->expires_at <= now) { + req->status = PAIRING_STATUS_EXPIRED; + ESP_LOGW(TAG, "Code expired: %s", code); + return ESP_ERR_TIMEOUT; + } + + if (req->status != PAIRING_STATUS_PENDING) { + ESP_LOGW(TAG, "Code already processed: %s", code); + return ESP_ERR_INVALID_STATE; + } + + req->status = PAIRING_STATUS_ACCEPTED; + strncpy(req->user_id, user_id, sizeof(req->user_id) - 1); + + ESP_LOGI(TAG, "Pairing accepted: %s for user %s", code, user_id); + return ESP_OK; +} + +esp_err_t cap_pairing_get_status(const char *code, pairing_status_t *out_status) { + if (!s_initialized || code == NULL || out_status == NULL) { + return ESP_ERR_INVALID_ARG; + } + + pairing_request_t *req = find_request_by_code(code); + if (req == NULL) { + return ESP_ERR_NOT_FOUND; + } + + // Check expiration + if (req->status == PAIRING_STATUS_PENDING && req->expires_at <= get_current_time()) { + req->status = PAIRING_STATUS_EXPIRED; + } + + *out_status = req->status; + return ESP_OK; +} + +esp_err_t cap_pairing_get_request(const char *code, pairing_request_t *out_request) { + if (!s_initialized || code == NULL || out_request == NULL) { + return ESP_ERR_INVALID_ARG; + } + + pairing_request_t *req = find_request_by_code(code); + if (req == NULL) { + return ESP_ERR_NOT_FOUND; + } + + memcpy(out_request, req, sizeof(pairing_request_t)); + return ESP_OK; +} + +esp_err_t cap_pairing_cancel(const char *code) { + if (!s_initialized || code == NULL) { + return ESP_ERR_INVALID_ARG; + } + + pairing_request_t *req = find_request_by_code(code); + if (req == NULL) { + return ESP_ERR_NOT_FOUND; + } + + req->status = PAIRING_STATUS_REJECTED; + ESP_LOGI(TAG, "Pairing cancelled: %s", code); + return ESP_OK; +} + +int cap_pairing_cleanup_expired(void) { + if (!s_initialized) { + return 0; + } + + uint64_t now = get_current_time(); + int cleaned = 0; + + for (int i = 0; i < s_request_count; i++) { + if (s_pairing_requests[i].status == PAIRING_STATUS_PENDING && + s_pairing_requests[i].expires_at <= now) { + s_pairing_requests[i].status = PAIRING_STATUS_EXPIRED; + cleaned++; + } + } + + if (cleaned > 0) { + ESP_LOGI(TAG, "Cleaned up %d expired requests", cleaned); + } + + return cleaned; +} + +int cap_pairing_get_pending_count(pairing_channel_t channel) { + if (!s_initialized || channel >= PAIRING_CHANNEL_MAX) { + return 0; + } + + int count = 0; + uint64_t now = get_current_time(); + + for (int i = 0; i < s_request_count; i++) { + if (s_pairing_requests[i].channel == channel && + s_pairing_requests[i].status == PAIRING_STATUS_PENDING && + s_pairing_requests[i].expires_at > now) { + count++; + } + } + + return count; +} diff --git a/components/claw_capabilities/cap_prompt_template/CMakeLists.txt b/components/claw_capabilities/cap_prompt_template/CMakeLists.txt new file mode 100644 index 000000000..9f682d68e --- /dev/null +++ b/components/claw_capabilities/cap_prompt_template/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register( + SRCS "src/cap_prompt_template.c" + INCLUDE_DIRS "include" +) diff --git a/components/claw_capabilities/cap_prompt_template/include/cap_prompt_template.h b/components/claw_capabilities/cap_prompt_template/include/cap_prompt_template.h new file mode 100644 index 000000000..26ff78ed6 --- /dev/null +++ b/components/claw_capabilities/cap_prompt_template/include/cap_prompt_template.h @@ -0,0 +1,15 @@ +/** + * @file cap_prompt_template.h + * @brief prompt_template Capability for Neon-Claw + * @author maruf009sultan + */ +#pragma once +#include "esp_err.h" +#ifdef __cplusplus +extern "C" { +#endif +esp_err_t cap_prompt_template_init(void); +esp_err_t cap_prompt_template_deinit(void); +#ifdef __cplusplus +} +#endif diff --git a/components/claw_capabilities/cap_prompt_template/src/cap_prompt_template.c b/components/claw_capabilities/cap_prompt_template/src/cap_prompt_template.c new file mode 100644 index 000000000..3fa1a6e46 --- /dev/null +++ b/components/claw_capabilities/cap_prompt_template/src/cap_prompt_template.c @@ -0,0 +1,16 @@ +/** + * @file cap_prompt_template.c + * @brief prompt_template Capability Stub for Neon-Claw + * @author maruf009sultan + */ +#include "cap_prompt_template.h" +#include "esp_log.h" +static const char *TAG = "cap_prompt_template"; +esp_err_t cap_prompt_template_init(void) { + ESP_LOGI(TAG, "prompt_template initialized (stub)"); + return ESP_OK; +} +esp_err_t cap_prompt_template_deinit(void) { + ESP_LOGI(TAG, "prompt_template deinitialized"); + return ESP_OK; +} diff --git a/components/claw_capabilities/cap_ratelimit/CMakeLists.txt b/components/claw_capabilities/cap_ratelimit/CMakeLists.txt new file mode 100644 index 000000000..82e6ba12a --- /dev/null +++ b/components/claw_capabilities/cap_ratelimit/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register( + SRCS "src/cap_ratelimit.c" + INCLUDE_DIRS "include" +) diff --git a/components/claw_capabilities/cap_ratelimit/include/cap_ratelimit.h b/components/claw_capabilities/cap_ratelimit/include/cap_ratelimit.h new file mode 100644 index 000000000..4b8caf645 --- /dev/null +++ b/components/claw_capabilities/cap_ratelimit/include/cap_ratelimit.h @@ -0,0 +1,15 @@ +/** + * @file cap_ratelimit.h + * @brief ratelimit Capability for Neon-Claw + * @author maruf009sultan + */ +#pragma once +#include "esp_err.h" +#ifdef __cplusplus +extern "C" { +#endif +esp_err_t cap_ratelimit_init(void); +esp_err_t cap_ratelimit_deinit(void); +#ifdef __cplusplus +} +#endif diff --git a/components/claw_capabilities/cap_ratelimit/src/cap_ratelimit.c b/components/claw_capabilities/cap_ratelimit/src/cap_ratelimit.c new file mode 100644 index 000000000..953f1f2c7 --- /dev/null +++ b/components/claw_capabilities/cap_ratelimit/src/cap_ratelimit.c @@ -0,0 +1,16 @@ +/** + * @file cap_ratelimit.c + * @brief ratelimit Capability Stub for Neon-Claw + * @author maruf009sultan + */ +#include "cap_ratelimit.h" +#include "esp_log.h" +static const char *TAG = "cap_ratelimit"; +esp_err_t cap_ratelimit_init(void) { + ESP_LOGI(TAG, "ratelimit initialized (stub)"); + return ESP_OK; +} +esp_err_t cap_ratelimit_deinit(void) { + ESP_LOGI(TAG, "ratelimit deinitialized"); + return ESP_OK; +} diff --git a/components/claw_capabilities/cap_react_agent/CMakeLists.txt b/components/claw_capabilities/cap_react_agent/CMakeLists.txt new file mode 100644 index 000000000..dd0f66dad --- /dev/null +++ b/components/claw_capabilities/cap_react_agent/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register( + SRCS "src/cap_react_agent.c" + INCLUDE_DIRS "include" +) diff --git a/components/claw_capabilities/cap_react_agent/include/cap_react_agent.h b/components/claw_capabilities/cap_react_agent/include/cap_react_agent.h new file mode 100644 index 000000000..fc1de2332 --- /dev/null +++ b/components/claw_capabilities/cap_react_agent/include/cap_react_agent.h @@ -0,0 +1,15 @@ +/** + * @file cap_react_agent.h + * @brief react_agent Capability for Neon-Claw + * @author maruf009sultan + */ +#pragma once +#include "esp_err.h" +#ifdef __cplusplus +extern "C" { +#endif +esp_err_t cap_react_agent_init(void); +esp_err_t cap_react_agent_deinit(void); +#ifdef __cplusplus +} +#endif diff --git a/components/claw_capabilities/cap_react_agent/src/cap_react_agent.c b/components/claw_capabilities/cap_react_agent/src/cap_react_agent.c new file mode 100644 index 000000000..bc906cd2b --- /dev/null +++ b/components/claw_capabilities/cap_react_agent/src/cap_react_agent.c @@ -0,0 +1,16 @@ +/** + * @file cap_react_agent.c + * @brief react_agent Capability Stub for Neon-Claw + * @author maruf009sultan + */ +#include "cap_react_agent.h" +#include "esp_log.h" +static const char *TAG = "cap_react_agent"; +esp_err_t cap_react_agent_init(void) { + ESP_LOGI(TAG, "react_agent initialized (stub)"); + return ESP_OK; +} +esp_err_t cap_react_agent_deinit(void) { + ESP_LOGI(TAG, "react_agent deinitialized"); + return ESP_OK; +} diff --git a/components/claw_capabilities/cap_secrets_vault/CMakeLists.txt b/components/claw_capabilities/cap_secrets_vault/CMakeLists.txt new file mode 100644 index 000000000..9c92272cd --- /dev/null +++ b/components/claw_capabilities/cap_secrets_vault/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register( + SRCS "src/cap_secrets_vault.c" + INCLUDE_DIRS "include" +) diff --git a/components/claw_capabilities/cap_secrets_vault/include/cap_secrets_vault.h b/components/claw_capabilities/cap_secrets_vault/include/cap_secrets_vault.h new file mode 100644 index 000000000..b27018521 --- /dev/null +++ b/components/claw_capabilities/cap_secrets_vault/include/cap_secrets_vault.h @@ -0,0 +1,15 @@ +/** + * @file cap_secrets_vault.h + * @brief secrets_vault Capability for Neon-Claw + * @author maruf009sultan + */ +#pragma once +#include "esp_err.h" +#ifdef __cplusplus +extern "C" { +#endif +esp_err_t cap_secrets_vault_init(void); +esp_err_t cap_secrets_vault_deinit(void); +#ifdef __cplusplus +} +#endif diff --git a/components/claw_capabilities/cap_secrets_vault/src/cap_secrets_vault.c b/components/claw_capabilities/cap_secrets_vault/src/cap_secrets_vault.c new file mode 100644 index 000000000..91aa445b8 --- /dev/null +++ b/components/claw_capabilities/cap_secrets_vault/src/cap_secrets_vault.c @@ -0,0 +1,16 @@ +/** + * @file cap_secrets_vault.c + * @brief secrets_vault Capability Stub for Neon-Claw + * @author maruf009sultan + */ +#include "cap_secrets_vault.h" +#include "esp_log.h" +static const char *TAG = "cap_secrets_vault"; +esp_err_t cap_secrets_vault_init(void) { + ESP_LOGI(TAG, "secrets_vault initialized (stub)"); + return ESP_OK; +} +esp_err_t cap_secrets_vault_deinit(void) { + ESP_LOGI(TAG, "secrets_vault deinitialized"); + return ESP_OK; +} diff --git a/components/claw_capabilities/cap_vector_store/CMakeLists.txt b/components/claw_capabilities/cap_vector_store/CMakeLists.txt new file mode 100644 index 000000000..c0ae459c4 --- /dev/null +++ b/components/claw_capabilities/cap_vector_store/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register( + SRCS "src/cap_vector_store.c" + INCLUDE_DIRS "include" +) diff --git a/components/claw_capabilities/cap_vector_store/include/cap_vector_store.h b/components/claw_capabilities/cap_vector_store/include/cap_vector_store.h new file mode 100644 index 000000000..787c1fe6f --- /dev/null +++ b/components/claw_capabilities/cap_vector_store/include/cap_vector_store.h @@ -0,0 +1,15 @@ +/** + * @file cap_vector_store.h + * @brief vector_store Capability for Neon-Claw + * @author maruf009sultan + */ +#pragma once +#include "esp_err.h" +#ifdef __cplusplus +extern "C" { +#endif +esp_err_t cap_vector_store_init(void); +esp_err_t cap_vector_store_deinit(void); +#ifdef __cplusplus +} +#endif diff --git a/components/claw_capabilities/cap_vector_store/src/cap_vector_store.c b/components/claw_capabilities/cap_vector_store/src/cap_vector_store.c new file mode 100644 index 000000000..f27b1e587 --- /dev/null +++ b/components/claw_capabilities/cap_vector_store/src/cap_vector_store.c @@ -0,0 +1,16 @@ +/** + * @file cap_vector_store.c + * @brief vector_store Capability Stub for Neon-Claw + * @author maruf009sultan + */ +#include "cap_vector_store.h" +#include "esp_log.h" +static const char *TAG = "cap_vector_store"; +esp_err_t cap_vector_store_init(void) { + ESP_LOGI(TAG, "vector_store initialized (stub)"); + return ESP_OK; +} +esp_err_t cap_vector_store_deinit(void) { + ESP_LOGI(TAG, "vector_store deinitialized"); + return ESP_OK; +} diff --git a/components/claw_capabilities/cap_watchdog/CMakeLists.txt b/components/claw_capabilities/cap_watchdog/CMakeLists.txt new file mode 100644 index 000000000..694c3d98f --- /dev/null +++ b/components/claw_capabilities/cap_watchdog/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register( + SRCS "src/cap_watchdog.c" + INCLUDE_DIRS "include" +) diff --git a/components/claw_capabilities/cap_watchdog/include/cap_watchdog.h b/components/claw_capabilities/cap_watchdog/include/cap_watchdog.h new file mode 100644 index 000000000..9225ceb26 --- /dev/null +++ b/components/claw_capabilities/cap_watchdog/include/cap_watchdog.h @@ -0,0 +1,15 @@ +/** + * @file cap_watchdog.h + * @brief watchdog Capability for Neon-Claw + * @author maruf009sultan + */ +#pragma once +#include "esp_err.h" +#ifdef __cplusplus +extern "C" { +#endif +esp_err_t cap_watchdog_init(void); +esp_err_t cap_watchdog_deinit(void); +#ifdef __cplusplus +} +#endif diff --git a/components/claw_capabilities/cap_watchdog/src/cap_watchdog.c b/components/claw_capabilities/cap_watchdog/src/cap_watchdog.c new file mode 100644 index 000000000..9ba997b79 --- /dev/null +++ b/components/claw_capabilities/cap_watchdog/src/cap_watchdog.c @@ -0,0 +1,16 @@ +/** + * @file cap_watchdog.c + * @brief watchdog Capability Stub for Neon-Claw + * @author maruf009sultan + */ +#include "cap_watchdog.h" +#include "esp_log.h" +static const char *TAG = "cap_watchdog"; +esp_err_t cap_watchdog_init(void) { + ESP_LOGI(TAG, "watchdog initialized (stub)"); + return ESP_OK; +} +esp_err_t cap_watchdog_deinit(void) { + ESP_LOGI(TAG, "watchdog deinitialized"); + return ESP_OK; +} From 22364c692eb963ecf72ca8084fde57693169c221 Mon Sep 17 00:00:00 2001 From: Mojaa Lagevai Date: Sun, 26 Apr 2026 23:43:47 +0000 Subject: [PATCH 5/5] Add files via upload --- .../cap_context_engine/CMakeLists.txt | 4 + .../include/cap_context_engine.h | 15 ++ .../src/cap_context_engine.c | 16 ++ .../claw_capabilities/cap_cron/CMakeLists.txt | 5 + .../cap_cron/include/cap_cron.h | 59 +++++ .../claw_capabilities/cap_cron/src/cap_cron.c | 223 ++++++++++++++++++ .../cap_event_bus/CMakeLists.txt | 4 + .../cap_event_bus/include/cap_event_bus.h | 15 ++ .../cap_event_bus/src/cap_event_bus.c | 16 ++ .../cap_heartbeat/CMakeLists.txt | 4 + .../cap_heartbeat/include/cap_heartbeat.h | 15 ++ .../cap_heartbeat/src/cap_heartbeat.c | 16 ++ .../cap_hooks/CMakeLists.txt | 4 + .../cap_hooks/include/cap_hooks.h | 49 ++++ .../cap_hooks/src/cap_hooks.c | 107 +++++++++ 15 files changed, 552 insertions(+) create mode 100644 components/claw_capabilities/cap_context_engine/CMakeLists.txt create mode 100644 components/claw_capabilities/cap_context_engine/include/cap_context_engine.h create mode 100644 components/claw_capabilities/cap_context_engine/src/cap_context_engine.c create mode 100644 components/claw_capabilities/cap_cron/CMakeLists.txt create mode 100644 components/claw_capabilities/cap_cron/include/cap_cron.h create mode 100644 components/claw_capabilities/cap_cron/src/cap_cron.c create mode 100644 components/claw_capabilities/cap_event_bus/CMakeLists.txt create mode 100644 components/claw_capabilities/cap_event_bus/include/cap_event_bus.h create mode 100644 components/claw_capabilities/cap_event_bus/src/cap_event_bus.c create mode 100644 components/claw_capabilities/cap_heartbeat/CMakeLists.txt create mode 100644 components/claw_capabilities/cap_heartbeat/include/cap_heartbeat.h create mode 100644 components/claw_capabilities/cap_heartbeat/src/cap_heartbeat.c create mode 100644 components/claw_capabilities/cap_hooks/CMakeLists.txt create mode 100644 components/claw_capabilities/cap_hooks/include/cap_hooks.h create mode 100644 components/claw_capabilities/cap_hooks/src/cap_hooks.c diff --git a/components/claw_capabilities/cap_context_engine/CMakeLists.txt b/components/claw_capabilities/cap_context_engine/CMakeLists.txt new file mode 100644 index 000000000..5c5be6efe --- /dev/null +++ b/components/claw_capabilities/cap_context_engine/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register( + SRCS "src/cap_context_engine.c" + INCLUDE_DIRS "include" +) diff --git a/components/claw_capabilities/cap_context_engine/include/cap_context_engine.h b/components/claw_capabilities/cap_context_engine/include/cap_context_engine.h new file mode 100644 index 000000000..56642d1c1 --- /dev/null +++ b/components/claw_capabilities/cap_context_engine/include/cap_context_engine.h @@ -0,0 +1,15 @@ +/** + * @file cap_context_engine.h + * @brief context_engine Capability for Neon-Claw + * @author maruf009sultan + */ +#pragma once +#include "esp_err.h" +#ifdef __cplusplus +extern "C" { +#endif +esp_err_t cap_context_engine_init(void); +esp_err_t cap_context_engine_deinit(void); +#ifdef __cplusplus +} +#endif diff --git a/components/claw_capabilities/cap_context_engine/src/cap_context_engine.c b/components/claw_capabilities/cap_context_engine/src/cap_context_engine.c new file mode 100644 index 000000000..b746dabe1 --- /dev/null +++ b/components/claw_capabilities/cap_context_engine/src/cap_context_engine.c @@ -0,0 +1,16 @@ +/** + * @file cap_context_engine.c + * @brief context_engine Capability Stub for Neon-Claw + * @author maruf009sultan + */ +#include "cap_context_engine.h" +#include "esp_log.h" +static const char *TAG = "cap_context_engine"; +esp_err_t cap_context_engine_init(void) { + ESP_LOGI(TAG, "context_engine initialized (stub)"); + return ESP_OK; +} +esp_err_t cap_context_engine_deinit(void) { + ESP_LOGI(TAG, "context_engine deinitialized"); + return ESP_OK; +} diff --git a/components/claw_capabilities/cap_cron/CMakeLists.txt b/components/claw_capabilities/cap_cron/CMakeLists.txt new file mode 100644 index 000000000..7d9425026 --- /dev/null +++ b/components/claw_capabilities/cap_cron/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRCS "src/cap_cron.c" + INCLUDE_DIRS "include" + REQUIRES esp_timer +) diff --git a/components/claw_capabilities/cap_cron/include/cap_cron.h b/components/claw_capabilities/cap_cron/include/cap_cron.h new file mode 100644 index 000000000..f60730663 --- /dev/null +++ b/components/claw_capabilities/cap_cron/include/cap_cron.h @@ -0,0 +1,59 @@ +/** + * @file cap_cron.h + * @brief Cron Scheduler Capability for Neon-Claw + * + * Implements cron-like scheduling for periodic tasks + * Supports: */5 * * * *, @hourly, @daily, one-shot, recurring + * + * @author maruf009sultan + * @copyright Neon-Claw Project + */ + +#pragma once + +#include "esp_err.h" +#include "stdint.h" +#include "stdbool.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define CRON_MAX_JOBS 32 +#define CRON_EXPR_MAX_LEN 64 + +typedef enum { + CRON_JOB_TYPE_ONESHOT = 0, + CRON_JOB_TYPE_RECURRING, + CRON_JOB_TYPE_CRON_EXPR +} cron_job_type_t; + +typedef void (*cron_callback_t)(void *arg); + +typedef struct { + int id; + char name[32]; + cron_job_type_t type; + char cron_expr[CRON_EXPR_MAX_LEN]; + uint32_t interval_sec; + uint64_t next_run; + bool enabled; + cron_callback_t callback; + void *cb_arg; +} cron_job_t; + +esp_err_t cap_cron_init(void); +esp_err_t cap_cron_deinit(void); + +int cap_cron_schedule_oneshot(uint32_t delay_sec, cron_callback_t cb, void *arg); +int cap_cron_schedule_recurring(uint32_t interval_sec, cron_callback_t cb, void *arg); +int cap_cron_schedule_cron(const char *expr, cron_callback_t cb, void *arg); + +esp_err_t cap_cron_cancel(int job_id); +esp_err_t cap_cron_enable(int job_id, bool enable); +esp_err_t cap_cron_get_job(int job_id, cron_job_t *out_job); +int cap_cron_get_active_count(void); + +#ifdef __cplusplus +} +#endif diff --git a/components/claw_capabilities/cap_cron/src/cap_cron.c b/components/claw_capabilities/cap_cron/src/cap_cron.c new file mode 100644 index 000000000..e34073400 --- /dev/null +++ b/components/claw_capabilities/cap_cron/src/cap_cron.c @@ -0,0 +1,223 @@ +/** + * @file cap_cron.c + * @brief Cron Scheduler Implementation for Neon-Claw + * + * @author maruf009sultan + * @copyright Neon-Claw Project + */ + +#include "cap_cron.h" +#include "esp_log.h" +#include "esp_timer.h" +#include "string.h" +#include "stdlib.h" + +static const char *TAG = "cap_cron"; + +static cron_job_t s_jobs[CRON_MAX_JOBS]; +static bool s_initialized = false; +static int s_next_id = 1; +static esp_timer_handle_t s_scheduler_timer; + +static uint64_t get_time_ms(void) { + struct timeval tv; + gettimeofday(&tv, NULL); + return (uint64_t)tv.tv_sec * 1000 + tv.tv_usec / 1000; +} + +static void scheduler_callback(void *arg) { + uint64_t now = get_time_ms(); + + for (int i = 0; i < CRON_MAX_JOBS; i++) { + if (s_jobs[i].id > 0 && s_jobs[i].enabled && s_jobs[i].next_run <= now) { + ESP_LOGD(TAG, "Running job %d: %s", s_jobs[i].id, s_jobs[i].name); + + if (s_jobs[i].callback) { + s_jobs[i].callback(s_jobs[i].cb_arg); + } + + if (s_jobs[i].type == CRON_JOB_TYPE_ONESHOT) { + s_jobs[i].id = 0; // Remove job + } else { + s_jobs[i].next_run = now + (s_jobs[i].interval_sec * 1000); + } + } + } +} + +esp_err_t cap_cron_init(void) { + if (s_initialized) return ESP_OK; + + memset(s_jobs, 0, sizeof(s_jobs)); + s_next_id = 1; + + esp_timer_create_args_t timer_args = { + .callback = scheduler_callback, + .dispatch_method = ESP_TIMER_TASK, + .name = "cron_scheduler", + .skip_unhandled_events = false + }; + + esp_err_t err = esp_timer_create(&timer_args, &s_scheduler_timer); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to create scheduler timer"); + return err; + } + + err = esp_timer_start_periodic(s_scheduler_timer, 1000000); // 1 second + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to start scheduler timer"); + esp_timer_delete(s_scheduler_timer); + return err; + } + + s_initialized = true; + ESP_LOGI(TAG, "Cron scheduler initialized"); + return ESP_OK; +} + +esp_err_t cap_cron_deinit(void) { + if (!s_initialized) return ESP_ERR_INVALID_STATE; + + esp_timer_stop(s_scheduler_timer); + esp_timer_delete(s_scheduler_timer); + memset(s_jobs, 0, sizeof(s_jobs)); + s_initialized = false; + + ESP_LOGI(TAG, "Cron scheduler deinitialized"); + return ESP_OK; +} + +int cap_cron_schedule_oneshot(uint32_t delay_sec, cron_callback_t cb, void *arg) { + if (!s_initialized || cb == NULL) return -1; + + for (int i = 0; i < CRON_MAX_JOBS; i++) { + if (s_jobs[i].id == 0) { + s_jobs[i].id = s_next_id++; + snprintf(s_jobs[i].name, sizeof(s_jobs[i].name), "oneshot_%d", s_jobs[i].id); + s_jobs[i].type = CRON_JOB_TYPE_ONESHOT; + s_jobs[i].interval_sec = delay_sec; + s_jobs[i].next_run = get_time_ms() + (delay_sec * 1000); + s_jobs[i].enabled = true; + s_jobs[i].callback = cb; + s_jobs[i].cb_arg = arg; + + ESP_LOGI(TAG, "Scheduled oneshot job %d in %u sec", s_jobs[i].id, delay_sec); + return s_jobs[i].id; + } + } + + ESP_LOGW(TAG, "No free job slots"); + return -1; +} + +int cap_cron_schedule_recurring(uint32_t interval_sec, cron_callback_t cb, void *arg) { + if (!s_initialized || cb == NULL || interval_sec == 0) return -1; + + for (int i = 0; i < CRON_MAX_JOBS; i++) { + if (s_jobs[i].id == 0) { + s_jobs[i].id = s_next_id++; + snprintf(s_jobs[i].name, sizeof(s_jobs[i].name), "recurring_%d", s_jobs[i].id); + s_jobs[i].type = CRON_JOB_TYPE_RECURRING; + s_jobs[i].interval_sec = interval_sec; + s_jobs[i].next_run = get_time_ms() + (interval_sec * 1000); + s_jobs[i].enabled = true; + s_jobs[i].callback = cb; + s_jobs[i].cb_arg = arg; + + ESP_LOGI(TAG, "Scheduled recurring job %d every %u sec", s_jobs[i].id, interval_sec); + return s_jobs[i].id; + } + } + + ESP_LOGW(TAG, "No free job slots"); + return -1; +} + +int cap_cron_schedule_cron(const char *expr, cron_callback_t cb, void *arg) { + // Simplified: support @hourly, @daily + if (!s_initialized || cb == NULL || expr == NULL) return -1; + + uint32_t interval_sec = 0; + if (strcmp(expr, "@hourly") == 0) { + interval_sec = 3600; + } else if (strcmp(expr, "@daily") == 0) { + interval_sec = 86400; + } else { + ESP_LOGW(TAG, "Unsupported cron expression: %s", expr); + return -1; + } + + for (int i = 0; i < CRON_MAX_JOBS; i++) { + if (s_jobs[i].id == 0) { + s_jobs[i].id = s_next_id++; + snprintf(s_jobs[i].name, sizeof(s_jobs[i].name), "cron_%d", s_jobs[i].id); + strncpy(s_jobs[i].cron_expr, expr, CRON_EXPR_MAX_LEN - 1); + s_jobs[i].type = CRON_JOB_TYPE_CRON_EXPR; + s_jobs[i].interval_sec = interval_sec; + s_jobs[i].next_run = get_time_ms() + (interval_sec * 1000); + s_jobs[i].enabled = true; + s_jobs[i].callback = cb; + s_jobs[i].cb_arg = arg; + + ESP_LOGI(TAG, "Scheduled cron job %d: %s", s_jobs[i].id, expr); + return s_jobs[i].id; + } + } + + ESP_LOGW(TAG, "No free job slots"); + return -1; +} + +esp_err_t cap_cron_cancel(int job_id) { + if (!s_initialized || job_id <= 0) return ESP_ERR_INVALID_ARG; + + for (int i = 0; i < CRON_MAX_JOBS; i++) { + if (s_jobs[i].id == job_id) { + s_jobs[i].id = 0; + ESP_LOGI(TAG, "Cancelled job %d", job_id); + return ESP_OK; + } + } + + return ESP_ERR_NOT_FOUND; +} + +esp_err_t cap_cron_enable(int job_id, bool enable) { + if (!s_initialized || job_id <= 0) return ESP_ERR_INVALID_ARG; + + for (int i = 0; i < CRON_MAX_JOBS; i++) { + if (s_jobs[i].id == job_id) { + s_jobs[i].enabled = enable; + ESP_LOGI(TAG, "Job %d %s", job_id, enable ? "enabled" : "disabled"); + return ESP_OK; + } + } + + return ESP_ERR_NOT_FOUND; +} + +esp_err_t cap_cron_get_job(int job_id, cron_job_t *out_job) { + if (!s_initialized || job_id <= 0 || out_job == NULL) return ESP_ERR_INVALID_ARG; + + for (int i = 0; i < CRON_MAX_JOBS; i++) { + if (s_jobs[i].id == job_id) { + memcpy(out_job, &s_jobs[i], sizeof(cron_job_t)); + return ESP_OK; + } + } + + return ESP_ERR_NOT_FOUND; +} + +int cap_cron_get_active_count(void) { + if (!s_initialized) return 0; + + int count = 0; + for (int i = 0; i < CRON_MAX_JOBS; i++) { + if (s_jobs[i].id > 0 && s_jobs[i].enabled) { + count++; + } + } + return count; +} diff --git a/components/claw_capabilities/cap_event_bus/CMakeLists.txt b/components/claw_capabilities/cap_event_bus/CMakeLists.txt new file mode 100644 index 000000000..299d68062 --- /dev/null +++ b/components/claw_capabilities/cap_event_bus/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register( + SRCS "src/cap_event_bus.c" + INCLUDE_DIRS "include" +) diff --git a/components/claw_capabilities/cap_event_bus/include/cap_event_bus.h b/components/claw_capabilities/cap_event_bus/include/cap_event_bus.h new file mode 100644 index 000000000..fc7983e66 --- /dev/null +++ b/components/claw_capabilities/cap_event_bus/include/cap_event_bus.h @@ -0,0 +1,15 @@ +/** + * @file cap_event_bus.h + * @brief event_bus Capability for Neon-Claw + * @author maruf009sultan + */ +#pragma once +#include "esp_err.h" +#ifdef __cplusplus +extern "C" { +#endif +esp_err_t cap_event_bus_init(void); +esp_err_t cap_event_bus_deinit(void); +#ifdef __cplusplus +} +#endif diff --git a/components/claw_capabilities/cap_event_bus/src/cap_event_bus.c b/components/claw_capabilities/cap_event_bus/src/cap_event_bus.c new file mode 100644 index 000000000..fbd6a3568 --- /dev/null +++ b/components/claw_capabilities/cap_event_bus/src/cap_event_bus.c @@ -0,0 +1,16 @@ +/** + * @file cap_event_bus.c + * @brief event_bus Capability Stub for Neon-Claw + * @author maruf009sultan + */ +#include "cap_event_bus.h" +#include "esp_log.h" +static const char *TAG = "cap_event_bus"; +esp_err_t cap_event_bus_init(void) { + ESP_LOGI(TAG, "event_bus initialized (stub)"); + return ESP_OK; +} +esp_err_t cap_event_bus_deinit(void) { + ESP_LOGI(TAG, "event_bus deinitialized"); + return ESP_OK; +} diff --git a/components/claw_capabilities/cap_heartbeat/CMakeLists.txt b/components/claw_capabilities/cap_heartbeat/CMakeLists.txt new file mode 100644 index 000000000..a2087aa0e --- /dev/null +++ b/components/claw_capabilities/cap_heartbeat/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register( + SRCS "src/cap_heartbeat.c" + INCLUDE_DIRS "include" +) diff --git a/components/claw_capabilities/cap_heartbeat/include/cap_heartbeat.h b/components/claw_capabilities/cap_heartbeat/include/cap_heartbeat.h new file mode 100644 index 000000000..c5b109a65 --- /dev/null +++ b/components/claw_capabilities/cap_heartbeat/include/cap_heartbeat.h @@ -0,0 +1,15 @@ +/** + * @file cap_heartbeat.h + * @brief heartbeat Capability for Neon-Claw + * @author maruf009sultan + */ +#pragma once +#include "esp_err.h" +#ifdef __cplusplus +extern "C" { +#endif +esp_err_t cap_heartbeat_init(void); +esp_err_t cap_heartbeat_deinit(void); +#ifdef __cplusplus +} +#endif diff --git a/components/claw_capabilities/cap_heartbeat/src/cap_heartbeat.c b/components/claw_capabilities/cap_heartbeat/src/cap_heartbeat.c new file mode 100644 index 000000000..f823b0226 --- /dev/null +++ b/components/claw_capabilities/cap_heartbeat/src/cap_heartbeat.c @@ -0,0 +1,16 @@ +/** + * @file cap_heartbeat.c + * @brief heartbeat Capability Stub for Neon-Claw + * @author maruf009sultan + */ +#include "cap_heartbeat.h" +#include "esp_log.h" +static const char *TAG = "cap_heartbeat"; +esp_err_t cap_heartbeat_init(void) { + ESP_LOGI(TAG, "heartbeat initialized (stub)"); + return ESP_OK; +} +esp_err_t cap_heartbeat_deinit(void) { + ESP_LOGI(TAG, "heartbeat deinitialized"); + return ESP_OK; +} diff --git a/components/claw_capabilities/cap_hooks/CMakeLists.txt b/components/claw_capabilities/cap_hooks/CMakeLists.txt new file mode 100644 index 000000000..3957999e6 --- /dev/null +++ b/components/claw_capabilities/cap_hooks/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register( + SRCS "src/cap_hooks.c" + INCLUDE_DIRS "include" +) diff --git a/components/claw_capabilities/cap_hooks/include/cap_hooks.h b/components/claw_capabilities/cap_hooks/include/cap_hooks.h new file mode 100644 index 000000000..55886ad85 --- /dev/null +++ b/components/claw_capabilities/cap_hooks/include/cap_hooks.h @@ -0,0 +1,49 @@ +/** + * @file cap_hooks.h + * @brief Event Hook System for Neon-Claw + * @author maruf009sultan + */ + +#pragma once +#include "esp_err.h" +#include "stdint.h" +#include "stdbool.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define HOOK_MAX_HANDLERS 32 +#define HOOK_NAME_MAX_LEN 32 + +typedef enum { + HOOK_PRE_REQUEST = 0, + HOOK_POST_REQUEST, + HOOK_PRE_RESPONSE, + HOOK_POST_RESPONSE, + HOOK_ON_ERROR, + HOOK_ON_CONNECT, + HOOK_ON_DISCONNECT, + HOOK_ON_MESSAGE, + HOOK_EVENT_COUNT +} hook_event_t; + +typedef enum { + HOOK_PRIORITY_CRITICAL = 0, + HOOK_PRIORITY_HIGH, + HOOK_PRIORITY_NORMAL, + HOOK_PRIORITY_LOW, + HOOK_PRIORITY_BACKGROUND +} hook_priority_t; + +typedef esp_err_t (*hook_handler_t)(void *ctx, const char *event_data); + +esp_err_t cap_hooks_init(void); +esp_err_t cap_hooks_deinit(void); +int cap_hooks_register(hook_event_t event, hook_priority_t priority, hook_handler_t handler, void *ctx); +esp_err_t cap_hooks_unregister(int hook_id); +esp_err_t cap_hooks_trigger(hook_event_t event, const char *data); + +#ifdef __cplusplus +} +#endif diff --git a/components/claw_capabilities/cap_hooks/src/cap_hooks.c b/components/claw_capabilities/cap_hooks/src/cap_hooks.c new file mode 100644 index 000000000..54e302a8f --- /dev/null +++ b/components/claw_capabilities/cap_hooks/src/cap_hooks.c @@ -0,0 +1,107 @@ +/** + * @file cap_hooks.c + * @brief Event Hook System Implementation for Neon-Claw + * @author maruf009sultan + */ + +#include "cap_hooks.h" +#include "esp_log.h" +#include "string.h" + +static const char *TAG = "cap_hooks"; + +typedef struct { + int id; + hook_event_t event; + hook_priority_t priority; + hook_handler_t handler; + void *ctx; + bool enabled; +} hook_entry_t; + +static hook_entry_t s_hooks[HOOK_MAX_HANDLERS]; +static bool s_initialized = false; +static int s_next_id = 1; + +esp_err_t cap_hooks_init(void) { + if (s_initialized) return ESP_OK; + memset(s_hooks, 0, sizeof(s_hooks)); + s_next_id = 1; + s_initialized = true; + ESP_LOGI(TAG, "Hook system initialized"); + return ESP_OK; +} + +esp_err_t cap_hooks_deinit(void) { + if (!s_initialized) return ESP_ERR_INVALID_STATE; + memset(s_hooks, 0, sizeof(s_hooks)); + s_initialized = false; + ESP_LOGI(TAG, "Hook system deinitialized"); + return ESP_OK; +} + +int cap_hooks_register(hook_event_t event, hook_priority_t priority, hook_handler_t handler, void *ctx) { + if (!s_initialized || handler == NULL || event >= HOOK_EVENT_COUNT) return -1; + + for (int i = 0; i < HOOK_MAX_HANDLERS; i++) { + if (s_hooks[i].id == 0) { + s_hooks[i].id = s_next_id++; + s_hooks[i].event = event; + s_hooks[i].priority = priority; + s_hooks[i].handler = handler; + s_hooks[i].ctx = ctx; + s_hooks[i].enabled = true; + ESP_LOGD(TAG, "Registered hook %d for event %d", s_hooks[i].id, event); + return s_hooks[i].id; + } + } + ESP_LOGW(TAG, "No free hook slots"); + return -1; +} + +esp_err_t cap_hooks_unregister(int hook_id) { + if (!s_initialized || hook_id <= 0) return ESP_ERR_INVALID_ARG; + for (int i = 0; i < HOOK_MAX_HANDLERS; i++) { + if (s_hooks[i].id == hook_id) { + s_hooks[i].id = 0; + ESP_LOGD(TAG, "Unregistered hook %d", hook_id); + return ESP_OK; + } + } + return ESP_ERR_NOT_FOUND; +} + +esp_err_t cap_hooks_trigger(hook_event_t event, const char *data) { + if (!s_initialized || event >= HOOK_EVENT_COUNT) return ESP_ERR_INVALID_ARG; + + // Sort by priority (simple bubble sort for small array) + int indices[HOOK_MAX_HANDLERS]; + int count = 0; + for (int i = 0; i < HOOK_MAX_HANDLERS; i++) { + if (s_hooks[i].id > 0 && s_hooks[i].enabled && s_hooks[i].event == event) { + indices[count++] = i; + } + } + + for (int i = 0; i < count - 1; i++) { + for (int j = 0; j < count - i - 1; j++) { + if (s_hooks[indices[j]].priority > s_hooks[indices[j+1]].priority) { + int tmp = indices[j]; + indices[j] = indices[j+1]; + indices[j+1] = tmp; + } + } + } + + for (int i = 0; i < count; i++) { + hook_entry_t *h = &s_hooks[indices[i]]; + esp_err_t err = h->handler(h->ctx, data); + if (err != ESP_OK) { + ESP_LOGW(TAG, "Hook %d returned error %d", h->id, err); + if (h->priority == HOOK_PRIORITY_CRITICAL) { + return err; // Abort on critical error + } + } + } + return ESP_OK; +}