diff --git a/claw/services/ai/ai_memory.c b/claw/services/ai/ai_memory.c index 75f54f6..4365cbe 100644 --- a/claw/services/ai/ai_memory.c +++ b/claw/services/ai/ai_memory.c @@ -229,103 +229,52 @@ typedef struct { static ltm_entry_t s_ltm[LTM_MAX_ENTRIES]; static int s_ltm_count; -#ifdef CLAW_PLATFORM_ESP_IDF - -#include "nvs_flash.h" -#include "nvs.h" +#include "osal/claw_kv.h" -#define LTM_NVS_NAMESPACE "claw_ltm" -#define LTM_NVS_KEY_DATA "data" -#define LTM_NVS_KEY_COUNT "cnt" +#define LTM_KV_NS "claw_ltm" +#define LTM_KV_DATA "data" +#define LTM_KV_CNT "cnt" -static int ltm_flush_nvs(void) +static int ltm_persist(void) { - nvs_handle_t h; - esp_err_t err = nvs_open(LTM_NVS_NAMESPACE, NVS_READWRITE, &h); - if (err != ESP_OK) { - CLAW_LOGE(TAG, "nvs_open failed: %s", esp_err_to_name(err)); - return CLAW_ERROR; - } - - nvs_set_u8(h, LTM_NVS_KEY_COUNT, (uint8_t)s_ltm_count); - nvs_set_blob(h, LTM_NVS_KEY_DATA, s_ltm, - s_ltm_count * sizeof(ltm_entry_t)); - err = nvs_commit(h); - nvs_close(h); - - if (err != ESP_OK) { - CLAW_LOGE(TAG, "nvs_commit failed: %s", esp_err_to_name(err)); + if (claw_kv_set_u8(LTM_KV_NS, LTM_KV_CNT, + (uint8_t)s_ltm_count) != CLAW_OK) { return CLAW_ERROR; } - return CLAW_OK; + return claw_kv_set_blob(LTM_KV_NS, LTM_KV_DATA, s_ltm, + s_ltm_count * sizeof(ltm_entry_t)); } -static int ltm_load_nvs(void) +static void ltm_load(void) { - nvs_handle_t h; - esp_err_t err = nvs_open(LTM_NVS_NAMESPACE, NVS_READONLY, &h); - if (err == ESP_ERR_NVS_NOT_FOUND) { - s_ltm_count = 0; - return CLAW_OK; - } - if (err != ESP_OK) { - CLAW_LOGE(TAG, "nvs_open failed: %s", esp_err_to_name(err)); - return CLAW_ERROR; - } - uint8_t cnt = 0; - err = nvs_get_u8(h, LTM_NVS_KEY_COUNT, &cnt); - if (err != ESP_OK) { - nvs_close(h); + if (claw_kv_get_u8(LTM_KV_NS, LTM_KV_CNT, &cnt) != CLAW_OK) { s_ltm_count = 0; - return CLAW_OK; + return; } - if (cnt > LTM_MAX_ENTRIES) { cnt = LTM_MAX_ENTRIES; } - size_t blob_size = cnt * sizeof(ltm_entry_t); - err = nvs_get_blob(h, LTM_NVS_KEY_DATA, s_ltm, &blob_size); - nvs_close(h); - - if (err == ESP_OK) { + if (claw_kv_get_blob(LTM_KV_NS, LTM_KV_DATA, + s_ltm, &blob_size) == CLAW_OK) { s_ltm_count = cnt; } else { s_ltm_count = 0; } - - return CLAW_OK; } -static int ltm_persist(void) { return ltm_flush_nvs(); } - int ai_ltm_init(void) { memset(s_ltm, 0, sizeof(s_ltm)); s_ltm_count = 0; - ltm_load_nvs(); - CLAW_LOGI(TAG, "long-term initialized, %d/%d entries from flash", + ltm_load(); + CLAW_LOGI(TAG, "long-term initialized, %d/%d entries", s_ltm_count, LTM_MAX_ENTRIES); return CLAW_OK; } -#else /* non-ESP-IDF: RAM-only LTM */ - -static int ltm_persist(void) { return CLAW_OK; } - -int ai_ltm_init(void) -{ - memset(s_ltm, 0, sizeof(s_ltm)); - s_ltm_count = 0; - CLAW_LOGI(TAG, "long-term initialized (RAM-only), max=%d", - LTM_MAX_ENTRIES); - return CLAW_OK; -} - -#endif - int ai_ltm_save(const char *key, const char *value) { if (!key || !value || key[0] == '\0') { diff --git a/claw/services/ai/ai_skill.c b/claw/services/ai/ai_skill.c index 933bc04..bd73c7b 100644 --- a/claw/services/ai/ai_skill.c +++ b/claw/services/ai/ai_skill.c @@ -30,25 +30,16 @@ typedef struct { static ai_skill_t s_skills[SKILL_MAX]; static int s_count; -/* --- NVS persistence (ESP-IDF only) --- */ +/* --- KV persistence (platform-independent via OSAL) --- */ -#ifdef CLAW_PLATFORM_ESP_IDF -#include "nvs_flash.h" -#include "nvs.h" +#include "osal/claw_kv.h" -#define SKILL_NVS_NS "claw_skill" -#define SKILL_NVS_CNT "cnt" -#define SKILL_NVS_DATA "data" +#define SKILL_KV_NS "claw_skill" +#define SKILL_KV_CNT "cnt" +#define SKILL_KV_DATA "data" -static int skill_flush_nvs(void) +static int skill_persist(void) { - nvs_handle_t h; - esp_err_t err = nvs_open(SKILL_NVS_NS, NVS_READWRITE, &h); - if (err != ESP_OK) { - CLAW_LOGE(TAG, "nvs_open: %s", esp_err_to_name(err)); - return CLAW_ERROR; - } - /* Count user-created skills */ uint8_t cnt = 0; for (int i = 0; i < s_count; i++) { @@ -57,13 +48,9 @@ static int skill_flush_nvs(void) } } - nvs_set_u8(h, SKILL_NVS_CNT, cnt); + claw_kv_set_u8(SKILL_KV_NS, SKILL_KV_CNT, cnt); if (cnt > 0) { - /* - * Pack user skills into a contiguous blob. - * Each entry: ai_skill_t (fixed size). - */ ai_skill_t buf[SKILL_MAX]; int idx = 0; for (int i = 0; i < s_count; i++) { @@ -71,38 +58,26 @@ static int skill_flush_nvs(void) memcpy(&buf[idx++], &s_skills[i], sizeof(ai_skill_t)); } } - nvs_set_blob(h, SKILL_NVS_DATA, buf, - idx * sizeof(ai_skill_t)); - } else { - nvs_erase_key(h, SKILL_NVS_DATA); + return claw_kv_set_blob(SKILL_KV_NS, SKILL_KV_DATA, buf, + idx * sizeof(ai_skill_t)); } - err = nvs_commit(h); - nvs_close(h); - return (err == ESP_OK) ? CLAW_OK : CLAW_ERROR; + claw_kv_delete(SKILL_KV_NS, SKILL_KV_DATA); + return CLAW_OK; } -static void skill_load_nvs(void) +static void skill_load(void) { - nvs_handle_t h; - esp_err_t err = nvs_open(SKILL_NVS_NS, NVS_READONLY, &h); - if (err != ESP_OK) { - return; - } - uint8_t cnt = 0; - err = nvs_get_u8(h, SKILL_NVS_CNT, &cnt); - if (err != ESP_OK || cnt == 0) { - nvs_close(h); + if (claw_kv_get_u8(SKILL_KV_NS, SKILL_KV_CNT, &cnt) != CLAW_OK + || cnt == 0) { return; } ai_skill_t buf[SKILL_MAX]; size_t blob_sz = cnt * sizeof(ai_skill_t); - err = nvs_get_blob(h, SKILL_NVS_DATA, buf, &blob_sz); - nvs_close(h); - - if (err != ESP_OK) { + if (claw_kv_get_blob(SKILL_KV_NS, SKILL_KV_DATA, + buf, &blob_sz) != CLAW_OK) { return; } @@ -112,16 +87,9 @@ static void skill_load_nvs(void) s_count++; } - CLAW_LOGI(TAG, "restored %d user skill(s) from NVS", (int)cnt); + CLAW_LOGI(TAG, "restored %d user skill(s) from KV", (int)cnt); } -#else /* non-ESP-IDF */ - -static int skill_flush_nvs(void) { return CLAW_OK; } -static void skill_load_nvs(void) {} - -#endif - /* --- Built-in skill registration --- */ static void register_builtins(void) @@ -161,7 +129,7 @@ int ai_skill_init(void) s_count = 0; register_builtins(); - skill_load_nvs(); + skill_load(); CLAW_LOGI(TAG, "initialized, %d skill(s)", s_count); return CLAW_OK; @@ -209,7 +177,7 @@ int ai_skill_remove(const char *name) } s_count--; memset(&s_skills[s_count], 0, sizeof(ai_skill_t)); - skill_flush_nvs(); + skill_persist(); CLAW_LOGI(TAG, "removed skill: %s", name); return CLAW_OK; } @@ -315,7 +283,7 @@ static int tool_create_skill(const cJSON *params, cJSON *result) return CLAW_ERROR; } - skill_flush_nvs(); + skill_persist(); cJSON_AddStringToObject(result, "status", "ok"); char msg[64]; diff --git a/claw/shell/shell_commands.c b/claw/shell/shell_commands.c index 564e2b7..8dd876a 100644 --- a/claw/shell/shell_commands.c +++ b/claw/shell/shell_commands.c @@ -26,68 +26,44 @@ #include "claw/services/swarm/swarm.h" #endif -#ifdef CLAW_PLATFORM_ESP_IDF -#include "nvs.h" -#endif +#include "osal/claw_kv.h" -/* ---- NVS persistence (ESP-IDF only) ---- */ +/* ---- KV persistence (platform-independent via OSAL) ---- */ void shell_nvs_save_str(const char *ns, const char *key, const char *val) { -#ifdef CLAW_PLATFORM_ESP_IDF - nvs_handle_t nvs; - - if (nvs_open(ns, NVS_READWRITE, &nvs) != ESP_OK) { - printf("[error] NVS open failed\n"); - return; + if (claw_kv_set_str(ns, key, val) != CLAW_OK) { + printf("[error] KV save failed\n"); } - nvs_set_str(nvs, key, val); - nvs_commit(nvs); - nvs_close(nvs); -#else - (void)ns; - (void)key; - (void)val; -#endif } void shell_nvs_config_load(void) { -#ifdef CLAW_PLATFORM_ESP_IDF - nvs_handle_t nvs; char buf[256]; - size_t len; - /* Load AI config from NVS (overrides compile-time defaults) */ - if (nvs_open(SHELL_NVS_NS_AI, NVS_READONLY, &nvs) == ESP_OK) { - len = sizeof(buf); - if (nvs_get_str(nvs, "api_key", buf, &len) == ESP_OK) { - ai_set_api_key(buf); - } - len = sizeof(buf); - if (nvs_get_str(nvs, "api_url", buf, &len) == ESP_OK) { - ai_set_api_url(buf); - } - len = sizeof(buf); - if (nvs_get_str(nvs, "model", buf, &len) == ESP_OK) { - ai_set_model(buf); - } - nvs_close(nvs); + /* Load AI config from KV (overrides compile-time defaults) */ + if (claw_kv_get_str(SHELL_NVS_NS_AI, "api_key", + buf, sizeof(buf)) == CLAW_OK) { + ai_set_api_key(buf); + } + if (claw_kv_get_str(SHELL_NVS_NS_AI, "api_url", + buf, sizeof(buf)) == CLAW_OK) { + ai_set_api_url(buf); + } + if (claw_kv_get_str(SHELL_NVS_NS_AI, "model", + buf, sizeof(buf)) == CLAW_OK) { + ai_set_model(buf); } - /* Load Feishu config from NVS */ - if (nvs_open(SHELL_NVS_NS_FEISHU, NVS_READONLY, &nvs) == ESP_OK) { - len = sizeof(buf); - if (nvs_get_str(nvs, "app_id", buf, &len) == ESP_OK) { - feishu_set_app_id(buf); - } - len = sizeof(buf); - if (nvs_get_str(nvs, "app_secret", buf, &len) == ESP_OK) { - feishu_set_app_secret(buf); - } - nvs_close(nvs); + /* Load Feishu config from KV */ + if (claw_kv_get_str(SHELL_NVS_NS_FEISHU, "app_id", + buf, sizeof(buf)) == CLAW_OK) { + feishu_set_app_id(buf); + } + if (claw_kv_get_str(SHELL_NVS_NS_FEISHU, "app_secret", + buf, sizeof(buf)) == CLAW_OK) { + feishu_set_app_secret(buf); } -#endif } /* ---- Common command handlers ---- */ diff --git a/include/osal/claw_kv.h b/include/osal/claw_kv.h new file mode 100644 index 0000000..108f55f --- /dev/null +++ b/include/osal/claw_kv.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2026, Chao Liu + * SPDX-License-Identifier: MIT + * + * OSAL Key-Value storage abstraction. + * ESP-IDF: backed by NVS Flash. + * RT-Thread: RAM-only fallback (persistent backends TBD). + */ + +#ifndef CLAW_KV_H +#define CLAW_KV_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +int claw_kv_init(void); + +int claw_kv_set_str(const char *ns, const char *key, const char *value); +int claw_kv_get_str(const char *ns, const char *key, + char *buf, size_t size); + +int claw_kv_set_blob(const char *ns, const char *key, + const void *data, size_t len); +int claw_kv_get_blob(const char *ns, const char *key, + void *data, size_t *len); + +int claw_kv_set_u8(const char *ns, const char *key, uint8_t val); +int claw_kv_get_u8(const char *ns, const char *key, uint8_t *val); + +int claw_kv_delete(const char *ns, const char *key); +int claw_kv_erase_ns(const char *ns); + +#ifdef __cplusplus +} +#endif + +#endif /* CLAW_KV_H */ diff --git a/osal/freertos/claw_kv_freertos.c b/osal/freertos/claw_kv_freertos.c new file mode 100644 index 0000000..5111d01 --- /dev/null +++ b/osal/freertos/claw_kv_freertos.c @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2026, Chao Liu + * SPDX-License-Identifier: MIT + * + * OSAL KV storage — ESP-IDF NVS Flash backend. + */ + +#include "osal/claw_kv.h" +#include "osal/claw_os.h" + +#ifdef CLAW_PLATFORM_ESP_IDF + +#include "nvs_flash.h" +#include "nvs.h" + +#define TAG "kv" + +int claw_kv_init(void) +{ + esp_err_t err = nvs_flash_init(); + if (err == ESP_ERR_NVS_NO_FREE_PAGES || + err == ESP_ERR_NVS_NEW_VERSION_FOUND) { + nvs_flash_erase(); + err = nvs_flash_init(); + } + if (err != ESP_OK) { + CLAW_LOGE(TAG, "nvs_flash_init: %s", esp_err_to_name(err)); + return CLAW_ERROR; + } + return CLAW_OK; +} + +int claw_kv_set_str(const char *ns, const char *key, const char *value) +{ + nvs_handle_t h; + if (nvs_open(ns, NVS_READWRITE, &h) != ESP_OK) { + return CLAW_ERROR; + } + nvs_set_str(h, key, value); + esp_err_t err = nvs_commit(h); + nvs_close(h); + return (err == ESP_OK) ? CLAW_OK : CLAW_ERROR; +} + +int claw_kv_get_str(const char *ns, const char *key, + char *buf, size_t size) +{ + nvs_handle_t h; + if (nvs_open(ns, NVS_READONLY, &h) != ESP_OK) { + return CLAW_ERROR; + } + size_t len = size; + esp_err_t err = nvs_get_str(h, key, buf, &len); + nvs_close(h); + return (err == ESP_OK) ? CLAW_OK : CLAW_ERROR; +} + +int claw_kv_set_blob(const char *ns, const char *key, + const void *data, size_t len) +{ + nvs_handle_t h; + if (nvs_open(ns, NVS_READWRITE, &h) != ESP_OK) { + return CLAW_ERROR; + } + nvs_set_blob(h, key, data, len); + esp_err_t err = nvs_commit(h); + nvs_close(h); + return (err == ESP_OK) ? CLAW_OK : CLAW_ERROR; +} + +int claw_kv_get_blob(const char *ns, const char *key, + void *data, size_t *len) +{ + nvs_handle_t h; + if (nvs_open(ns, NVS_READONLY, &h) != ESP_OK) { + return CLAW_ERROR; + } + esp_err_t err = nvs_get_blob(h, key, data, len); + nvs_close(h); + return (err == ESP_OK) ? CLAW_OK : CLAW_ERROR; +} + +int claw_kv_set_u8(const char *ns, const char *key, uint8_t val) +{ + nvs_handle_t h; + if (nvs_open(ns, NVS_READWRITE, &h) != ESP_OK) { + return CLAW_ERROR; + } + nvs_set_u8(h, key, val); + esp_err_t err = nvs_commit(h); + nvs_close(h); + return (err == ESP_OK) ? CLAW_OK : CLAW_ERROR; +} + +int claw_kv_get_u8(const char *ns, const char *key, uint8_t *val) +{ + nvs_handle_t h; + if (nvs_open(ns, NVS_READONLY, &h) != ESP_OK) { + return CLAW_ERROR; + } + esp_err_t err = nvs_get_u8(h, key, val); + nvs_close(h); + return (err == ESP_OK) ? CLAW_OK : CLAW_ERROR; +} + +int claw_kv_delete(const char *ns, const char *key) +{ + nvs_handle_t h; + if (nvs_open(ns, NVS_READWRITE, &h) != ESP_OK) { + return CLAW_ERROR; + } + nvs_erase_key(h, key); + esp_err_t err = nvs_commit(h); + nvs_close(h); + return (err == ESP_OK) ? CLAW_OK : CLAW_ERROR; +} + +int claw_kv_erase_ns(const char *ns) +{ + nvs_handle_t h; + if (nvs_open(ns, NVS_READWRITE, &h) != ESP_OK) { + return CLAW_ERROR; + } + nvs_erase_all(h); + esp_err_t err = nvs_commit(h); + nvs_close(h); + return (err == ESP_OK) ? CLAW_OK : CLAW_ERROR; +} + +#else /* standalone FreeRTOS without NVS */ + +int claw_kv_init(void) { return CLAW_OK; } +int claw_kv_set_str(const char *ns, const char *key, + const char *value) +{ (void)ns; (void)key; (void)value; return CLAW_ERROR; } +int claw_kv_get_str(const char *ns, const char *key, + char *buf, size_t size) +{ (void)ns; (void)key; (void)buf; (void)size; return CLAW_ERROR; } +int claw_kv_set_blob(const char *ns, const char *key, + const void *data, size_t len) +{ (void)ns; (void)key; (void)data; (void)len; return CLAW_ERROR; } +int claw_kv_get_blob(const char *ns, const char *key, + void *data, size_t *len) +{ (void)ns; (void)key; (void)data; (void)len; return CLAW_ERROR; } +int claw_kv_set_u8(const char *ns, const char *key, uint8_t val) +{ (void)ns; (void)key; (void)val; return CLAW_ERROR; } +int claw_kv_get_u8(const char *ns, const char *key, uint8_t *val) +{ (void)ns; (void)key; (void)val; return CLAW_ERROR; } +int claw_kv_delete(const char *ns, const char *key) +{ (void)ns; (void)key; return CLAW_ERROR; } +int claw_kv_erase_ns(const char *ns) +{ (void)ns; return CLAW_ERROR; } + +#endif diff --git a/osal/meson.build b/osal/meson.build index a5ae196..d35f250 100644 --- a/osal/meson.build +++ b/osal/meson.build @@ -4,6 +4,7 @@ if osal_backend == 'rtthread' osal_src = files( 'rtthread/claw_os_rtthread.c', 'rtthread/claw_net_rtthread.c', + 'rtthread/claw_kv_rtthread.c', ) osal_lib = static_library('osal', osal_src, include_directories: [claw_inc] + platform_incs, @@ -17,6 +18,7 @@ elif osal_backend == 'freertos' osal_src = files( 'freertos/claw_os_freertos.c', 'freertos/claw_net_freertos.c', + 'freertos/claw_kv_freertos.c', ) osal_lib = static_library('osal', osal_src, include_directories: [claw_inc] + platform_incs, diff --git a/osal/rtthread/claw_kv_rtthread.c b/osal/rtthread/claw_kv_rtthread.c new file mode 100644 index 0000000..8530a6a --- /dev/null +++ b/osal/rtthread/claw_kv_rtthread.c @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2026, Chao Liu + * SPDX-License-Identifier: MIT + * + * OSAL KV storage — RT-Thread RAM-only backend. + * Data is lost on reboot. Persistent backend (filesystem / EasyFlash) + * can be added later without changing callers. + */ + +#include "osal/claw_kv.h" +#include "osal/claw_os.h" + +#include +#include + +#define TAG "kv" + +#define KV_MAX_ENTRIES 48 +#define KV_NS_MAX 16 +#define KV_KEY_MAX 16 +#define KV_VAL_MAX 256 + +typedef struct { + char ns[KV_NS_MAX]; + char key[KV_KEY_MAX]; + uint8_t data[KV_VAL_MAX]; + size_t len; +} kv_entry_t; + +static kv_entry_t s_kv[KV_MAX_ENTRIES]; +static int s_kv_count; + +static kv_entry_t *kv_find(const char *ns, const char *key) +{ + for (int i = 0; i < s_kv_count; i++) { + if (strcmp(s_kv[i].ns, ns) == 0 && + strcmp(s_kv[i].key, key) == 0) { + return &s_kv[i]; + } + } + return NULL; +} + +static kv_entry_t *kv_alloc(const char *ns, const char *key) +{ + kv_entry_t *e = kv_find(ns, key); + if (e) { + return e; + } + if (s_kv_count >= KV_MAX_ENTRIES) { + return NULL; + } + e = &s_kv[s_kv_count++]; + snprintf(e->ns, KV_NS_MAX, "%s", ns); + snprintf(e->key, KV_KEY_MAX, "%s", key); + return e; +} + +int claw_kv_init(void) +{ + memset(s_kv, 0, sizeof(s_kv)); + s_kv_count = 0; + CLAW_LOGI(TAG, "RAM-only KV initialized, max=%d", KV_MAX_ENTRIES); + return CLAW_OK; +} + +int claw_kv_set_str(const char *ns, const char *key, const char *value) +{ + kv_entry_t *e = kv_alloc(ns, key); + if (!e) { + return CLAW_ERROR; + } + size_t len = strlen(value) + 1; + if (len > KV_VAL_MAX) { + len = KV_VAL_MAX; + } + memcpy(e->data, value, len); + e->data[KV_VAL_MAX - 1] = '\0'; + e->len = len; + return CLAW_OK; +} + +int claw_kv_get_str(const char *ns, const char *key, + char *buf, size_t size) +{ + kv_entry_t *e = kv_find(ns, key); + if (!e) { + return CLAW_ERROR; + } + snprintf(buf, size, "%s", (const char *)e->data); + return CLAW_OK; +} + +int claw_kv_set_blob(const char *ns, const char *key, + const void *data, size_t len) +{ + kv_entry_t *e = kv_alloc(ns, key); + if (!e || len > KV_VAL_MAX) { + return CLAW_ERROR; + } + memcpy(e->data, data, len); + e->len = len; + return CLAW_OK; +} + +int claw_kv_get_blob(const char *ns, const char *key, + void *data, size_t *len) +{ + kv_entry_t *e = kv_find(ns, key); + if (!e) { + return CLAW_ERROR; + } + if (*len < e->len) { + return CLAW_ERROR; + } + memcpy(data, e->data, e->len); + *len = e->len; + return CLAW_OK; +} + +int claw_kv_set_u8(const char *ns, const char *key, uint8_t val) +{ + kv_entry_t *e = kv_alloc(ns, key); + if (!e) { + return CLAW_ERROR; + } + e->data[0] = val; + e->len = 1; + return CLAW_OK; +} + +int claw_kv_get_u8(const char *ns, const char *key, uint8_t *val) +{ + kv_entry_t *e = kv_find(ns, key); + if (!e || e->len < 1) { + return CLAW_ERROR; + } + *val = e->data[0]; + return CLAW_OK; +} + +int claw_kv_delete(const char *ns, const char *key) +{ + for (int i = 0; i < s_kv_count; i++) { + if (strcmp(s_kv[i].ns, ns) == 0 && + strcmp(s_kv[i].key, key) == 0) { + if (i < s_kv_count - 1) { + memmove(&s_kv[i], &s_kv[i + 1], + (s_kv_count - i - 1) * sizeof(kv_entry_t)); + } + s_kv_count--; + return CLAW_OK; + } + } + return CLAW_ERROR; +} + +int claw_kv_erase_ns(const char *ns) +{ + int dst = 0; + for (int i = 0; i < s_kv_count; i++) { + if (strcmp(s_kv[i].ns, ns) != 0) { + if (dst != i) { + s_kv[dst] = s_kv[i]; + } + dst++; + } + } + s_kv_count = dst; + return CLAW_OK; +} diff --git a/platform/esp32c3/main/main.c b/platform/esp32c3/main/main.c index b76f13f..bb93148 100644 --- a/platform/esp32c3/main/main.c +++ b/platform/esp32c3/main/main.c @@ -7,6 +7,7 @@ */ #include "osal/claw_os.h" +#include "osal/claw_kv.h" #include "claw/claw_init.h" #include "claw/services/ai/ai_engine.h" #include "claw/shell/shell_commands.h" @@ -497,14 +498,8 @@ static void shell_loop(void) void app_main(void) { #ifdef CLAW_PLATFORM_ESP_IDF - /* Initialize NVS flash */ - esp_err_t err = nvs_flash_init(); - if (err == ESP_ERR_NVS_NO_FREE_PAGES || - err == ESP_ERR_NVS_NEW_VERSION_FOUND) { - ESP_LOGW(TAG, "NVS truncated, erasing..."); - nvs_flash_erase(); - nvs_flash_init(); - } + /* Initialize KV storage (wraps nvs_flash_init) */ + claw_kv_init(); #ifdef CONFIG_RTCLAW_SHELL_ENABLE esp_log_level_set("*", ESP_LOG_WARN); diff --git a/platform/esp32s3/main/main.c b/platform/esp32s3/main/main.c index f9f6b54..f1ffa23 100644 --- a/platform/esp32s3/main/main.c +++ b/platform/esp32s3/main/main.c @@ -7,6 +7,7 @@ */ #include "osal/claw_os.h" +#include "osal/claw_kv.h" #include "claw/claw_init.h" #include "claw/services/ai/ai_engine.h" #include "claw/shell/shell_commands.h" @@ -229,14 +230,8 @@ static void shell_loop(void) void app_main(void) { #ifdef CLAW_PLATFORM_ESP_IDF - /* Initialize NVS flash */ - esp_err_t err = nvs_flash_init(); - if (err == ESP_ERR_NVS_NO_FREE_PAGES || - err == ESP_ERR_NVS_NEW_VERSION_FOUND) { - ESP_LOGW(TAG, "NVS truncated, erasing..."); - nvs_flash_erase(); - nvs_flash_init(); - } + /* Initialize KV storage (wraps nvs_flash_init) */ + claw_kv_init(); #ifdef CONFIG_RTCLAW_SHELL_ENABLE esp_log_level_set("*", ESP_LOG_NONE);