跳转到内容

cap_files — 文件系统操作

源码:cap_files.c · 头文件:cap_files.h

cap_files文件系统类 Capability 的代表实现,向 LLM 和自动化规则暴露对受管 FATFS 目录的完整读写能力。它展示了如何在嵌入式环境中安全地将文件系统访问抽象为工具接口。

默认管理目录为 /fatfs,可通过 cap_files_set_base_dir() 在应用初始化时修改。

工具 ID功能输入
read_file读取文本文件内容path
write_file创建或覆盖文件path, content
edit_file替换文件中第一个匹配字符串path, old_string, new_string
delete_file删除文件path
list_dir递归列举目录下所有文件prefix(可选)

所有工具均标记为 CLAW_CAP_FLAG_CALLABLE_BY_LLM

cap_files 的核心安全设计是严格的路径边界校验,防止路径遍历攻击(path traversal):

static bool cap_files_path_is_valid(const char *path)
{
    // 禁止 ".." 序列(防止跳出基目录)
    if (strstr(path, "..") != NULL) return false;

    // 路径必须以 base_dir 开头
    if (strncmp(path, s_files_base_dir, base_len) != 0) return false;

    // 允许精确匹配基目录本身,或 base_dir/ 开头的子路径
    return path[base_len] == '\0' || path[base_len] == '/';
}

cap_files_resolve_path 同时支持绝对路径相对路径输入:

// 绝对路径:直接校验是否在 base_dir 下
// "/fatfs/notes/hello.txt" → 合法

// 相对路径:自动拼接 base_dir
// "notes/hello.txt" → "/fatfs/notes/hello.txt"
snprintf(resolved, resolved_size, "%s/%s", s_files_base_dir, path);

这种设计让 LLM 可以使用简短的相对路径,同时保证安全边界。

文件读取有 32 KB 上限(CAP_FILES_MAX_FILE_SIZE),超出部分被截断。这是为了防止大文件将整个 LLM 上下文占满:

max_read = output_size - 1;
if (max_read > CAP_FILES_MAX_FILE_SIZE) {
    max_read = CAP_FILES_MAX_FILE_SIZE;
}
read_size = fread(output, 1, max_read, file);
output[read_size] = '\0';

write_file 在写入前会递归创建不存在的父目录,无需用户预先 mkdir

cap_files_ensure_parent_dirs(resolved_path);
// 依次创建 /fatfs → /fatfs/notes → /fatfs/notes/2026 ...

edit_file 实现的是第一次匹配替换strstr + 内存复制),不是全局替换:

match = strstr(buffer, old_string);
if (!match) {
    snprintf(output, output_size, "Error: old_string not found in %s", resolved_path);
    return ESP_ERR_NOT_FOUND;
}
// 将 prefix + new_string + suffix 组合为新内容,一次性写回

这让 LLM 可以精确修改文件的某一处内容,同时错误信息明确(找不到时告知 LLM 提供的旧字符串不正确)。

// 递归遍历 base_dir,输出每个文件的完整路径
// 支持可选的 prefix 过滤(只列出前缀匹配的文件)
cap_files_list_recursive(s_files_base_dir, prefix, output, output_size, &offset, &count);

输出格式为每行一个绝对路径:

/fatfs/notes/hello.txt
/fatfs/scripts/blink.lua
/fatfs/skills/cap_lua_run.md

若目录下无文件,返回 "(no files found)" 而非空字符串,避免 LLM 误判。

cap_filescap_lua 分别管理不同的目录子树,互不干涉:

模块管理目录文件类型
cap_files/fatfs(可配置)任意文本文件
cap_lua/fatfs/scripts(默认).lua 文件

LLM 可用 cap_files 读取和修改 Lua 脚本的源码,再用 cap_lua 执行;也可用 cap_files 读取 cap_skill 所管理的 .md Skill 文档。

// 修改基目录(必须在 register_group 前调用)
cap_files_set_base_dir("/sdcard");

// 注册到 claw_cap
cap_files_register_group();