mirror of
https://github.com/m5stack/ESP-Claw.git
synced 2026-05-20 11:51:49 -07:00
feat: the initial commit
This commit is contained in:
+41
@@ -0,0 +1,41 @@
|
||||
# Build output
|
||||
build/
|
||||
dist/
|
||||
tmp/
|
||||
releases/
|
||||
|
||||
# ESP-IDF
|
||||
sdkconfig
|
||||
sdkconfig.old
|
||||
dependencies.lock
|
||||
managed_components/
|
||||
|
||||
# IDE / Editor
|
||||
.vscode/
|
||||
.devcontainer/
|
||||
.idea/
|
||||
.clangd
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# Environment
|
||||
.env
|
||||
# Cache / Compiled
|
||||
.cache
|
||||
*.pyc
|
||||
*.bin
|
||||
|
||||
# MCP / Memov
|
||||
.mcp.json
|
||||
.mem/
|
||||
.memignore
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
references/
|
||||
.venv/
|
||||
|
||||
.codex
|
||||
openspec
|
||||
AGENTS.md
|
||||
@@ -0,0 +1,33 @@
|
||||
spec:
|
||||
inputs:
|
||||
force_push_to_github:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
---
|
||||
|
||||
stages:
|
||||
- build
|
||||
|
||||
workflow:
|
||||
rules:
|
||||
# Disable those non-protected push triggered pipelines
|
||||
- if: '$CI_COMMIT_REF_NAME != "master" && $CI_COMMIT_BRANCH !~ /^release\/v/ && $CI_COMMIT_TAG !~ /^v\d+\.\d+(\.\d+)?($|-)/ && $CI_PIPELINE_SOURCE == "push"'
|
||||
when: never
|
||||
# when running merged result pipelines, it would create a temp commit id. use $CI_MERGE_REQUEST_SOURCE_BRANCH_SHA instead of $CI_COMMIT_SHA.
|
||||
# Please use PIPELINE_COMMIT_SHA at all places that require a commit sha
|
||||
- if: $CI_OPEN_MERGE_REQUESTS != null
|
||||
variables:
|
||||
PIPELINE_COMMIT_SHA: $CI_MERGE_REQUEST_SOURCE_BRANCH_SHA
|
||||
- if: $CI_OPEN_MERGE_REQUESTS == null
|
||||
variables:
|
||||
PIPELINE_COMMIT_SHA: $CI_COMMIT_SHA
|
||||
- when: always
|
||||
|
||||
variables:
|
||||
IOT_SOLUTION_PATH: "$CI_PROJECT_DIR"
|
||||
CI_FORCE_PUSH_TO_GITHUB: "$[[ inputs.force_push_to_github ]]"
|
||||
|
||||
include:
|
||||
- '.gitlab/ci/rules.yml'
|
||||
- '.gitlab/ci/build.yml'
|
||||
@@ -44,7 +44,7 @@
|
||||
script:
|
||||
- pip install idf_build_apps
|
||||
- |
|
||||
python build_apps.py ${EXAMPLE_DIR} \
|
||||
python .gitlab/ci/build_apps.py ${EXAMPLE_DIR} \
|
||||
--config ${EXAMPLE_CONFIG} \
|
||||
-t all \
|
||||
-r ${BUILD_RECURSIVE} \
|
||||
@@ -77,10 +77,10 @@
|
||||
- IMAGE: espressif/idf:release-v5.5
|
||||
- IMAGE: espressif/idf:release-v6.0
|
||||
|
||||
build_example_ai_esp_dl_human_activity_recognition:
|
||||
build_application_basic_demo:
|
||||
extends:
|
||||
- .build_examples_template
|
||||
- .rules:build:example_ai_esp_dl_human_activity_recognition
|
||||
- .rules:build:application_basic_demo
|
||||
- .build_idf_active_release_version
|
||||
variables:
|
||||
EXAMPLE_DIR: examples/ai/esp_dl/human_activity_recognition
|
||||
EXAMPLE_DIR: application/basic_demo
|
||||
|
||||
@@ -35,7 +35,7 @@ logger = logging.getLogger('idf_build_apps')
|
||||
IDF_PATH = os.getenv('IDF_PATH', '')
|
||||
BOARD_NAME = 'default'
|
||||
|
||||
PROJECT_ROOT = Path(__file__).parent.parent.absolute()
|
||||
PROJECT_ROOT = Path(__file__).parent.parent.parent.absolute()
|
||||
APPS_BUILD_PER_JOB = 30
|
||||
IGNORE_WARNINGS = [
|
||||
r'memory region \`iram_loader_seg\' not declared',
|
||||
@@ -255,9 +255,7 @@ def get_cmake_apps(
|
||||
no_preserve=False,
|
||||
default_build_targets=default_build_targets,
|
||||
manifest_files=[
|
||||
str(Path(PROJECT_ROOT) /'components'/'.build-rules.yml'),
|
||||
str(Path(PROJECT_ROOT) /'examples'/'.build-rules.yml'),
|
||||
str(Path(PROJECT_ROOT) /'tools'/'.build-rules.yml'),
|
||||
str(Path(PROJECT_ROOT) /'application'/'.build-rules.yml'),
|
||||
],
|
||||
build_system=CustomApp,
|
||||
)
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
############
|
||||
# Patterns #
|
||||
############
|
||||
|
||||
# build system, if changed, build all apps
|
||||
.patterns-build_system: &patterns-build_system
|
||||
# For test
|
||||
# - "tools/build_apps.py"
|
||||
- "conftest.py"
|
||||
- "pytest.ini"
|
||||
|
||||
# components folder, in the alphabetic order
|
||||
.patterns-components_cap_cli: &patterns-components_cap_cli
|
||||
- "components/cap_cli/**/*"
|
||||
|
||||
.patterns-components_cap_files: &patterns-components_cap_files
|
||||
- "components/cap_files/**/*"
|
||||
|
||||
.patterns-components_cap_im_attachment: &patterns-components_cap_im_attachment
|
||||
- "components/cap_im_attachment/**/*"
|
||||
|
||||
.patterns-components_cap_im_qq: &patterns-components_cap_im_qq
|
||||
- "components/cap_im_qq/**/*"
|
||||
|
||||
.patterns-components_cap_im_tg: &patterns-components_cap_im_tg
|
||||
- "components/cap_im_tg/**/*"
|
||||
|
||||
.patterns-components_cap_im_wechat: &patterns-components_cap_im_wechat
|
||||
- "components/cap_im_wechat/**/*"
|
||||
|
||||
.patterns-components_cap_llm_inspect: &patterns-components_cap_llm_inspect
|
||||
- "components/cap_llm_inspect/**/*"
|
||||
|
||||
.patterns-components_cap_lua: &patterns-components_cap_lua
|
||||
- "components/cap_lua/**/*"
|
||||
|
||||
.patterns-components_cap_mcp_client: &patterns-components_cap_mcp_client
|
||||
- "components/cap_mcp_client/**/*"
|
||||
|
||||
.patterns-components_cap_mcp_server: &patterns-components_cap_mcp_server
|
||||
- "components/cap_mcp_server/**/*"
|
||||
|
||||
.patterns-components_cap_skill: &patterns-components_cap_skill
|
||||
- "components/cap_skill/**/*"
|
||||
|
||||
.patterns-components_cap_time: &patterns-components_cap_time
|
||||
- "components/cap_time/**/*"
|
||||
|
||||
.patterns-components_cap_web_search: &patterns-components_cap_web_search
|
||||
- "components/cap_web_search/**/*"
|
||||
|
||||
.patterns-components_claw_cap: &patterns-components_claw_cap
|
||||
- "components/claw_cap/**/*"
|
||||
|
||||
.patterns-components_claw_core: &patterns-components_claw_core
|
||||
- "components/claw_core/**/*"
|
||||
|
||||
.patterns-components_claw_event_router: &patterns-components_claw_event_router
|
||||
- "components/claw_event_router/**/*"
|
||||
|
||||
.patterns-components_claw_memory: &patterns-components_claw_memory
|
||||
- "components/claw_memory/**/*"
|
||||
|
||||
.patterns-components_claw_skill: &patterns-components_claw_skill
|
||||
- "components/claw_skill/**/*"
|
||||
|
||||
# application folder, in the alphabetic order
|
||||
.patterns-application_basic_demo: &patterns-application_basic_demo
|
||||
- "application/basic_demo/**/*"
|
||||
|
||||
.patterns-yml: &patterns-yml
|
||||
- "**/*.yml"
|
||||
|
||||
##############
|
||||
# if anchors #
|
||||
##############
|
||||
.if-protected: &if-protected
|
||||
if: '($CI_COMMIT_REF_NAME == "master" || $CI_COMMIT_BRANCH =~ /^release\/v/ || $CI_COMMIT_TAG =~ /^v\d+\.\d+(\.\d+)?($|-)/)'
|
||||
|
||||
.if-dev-push: &if-dev-push
|
||||
if: '$CI_COMMIT_REF_NAME != "master" && $CI_COMMIT_BRANCH !~ /^release\/v/ && $CI_COMMIT_TAG !~ /^v\d+\.\d+(\.\d+)?($|-)/ && ($CI_PIPELINE_SOURCE == "push" || $CI_PIPELINE_SOURCE == "merge_request_event")'
|
||||
|
||||
##################
|
||||
# Auto Generated #
|
||||
##################
|
||||
.if-trigger-job: &if-trigger-job
|
||||
if: "$BOT_DYNAMIC_TEST_JOBS && $BOT_DYNAMIC_TEST_JOBS =~ $CI_JOB_NAME"
|
||||
|
||||
.if-label-build: &if-label-build
|
||||
if: '$BOT_LABEL_BUILD || $CI_MERGE_REQUEST_LABELS =~ /^(?:[^,\n\r]+,)*build(?:,[^,\n\r]+)*$/i'
|
||||
|
||||
.if-label-target_test: &if-label-target_test
|
||||
if: '$BOT_LABEL_TARGET_TEST || $CI_MERGE_REQUEST_LABELS =~ /^(?:[^,\n\r]+,)*target_test(?:,[^,\n\r]+)*$/i'
|
||||
|
||||
.if-label-build_docs: &if-label-build_docs
|
||||
if: '$BOT_LABEL_BUILD_DOCS || $CI_MERGE_REQUEST_LABELS =~ /^(?:[^,\n\r]+,)*build_docs(?:,[^,\n\r]+)*$/i'
|
||||
|
||||
.if-label-pre_check: &if-label-pre_check
|
||||
if: '$BOT_LABEL_PRE_CHECK || $CI_MERGE_REQUEST_LABELS =~ /^(?:[^,\n\r]+,)*pre_check(?:,[^,\n\r]+)*$/i'
|
||||
|
||||
.if_label-deploy: &if-label-deploy
|
||||
if: '$BOT_LABEL_DEPLOY || $CI_MERGE_REQUEST_LABELS =~ /^(?:[^,\n\r]+,)*deploy(?:,[^,\n\r]+)*$/i'
|
||||
|
||||
# rules for applications
|
||||
.rules:build:application_basic_demo:
|
||||
rules:
|
||||
- <<: *if-protected
|
||||
- <<: *if-label-build
|
||||
- <<: *if-trigger-job
|
||||
- <<: *if-dev-push
|
||||
changes: *patterns-build_system
|
||||
- <<: *if-dev-push
|
||||
changes: *patterns-components_cap_cli
|
||||
- <<: *if-dev-push
|
||||
changes: *patterns-components_cap_files
|
||||
- <<: *if-dev-push
|
||||
changes: *patterns-components_cap_im_attachment
|
||||
- <<: *if-dev-push
|
||||
changes: *patterns-components_cap_im_qq
|
||||
- <<: *if-dev-push
|
||||
changes: *patterns-components_cap_im_tg
|
||||
- <<: *if-dev-push
|
||||
changes: *patterns-components_cap_im_wechat
|
||||
- <<: *if-dev-push
|
||||
changes: *patterns-components_cap_llm_inspect
|
||||
- <<: *if-dev-push
|
||||
changes: *patterns-components_cap_lua
|
||||
- <<: *if-dev-push
|
||||
changes: *patterns-components_cap_mcp_client
|
||||
- <<: *if-dev-push
|
||||
changes: *patterns-components_cap_mcp_server
|
||||
- <<: *if-dev-push
|
||||
changes: *patterns-components_cap_skill
|
||||
- <<: *if-dev-push
|
||||
changes: *patterns-components_cap_time
|
||||
- <<: *if-dev-push
|
||||
changes: *patterns-components_cap_web_search
|
||||
- <<: *if-dev-push
|
||||
changes: *patterns-components_claw_cap
|
||||
- <<: *if-dev-push
|
||||
changes: *patterns-components_claw_core
|
||||
- <<: *if-dev-push
|
||||
changes: *patterns-components_claw_event_router
|
||||
- <<: *if-dev-push
|
||||
changes: *patterns-components_claw_memory
|
||||
- <<: *if-dev-push
|
||||
changes: *patterns-components_claw_skill
|
||||
- <<: *if-dev-push
|
||||
changes: *patterns-application_basic_demo
|
||||
@@ -0,0 +1,235 @@
|
||||
# ESP-Clawgent
|
||||
|
||||
**事件驱动的 OpenClaw,为嵌入式场景量身定制。**
|
||||
|
||||
`ESP-Clawgent` 是一套运行于 ESP32 上的事件驱动 AI assistant,适用于需要长期运行、可持续扩展、功能持续迭代的设备侧 AI 场景
|
||||
|
||||
- 事件驱动:不局限于消息输入,多种外部事件都可以触发 Agent Loop
|
||||
- 组件化:可按需裁切模块
|
||||
- 离线自动化:在无网络条件下执行本地规则
|
||||
- 自编程:内嵌 Lua 解释器,可由 AI 自主规划实现功能
|
||||
- 渐进式工具披露:当前 agent 能做什么,由已加载和已激活的 skills 决定
|
||||
- 超低资源占用:ESP32-C 系列上也能流畅运行
|
||||
|
||||
## 1. What is ESP-Clawgent
|
||||
|
||||
项目包含示例与基础功能组件。
|
||||
|
||||
- `application/basic_demo/`:当前主应用,用于将这些模块装配成可运行固件
|
||||
|
||||
`components/` 包含以下核心部分:
|
||||
|
||||
- `claw_core`:负责完整的 agent 执行流
|
||||
- `claw_cap`:负责能力注册、调度和工具调用
|
||||
- `claw_memory`:负责会话历史和长期记忆
|
||||
- `claw_skill`:负责技能加载、技能文档和激活态技能上下文
|
||||
- `claw_event_router`:负责标准事件接入、规则路由、Agent/脚本分发和出站路由
|
||||
- `cap_*`:负责拓展具体能力,比如 QQ、Telegram、文件、时间、Web Search、MCP、Lua 等
|
||||
|
||||
## 2. How it works
|
||||
|
||||
主启动入口位于 `application/basic_demo/main/main.c`。
|
||||
|
||||
设备启动后,整体流程如下:
|
||||
|
||||
1. 初始化 NVS,加载设备配置
|
||||
2. 挂载 FATFS 到 `/fatfs/data`
|
||||
3. 初始化 Wi-Fi 和本地 HTTP 配置服务
|
||||
4. 进入 `app_clawgent_start()`
|
||||
5. 初始化 event router、memory、skills、capabilities
|
||||
6. 初始化并启动 `claw_core`
|
||||
7. 启动 CLI,开始响应请求和事件
|
||||
|
||||
当前运行时依赖以下本地目录:
|
||||
|
||||
- `/fatfs/data/sessions`:会话历史
|
||||
- `/fatfs/data/memory/MEMORY.md`:长期记忆
|
||||
- `/fatfs/data/skills`:skills 文档和清单
|
||||
- `/fatfs/data/lua`:Lua 脚本
|
||||
- `/fatfs/data/automation/automations.json`:自动化规则
|
||||
- `/fatfs/data/inbox`:消息附件存储目录
|
||||
|
||||
### Event-driven
|
||||
|
||||
`ESP-Clawgent` 的核心是“收到事件,触发行为”。
|
||||
|
||||
事件可以来自:
|
||||
|
||||
- 即时通讯入口
|
||||
- 配置界面
|
||||
- 本地事件规则
|
||||
- 文件或附件输入
|
||||
- 后续扩展的 cap 事件源
|
||||
|
||||
行为可以是:
|
||||
|
||||
- 调模型
|
||||
- 调工具
|
||||
- 读写本地文件
|
||||
- 执行 Lua
|
||||
- 触发事件路由链路
|
||||
- 给外部 IM 回消息
|
||||
|
||||
### Progressive tool exposure
|
||||
|
||||
工具不会默认一次性全部暴露给模型。
|
||||
|
||||
`claw_core` 在运行时会接入这些上下文提供器:
|
||||
|
||||
- 长期记忆
|
||||
- 会话历史
|
||||
- skills list
|
||||
- 已激活 skill 的文档
|
||||
- 当前 cap 工具描述
|
||||
|
||||
初始情况下,仅暴露由 `skills_list.json` 声明的基础能力。
|
||||
|
||||
- skill 没加载,模型看不到对应能力的说明
|
||||
- skill 没激活,模型拿不到对应文档上下文
|
||||
- 当前会话能做什么,是逐步展开的
|
||||
|
||||
|
||||
### Self-programming with Lua
|
||||
|
||||
项目内嵌 Lua 解释器,支持 Lua 脚本编辑与运行。
|
||||
|
||||
这使得以下能力成为可能:
|
||||
|
||||
- 把设备逻辑写成 Lua
|
||||
- 把某些 agent 行为抽成脚本
|
||||
- 让 assistant 在现有能力之上继续组合出新的功能
|
||||
|
||||
许多扩展可以先在 Lua 层完成,无需重新烧录固件。只要向 LLM 提出具体需求,系统即可直接生成并实现对应功能。
|
||||
|
||||
## 3. Project architecture
|
||||
|
||||
仓库结构如下:
|
||||
|
||||
```text
|
||||
esp-clawgent-master/
|
||||
├── components/
|
||||
│ ├── claw_core/
|
||||
│ ├── claw_cap/
|
||||
│ ├── claw_event_router/
|
||||
│ ├── claw_memory/
|
||||
│ ├── claw_skill/
|
||||
│ └── cap_*/
|
||||
└── application/
|
||||
└── basic_demo/
|
||||
├── main/
|
||||
└── build/
|
||||
```
|
||||
|
||||
### Runtime layers
|
||||
|
||||
- Application layer
|
||||
负责启动、配置、文件系统、Wi-Fi、HTTP 配置页和整机装配
|
||||
- Core layer
|
||||
`claw_core` 负责 agent 执行流
|
||||
- cap layer
|
||||
`claw_cap` 和各类 `cap_*` 负责工具和具体能力实现
|
||||
- Event routing layer
|
||||
`claw_event_router` 负责统一事件入口、规则匹配、脚本/agent 调度和出站分发
|
||||
- Memory layer
|
||||
`claw_memory` 负责持久化上下文
|
||||
- Skill layer
|
||||
`claw_skill` 负责按 skill 控制上下文与功能暴露
|
||||
|
||||
### Current capabilities
|
||||
|
||||
当前 `basic_demo` 已接入的能力包括:
|
||||
|
||||
- `cap_im_qq`
|
||||
- `cap_im_tg`
|
||||
- `cap_files`
|
||||
- `cap_lua`
|
||||
- `cap_mcp_client`
|
||||
- `cap_mcp_server`
|
||||
- `cap_skill`
|
||||
- `cap_time`
|
||||
- `cap_llm_inspect`
|
||||
- `cap_web_search`
|
||||
|
||||
### Design style
|
||||
|
||||
项目的架构关键词包括:
|
||||
|
||||
- event-driven
|
||||
- componentized
|
||||
- local-first
|
||||
|
||||
联网并不是系统运行的唯一前提。无网络时,本地事件路由、Lua、文件系统和既有记忆仍可继续工作。
|
||||
|
||||
## 4. Quick Start
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- ESP-IDF 环境已安装并导出
|
||||
- 建议使用 `ESP-IDF v5.5.1`
|
||||
- 默认目标芯片为 `esp32s3`
|
||||
|
||||
```bash
|
||||
. /esp-idf/export.sh
|
||||
```
|
||||
|
||||
### Build
|
||||
|
||||
所有 ESP-IDF 命令都在 `application/basic_demo/` 下执行:
|
||||
|
||||
```bash
|
||||
cd application/basic_demo
|
||||
idf.py set-target esp32s3
|
||||
idf.py build
|
||||
```
|
||||
|
||||
### Configure
|
||||
|
||||
当前 Demo 的关键配置包括:
|
||||
|
||||
- Wi-Fi SSID / Password
|
||||
- LLM API Key / Provider / Model
|
||||
- QQ App ID / App Secret
|
||||
- Telegram Bot Token
|
||||
- Brave / Tavily Search Key
|
||||
- Timezone
|
||||
|
||||
可以通过 `menuconfig` 调整编译期默认值:
|
||||
|
||||
```bash
|
||||
cd application/basic_demo
|
||||
idf.py menuconfig
|
||||
```
|
||||
|
||||
设备运行后,配置也会通过 NVS 持久化。
|
||||
|
||||
### Notes on Keys
|
||||
|
||||
- IM bot token:可通过 Telegram 的 [@BotFather](https://t.me/BotFather) 或 [QQ Bot](https://q.qq.com/qqbot/openclaw/login.html) 获取
|
||||
- LLM API key:可使用 [Anthropic Console](https://console.anthropic.com)、[OpenAI Platform](https://platform.openai.com) 或 [阿里云百炼](https://bailian.console.aliyun.com/#/api-key) 提供的 Key
|
||||
|
||||
|
||||
### Flash
|
||||
|
||||
```bash
|
||||
cd application/basic_demo
|
||||
idf.py flash monitor
|
||||
```
|
||||
|
||||
如果串口不是默认值:
|
||||
|
||||
```bash
|
||||
cd application/basic_demo
|
||||
idf.py flash monitor -p /dev/ttyUSB0
|
||||
```
|
||||
|
||||
### First boot
|
||||
|
||||
首次启动后,通常会看到以下阶段:
|
||||
|
||||
- NVS 初始化
|
||||
- FATFS 挂载
|
||||
- 设置加载
|
||||
- Wi-Fi 和本地配置服务启动
|
||||
- memory / skills / capabilities 初始化
|
||||
- `claw_core` 启动
|
||||
- CLI 启动
|
||||
@@ -0,0 +1,8 @@
|
||||
# Note: All operators are binary operators. For more than two operands, you may use the nested parentheses trick.
|
||||
# For example:
|
||||
# * A == 1 or (B == 2 and C in [1,2,3])
|
||||
# * (A == 1 and B == 2) or (C not in ["3", "4", 5])
|
||||
|
||||
application/basic_demo:
|
||||
enable:
|
||||
- if: INCLUDE_DEFAULT == 1
|
||||
@@ -0,0 +1,11 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
set(EXTRA_COMPONENT_DIRS "../../components")
|
||||
if(NOT DEFINED IDF_TARGET)
|
||||
set(IDF_TARGET "esp32s3" CACHE STRING "ESP-IDF target")
|
||||
endif()
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(basic_demo)
|
||||
|
||||
fatfs_create_spiflash_image(storage fatfs_image FLASH_IN_PROJECT)
|
||||
@@ -0,0 +1,97 @@
|
||||
[
|
||||
{
|
||||
"id": "im_hello_clawgent",
|
||||
"description": "Intercept IM hello! clawgent and reply directly.",
|
||||
"enabled": true,
|
||||
"ack": "{{event.source_channel}} hello intercepted",
|
||||
"match": {
|
||||
"event_type": "message",
|
||||
"event_key": "text",
|
||||
"content_type": "text",
|
||||
"text": "hello! clawgent"
|
||||
},
|
||||
"actions": [
|
||||
{
|
||||
"type": "send_message",
|
||||
"input": {
|
||||
"channel": "{{event.source_channel}}",
|
||||
"chat_id": "{{event.chat_id}}",
|
||||
"message": "Nice to meet you!"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "im_attachment_saved_reply",
|
||||
"description": "Reply when an IM attachment is saved.",
|
||||
"enabled": true,
|
||||
"ack": "{{event.source_channel}} attachment saved",
|
||||
"match": {
|
||||
"event_type": "attachment_saved"
|
||||
},
|
||||
"actions": [
|
||||
{
|
||||
"type": "send_message",
|
||||
"input": {
|
||||
"channel": "{{event.source_channel}}",
|
||||
"chat_id": "{{event.chat_id}}",
|
||||
"message": "File received from {{event.source_channel}}"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "mcp_state_report_to_script",
|
||||
"description": "Route MCP state reports into Lua for deterministic handling.",
|
||||
"enabled": true,
|
||||
"ack": "mcp state report scripted",
|
||||
"match": {
|
||||
"event_type": "mcp_device_state_report",
|
||||
"source_cap": "mcp_server",
|
||||
"source_channel": "mcp"
|
||||
},
|
||||
"actions": [
|
||||
{
|
||||
"type": "run_script",
|
||||
"input": {
|
||||
"path": "/fatfs/data/lua/hello.lua",
|
||||
"async": false,
|
||||
"event_type": "{{event.event_type}}",
|
||||
"source_channel": "{{event.source_channel}}",
|
||||
"text": "{{event.text}}",
|
||||
"payload_json": "{{event.payload_json}}",
|
||||
"chat_id": "{{event.chat_id}}"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "im_any_message_agent",
|
||||
"description": "Route IM text messages to the agent.",
|
||||
"enabled": true,
|
||||
"consume_on_match": true,
|
||||
"ack": "{{event.source_channel}} routed to agent",
|
||||
"match": {
|
||||
"event_type": "message",
|
||||
"event_key": "text",
|
||||
"content_type": "text"
|
||||
},
|
||||
"actions": [
|
||||
{
|
||||
"type": "run_agent",
|
||||
"input": {
|
||||
"target_channel": "{{event.source_channel}}",
|
||||
"session_policy": "chat"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "send_message",
|
||||
"input": {
|
||||
"channel": "{{event.source_channel}}",
|
||||
"chat_id": "{{event.chat_id}}",
|
||||
"message": "{{last.output}}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1 @@
|
||||
print("hello lua!")
|
||||
@@ -0,0 +1,3 @@
|
||||
# Long-term Memory
|
||||
|
||||
(empty - Clawgent will write memories here as it learns)
|
||||
@@ -0,0 +1,57 @@
|
||||
# IM File Return
|
||||
|
||||
Use this skill when the user wants the device to send an existing local non-image file back to a chat.
|
||||
|
||||
## When to use
|
||||
- The user asks to send back a file, report, log, archive, JSON, CSV, or other non-image output.
|
||||
- The target conversation is already the active Telegram, QQ, or WeChat chat, or the user provides an explicit target `chat_id`.
|
||||
|
||||
## Available tools
|
||||
- `list_dir`: inspect device storage and confirm the file path
|
||||
- `read_file`: inspect small text files before sending when needed
|
||||
- `cap_cli`: run `cap call <cap_name> '<json>'` to invoke the transport-specific send capability indirectly
|
||||
|
||||
## WeChat status
|
||||
- This firmware variant exposes WeChat text and image send, but does not expose `wechat_send_file`.
|
||||
- If the user asks to send a generic non-image file to WeChat, explain that the current WeChat path only supports text and image send.
|
||||
- If the requested asset is actually an image, switch to the `im_send_picture` skill and use `wechat_send_image` through `cap_cli`.
|
||||
|
||||
## Path guidance
|
||||
- Prefer real local paths that already exist on the device.
|
||||
- Common roots in this demo are `/spiffs`, `/spiffs/lua`, `/spiffs/qq`, `/spiffs/tg`, or application-managed subdirectories.
|
||||
- Use `list_dir` first if the exact path is unknown.
|
||||
- Use `read_file` only for small text inspection, not for binary payloads.
|
||||
|
||||
## Sending rules
|
||||
- Use this skill only for non-image files such as `.txt`, `.json`, `.log`, `.csv`, `.zip`, or archives.
|
||||
- Use `cap_cli` with `cap call tg_send_file '<json>'` or `cap call qq_send_file '<json>'`.
|
||||
- The JSON payload should include an explicit `chat_id`, `path`, and optional `caption`.
|
||||
- Use Telegram file capability for Telegram chats and QQ file capability for QQ chats.
|
||||
- Do not claim WeChat generic file-send support unless a real `wechat_send_file` capability exists.
|
||||
- Pass `caption` only when the user wants an accompanying message.
|
||||
- The second argument of `cap call` must be one complete JSON string. Do not rewrite it as CLI flags or `key=value`.
|
||||
|
||||
## Examples
|
||||
|
||||
Send a file to a Telegram chat through `cap_cli`:
|
||||
```text
|
||||
cap call tg_send_file '{"chat_id":"123456789","path":"/spiffs/reports/status.json","caption":"Latest status report."}'
|
||||
```
|
||||
|
||||
Send a file to a QQ group through `cap_cli`:
|
||||
```text
|
||||
cap call qq_send_file '{"chat_id":"group1234567890","path":"/spiffs/reports/status.json","caption":"Latest status report."}'
|
||||
```
|
||||
|
||||
## Workflow
|
||||
1. Confirm the target file exists with `list_dir` if needed.
|
||||
2. Identify the target channel: Telegram, QQ, or WeChat.
|
||||
3. Choose `tg_send_file` or `qq_send_file` based on the target channel.
|
||||
4. For WeChat, stop and explain that this firmware variant does not expose `wechat_send_file`.
|
||||
5. Execute the capability through `cap_cli` as `cap call <cap_name> '<json>'` with explicit `chat_id`, `path`, and optional `caption`.
|
||||
6. Tell the user whether the send succeeded.
|
||||
|
||||
## Notes
|
||||
- This skill only sends files that already exist on the device filesystem.
|
||||
- Do not use this skill for `.jpg`, `.jpeg`, `.png`, `.gif`, or `.webp`; use the im_send_picture skill instead.
|
||||
- If the user wants WeChat delivery and the asset is an image, use `wechat_send_image` through the picture skill instead.
|
||||
@@ -0,0 +1,53 @@
|
||||
# IM Picture Return
|
||||
|
||||
Use this skill when the user wants the device to send an existing local image back to a chat.
|
||||
|
||||
## When to use
|
||||
- The user asks to send back a picture, image, photo, screenshot, camera frame, or visual result.
|
||||
- The target conversation is already the active Telegram, QQ, or WeChat chat, or the user provides an explicit target `chat_id`.
|
||||
|
||||
## Available tools
|
||||
- `list_dir`: inspect device storage and confirm the image path
|
||||
- `cap_cli`: run `cap call <cap_name> '<json>'` to invoke the transport-specific send capability indirectly
|
||||
|
||||
## Path guidance
|
||||
- Prefer real local paths that already exist on the device.
|
||||
- Common roots in this demo are `/spiffs`, `/spiffs/qq`, `/spiffs/tg`, `/spiffs/wechat`, `/spiffs/captures`, or application-managed subdirectories.
|
||||
- Use `list_dir` first if the exact path is unknown.
|
||||
|
||||
## Sending rules
|
||||
- Use this skill only for image files such as `.jpg`, `.jpeg`, `.png`, `.gif`, or `.webp`.
|
||||
- Use `cap_cli` with `cap call tg_send_image '<json>'`, `cap call qq_send_image '<json>'`, or `cap call wechat_send_image '<json>'`.
|
||||
- The JSON payload should include an explicit `chat_id`, `path`, and optional `caption`.
|
||||
- Use `tg_send_image` for Telegram chats, `qq_send_image` for QQ chats, and `wechat_send_image` for WeChat chats.
|
||||
- Pass `caption` only when the user wants an accompanying message.
|
||||
- The second argument of `cap call` must be one complete JSON string. Do not rewrite it as `--chat_id`, `path=...`, or other non-JSON forms.
|
||||
|
||||
## Examples
|
||||
|
||||
Send an image to a Telegram chat through `cap_cli`:
|
||||
```text
|
||||
cap call tg_send_image '{"chat_id":"123456","path":"/spiffs/captures/latest.jpg","caption":"Here is the image."}'
|
||||
```
|
||||
|
||||
Send an image to a QQ chat through `cap_cli`:
|
||||
```text
|
||||
cap call qq_send_image '{"chat_id":"group123","path":"/spiffs/qq/capture.jpg","caption":"Here is the image."}'
|
||||
```
|
||||
|
||||
Send an image to a WeChat chat through `cap_cli`:
|
||||
```text
|
||||
cap call wechat_send_image '{"chat_id":"room123","path":"/spiffs/wechat/capture.jpg","caption":"Here is the image."}'
|
||||
```
|
||||
|
||||
## Workflow
|
||||
1. Confirm the target image exists with `list_dir` if needed.
|
||||
2. Identify the target channel: Telegram, QQ, or WeChat.
|
||||
3. Build the matching capability call: `tg_send_image`, `qq_send_image`, or `wechat_send_image`.
|
||||
4. Execute it through `cap_cli` as `cap call <cap_name> '<json>'` with explicit `chat_id`, `path`, and optional `caption`.
|
||||
5. Tell the user whether the send succeeded.
|
||||
|
||||
## Notes
|
||||
- This skill only sends images that already exist on the device filesystem.
|
||||
- If the user wants to send `.txt`, `.json`, `.log`, `.csv`, or archives, use the im_send_file skill instead.
|
||||
- In this firmware variant, WeChat supports text and image send, but generic non-image file send is not exposed through the current capability surface.
|
||||
@@ -0,0 +1,46 @@
|
||||
# Lua Script Execution
|
||||
|
||||
Use this skill when the user wants to see existing Lua scripts, run one, or inspect async execution jobs.
|
||||
|
||||
## Current Managed Scripts
|
||||
At the moment the image includes:
|
||||
- `hello.lua`
|
||||
|
||||
Current script content summary:
|
||||
- `hello.lua`: prints `hello lua!`
|
||||
|
||||
## Listing Scripts
|
||||
Use `lua_list_scripts` to inspect the current managed script set.
|
||||
- Optional input: `prefix`
|
||||
- Example: list everything with `{}` or filter a subdirectory with `{"prefix":"effects"}`
|
||||
|
||||
## Running a Script Synchronously
|
||||
Use `lua_run_script` when the user wants immediate output.
|
||||
- Required: `path`
|
||||
- Optional: `args`, `timeout_ms`
|
||||
- Prefer relative paths such as `hello.lua`
|
||||
|
||||
Example:
|
||||
```json
|
||||
{
|
||||
"path": "hello.lua"
|
||||
}
|
||||
```
|
||||
|
||||
If the script expects structured inputs, pass them through `args`. The runtime exposes them to Lua as the global `args`.
|
||||
|
||||
## Running a Script Asynchronously
|
||||
Use `lua_run_script_async` for long-running or continuous scripts.
|
||||
- Required: `path`
|
||||
- Optional: `args`, `timeout_ms`
|
||||
- Returns a `job_id`
|
||||
|
||||
After starting an async script:
|
||||
- Use `lua_list_async_jobs` to see all jobs or filter by status
|
||||
- Use `lua_get_async_job` with the returned `job_id` to inspect one job
|
||||
|
||||
## Execution Notes
|
||||
- Paths must resolve under `/spiffs/lua` and end with `.lua`.
|
||||
- Prefer `lua_run_script` for short scripts that should finish and return text.
|
||||
- Prefer `lua_run_script_async` for loops, animations, watchers, or long-running device behaviors.
|
||||
- If the user asks to run a script that does not exist yet, switch to the Lua authoring flow first.
|
||||
@@ -0,0 +1,64 @@
|
||||
# Lua Script Authoring
|
||||
|
||||
Use this skill when the user wants to write, generate, or modify a managed Lua script for this device.
|
||||
|
||||
## Runtime Constraints
|
||||
- Managed Lua scripts must stay under `/spiffs/lua`.
|
||||
- The script path must end with `.lua`.
|
||||
- Prefer relative paths such as `blink.lua` or `rainbow.lua`; the runtime resolves them under `/spiffs/lua`.
|
||||
- Use `lua_write_script` to save or overwrite script content.
|
||||
|
||||
## Available Lua Modules
|
||||
The runtime includes these built-in and application-registered modules:
|
||||
|
||||
### `delay`
|
||||
- `delay.delay_ms(ms)`
|
||||
- Use for short blocking delays inside a script.
|
||||
|
||||
### `storage`
|
||||
- `storage.mkdir(path)`
|
||||
- `storage.write_file(path, content)`
|
||||
- `storage.read_file(path)`
|
||||
- Use only for files the script needs to manage.
|
||||
|
||||
### `gpio`
|
||||
- `gpio.set_direction(pin, mode)`
|
||||
- `gpio.set_level(pin, level)`
|
||||
- `gpio.get_level(pin)`
|
||||
- Supported modes: `input`, `output`, `input_output`, `output_od`, `input_output_od`, `disable`
|
||||
|
||||
### `led_strip`
|
||||
- `local strip = led_strip.new(gpio_pin, max_leds)`
|
||||
- `strip:set_pixel(index, r, g, b)`
|
||||
- `strip:refresh()`
|
||||
- `strip:clear()`
|
||||
- `strip:close()`
|
||||
- This is for WS2812-style LED strips on a GPIO pin.
|
||||
|
||||
## Writing Guidance
|
||||
- Write plain Lua script files, not markdown or pseudocode.
|
||||
- Keep dependencies limited to standard Lua plus the modules listed above.
|
||||
- Prefer small scripts with a clear entry flow and explicit comments for pin usage.
|
||||
- If the script touches GPIO or LED hardware, state the pin numbers and expected electrical behavior in comments.
|
||||
- If a requested peripheral is not covered by `gpio` or `led_strip`, say that the current runtime does not expose that peripheral module.
|
||||
|
||||
## Example Shape
|
||||
```lua
|
||||
local gpio = require("gpio")
|
||||
local delay = require("delay")
|
||||
|
||||
gpio.set_direction(2, "output")
|
||||
|
||||
while true do
|
||||
gpio.set_level(2, 1)
|
||||
delay.delay_ms(500)
|
||||
gpio.set_level(2, 0)
|
||||
delay.delay_ms(500)
|
||||
end
|
||||
```
|
||||
|
||||
## Save Rule
|
||||
When the script is ready, call `lua_write_script` with:
|
||||
- `path`: relative `.lua` path under `/spiffs/lua`
|
||||
- `content`: full Lua source
|
||||
- `overwrite`: `true` only when replacing an existing script intentionally
|
||||
@@ -0,0 +1,48 @@
|
||||
# QQ File Return
|
||||
|
||||
Use this skill when the user wants the device to send a local file or image back to a QQ chat.
|
||||
|
||||
## When to use
|
||||
- The user asks to send back a file, attachment, image, photo, log, or generated output through QQ.
|
||||
- The target conversation is already the active QQ chat, or the user provides an explicit QQ `chat_id`.
|
||||
|
||||
## Available tools
|
||||
- `list_dir`: inspect device storage and confirm the file path
|
||||
- `read_file`: inspect small text files before sending when needed
|
||||
- `cap_cli`: run `cap call qq_send_image '<json>'` or `cap call qq_send_file '<json>'`
|
||||
|
||||
## Path guidance
|
||||
- Prefer real local paths already stored on the device.
|
||||
- Common roots in this demo are `/spiffs`, `/spiffs/qq`, `/spiffs/lua`, or other application-managed storage paths.
|
||||
- Use `list_dir` first if the exact file path is unknown.
|
||||
- Use `read_file` only for small text inspection, not for binary payloads.
|
||||
|
||||
## Sending rules
|
||||
- Use `qq_send_image` for image files such as `.jpg`, `.jpeg`, `.png`, `.gif`, or `.webp`.
|
||||
- Use `qq_send_file` for non-image files such as `.txt`, `.json`, `.log`, `.csv`, or archives.
|
||||
- Execute the chosen QQ capability through `cap_cli` as `cap call <cap_name> '<json>'`.
|
||||
- Pass `caption` only when the user wants an accompanying message.
|
||||
- The JSON payload should include an explicit QQ `chat_id`, `path`, and optional `caption`.
|
||||
- The second argument of `cap call` must be one complete JSON string. Do not rewrite it as `--chat_id` flags or `key=value`.
|
||||
|
||||
## Examples
|
||||
|
||||
Send an image to a QQ chat through `cap_cli`:
|
||||
```text
|
||||
cap call qq_send_image '{"chat_id":"group123","path":"/spiffs/qq/capture.jpg","caption":"Here is the image."}'
|
||||
```
|
||||
|
||||
Send a file to a QQ group through `cap_cli`:
|
||||
```text
|
||||
cap call qq_send_file '{"chat_id":"group1234567890","path":"/spiffs/reports/status.json","caption":"Latest status report."}'
|
||||
```
|
||||
|
||||
## Workflow
|
||||
1. Confirm the target file exists with `list_dir` if needed.
|
||||
2. Choose `qq_send_image` or `qq_send_file` based on file type.
|
||||
3. Execute the QQ capability through `cap_cli` with `cap call <cap_name> '<json>'`.
|
||||
4. Tell the user whether the send succeeded.
|
||||
|
||||
## Notes
|
||||
- This skill only sends files that already exist on the device filesystem.
|
||||
- QQ generic file delivery may depend on platform-side enablement. If `qq_send_file` fails, prefer falling back to `qq_send_image` for images or explain that QQ rejected generic file upload.
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"skills": [
|
||||
{
|
||||
"id": "weather",
|
||||
"file": "weather.md",
|
||||
"title": "Weather",
|
||||
"summary": "Get current weather and forecast information with web search."
|
||||
},
|
||||
{
|
||||
"id": "lua_write",
|
||||
"file": "lua_write.md",
|
||||
"title": "Lua Script Authoring",
|
||||
"summary": "Write managed Lua scripts with the correct runtime modules, path rules, and syntax constraints."
|
||||
},
|
||||
{
|
||||
"id": "lua_run",
|
||||
"file": "lua_run.md",
|
||||
"title": "Lua Script Execution",
|
||||
"summary": "List and run managed Lua scripts, including async execution and job inspection."
|
||||
},
|
||||
{
|
||||
"id": "im_send_file",
|
||||
"file": "im_send_file.md",
|
||||
"title": "IM Send File",
|
||||
"summary": "Send an existing local non-image file to a Telegram or QQ chat."
|
||||
},
|
||||
{
|
||||
"id": "im_send_picture",
|
||||
"file": "im_send_picture.md",
|
||||
"title": "IM Send Picture",
|
||||
"summary": "Send an existing local image to a Telegram or QQ chat."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
# Weather 天气
|
||||
|
||||
Get current weather and forecasts through `cap_cli`. Support weather, forecast, temperature, 天气, 预报, 温度 queries.
|
||||
|
||||
## When to use
|
||||
When the user asks about weather, temperature, forecasts, 天气, 温度, or 预报.
|
||||
|
||||
## How to use
|
||||
1. Use `cap_cli` to run `time --now` and learn the current date
|
||||
2. Use `cap_cli` to run `web_search --query "weather in [city] today"`
|
||||
3. Extract temperature, conditions, and forecast from results
|
||||
4. Present in a concise, friendly format
|
||||
|
||||
## Example
|
||||
User: "What's the weather in Tokyo?"
|
||||
-> cap_cli: `time --now`
|
||||
-> cap_cli: `web_search --query "weather Tokyo today February 2026"`
|
||||
-> "Tokyo: 8C, partly cloudy. High 12C, low 4C. Light wind from the north."
|
||||
@@ -0,0 +1,46 @@
|
||||
idf_component_register(
|
||||
SRCS
|
||||
"app_clawgent.c"
|
||||
"main.c"
|
||||
"basic_demo_cli.c"
|
||||
"basic_demo_lua_modules.c"
|
||||
"basic_demo_settings.c"
|
||||
"basic_demo_wifi.c"
|
||||
"config_http_server.c"
|
||||
"lua_module/lua_module_gpio.c"
|
||||
"lua_module/lua_module_led_strip.c"
|
||||
INCLUDE_DIRS
|
||||
"."
|
||||
REQUIRES
|
||||
cap_cli
|
||||
cap_files
|
||||
cap_im_qq
|
||||
cap_im_tg
|
||||
cap_im_wechat
|
||||
cap_llm_inspect
|
||||
cap_lua
|
||||
cap_mcp_client
|
||||
cap_mcp_server
|
||||
cap_skill
|
||||
cap_time
|
||||
cap_web_search
|
||||
claw_event_router
|
||||
claw_cap
|
||||
claw_core
|
||||
claw_memory
|
||||
claw_skill
|
||||
console
|
||||
driver
|
||||
esp_event
|
||||
esp_http_server
|
||||
esp_netif
|
||||
esp_wifi
|
||||
fatfs
|
||||
json
|
||||
nvs_flash
|
||||
wear_levelling
|
||||
EMBED_TXTFILES
|
||||
"web/index.html"
|
||||
"web/styles.css"
|
||||
"web/app.js"
|
||||
)
|
||||
@@ -0,0 +1,117 @@
|
||||
menu "Basic Demo Config"
|
||||
|
||||
config BASIC_DEMO_WIFI_SSID
|
||||
string "Default Wi-Fi SSID"
|
||||
default ""
|
||||
help
|
||||
Default Wi-Fi SSID used to populate basic demo settings.
|
||||
|
||||
config BASIC_DEMO_WIFI_PASSWORD
|
||||
string "Default Wi-Fi password"
|
||||
default ""
|
||||
help
|
||||
Default Wi-Fi password used to populate basic demo settings.
|
||||
|
||||
config BASIC_DEMO_LLM_API_KEY
|
||||
string "Default LLM API key"
|
||||
default ""
|
||||
help
|
||||
Default API key used to populate the basic demo LLM settings.
|
||||
|
||||
config BASIC_DEMO_LLM_BACKEND_TYPE
|
||||
string "Default LLM backend type"
|
||||
default "openai_compatible"
|
||||
help
|
||||
Default backend type for claw_core, for example openai_compatible or custom.
|
||||
|
||||
config BASIC_DEMO_LLM_PROFILE
|
||||
string "Default LLM profile"
|
||||
default "qwen_compatible"
|
||||
help
|
||||
Default runtime profile for claw_core, for example openai, qwen_compatible, or custom_openai_compatible.
|
||||
|
||||
config BASIC_DEMO_LLM_MODEL
|
||||
string "Default LLM model"
|
||||
default "qwen3-max-2026-01-23"
|
||||
help
|
||||
Default model name for claw_core requests.
|
||||
|
||||
config BASIC_DEMO_LLM_BASE_URL
|
||||
string "Default LLM base URL"
|
||||
default ""
|
||||
help
|
||||
Optional override for the selected LLM profile base URL.
|
||||
|
||||
config BASIC_DEMO_LLM_AUTH_TYPE
|
||||
string "Default LLM auth type"
|
||||
default "bearer"
|
||||
help
|
||||
Auth mode for the LLM backend, for example bearer, api-key, or none.
|
||||
|
||||
config BASIC_DEMO_LLM_TIMEOUT_MS
|
||||
string "Default LLM timeout ms"
|
||||
default "120000"
|
||||
help
|
||||
Timeout in milliseconds for outbound LLM HTTP requests.
|
||||
|
||||
config BASIC_DEMO_QQ_APP_ID
|
||||
string "Default QQ app ID"
|
||||
default ""
|
||||
help
|
||||
Default QQ app ID used by the IM cap.
|
||||
|
||||
config BASIC_DEMO_QQ_APP_SECRET
|
||||
string "Default QQ app secret"
|
||||
default ""
|
||||
help
|
||||
Default QQ app secret used by the IM cap.
|
||||
|
||||
config BASIC_DEMO_TG_BOT_TOKEN
|
||||
string "Default Telegram bot token"
|
||||
default ""
|
||||
help
|
||||
Default Telegram bot token used by the IM cap.
|
||||
|
||||
config BASIC_DEMO_WECHAT_TOKEN
|
||||
string "Default WeChat token"
|
||||
default ""
|
||||
help
|
||||
Default WeChat bot token used by the IM cap.
|
||||
|
||||
config BASIC_DEMO_WECHAT_BASE_URL
|
||||
string "Default WeChat base URL"
|
||||
default "https://ilinkai.weixin.qq.com"
|
||||
help
|
||||
Default WeChat API base URL used by the IM cap.
|
||||
|
||||
config BASIC_DEMO_WECHAT_CDN_BASE_URL
|
||||
string "Default WeChat CDN base URL"
|
||||
default "https://novac2c.cdn.weixin.qq.com/c2c"
|
||||
help
|
||||
Default WeChat CDN base URL used for media upload and download.
|
||||
|
||||
config BASIC_DEMO_WECHAT_ACCOUNT_ID
|
||||
string "Default WeChat account ID"
|
||||
default "default"
|
||||
help
|
||||
Default logical account ID for local WeChat channel state.
|
||||
|
||||
config BASIC_DEMO_SEARCH_BRAVE_KEY
|
||||
string "Default Brave Search API key"
|
||||
default ""
|
||||
help
|
||||
Default Brave Search API key for the web search cap.
|
||||
|
||||
config BASIC_DEMO_SEARCH_TAVILY_KEY
|
||||
string "Default Tavily API key"
|
||||
default ""
|
||||
help
|
||||
Default Tavily API key for the web search cap.
|
||||
|
||||
config BASIC_DEMO_TIME_TIMEZONE
|
||||
string "Default time cap timezone"
|
||||
default "UTC0"
|
||||
help
|
||||
POSIX-style timezone string used by the time cap.
|
||||
|
||||
endmenu
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user