From 82f93b617bbc933d0354a0dbef22ee8c03a5fa42 Mon Sep 17 00:00:00 2001 From: crispyberry Date: Sat, 7 Feb 2026 23:04:24 +0800 Subject: [PATCH] refactor: remove NVS/CLI config, use mimi_secrets.h as sole configuration method All configuration is now done exclusively through mimi_secrets.h at build time. Removed NVS read/write logic, CLI config commands (wifi_set, set_tg_token, set_api_key, set_model, set_proxy, clear_proxy, set_search_key), and setter functions from all modules. CLI retains debug/maintenance commands only. Updated all documentation to reflect the change. Co-Authored-By: Claude Opus 4.6 --- README.md | 35 ++---- README_CN.md | 51 ++------- docs/ARCHITECTURE.md | 55 +++++----- docs/TODO.md | 13 ++- main/cli/serial_cli.c | 199 ----------------------------------- main/llm/llm_proxy.c | 48 +-------- main/llm/llm_proxy.h | 12 +-- main/mimi.c | 4 +- main/mimi_config.h | 17 +-- main/mimi_secrets.h.example | 4 +- main/proxy/http_proxy.c | 43 -------- main/proxy/http_proxy.h | 12 +-- main/telegram/telegram_bot.c | 26 +---- main/telegram/telegram_bot.h | 5 - main/tools/tool_web_search.c | 27 +---- main/tools/tool_web_search.h | 6 +- main/wifi/wifi_manager.c | 47 ++------- main/wifi/wifi_manager.h | 6 -- 18 files changed, 63 insertions(+), 547 deletions(-) diff --git a/README.md b/README.md index 26f5177..2fdea20 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ idf.py set-target esp32s3 ### Configure -**Option A: Config file (recommended)** — fill in once, baked into firmware at build time: +All configuration is done through `mimi_secrets.h` at build time: ```bash cp main/mimi_secrets.h.example main/mimi_secrets.h @@ -91,39 +91,16 @@ idf.py build idf.py -p /dev/ttyACM0 flash monitor ``` -Config file values have the **highest priority** — they override anything set via CLI. - -> **Note:** After editing `mimi_secrets.h`, run `touch main/mimi_config.h` before `idf.py build` to force recompilation. - -**Option B: Serial CLI** — configure at runtime after flashing: - -```bash -idf.py build -idf.py -p /dev/ttyACM0 flash monitor -``` - -``` -mimi> wifi_set YourWiFiName YourWiFiPassword -mimi> set_tg_token 123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11 -mimi> set_api_key sk-ant-api03-xxxxx -mimi> set_search_key BSA-xxxxx # optional: Brave Search API key for web_search -mimi> restart -``` - -CLI values are stored in NVS (persistent flash) and used when no config file value is set. +> **Important:** After editing `mimi_secrets.h`, you must do a full rebuild: `idf.py fullclean && idf.py build` ### CLI Commands +The serial CLI provides debug and maintenance commands: + ``` -mimi> wifi_set # set WiFi credentials mimi> wifi_status # am I connected? -mimi> set_tg_token # set Telegram bot token -mimi> set_api_key # set Anthropic API key -mimi> set_model claude-opus-4-6 # use a different model -mimi> set_search_key # set Brave Search API key (for web_search tool) -mimi> set_proxy 10.0.0.1 7897 # route through HTTP proxy -mimi> clear_proxy # remove proxy, connect directly mimi> memory_read # see what the bot remembers +mimi> memory_write "content" # write to MEMORY.md mimi> heap_info # how much RAM is free? mimi> session_list # list all chat sessions mimi> session_clear 12345 # wipe a conversation @@ -151,7 +128,7 @@ MimiClaw uses Anthropic's tool use protocol — Claude can call tools during a c | `web_search` | Search the web via Brave Search API for current information | | `get_current_time` | Fetch current date/time via HTTP and set the system clock | -To enable web search, set a [Brave Search API key](https://brave.com/search/api/) in your config file or via CLI (`set_search_key`). +To enable web search, set a [Brave Search API key](https://brave.com/search/api/) via `MIMI_SECRET_SEARCH_KEY` in `mimi_secrets.h`. ## Also Included diff --git a/README_CN.md b/README_CN.md index 982c46c..1f210f5 100644 --- a/README_CN.md +++ b/README_CN.md @@ -66,7 +66,7 @@ idf.py set-target esp32s3 ### 配置 -**方式 A:配置文件(推荐)** — 填一次,编译时写入固件: +所有配置通过 `mimi_secrets.h` 在编译时写入: ```bash cp main/mimi_secrets.h.example main/mimi_secrets.h @@ -91,26 +91,7 @@ idf.py build idf.py -p /dev/ttyACM0 flash monitor ``` -配置文件的值**优先级最高** — 会覆盖 CLI 设置的值。 - -> **注意**:修改 `mimi_secrets.h` 后,需要先执行 `touch main/mimi_config.h` 再 `idf.py build`,否则不会重新编译。 - -**方式 B:串口命令行** — 烧录后在运行时配置: - -```bash -idf.py build -idf.py -p /dev/ttyACM0 flash monitor -``` - -``` -mimi> wifi_set 你的WiFi名 你的WiFi密码 -mimi> set_tg_token 123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11 -mimi> set_api_key sk-ant-api03-xxxxx -mimi> set_search_key BSA-xxxxx # 可选:Brave Search API key,启用网页搜索 -mimi> restart -``` - -CLI 设置的值存在 NVS(持久 Flash)中,仅在配置文件未设置对应值时生效。 +> **重要**:修改 `mimi_secrets.h` 后必须完整重编译:`idf.py fullclean && idf.py build` ### 代理配置(国内用户) @@ -118,34 +99,18 @@ CLI 设置的值存在 NVS(持久 Flash)中,仅在配置文件未设置对 **前提**:局域网内有一个支持 HTTP CONNECT 的代理(Clash Verge、V2Ray 等),并开启了「允许局域网连接」。 -推荐直接在 `mimi_secrets.h` 中配置代理(见上方方式 A),也可以用命令行: - -``` -mimi> set_proxy 10.0.0.1 7897 -mimi> restart -``` - -清除代理恢复直连: - -``` -mimi> clear_proxy -mimi> restart -``` +在 `mimi_secrets.h` 中设置 `MIMI_SECRET_PROXY_HOST` 和 `MIMI_SECRET_PROXY_PORT`。清除代理只需把这两项改回空字符串 `""`,然后重新编译。 > **提示**:确保 ESP32-S3 和代理机器在同一局域网。Clash Verge 在「设置 → 允许局域网」中开启。 -### 所有命令 +### CLI 命令 + +串口 CLI 提供调试和运维命令: ``` -mimi> wifi_set # 设置 WiFi mimi> wifi_status # 连上了吗? -mimi> set_tg_token # 设置 Telegram Bot Token -mimi> set_api_key # 设置 Anthropic API Key -mimi> set_model claude-opus-4-6 # 换个模型 -mimi> set_search_key # 设置 Brave Search API Key(web_search 工具用) -mimi> set_proxy 10.0.0.1 7897 # 通过 HTTP 代理 -mimi> clear_proxy # 清除代理,直连 mimi> memory_read # 看看它记住了什么 +mimi> memory_write "内容" # 写入 MEMORY.md mimi> heap_info # 还剩多少内存? mimi> session_list # 列出所有会话 mimi> session_clear 12345 # 删除一个会话 @@ -173,7 +138,7 @@ MimiClaw 使用 Anthropic 的 tool use 协议 — Claude 在对话中可以调 | `web_search` | 通过 Brave Search API 搜索网页,获取实时信息 | | `get_current_time` | 通过 HTTP 获取当前日期和时间,并设置系统时钟 | -启用网页搜索需要设置 [Brave Search API key](https://brave.com/search/api/),在配置文件或 CLI(`set_search_key`)中设置。 +启用网页搜索需要在 `mimi_secrets.h` 中设置 [Brave Search API key](https://brave.com/search/api/)(`MIMI_SECRET_SEARCH_KEY`)。 ## 其他功能 diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 8e613bf..b8b399a 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -106,7 +106,7 @@ main/ │ ├── wifi/ │ ├── wifi_manager.h WiFi STA lifecycle API -│ └── wifi_manager.c NVS credentials, event handler, exponential backoff +│ └── wifi_manager.c Event handler, exponential backoff │ ├── telegram/ │ ├── telegram_bot.h Bot init/start, send_message API @@ -144,7 +144,7 @@ main/ │ ├── cli/ │ ├── serial_cli.h CLI init API -│ └── serial_cli.c esp_console REPL with 15 commands +│ └── serial_cli.c esp_console REPL with debug/maintenance commands │ └── ota/ ├── ota_manager.h OTA update API @@ -190,7 +190,7 @@ Large buffers (32 KB+) are allocated from PSRAM via `heap_caps_calloc(1, size, M ``` Offset Size Name Purpose ───────────────────────────────────────────── -0x009000 24 KB nvs WiFi creds, TG token, API key, model +0x009000 24 KB nvs ESP-IDF internal use (WiFi calibration etc.) 0x00F000 8 KB otadata OTA boot state 0x011000 4 KB phy_init WiFi PHY calibration 0x020000 2 MB ota_0 Firmware slot A @@ -223,22 +223,22 @@ Session files are JSONL (one JSON object per line): --- -## NVS Configuration +## Configuration -| Namespace | Key | Description | -|-----------------|--------------|-----------------------------------------| -| `wifi_config` | `ssid` | WiFi SSID | -| `wifi_config` | `password` | WiFi password | -| `tg_config` | `bot_token` | Telegram Bot API token | -| `llm_config` | `api_key` | Anthropic API key | -| `llm_config` | `model` | Model ID (default: claude-opus-4-6) | -| `proxy_config` | `host` | HTTP proxy hostname/IP | -| `proxy_config` | `port` | HTTP proxy port | -| `search_config` | `api_key` | Brave Search API key | +All configuration is done exclusively through `mimi_secrets.h` at build time. There is no runtime configuration — changing any setting requires `idf.py fullclean && idf.py build`. -**Configuration priority**: `mimi_secrets.h` (build-time) > NVS (CLI-set) > defaults. +| Define | Description | +|------------------------------|-----------------------------------------| +| `MIMI_SECRET_WIFI_SSID` | WiFi SSID | +| `MIMI_SECRET_WIFI_PASS` | WiFi password | +| `MIMI_SECRET_TG_TOKEN` | Telegram Bot API token | +| `MIMI_SECRET_API_KEY` | Anthropic API key | +| `MIMI_SECRET_MODEL` | Model ID (default: claude-opus-4-6) | +| `MIMI_SECRET_PROXY_HOST` | HTTP proxy hostname/IP (optional) | +| `MIMI_SECRET_PROXY_PORT` | HTTP proxy port (optional) | +| `MIMI_SECRET_SEARCH_KEY` | Brave Search API key (optional) | -All configurable via Serial CLI or build-time config file (`mimi_secrets.h`). +NVS is still initialized (required by ESP-IDF WiFi internals) but is not used for application configuration. --- @@ -340,14 +340,14 @@ app_main() ├── memory_store_init() Verify SPIFFS paths ├── session_mgr_init() ├── wifi_manager_init() Init WiFi STA mode + event handlers - ├── http_proxy_init() Load proxy config (secrets > NVS) - ├── telegram_bot_init() Load bot token (secrets > NVS) - ├── llm_proxy_init() Load API key + model (secrets > NVS) + ├── http_proxy_init() Load proxy config from build-time secrets + ├── telegram_bot_init() Load bot token from build-time secrets + ├── llm_proxy_init() Load API key + model from build-time secrets ├── tool_registry_init() Register tools, build tools JSON ├── agent_loop_init() ├── serial_cli_init() Start REPL (works without WiFi) │ - ├── wifi_manager_start() Connect (secrets > NVS credentials) + ├── wifi_manager_start() Connect using build-time credentials │ └── wifi_manager_wait_connected(30s) │ └── [if WiFi connected] @@ -357,22 +357,17 @@ app_main() └── outbound_dispatch task Launch outbound task (Core 0) ``` -If WiFi credentials are missing or connection times out, the CLI remains available for configuration. +If WiFi credentials are missing or connection times out, the CLI remains available for diagnostics. --- ## Serial CLI Commands +The CLI provides debug and maintenance commands only. All configuration is done via `mimi_secrets.h`. + | Command | Description | |--------------------------------|--------------------------------------| -| `wifi_set ` | Save WiFi credentials to NVS | | `wifi_status` | Show connection status and IP | -| `set_tg_token ` | Save Telegram bot token | -| `set_api_key ` | Save Anthropic API key | -| `set_model ` | Set LLM model identifier | -| `set_search_key ` | Save Brave Search API key | -| `set_proxy ` | Set HTTP CONNECT proxy | -| `clear_proxy` | Remove proxy, use direct connection | | `memory_read` | Print MEMORY.md contents | | `memory_write ` | Overwrite MEMORY.md | | `session_list` | List all session files | @@ -381,8 +376,6 @@ If WiFi credentials are missing or connection times out, the CLI remains availab | `restart` | Reboot the device | | `help` | List all available commands | -> **Note**: CLI-set values are stored in NVS but are overridden by `mimi_secrets.h` build-time values if set. - --- ## Nanobot Reference Mapping @@ -396,7 +389,7 @@ If WiFi credentials are missing or connection times out, the CLI remains availab | `channels/telegram.py` | `telegram/telegram_bot.c` | Raw HTTP, no python-telegram-bot | | `bus/events.py` + `queue.py`| `bus/message_bus.c` | FreeRTOS queues vs asyncio | | `providers/litellm_provider.py` | `llm/llm_proxy.c` | Direct Anthropic API only | -| `config/schema.py` | `mimi_config.h` + `mimi_secrets.h` + NVS | Build-time secrets > NVS | +| `config/schema.py` | `mimi_config.h` + `mimi_secrets.h` | Build-time secrets only | | `cli/commands.py` | `cli/serial_cli.c` | esp_console REPL | | `agent/tools/*` | `tools/tool_registry.c` + `tool_web_search.c` | web_search via Brave API | | `agent/subagent.py` | *(not yet implemented)* | See TODO.md | diff --git a/docs/TODO.md b/docs/TODO.md index 92026a3..0e5d33f 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -36,7 +36,7 @@ ### [ ] Telegram User Allowlist (allow_from) - **nanobot**: `channels/base.py` L59-82 — `is_allowed()` checks sender_id against allow_list - **MimiClaw**: No authentication; anyone can message the bot and consume API credits -- **Recommendation**: Store allow_from list in NVS, filter in `process_updates()` +- **Recommendation**: Store allow_from list in `mimi_secrets.h` as a build-time define, filter in `process_updates()` ### [ ] Telegram Markdown to HTML Conversion - **nanobot**: `channels/telegram.py` L16-76 — `_markdown_to_telegram_html()` full converter: code blocks, inline code, bold, italic, links, strikethrough, lists @@ -105,7 +105,7 @@ - **Recommendation**: Requires extra HTTPS request to Whisper API: download Telegram voice -> forward -> get text ### [x] ~~Build-time Config File~~ -- Implemented: `mimi_secrets.h` — build-time credentials with highest priority over NVS/CLI +- Implemented: `mimi_secrets.h` — sole configuration method (build-time only, no NVS/CLI) - Replaces need for YAML config; suitable for MCU workflow ### [ ] WebSocket Gateway Protocol Enhancement @@ -124,7 +124,7 @@ - **Recommendation**: Low priority, Telegram is sufficient ### [x] ~~Telegram Proxy Support (HTTP CONNECT)~~ -- Implemented: HTTP CONNECT tunnel via `proxy/http_proxy.c`, configurable via NVS + CLI (`set_proxy`/`clear_proxy`) +- Implemented: HTTP CONNECT tunnel via `proxy/http_proxy.c`, configurable via `mimi_secrets.h` (`MIMI_SECRET_PROXY_HOST`/`MIMI_SECRET_PROXY_PORT`) ### [ ] Session Metadata Persistence - **nanobot**: `session/manager.py` L136-153 — session file includes metadata line (created_at, updated_at) @@ -144,13 +144,12 @@ - [x] Memory Store (MEMORY.md + daily notes) - [x] Session Manager (JSONL per chat_id, ring buffer history) - [x] WebSocket Gateway (port 18789, JSON protocol) -- [x] Serial CLI (esp_console, 15 commands) +- [x] Serial CLI (esp_console, debug/maintenance commands) - [x] HTTP CONNECT Proxy (Telegram + Claude API + Brave Search via proxy tunnel) - [x] OTA Update -- [x] WiFi Manager (NVS credentials, exponential backoff) +- [x] WiFi Manager (build-time credentials, exponential backoff) - [x] SPIFFS storage -- [x] Build-time config (`mimi_secrets.h`, highest priority over NVS) -- [x] NVS configuration (token, API key, model, search key) +- [x] Build-time config (`mimi_secrets.h`, sole configuration method) --- diff --git a/main/cli/serial_cli.c b/main/cli/serial_cli.c index 1a28f9b..383ab6b 100644 --- a/main/cli/serial_cli.c +++ b/main/cli/serial_cli.c @@ -1,12 +1,8 @@ #include "serial_cli.h" #include "mimi_config.h" #include "wifi/wifi_manager.h" -#include "telegram/telegram_bot.h" -#include "llm/llm_proxy.h" #include "memory/memory_store.h" #include "memory/session_mgr.h" -#include "proxy/http_proxy.h" -#include "tools/tool_web_search.h" #include #include @@ -18,26 +14,6 @@ static const char *TAG = "cli"; -/* --- wifi_set command --- */ -static struct { - struct arg_str *ssid; - struct arg_str *password; - struct arg_end *end; -} wifi_set_args; - -static int cmd_wifi_set(int argc, char **argv) -{ - int nerrors = arg_parse(argc, argv, (void **)&wifi_set_args); - if (nerrors != 0) { - arg_print_errors(stderr, wifi_set_args.end, argv[0]); - return 1; - } - wifi_manager_set_credentials(wifi_set_args.ssid->sval[0], - wifi_set_args.password->sval[0]); - printf("WiFi credentials saved. Restart to apply.\n"); - return 0; -} - /* --- wifi_status command --- */ static int cmd_wifi_status(int argc, char **argv) { @@ -46,60 +22,6 @@ static int cmd_wifi_status(int argc, char **argv) return 0; } -/* --- set_tg_token command --- */ -static struct { - struct arg_str *token; - struct arg_end *end; -} tg_token_args; - -static int cmd_set_tg_token(int argc, char **argv) -{ - int nerrors = arg_parse(argc, argv, (void **)&tg_token_args); - if (nerrors != 0) { - arg_print_errors(stderr, tg_token_args.end, argv[0]); - return 1; - } - telegram_set_token(tg_token_args.token->sval[0]); - printf("Telegram bot token saved.\n"); - return 0; -} - -/* --- set_api_key command --- */ -static struct { - struct arg_str *key; - struct arg_end *end; -} api_key_args; - -static int cmd_set_api_key(int argc, char **argv) -{ - int nerrors = arg_parse(argc, argv, (void **)&api_key_args); - if (nerrors != 0) { - arg_print_errors(stderr, api_key_args.end, argv[0]); - return 1; - } - llm_set_api_key(api_key_args.key->sval[0]); - printf("API key saved.\n"); - return 0; -} - -/* --- set_model command --- */ -static struct { - struct arg_str *model; - struct arg_end *end; -} model_args; - -static int cmd_set_model(int argc, char **argv) -{ - int nerrors = arg_parse(argc, argv, (void **)&model_args); - if (nerrors != 0) { - arg_print_errors(stderr, model_args.end, argv[0]); - return 1; - } - llm_set_model(model_args.model->sval[0]); - printf("Model set.\n"); - return 0; -} - /* --- memory_read command --- */ static int cmd_memory_read(int argc, char **argv) { @@ -176,51 +98,6 @@ static int cmd_heap_info(int argc, char **argv) return 0; } -/* --- set_proxy command --- */ -static struct { - struct arg_str *host; - struct arg_int *port; - struct arg_end *end; -} proxy_args; - -static int cmd_set_proxy(int argc, char **argv) -{ - int nerrors = arg_parse(argc, argv, (void **)&proxy_args); - if (nerrors != 0) { - arg_print_errors(stderr, proxy_args.end, argv[0]); - return 1; - } - http_proxy_set(proxy_args.host->sval[0], (uint16_t)proxy_args.port->ival[0]); - printf("Proxy set. Restart to apply.\n"); - return 0; -} - -/* --- clear_proxy command --- */ -static int cmd_clear_proxy(int argc, char **argv) -{ - http_proxy_clear(); - printf("Proxy cleared. Restart to apply.\n"); - return 0; -} - -/* --- set_search_key command --- */ -static struct { - struct arg_str *key; - struct arg_end *end; -} search_key_args; - -static int cmd_set_search_key(int argc, char **argv) -{ - int nerrors = arg_parse(argc, argv, (void **)&search_key_args); - if (nerrors != 0) { - arg_print_errors(stderr, search_key_args.end, argv[0]); - return 1; - } - tool_web_search_set_key(search_key_args.key->sval[0]); - printf("Search API key saved.\n"); - return 0; -} - /* --- restart command --- */ static int cmd_restart(int argc, char **argv) { @@ -244,18 +121,6 @@ esp_err_t serial_cli_init(void) /* Register commands */ - /* wifi_set */ - wifi_set_args.ssid = arg_str1(NULL, NULL, "", "WiFi SSID"); - wifi_set_args.password = arg_str1(NULL, NULL, "", "WiFi password"); - wifi_set_args.end = arg_end(2); - esp_console_cmd_t wifi_set_cmd = { - .command = "wifi_set", - .help = "Set WiFi SSID and password", - .func = &cmd_wifi_set, - .argtable = &wifi_set_args, - }; - esp_console_cmd_register(&wifi_set_cmd); - /* wifi_status */ esp_console_cmd_t wifi_status_cmd = { .command = "wifi_status", @@ -264,39 +129,6 @@ esp_err_t serial_cli_init(void) }; esp_console_cmd_register(&wifi_status_cmd); - /* set_tg_token */ - tg_token_args.token = arg_str1(NULL, NULL, "", "Telegram bot token"); - tg_token_args.end = arg_end(1); - esp_console_cmd_t tg_token_cmd = { - .command = "set_tg_token", - .help = "Set Telegram bot token", - .func = &cmd_set_tg_token, - .argtable = &tg_token_args, - }; - esp_console_cmd_register(&tg_token_cmd); - - /* set_api_key */ - api_key_args.key = arg_str1(NULL, NULL, "", "Anthropic API key"); - api_key_args.end = arg_end(1); - esp_console_cmd_t api_key_cmd = { - .command = "set_api_key", - .help = "Set Claude API key", - .func = &cmd_set_api_key, - .argtable = &api_key_args, - }; - esp_console_cmd_register(&api_key_cmd); - - /* set_model */ - model_args.model = arg_str1(NULL, NULL, "", "Model identifier"); - model_args.end = arg_end(1); - esp_console_cmd_t model_cmd = { - .command = "set_model", - .help = "Set LLM model (default: " MIMI_LLM_DEFAULT_MODEL ")", - .func = &cmd_set_model, - .argtable = &model_args, - }; - esp_console_cmd_register(&model_cmd); - /* memory_read */ esp_console_cmd_t mem_read_cmd = { .command = "memory_read", @@ -343,37 +175,6 @@ esp_err_t serial_cli_init(void) }; esp_console_cmd_register(&heap_cmd); - /* set_search_key */ - search_key_args.key = arg_str1(NULL, NULL, "", "Brave Search API key"); - search_key_args.end = arg_end(1); - esp_console_cmd_t search_key_cmd = { - .command = "set_search_key", - .help = "Set Brave Search API key for web_search tool", - .func = &cmd_set_search_key, - .argtable = &search_key_args, - }; - esp_console_cmd_register(&search_key_cmd); - - /* set_proxy */ - proxy_args.host = arg_str1(NULL, NULL, "", "Proxy host/IP"); - proxy_args.port = arg_int1(NULL, NULL, "", "Proxy port"); - proxy_args.end = arg_end(2); - esp_console_cmd_t proxy_cmd = { - .command = "set_proxy", - .help = "Set HTTP proxy (e.g. set_proxy 192.168.1.83 7897)", - .func = &cmd_set_proxy, - .argtable = &proxy_args, - }; - esp_console_cmd_register(&proxy_cmd); - - /* clear_proxy */ - esp_console_cmd_t clear_proxy_cmd = { - .command = "clear_proxy", - .help = "Remove proxy configuration", - .func = &cmd_clear_proxy, - }; - esp_console_cmd_register(&clear_proxy_cmd); - /* restart */ esp_console_cmd_t restart_cmd = { .command = "restart", diff --git a/main/llm/llm_proxy.c b/main/llm/llm_proxy.c index 7983952..df12e31 100644 --- a/main/llm/llm_proxy.c +++ b/main/llm/llm_proxy.c @@ -8,7 +8,6 @@ #include "esp_http_client.h" #include "esp_crt_bundle.h" #include "esp_heap_caps.h" -#include "nvs.h" #include "cJSON.h" static const char *TAG = "llm"; @@ -71,7 +70,6 @@ static esp_err_t http_event_handler(esp_http_client_event_t *evt) esp_err_t llm_proxy_init(void) { - /* Build-time secrets take highest priority */ if (MIMI_SECRET_API_KEY[0] != '\0') { strncpy(s_api_key, MIMI_SECRET_API_KEY, sizeof(s_api_key) - 1); } @@ -79,27 +77,10 @@ esp_err_t llm_proxy_init(void) strncpy(s_model, MIMI_SECRET_MODEL, sizeof(s_model) - 1); } - /* Fall back to NVS for values not set at build time */ - if (s_api_key[0] == '\0' || s_model[0] == '\0') { - nvs_handle_t nvs; - esp_err_t err = nvs_open(MIMI_NVS_LLM, NVS_READONLY, &nvs); - if (err == ESP_OK) { - if (s_api_key[0] == '\0') { - size_t len = sizeof(s_api_key); - nvs_get_str(nvs, MIMI_NVS_KEY_API_KEY, s_api_key, &len); - } - if (strcmp(s_model, MIMI_LLM_DEFAULT_MODEL) == 0) { - size_t len = sizeof(s_model); - nvs_get_str(nvs, MIMI_NVS_KEY_MODEL, s_model, &len); - } - nvs_close(nvs); - } - } - if (s_api_key[0]) { ESP_LOGI(TAG, "LLM proxy initialized (model: %s)", s_model); } else { - ESP_LOGW(TAG, "No API key. Use CLI: set_api_key "); + ESP_LOGW(TAG, "No API key. Set MIMI_SECRET_API_KEY in mimi_secrets.h"); } return ESP_OK; } @@ -467,30 +448,3 @@ esp_err_t llm_chat_tools(const char *system_prompt, return ESP_OK; } -/* ── NVS helpers ──────────────────────────────────────────────── */ - -esp_err_t llm_set_api_key(const char *api_key) -{ - nvs_handle_t nvs; - ESP_ERROR_CHECK(nvs_open(MIMI_NVS_LLM, NVS_READWRITE, &nvs)); - ESP_ERROR_CHECK(nvs_set_str(nvs, MIMI_NVS_KEY_API_KEY, api_key)); - ESP_ERROR_CHECK(nvs_commit(nvs)); - nvs_close(nvs); - - strncpy(s_api_key, api_key, sizeof(s_api_key) - 1); - ESP_LOGI(TAG, "API key saved"); - return ESP_OK; -} - -esp_err_t llm_set_model(const char *model) -{ - nvs_handle_t nvs; - ESP_ERROR_CHECK(nvs_open(MIMI_NVS_LLM, NVS_READWRITE, &nvs)); - ESP_ERROR_CHECK(nvs_set_str(nvs, MIMI_NVS_KEY_MODEL, model)); - ESP_ERROR_CHECK(nvs_commit(nvs)); - nvs_close(nvs); - - strncpy(s_model, model, sizeof(s_model) - 1); - ESP_LOGI(TAG, "Model set to: %s", s_model); - return ESP_OK; -} diff --git a/main/llm/llm_proxy.h b/main/llm/llm_proxy.h index 1b9ae50..43c8f5c 100644 --- a/main/llm/llm_proxy.h +++ b/main/llm/llm_proxy.h @@ -8,7 +8,7 @@ #include "mimi_config.h" /** - * Initialize the LLM proxy. Reads API key and model from NVS. + * Initialize the LLM proxy. */ esp_err_t llm_proxy_init(void); @@ -24,16 +24,6 @@ esp_err_t llm_proxy_init(void); esp_err_t llm_chat(const char *system_prompt, const char *messages_json, char *response_buf, size_t buf_size); -/** - * Save the Anthropic API key to NVS. - */ -esp_err_t llm_set_api_key(const char *api_key); - -/** - * Save the model identifier to NVS. - */ -esp_err_t llm_set_model(const char *model); - /* ── Tool Use Support ──────────────────────────────────────────── */ typedef struct { diff --git a/main/mimi.c b/main/mimi.c index 9f107d6..0c61150 100644 --- a/main/mimi.c +++ b/main/mimi.c @@ -134,10 +134,10 @@ void app_main(void) ESP_LOGI(TAG, "All services started!"); } else { - ESP_LOGW(TAG, "WiFi connection timeout. Configure via CLI: wifi_set "); + ESP_LOGW(TAG, "WiFi connection timeout. Check MIMI_SECRET_WIFI_SSID in mimi_secrets.h"); } } else { - ESP_LOGW(TAG, "No WiFi credentials. Configure via CLI: wifi_set "); + ESP_LOGW(TAG, "No WiFi credentials. Set MIMI_SECRET_WIFI_SSID in mimi_secrets.h"); } ESP_LOGI(TAG, "MimiClaw ready. Type 'help' for CLI commands."); diff --git a/main/mimi_config.h b/main/mimi_config.h index e447047..1979d4f 100644 --- a/main/mimi_config.h +++ b/main/mimi_config.h @@ -2,7 +2,7 @@ /* MimiClaw Global Configuration */ -/* Build-time secrets (highest priority, override NVS) */ +/* Build-time secrets (sole configuration method) */ #if __has_include("mimi_secrets.h") #include "mimi_secrets.h" #endif @@ -88,18 +88,3 @@ #define MIMI_CLI_PRIO 3 #define MIMI_CLI_CORE 0 -/* NVS Namespaces */ -#define MIMI_NVS_WIFI "wifi_config" -#define MIMI_NVS_TG "tg_config" -#define MIMI_NVS_LLM "llm_config" -#define MIMI_NVS_PROXY "proxy_config" -#define MIMI_NVS_SEARCH "search_config" - -/* NVS Keys */ -#define MIMI_NVS_KEY_SSID "ssid" -#define MIMI_NVS_KEY_PASS "password" -#define MIMI_NVS_KEY_TG_TOKEN "bot_token" -#define MIMI_NVS_KEY_API_KEY "api_key" -#define MIMI_NVS_KEY_MODEL "model" -#define MIMI_NVS_KEY_PROXY_HOST "host" -#define MIMI_NVS_KEY_PROXY_PORT "port" diff --git a/main/mimi_secrets.h.example b/main/mimi_secrets.h.example index 40d6784..3c579f8 100644 --- a/main/mimi_secrets.h.example +++ b/main/mimi_secrets.h.example @@ -1,9 +1,9 @@ /* * MimiClaw Build-time Secrets * + * This is the ONLY way to configure MimiClaw. * Copy this file to mimi_secrets.h and fill in your values. - * Non-empty values here take HIGHEST priority (override NVS/CLI). - * Leave empty ("") to use NVS values set via CLI. + * After any change, rebuild: idf.py fullclean && idf.py build * * cp mimi_secrets.h.example mimi_secrets.h */ diff --git a/main/proxy/http_proxy.c b/main/proxy/http_proxy.c index f5f2a30..4d71e22 100644 --- a/main/proxy/http_proxy.c +++ b/main/proxy/http_proxy.c @@ -9,7 +9,6 @@ #include #include "esp_log.h" -#include "nvs.h" #include "esp_tls.h" #include "esp_crt_bundle.h" @@ -21,26 +20,14 @@ __attribute__((constructor)) static void proxy_log_level(void) esp_log_level_set(TAG, ESP_LOG_WARN); } -/* ── Config (cached from NVS) ─────────────────────────────────── */ - static char s_proxy_host[64] = {0}; static uint16_t s_proxy_port = 0; esp_err_t http_proxy_init(void) { - /* Build-time secrets take highest priority */ if (MIMI_SECRET_PROXY_HOST[0] != '\0' && MIMI_SECRET_PROXY_PORT[0] != '\0') { strncpy(s_proxy_host, MIMI_SECRET_PROXY_HOST, sizeof(s_proxy_host) - 1); s_proxy_port = (uint16_t)atoi(MIMI_SECRET_PROXY_PORT); - } else { - nvs_handle_t nvs; - esp_err_t err = nvs_open(MIMI_NVS_PROXY, NVS_READONLY, &nvs); - if (err == ESP_OK) { - size_t len = sizeof(s_proxy_host); - nvs_get_str(nvs, MIMI_NVS_KEY_PROXY_HOST, s_proxy_host, &len); - nvs_get_u16(nvs, MIMI_NVS_KEY_PROXY_PORT, &s_proxy_port); - nvs_close(nvs); - } } if (s_proxy_host[0] && s_proxy_port) { @@ -56,36 +43,6 @@ bool http_proxy_is_enabled(void) return s_proxy_host[0] != '\0' && s_proxy_port != 0; } -esp_err_t http_proxy_set(const char *host, uint16_t port) -{ - nvs_handle_t nvs; - ESP_ERROR_CHECK(nvs_open(MIMI_NVS_PROXY, NVS_READWRITE, &nvs)); - ESP_ERROR_CHECK(nvs_set_str(nvs, MIMI_NVS_KEY_PROXY_HOST, host)); - ESP_ERROR_CHECK(nvs_set_u16(nvs, MIMI_NVS_KEY_PROXY_PORT, port)); - ESP_ERROR_CHECK(nvs_commit(nvs)); - nvs_close(nvs); - - strncpy(s_proxy_host, host, sizeof(s_proxy_host) - 1); - s_proxy_port = port; - ESP_LOGI(TAG, "Proxy set to %s:%d", s_proxy_host, s_proxy_port); - return ESP_OK; -} - -esp_err_t http_proxy_clear(void) -{ - nvs_handle_t nvs; - ESP_ERROR_CHECK(nvs_open(MIMI_NVS_PROXY, NVS_READWRITE, &nvs)); - nvs_erase_key(nvs, MIMI_NVS_KEY_PROXY_HOST); - nvs_erase_key(nvs, MIMI_NVS_KEY_PROXY_PORT); - nvs_commit(nvs); - nvs_close(nvs); - - s_proxy_host[0] = '\0'; - s_proxy_port = 0; - ESP_LOGI(TAG, "Proxy cleared"); - return ESP_OK; -} - /* ── Proxied TLS connection ───────────────────────────────────── */ struct proxy_conn { diff --git a/main/proxy/http_proxy.h b/main/proxy/http_proxy.h index 522e514..bf1e746 100644 --- a/main/proxy/http_proxy.h +++ b/main/proxy/http_proxy.h @@ -5,7 +5,7 @@ #include /** - * Initialize proxy module — loads config from NVS. + * Initialize proxy module. */ esp_err_t http_proxy_init(void); @@ -14,16 +14,6 @@ esp_err_t http_proxy_init(void); */ bool http_proxy_is_enabled(void); -/** - * Save proxy host and port to NVS. - */ -esp_err_t http_proxy_set(const char *host, uint16_t port); - -/** - * Remove proxy config from NVS. - */ -esp_err_t http_proxy_clear(void); - /* ── Proxied HTTPS connection ─────────────────────────────────── */ typedef struct proxy_conn proxy_conn_t; diff --git a/main/telegram/telegram_bot.c b/main/telegram/telegram_bot.c index 519eb66..763c5e6 100644 --- a/main/telegram/telegram_bot.c +++ b/main/telegram/telegram_bot.c @@ -8,7 +8,6 @@ #include "esp_log.h" #include "esp_http_client.h" #include "esp_crt_bundle.h" -#include "nvs.h" #include "cJSON.h" static const char *TAG = "telegram"; @@ -257,21 +256,10 @@ static void telegram_poll_task(void *arg) esp_err_t telegram_bot_init(void) { - /* Build-time secret takes highest priority */ - if (s_bot_token[0] == '\0') { - nvs_handle_t nvs; - esp_err_t err = nvs_open(MIMI_NVS_TG, NVS_READONLY, &nvs); - if (err == ESP_OK) { - size_t len = sizeof(s_bot_token); - nvs_get_str(nvs, MIMI_NVS_KEY_TG_TOKEN, s_bot_token, &len); - nvs_close(nvs); - } - } - if (s_bot_token[0]) { ESP_LOGI(TAG, "Telegram bot token loaded (len=%d)", (int)strlen(s_bot_token)); } else { - ESP_LOGW(TAG, "No Telegram bot token. Use CLI: set_tg_token "); + ESP_LOGW(TAG, "No Telegram bot token. Set MIMI_SECRET_TG_TOKEN in mimi_secrets.h"); } return ESP_OK; } @@ -369,15 +357,3 @@ esp_err_t telegram_send_message(const char *chat_id, const char *text) return ESP_OK; } -esp_err_t telegram_set_token(const char *token) -{ - nvs_handle_t nvs; - ESP_ERROR_CHECK(nvs_open(MIMI_NVS_TG, NVS_READWRITE, &nvs)); - ESP_ERROR_CHECK(nvs_set_str(nvs, MIMI_NVS_KEY_TG_TOKEN, token)); - ESP_ERROR_CHECK(nvs_commit(nvs)); - nvs_close(nvs); - - strncpy(s_bot_token, token, sizeof(s_bot_token) - 1); - ESP_LOGI(TAG, "Telegram bot token saved"); - return ESP_OK; -} diff --git a/main/telegram/telegram_bot.h b/main/telegram/telegram_bot.h index dc92a6b..f0a6e13 100644 --- a/main/telegram/telegram_bot.h +++ b/main/telegram/telegram_bot.h @@ -4,7 +4,6 @@ /** * Initialize the Telegram bot. - * Reads bot token from NVS. */ esp_err_t telegram_bot_init(void); @@ -21,7 +20,3 @@ esp_err_t telegram_bot_start(void); */ esp_err_t telegram_send_message(const char *chat_id, const char *text); -/** - * Save the Telegram bot token to NVS. - */ -esp_err_t telegram_set_token(const char *token); diff --git a/main/tools/tool_web_search.c b/main/tools/tool_web_search.c index cebe5ca..b5f8f1d 100644 --- a/main/tools/tool_web_search.c +++ b/main/tools/tool_web_search.c @@ -8,7 +8,6 @@ #include "esp_http_client.h" #include "esp_crt_bundle.h" #include "esp_heap_caps.h" -#include "nvs.h" #include "cJSON.h" static const char *TAG = "web_search"; @@ -44,40 +43,18 @@ static esp_err_t http_event_handler(esp_http_client_event_t *evt) esp_err_t tool_web_search_init(void) { - /* Build-time secret takes highest priority */ if (MIMI_SECRET_SEARCH_KEY[0] != '\0') { strncpy(s_search_key, MIMI_SECRET_SEARCH_KEY, sizeof(s_search_key) - 1); - } else { - nvs_handle_t nvs; - esp_err_t err = nvs_open(MIMI_NVS_SEARCH, NVS_READONLY, &nvs); - if (err == ESP_OK) { - size_t len = sizeof(s_search_key); - nvs_get_str(nvs, MIMI_NVS_KEY_API_KEY, s_search_key, &len); - nvs_close(nvs); - } } if (s_search_key[0]) { ESP_LOGI(TAG, "Web search initialized (key configured)"); } else { - ESP_LOGW(TAG, "No search API key. Use CLI: set_search_key "); + ESP_LOGW(TAG, "No search API key. Set MIMI_SECRET_SEARCH_KEY in mimi_secrets.h"); } return ESP_OK; } -esp_err_t tool_web_search_set_key(const char *api_key) -{ - nvs_handle_t nvs; - ESP_ERROR_CHECK(nvs_open(MIMI_NVS_SEARCH, NVS_READWRITE, &nvs)); - ESP_ERROR_CHECK(nvs_set_str(nvs, MIMI_NVS_KEY_API_KEY, api_key)); - ESP_ERROR_CHECK(nvs_commit(nvs)); - nvs_close(nvs); - - strncpy(s_search_key, api_key, sizeof(s_search_key) - 1); - ESP_LOGI(TAG, "Search API key saved"); - return ESP_OK; -} - /* ── URL-encode a query string ────────────────────────────────── */ static size_t url_encode(const char *src, char *dst, size_t dst_size) @@ -237,7 +214,7 @@ static esp_err_t search_via_proxy(const char *path, search_buf_t *sb) esp_err_t tool_web_search_execute(const char *input_json, char *output, size_t output_size) { if (s_search_key[0] == '\0') { - snprintf(output, output_size, "Error: No search API key configured. Use set_search_key command."); + snprintf(output, output_size, "Error: No search API key configured. Set MIMI_SECRET_SEARCH_KEY in mimi_secrets.h"); return ESP_ERR_INVALID_STATE; } diff --git a/main/tools/tool_web_search.h b/main/tools/tool_web_search.h index 5cb4d96..371dc14 100644 --- a/main/tools/tool_web_search.h +++ b/main/tools/tool_web_search.h @@ -4,7 +4,7 @@ #include /** - * Initialize web search tool — loads API key from NVS. + * Initialize web search tool. */ esp_err_t tool_web_search_init(void); @@ -18,7 +18,3 @@ esp_err_t tool_web_search_init(void); */ esp_err_t tool_web_search_execute(const char *input_json, char *output, size_t output_size); -/** - * Save Brave Search API key to NVS. - */ -esp_err_t tool_web_search_set_key(const char *api_key); diff --git a/main/wifi/wifi_manager.c b/main/wifi/wifi_manager.c index 71c1ebf..20884ba 100644 --- a/main/wifi/wifi_manager.c +++ b/main/wifi/wifi_manager.c @@ -6,8 +6,6 @@ #include "esp_log.h" #include "esp_wifi.h" #include "esp_netif.h" -#include "nvs_flash.h" -#include "nvs.h" static const char *TAG = "wifi"; @@ -72,34 +70,15 @@ esp_err_t wifi_manager_init(void) esp_err_t wifi_manager_start(void) { - wifi_config_t wifi_cfg = {0}; - - /* Build-time secrets take highest priority */ - if (MIMI_SECRET_WIFI_SSID[0] != '\0') { - strncpy((char *)wifi_cfg.sta.ssid, MIMI_SECRET_WIFI_SSID, sizeof(wifi_cfg.sta.ssid) - 1); - strncpy((char *)wifi_cfg.sta.password, MIMI_SECRET_WIFI_PASS, sizeof(wifi_cfg.sta.password) - 1); - } else { - /* Fall back to NVS */ - nvs_handle_t nvs; - esp_err_t err = nvs_open(MIMI_NVS_WIFI, NVS_READONLY, &nvs); - if (err != ESP_OK) { - ESP_LOGW(TAG, "No WiFi credentials. Use CLI: wifi_set "); - return ESP_ERR_NOT_FOUND; - } - - size_t len = sizeof(wifi_cfg.sta.ssid); - err = nvs_get_str(nvs, MIMI_NVS_KEY_SSID, (char *)wifi_cfg.sta.ssid, &len); - if (err != ESP_OK) { - nvs_close(nvs); - ESP_LOGW(TAG, "SSID not found in NVS"); - return ESP_ERR_NOT_FOUND; - } - - len = sizeof(wifi_cfg.sta.password); - nvs_get_str(nvs, MIMI_NVS_KEY_PASS, (char *)wifi_cfg.sta.password, &len); - nvs_close(nvs); + if (MIMI_SECRET_WIFI_SSID[0] == '\0') { + ESP_LOGW(TAG, "No WiFi credentials. Set MIMI_SECRET_WIFI_SSID in mimi_secrets.h"); + return ESP_ERR_NOT_FOUND; } + wifi_config_t wifi_cfg = {0}; + strncpy((char *)wifi_cfg.sta.ssid, MIMI_SECRET_WIFI_SSID, sizeof(wifi_cfg.sta.ssid) - 1); + strncpy((char *)wifi_cfg.sta.password, MIMI_SECRET_WIFI_PASS, sizeof(wifi_cfg.sta.password) - 1); + ESP_LOGI(TAG, "Connecting to SSID: %s", wifi_cfg.sta.ssid); ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_cfg)); @@ -126,18 +105,6 @@ bool wifi_manager_is_connected(void) return s_connected; } -esp_err_t wifi_manager_set_credentials(const char *ssid, const char *password) -{ - nvs_handle_t nvs; - ESP_ERROR_CHECK(nvs_open(MIMI_NVS_WIFI, NVS_READWRITE, &nvs)); - ESP_ERROR_CHECK(nvs_set_str(nvs, MIMI_NVS_KEY_SSID, ssid)); - ESP_ERROR_CHECK(nvs_set_str(nvs, MIMI_NVS_KEY_PASS, password)); - ESP_ERROR_CHECK(nvs_commit(nvs)); - nvs_close(nvs); - ESP_LOGI(TAG, "WiFi credentials saved for SSID: %s", ssid); - return ESP_OK; -} - const char *wifi_manager_get_ip(void) { return s_ip_str; diff --git a/main/wifi/wifi_manager.h b/main/wifi/wifi_manager.h index 79657e8..1d8a205 100644 --- a/main/wifi/wifi_manager.h +++ b/main/wifi/wifi_manager.h @@ -10,7 +10,6 @@ /** * Initialize WiFi subsystem (STA mode). - * Reads SSID/password from NVS. If not set, waits for serial configuration. */ esp_err_t wifi_manager_init(void); @@ -31,11 +30,6 @@ esp_err_t wifi_manager_wait_connected(uint32_t timeout_ms); */ bool wifi_manager_is_connected(void); -/** - * Save WiFi credentials to NVS. - */ -esp_err_t wifi_manager_set_credentials(const char *ssid, const char *password); - /** * Get the current IP address string (or "0.0.0.0" if not connected). */