30 KiB
NanoClaw 规范
一款个人 Claude 助手,支持多渠道、按会话持久化记忆、定时任务以及容器隔离的 agent(智能体)执行。
目录
架构
┌──────────────────────────────────────────────────────────────────────┐
│ HOST(宿主机,macOS / Linux) │
│ (主 Node.js 进程) │
├──────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────┐ ┌────────────────────┐ │
│ │ Channels │─────────────────▶│ SQLite 数据库 │ │
│ │ (渠道,启动时 │◀────────────────│ (messages.db) │ │
│ │ 自行注册) │ 存储/发送 └─────────┬──────────┘ │
│ └──────────────────┘ │ │
│ │ │
│ ┌─────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────┐ ┌──────────────────┐ ┌───────────────┐ │
│ │ Message Loop │ │ Scheduler Loop │ │ IPC Watcher │ │
│ │ (消息轮询循环) │ │ (调度器循环) │ │ (IPC 监听器) │ │
│ └────────┬─────────┘ └────────┬─────────┘ └───────────────┘ │
│ │ │ │
│ └───────────┬───────────┘ │
│ │ 启动容器 │
│ ▼ │
├──────────────────────────────────────────────────────────────────────┤
│ CONTAINER(容器,Linux VM) │
├──────────────────────────────────────────────────────────────────────┤
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ AGENT RUNNER(智能体运行器) │ │
│ │ │ │
│ │ 工作目录: /workspace/group(从宿主机挂载) │ │
│ │ 卷挂载: │ │
│ │ • groups/{name}/ → /workspace/group │ │
│ │ • groups/global/ → /workspace/global/(仅非主群组) │ │
│ │ • data/sessions/{group}/.claude/ → /home/node/.claude/ │ │
│ │ • 额外目录 → /workspace/extra/* │ │
│ │ │ │
│ │ 工具(所有群组): │ │
│ │ • Bash(安全——在容器内沙箱化!) │ │
│ │ • Read, Write, Edit, Glob, Grep(文件操作) │ │
│ │ • WebSearch, WebFetch(互联网访问) │ │
│ │ • agent-browser(浏览器自动化) │ │
│ │ • mcp__nanoclaw__*(通过 IPC 的调度器工具) │ │
│ │ │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │
└───────────────────────────────────────────────────────────────────────┘
技术栈
| 组件 | 技术 | 用途 |
|---|---|---|
| Channel System | Channel registry (src/channels/registry.ts) |
渠道在启动时自行注册 |
| Message Storage | SQLite (better-sqlite3) | 存储消息供轮询 |
| Container Runtime | Containers(Linux VM) | 用于 agent 执行的隔离环境 |
| Agent | @anthropic-ai/claude-agent-sdk (0.2.29) | 带工具和 MCP 服务器的 Claude 运行环境 |
| Browser Automation | agent-browser + Chromium | Web 交互与截图 |
| Runtime | Node.js 20+ | 用于路由和调度的宿主机进程 |
架构:渠道系统
核心不含任何内置渠道——每个渠道(WhatsApp、Telegram、Slack、Discord、Gmail)作为 Claude Code skill 安装,将渠道代码添加到您的 fork 中。渠道在启动时自行注册;已安装但缺少凭证的渠道会发出 WARN 日志并被跳过。
系统图
graph LR
subgraph Channels["渠道"]
WA[WhatsApp]
TG[Telegram]
SL[Slack]
DC[Discord]
New["其他渠道 (Signal, Gmail...)"]
end
subgraph Orchestrator["编排器 — index.ts"]
ML[Message Loop]
GQ[Group Queue]
RT[Router]
TS[Task Scheduler]
DB[(SQLite)]
end
subgraph Execution["容器执行"]
CR[Container Runner]
LC["Linux Container"]
IPC[IPC Watcher]
end
%% Flow
WA & TG & SL & DC & New -->|onMessage| ML
ML --> GQ
GQ -->|并发控制| CR
CR --> LC
LC -->|文件系统 IPC| IPC
IPC -->|任务与消息| RT
RT -->|Channel.sendMessage| Channels
TS -->|到期任务| CR
%% DB Connections
DB <--> ML
DB <--> TS
%% Styling for the dynamic channel
style New stroke-dasharray: 5 5,stroke-width:2px
渠道注册表
渠道系统建立在 src/channels/registry.ts 中的工厂注册表之上:
export type ChannelFactory = (opts: ChannelOpts) => Channel | null;
const registry = new Map<string, ChannelFactory>();
export function registerChannel(name: string, factory: ChannelFactory): void {
registry.set(name, factory);
}
export function getChannelFactory(name: string): ChannelFactory | undefined {
return registry.get(name);
}
export function getRegisteredChannelNames(): string[] {
return [...registry.keys()];
}
每个工厂接收 ChannelOpts(包含回调 onMessage、onChatMetadata 和 registeredGroups),并返回一个 Channel 实例,如果该渠道的凭证未配置则返回 null。
渠道接口
每个渠道实现此接口(定义在 src/types.ts 中):
interface Channel {
name: string;
connect(): Promise<void>;
sendMessage(jid: string, text: string): Promise<void>;
isConnected(): boolean;
ownsJid(jid: string): boolean;
disconnect(): Promise<void>;
setTyping?(jid: string, isTyping: boolean): Promise<void>;
syncGroups?(force: boolean): Promise<void>;
}
自行注册模式
渠道使用 barrel 导入模式自行注册:
-
每个渠道 skill 向
src/channels/添加一个文件(例如whatsapp.ts、telegram.ts),在模块加载时调用registerChannel():// src/channels/whatsapp.ts import { registerChannel, ChannelOpts } from './registry.js'; export class WhatsAppChannel implements Channel { /* ... */ } registerChannel('whatsapp', (opts: ChannelOpts) => { // 如果凭证缺失则返回 null if (!existsSync(authPath)) return null; return new WhatsAppChannel(opts); }); -
barrel 文件
src/channels/index.ts导入所有渠道模块,触发注册:import './whatsapp.js'; import './telegram.js'; // ... 每个 skill 在此处添加其导入 -
启动时,编排器(
src/index.ts)循环遍历已注册的渠道,连接返回有效实例的渠道:for (const name of getRegisteredChannelNames()) { const factory = getChannelFactory(name); const channel = factory?.(channelOpts); if (channel) { await channel.connect(); channels.push(channel); } }
关键文件
| 文件 | 用途 |
|---|---|
src/channels/registry.ts |
渠道工厂注册表 |
src/channels/index.ts |
触发渠道自行注册的 barrel 导入 |
src/types.ts |
Channel 接口、ChannelOpts、消息类型 |
src/index.ts |
编排器——实例化渠道、运行消息循环 |
src/router.ts |
查找 JID 所属的渠道,格式化消息 |
添加新渠道
要添加新渠道,请向 .claude/skills/add-<name>/ 贡献一个 skill,该 skill 需要:
- 添加一个
src/channels/<name>.ts文件,实现Channel接口 - 在模块加载时调用
registerChannel(name, factory) - 如果凭证缺失,工厂返回
null - 向
src/channels/index.ts添加一条导入行
请参考已有的 skills(/add-whatsapp、/add-telegram、/add-slack、/add-discord、/add-gmail)了解模式。
目录结构
nanoclaw/
├── CLAUDE.md # Claude Code 的项目上下文
├── docs/
│ ├── SPEC.md # 本规范文档
│ ├── REQUIREMENTS.md # 架构决策
│ └── SECURITY.md # 安全模型
├── README.md # 用户文档
├── package.json # Node.js 依赖
├── tsconfig.json # TypeScript 配置
├── .mcp.json # MCP 服务器配置(参考)
├── .gitignore
│
├── src/
│ ├── index.ts # 编排器:状态、消息循环、agent 调用
│ ├── channels/
│ │ ├── registry.ts # 渠道工厂注册表
│ │ └── index.ts # 渠道自行注册的 barrel 导入
│ ├── ipc.ts # IPC 监听器与任务处理
│ ├── router.ts # 消息格式化和出站路由
│ ├── config.ts # 配置常量
│ ├── types.ts # TypeScript 接口(包含 Channel)
│ ├── logger.ts # Pino 日志器配置
│ ├── db.ts # SQLite 数据库初始化与查询
│ ├── group-queue.ts # 带全局并发限制的按群组队列
│ ├── mount-security.ts # 容器挂载白名单验证
│ ├── whatsapp-auth.ts # 独立的 WhatsApp 认证
│ ├── task-scheduler.ts # 到期时运行定时任务
│ └── container-runner.ts # 在容器中启动 agent
│
├── container/
│ ├── Dockerfile # 容器镜像(以 'node' 用户运行,包含 Claude Code CLI)
│ ├── build.sh # 容器镜像的构建脚本
│ ├── agent-runner/ # 在容器内运行的代码
│ │ ├── package.json
│ │ ├── tsconfig.json
│ │ └── src/
│ │ ├── index.ts # 入口点(查询循环、IPC 轮询、会话恢复)
│ │ └── ipc-mcp-stdio.ts # 基于 stdio 的 MCP 服务器,用于宿主机通信
│ └── skills/
│ └── agent-browser.md # 浏览器自动化 skill
│
├── dist/ # 编译后的 JavaScript(gitignored)
│
├── .claude/
│ └── skills/
│ ├── setup/SKILL.md # /setup - 首次安装
│ ├── customize/SKILL.md # /customize - 添加能力
│ ├── debug/SKILL.md # /debug - 容器调试
│ ├── add-telegram/SKILL.md # /add-telegram - Telegram 渠道
│ ├── add-gmail/SKILL.md # /add-gmail - Gmail 集成
│ ├── add-voice-transcription/ # /add-voice-transcription - Whisper
│ ├── x-integration/SKILL.md # /x-integration - X/Twitter
│ ├── convert-to-apple-container/ # /convert-to-apple-container - Apple Container 运行时
│ └── add-parallel/SKILL.md # /add-parallel - 并行 agent
│
├── groups/
│ ├── CLAUDE.md # 全局记忆(所有群组都读取此文件)
│ ├── {channel}_main/ # 主控制渠道(例如 whatsapp_main/)
│ │ ├── CLAUDE.md # 主渠道记忆
│ │ └── logs/ # 任务执行日志
│ └── {channel}_{group-name}/ # 按群组目录(注册时创建)
│ ├── CLAUDE.md # 群组专属记忆
│ ├── logs/ # 此群组的任务日志
│ └── *.md # agent 创建的文件
│
├── store/ # 本地数据(gitignored)
│ ├── auth/ # WhatsApp 认证状态
│ └── messages.db # SQLite 数据库(messages、chats、scheduled_tasks、task_run_logs、registered_groups、sessions、router_state)
│
├── data/ # 应用状态(gitignored)
│ ├── sessions/ # 按群组会话数据(.claude/ 目录,含 JSONL 对话记录)
│ ├── env/env # .env 的副本,用于挂载到容器
│ └── ipc/ # 容器 IPC(messages/、tasks/)
│
├── logs/ # 运行时日志(gitignored)
│ ├── nanoclaw.log # 宿主机 stdout
│ └── nanoclaw.error.log # 宿主机 stderr
│ # 注意:每个容器的日志在 groups/{folder}/logs/container-*.log
│
└── launchd/
└── com.nanoclaw.plist # macOS 服务配置
配置
配置常量在 src/config.ts 中定义:
import path from 'path';
export const ASSISTANT_NAME = process.env.ASSISTANT_NAME || 'Andy';
// 路径必须是绝对路径(容器挂载需要)
const PROJECT_ROOT = process.cwd();
export const STORE_DIR = path.resolve(PROJECT_ROOT, 'store');
export const GROUPS_DIR = path.resolve(PROJECT_ROOT, 'groups');
export const DATA_DIR = path.resolve(PROJECT_ROOT, 'data');
// 容器配置
export const CONTAINER_IMAGE = process.env.CONTAINER_IMAGE || 'nanoclaw-agent:latest';
export const CONTAINER_TIMEOUT = parseInt(process.env.CONTAINER_TIMEOUT || '1800000', 10); // 默认30分钟
export const IDLE_TIMEOUT = parseInt(process.env.IDLE_TIMEOUT || '1800000', 10); // 30分钟——最后一次结果后保持容器存活
export const MAX_CONCURRENT_CONTAINERS = Math.max(1, parseInt(process.env.MAX_CONCURRENT_CONTAINERS || '5', 10) || 5);
export const TRIGGER_PATTERN = new RegExp(`^@${ASSISTANT_NAME}\\b`, 'i');
注意: 路径必须是绝对路径,容器卷挂载才能正常工作。
容器配置
群组可以通过 SQLite registered_groups 表中的 containerConfig(以 JSON 形式存储在 container_config 列中)挂载额外目录。注册示例:
setRegisteredGroup("1234567890@g.us", {
name: "Dev Team",
folder: "whatsapp_dev-team",
trigger: "@Andy",
added_at: new Date().toISOString(),
containerConfig: {
additionalMounts: [
{
hostPath: "~/projects/webapp",
containerPath: "webapp",
readonly: false,
},
],
timeout: 600000,
},
});
目录命名遵循 {channel}_{group-name} 约定(例如 whatsapp_family-chat、telegram_dev-team)。主群组在注册时会设置 isMain: true。
额外挂载在容器内显示为 /workspace/extra/{containerPath}。
挂载语法说明: 读写挂载使用 -v host:container,但只读挂载需要使用 --mount "type=bind,source=...,target=...,readonly"(:ro 后缀可能不是所有运行时都支持)。
Claude 认证
在项目根目录的 .env 文件中配置认证。有两种选择:
选项1:Claude 订阅(OAuth token)
CLAUDE_CODE_OAUTH_TOKEN=sk-ant-oat01-...
如果您已登录 Claude Code,可以从 ~/.claude/.credentials.json 中提取 token。
选项2:按用量付费的 API Key
ANTHROPIC_API_KEY=sk-ant-api03-...
只有认证变量(CLAUDE_CODE_OAUTH_TOKEN 和 ANTHROPIC_API_KEY)从 .env 中提取并写入 data/env/env,然后挂载到容器中的 /workspace/env-dir/env,由入口点脚本加载。这确保 .env 中的其他环境变量不会暴露给 agent。此变通方案是必要的,因为某些容器运行时在使用 -i(带管道 stdin 的交互模式)时会丢失 -e 环境变量。
更改助手名称
设置 ASSISTANT_NAME 环境变量:
ASSISTANT_NAME=Bot pnpm start
或编辑 src/config.ts 中的默认值。这会更改:
- 触发模式(消息必须以
@YourName开头) - 响应前缀(自动添加
YourName:)
launchd 中的占位符值
包含 {{PLACEHOLDER}} 值的文件需要进行配置:
{{PROJECT_ROOT}}- nanoclaw 安装的绝对路径{{NODE_PATH}}- node 二进制文件的路径(通过which node检测){{HOME}}- 用户的主目录
记忆系统
NanoClaw 使用基于 CLAUDE.md 文件的层级记忆系统。
记忆层级
| 层级 | 位置 | 读取者 | 写入者 | 用途 |
|---|---|---|---|---|
| 全局 | groups/CLAUDE.md |
所有群组 | 仅主群组 | 在所有对话之间共享的偏好、事实、上下文 |
| 群组 | groups/{name}/CLAUDE.md |
该群组 | 该群组 | 群组专属上下文、对话记忆 |
| 文件 | groups/{name}/*.md |
该群组 | 该群组 | 对话过程中创建的笔记、研究、文档 |
记忆如何运作
-
Agent 上下文加载
- Agent 运行在
groups/{group-name}/作为cwd - Claude Agent SDK 使用
settingSources: ['project']自动加载:../CLAUDE.md(上级目录 = 全局记忆)./CLAUDE.md(当前目录 = 群组记忆)
- Agent 运行在
-
写入记忆
- 当用户说"记住这个",agent 写入
./CLAUDE.md - 当用户说"全局记住这个"(仅主渠道),agent 写入
../CLAUDE.md - Agent 可以在群组目录中创建
notes.md、research.md等文件
- 当用户说"记住这个",agent 写入
-
主渠道权限
- 只有"主"群组(自我聊天)可以向全局记忆写入
- 主群组可以管理已注册的群组并为任何群组安排定时任务
- 主群组可以为任何群组配置额外的目录挂载
- 所有群组都有 Bash 访问权限(安全,因为在容器内运行)
Session 管理
Session 实现了对话连续性——Claude 会记住你们之前谈过的内容。
Session 如何运作
- 每个群组在 SQLite 中有一个 session ID(
sessions表,按group_folder索引) - Session ID 传递给 Claude Agent SDK 的
resume选项 - Claude 以完整上下文继续对话
- Session 对话记录以 JSONL 文件形式存储在
data/sessions/{group}/.claude/
消息流转
入站消息流程
1. 用户通过任何已连接的渠道发送消息
│
▼
2. 渠道接收消息(例如 WhatsApp 使用 Baileys,Telegram 使用 Bot API)
│
▼
3. 消息存入 SQLite(store/messages.db)
│
▼
4. 消息循环轮询 SQLite(每2秒)
│
▼
5. 路由检查:
├── chat_jid 是否在已注册群组中(SQLite)?→ 否:忽略
└── 消息是否匹配触发模式?→ 否:存储但不处理
│
▼
6. 路由追上对话:
├── 获取自上次 agent 交互以来的所有消息
├── 使用时间戳和发送者名称格式化
└── 构建包含完整对话上下文的 prompt
│
▼
7. 路由调用 Claude Agent SDK:
├── cwd: groups/{group-name}/
├── prompt: 对话历史 + 当前消息
├── resume: session_id(用于连续性)
└── mcpServers: nanoclaw(调度器)
│
▼
8. Claude 处理消息:
├── 读取 CLAUDE.md 文件获取上下文
└── 按需使用工具(搜索、邮件等)
│
▼
9. 路由在响应前添加助手名称前缀,并通过所属渠道发送
│
▼
10. 路由更新最后 agent 时间戳并保存 session ID
触发词匹配
消息必须以触发模式开头(默认:@Andy):
@Andy what's the weather?→ ✅ 触发 Claude@andy help me→ ✅ 触发(不区分大小写)Hey @Andy→ ❌ 忽略(触发词不在开头)What's up?→ ❌ 忽略(无触发词)
对话追上
当触发消息到达时,agent 会收到自上次在该聊天中交互以来的所有消息。每条消息都带有时间戳和发送者名称:
[Jan 31 2:32 PM] John: hey everyone, should we do pizza tonight?
[Jan 31 2:33 PM] Sarah: sounds good to me
[Jan 31 2:35 PM] John: @Andy what toppings do you recommend?
这使得 agent 能够理解对话上下文,即使它没有在每条消息中被提及。
命令
任何群组中可用的命令
| 命令 | 示例 | 效果 |
|---|---|---|
@Assistant [message] |
@Andy what's the weather? |
与 Claude 对话 |
仅主渠道可用的命令
| 命令 | 示例 | 效果 |
|---|---|---|
@Assistant add group "Name" |
@Andy add group "Family Chat" |
注册新群组 |
@Assistant remove group "Name" |
@Andy remove group "Work Team" |
取消注册群组 |
@Assistant list groups |
@Andy list groups |
显示已注册群组 |
@Assistant remember [fact] |
@Andy remember I prefer dark mode |
添加到全局记忆 |
定时任务
NanoClaw 内置调度器,将任务作为完整 agent 在其群组上下文中运行。
调度如何运作
- 群组上下文:在群组中创建的任务使用该群组的工作目录和记忆运行
- 完整的 Agent 能力:定时任务可以访问所有工具(WebSearch、文件操作等)
- 可选的消息发送:任务可以使用
send_message工具向其群组发送消息,也可以静默完成 - 主渠道权限:主渠道可以为任何群组安排任务并查看所有任务
调度类型
| 类型 | 值格式 | 示例 |
|---|---|---|
cron |
Cron 表达式 | 0 9 * * 1(每周一上午9点) |
interval |
毫秒 | 3600000(每小时) |
once |
ISO 时间戳 | 2024-12-25T09:00:00Z |
创建任务
User: @Andy remind me every Monday at 9am to review the weekly metrics
Claude: [调用 mcp__nanoclaw__schedule_task]
{
"prompt": "Send a reminder to review weekly metrics. Be encouraging!",
"schedule_type": "cron",
"schedule_value": "0 9 * * 1"
}
Claude: Done! I'll remind you every Monday at 9am.
一次性任务
User: @Andy at 5pm today, send me a summary of today's emails
Claude: [调用 mcp__nanoclaw__schedule_task]
{
"prompt": "Search for today's emails, summarize the important ones, and send the summary to the group.",
"schedule_type": "once",
"schedule_value": "2024-01-31T17:00:00Z"
}
管理任务
在任何群组中:
@Andy list my scheduled tasks- 查看此群组的任务@Andy pause task [id]- 暂停任务@Andy resume task [id]- 恢复暂停的任务@Andy cancel task [id]- 删除任务
在主渠道中:
@Andy list all tasks- 查看所有群组的任务@Andy schedule task for "Family Chat": [prompt]- 为其他群组安排任务
MCP 服务器
NanoClaw MCP(内置)
nanoclaw MCP 服务器在每次 agent 调用时动态创建,带有当前群组的上下文。
可用工具:
| 工具 | 用途 |
|---|---|
schedule_task |
安排定期或一次性任务 |
list_tasks |
显示任务(本群组的任务,主群组则显示全部) |
get_task |
获取任务详情和运行历史 |
update_task |
修改任务的 prompt 或调度 |
pause_task |
暂停任务 |
resume_task |
恢复暂停的任务 |
cancel_task |
删除任务 |
send_message |
通过群组的渠道发送消息 |
部署
NanoClaw 作为单个 macOS launchd 服务运行。
启动序列
NanoClaw 启动时会:
- 确保容器运行时正在运行——如需要自动启动;终止前次运行遗留的 NanoClaw 孤儿容器
- 初始化 SQLite 数据库(如果存在 JSON 文件则从中迁移)
- 从 SQLite 加载状态(已注册群组、sessions、路由状态)
- 连接渠道——遍历已注册渠道,实例化有凭证的渠道,对每个调用
connect() - 一旦至少有一个渠道连接:
- 启动调度器循环
- 启动用于容器消息的 IPC 监听器
- 设置按群组队列及
processGroupMessages - 恢复关闭前未处理的消息
- 启动消息轮询循环
服务:com.nanoclaw
launchd/com.nanoclaw.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "...">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.nanoclaw</string>
<key>ProgramArguments</key>
<array>
<string>{{NODE_PATH}}</string>
<string>{{PROJECT_ROOT}}/dist/index.js</string>
</array>
<key>WorkingDirectory</key>
<string>{{PROJECT_ROOT}}</string>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>{{HOME}}/.local/bin:/usr/local/bin:/usr/bin:/bin</string>
<key>HOME</key>
<string>{{HOME}}</string>
<key>ASSISTANT_NAME</key>
<string>Andy</string>
</dict>
<key>StandardOutPath</key>
<string>{{PROJECT_ROOT}}/logs/nanoclaw.log</string>
<key>StandardErrorPath</key>
<string>{{PROJECT_ROOT}}/logs/nanoclaw.error.log</string>
</dict>
</plist>
管理服务
# 安装服务
cp launchd/com.nanoclaw.plist ~/Library/LaunchAgents/
# 启动服务
launchctl load ~/Library/LaunchAgents/com.nanoclaw.plist
# 停止服务
launchctl unload ~/Library/LaunchAgents/com.nanoclaw.plist
# 检查状态
launchctl list | grep nanoclaw
# 查看日志
tail -f logs/nanoclaw.log
安全考量
容器隔离
所有 agent 在容器(轻量级 Linux VM)内运行,提供:
- 文件系统隔离:Agent 只能访问已挂载的目录
- 安全的 Bash 访问:命令在容器内运行,而不是在您的 Mac 上
- 网络隔离:可按容器配置(如需要)
- 进程隔离:容器进程无法影响宿主机
- 非 root 用户:容器以非特权
node用户(uid 1000)运行
Prompt 注入风险
WhatsApp 消息可能包含试图操纵 Claude 行为的恶意指令。
缓解措施:
- 容器隔离限制爆炸半径
- 仅处理已注册的群组
- 需要触发词(减少误处理)
- Agent 只能访问其群组挂载的目录
- 主群组可以按群组配置额外目录
- Claude 内置的安全训练
建议:
- 仅注册受信任的群组
- 仔细审查额外的目录挂载
- 定期审查定时任务
- 监控日志中的异常活动
凭证存储
| 凭证 | 存储位置 | 说明 |
|---|---|---|
| Claude CLI Auth | data/sessions/{group}/.claude/ | 按群组隔离,挂载到 /home/node/.claude/ |
| WhatsApp Session | store/auth/ | 自动创建,持续约20天 |
文件权限
groups/ 目录包含个人记忆,应加以保护:
chmod 700 groups/
故障排除
常见问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 消息无响应 | 服务未运行 | 检查 `launchctl list |
| "Claude Code process exited with code 1" | 容器运行时启动失败 | 检查日志;NanoClaw 会自动启动容器运行时,但可能失败 |
| "Claude Code process exited with code 1" | Session 挂载路径错误 | 确保挂载到 /home/node/.claude/ 而非 /root/.claude/ |
| Session 无法继续 | Session ID 未保存 | 检查 SQLite:sqlite3 store/messages.db "SELECT * FROM sessions" |
| Session 无法继续 | 挂载路径不匹配 | 容器用户是 node,HOME=/home/node;sessions 必须位于 /home/node/.claude/ |
| "QR code expired" | WhatsApp session 过期 | 删除 store/auth/ 并重启 |
| "No groups registered" | 尚未添加群组 | 在主渠道中使用 @Andy add group "Name" |
日志位置
logs/nanoclaw.log- stdoutlogs/nanoclaw.error.log- stderr
调试模式
手动运行以获取详细输出:
pnpm run dev
# 或
node dist/index.js