Creel-ai/creel

GitHub: Creel-ai/creel

一个采用容器级凭证隔离的多层安全架构自托管个人 AI 助手,通过将 LLM 处理与工具执行严格分离来防范提示注入风险。

Stars: 0 | Forks: 0

# Creel

Creel

一个安全的 LLM 任务运行器和个人 AI 助手,它将包含凭证的数据获取与 LLM 处理分离。支持计划任务(晨间简报、天气摘要)和交互式 agent 模式(通过 CLI 或 iMessage 聊天并调用工具)。 Creel 是一种柳条篮,通常用于携带鱼或泥炭块。它也是一种用于捕捉龙虾和其他甲壳类动物的捕鱼陷阱。 ## 为什么 Agentic LLM 系统通常会让模型同时访问工具、凭证和不受信任的输入。Creel 强制执行 **按工具隔离** —— 每个执行器都在自己的容器中运行,仅拥有其所需的凭证: | 组件 | 拥有权限 | 无权访问 | |-----------|--------------|---------------| | 每个执行器 | 仅其自己的凭证(一个 OAuth scope,一个 API key) | LLM,其他工具的凭证 | | Bridge 执行器 | 用于单个 macOS 应用的限定范围 HTTP token | LLM,其他 bridge 端点 | | LLM Runner | 仅 Anthropic API key | 任何工具凭证 | | Orchestrator | 所有密钥,LLM 输出 | 不受信任的外部输入 | 即使发生提示注入(例如,通过日历事件标题),LLM 容器除了其自己的 API key 外也没有任何东西可以泄露。受损的执行器只能访问其单一受限凭证 —— 而不是你的电子邮件、文件或消息。 ## 架构 ``` flowchart TD subgraph orch["Orchestrator"] direction TB schedule["Scheduler / Agent Loop"] guardian["Guardian Pipeline"] template["Prompt Builder"] output["Output Router"] end subgraph containers["Docker Executors (isolated)"] direction TB google["Google Suite\n📅 Calendar · 📧 Gmail · 📁 Drive"] web["Web Tools\n🔍 Search · 🌐 Fetch · 🌤 Weather"] exec["Shell / Exec\n⚙️ Mounted paths only"] end subgraph bridge["Host Bridge (macOS native)"] direction TB bridge_api["FastAPI Server"] native["📝 Notes · ✅ Reminders\n📋 Things 3 · 💬 iMessage"] end subgraph llm_container["LLM Container"] llm["Claude\n🔑 Anthropic API key only"] end subgraph channels["Channels"] cli["TUI / CLI"] imsg["iMessage"] end channels -- "message" --> orch schedule -- "tool call" --> guardian guardian -- "approved" --> containers guardian -- "approved" --> bridge_api bridge_api --> native containers -- "JSON result" --> template bridge_api -- "JSON result" --> template template -- "prompt\n(no secrets)" --> llm llm -- "response" --> output output --> channels style containers fill:#2d333b,stroke:#f47067,stroke-width:2px,color:#f0f0f0 style bridge fill:#2d333b,stroke:#fd7e14,stroke-width:2px,color:#f0f0f0 style llm_container fill:#2d333b,stroke:#f47067,stroke-width:2px,color:#f0f0f0 style orch fill:#2d333b,stroke:#58a6ff,stroke-width:2px,color:#f0f0f0 style channels fill:#2d333b,stroke:#3fb950,stroke-width:2px,color:#f0f0f0 ``` ### Agent 模式 在 agent 模式下,同样适用安全边界 —— LLM 请求工具调用,但 orchestrator 负责处理密钥注入和执行器执行: ``` flowchart TD CH["Channels\nstdin | iMsg | BB"] -- "incoming message" --> SM["Session Manager\n(JSON files)"] SM -- "message + history" --> AL["Agent Loop"] AL --> LLM["LLM call"] LLM --> TU{"tool_use?"} TU -- no --> resp["Response"] TU -- yes --> EX["Execute via executor\n(secrets injected)"] EX --> TR["tool_result"] --> AL ``` 计划任务也可以通过在任务 YAML 中设置 `mode: agent` 来使用 agent 模式。 ### Guardian(守护层) Guardian 层在输入执行之前筛选输入并验证工具调用。所有阶段都是可选的,并且可以在 `agent.yaml` 中独立配置: ``` flowchart TD A["Incoming message"] --> B["screen_input(text)\n← before session history"] B --> FC["FastClassifier\nDeBERTa/ONNX, ~10ms"] B --> LJ["LLMJudge\nHaiku, ~300ms, off by default"] FC --> blocked{"blocked?"} LJ --> blocked blocked -- yes --> reject["Return rejection,\nskip agent loop"] blocked -- no --> agent["Agent loop → LLM returns tool_use"] agent --> VA["validate_action(tool, args)\n← before execute_tool_call"] VA --> PE["PolicyEngine\nYAML rules, <1ms"] VA --> CC["CoherenceCheck\nHaiku, ~300ms"] PE -- allow --> execute["Execute"] PE -- review --> approval["Human approval\nor auto_approve"] PE -- deny --> err1["Return error to LLM"] CC -- coherent --> execute CC -- incoherent --> err2["Return error to LLM"] ``` **阶段:** | 阶段 | 组件 | 作用 | |-------|-----------|--------------| | 1 | 快速分类器 | 本地 DeBERTa 模型根据置信度阈值对提示注入可能性进行评分 | | 2 | LLM 裁决 | 基于 Haiku 的二次检查(默认禁用) | | 3 | 策略引擎 | `policies/default.yaml` 中的 `fnmatch` 规则将工具名称映射到 allow/review/deny | | 4 | 一致性检查 | 基于 LLM 的检查,确保工具调用符合用户的原始意图(捕获导致无关操作的提示注入) | **策略规则** (`policies/default.yaml`): ``` allow: [check_weather, check_calendar, check_email, read_email, check_drive, check_messages, get_chats, react_imessage] review: [send_*, upload_*, create_*, mark_*, trash_*] deny: [delete_*] # 匹配 'review' 规则但可跳过人工审批的工具 auto_approve: - mark_read - react_imessage ``` Deny 优先于 review,review 优先于 allow。未知工具默认为 review。列在 `auto_approve` 中的工具即使匹配 review 规则,也会跳过人工确认提示。 **Human-in-the-loop 审查:** 匹配 `review` 规则的工具在执行前会提示用户批准。在 TUI 中,这显示为内联确认对话框。可配置的超时时间(默认 60 秒)如果未收到响应将拒绝该操作。 **审计日志** 写入 `guardian_audit.jsonl`,包含哈希处理的输入(绝不包含原始文本)、工具名称、参数键(不含值)、裁决和置信度分数。 **配置** 位于 `agent.yaml`: ``` guardian: enabled: true review: timeout_seconds: 60 default_on_timeout: deny fast_classifier: enabled: true threshold: 0.95 model_name: protectai/deberta-v3-base-prompt-injection-v2 llm_judge: enabled: false coherence: enabled: true model: claude-haiku-4-5-20251001 max_tokens: 256 policy: enabled: true policy_file: policies/default.yaml audit: enabled: true log_file: guardian_audit.jsonl ``` ## Host Bridge 对于 macOS 特定工具(Apple Notes、Apple Reminders、Things 3、iMessage),Docker 容器无法直接执行 AppleScript 或访问 macOS 应用程序。Host bridge 通过在主机系统上运行 FastAPI 服务器来解决此问题,该服务器为容器化执行器提供经过身份验证的 HTTP 端点。 **原因**:Docker 容器是沙盒化的,无法访问 Notes、Reminders 和 Things 3 等工具所需的 macOS 脚本桥接或应用程序 API。 **方式**:FastAPI 服务器(`python -m bridge.server`)作为主机进程运行,并在 `/notes/*`、`/reminders/*`、`/things/*` 和 `/imessage/*` 暴露 REST 端点。容器化执行器使用限定范围的认证 token 向这些端点发出 HTTP 请求。 **安全性**:每个执行器接收一个限定范围的 token,该 token 仅授予对其特定工具端点的访问权限。例如,`apple_notes` 执行器只能调用 `/notes/*` 端点,而不能调用 `/reminders/*` 或 `/things/*`。 **CLI 集成**:Bridge 服务器委托给命令行工具: - **Apple Notes**:用于读/写笔记的 `memo` CLI - **Apple Reminders**:用于管理提醒事项的 `remindctl` CLI - **Things 3**:用于任务管理的 `things` CLI - **iMessage**:用于发送/接收消息的 `imsg` CLI **启动 bridge**:`python -m bridge.server --host 127.0.0.1 --port 8099` 启动带有身份验证中间件和限定范围端点路由的 FastAPI 服务器。 ## Exec Tool Exec 执行器在隔离的 Docker 容器内提供沙盒化的 shell 命令执行。它专为运行系统命令、脚本和 CLI 工具而设计,同时通过文件系统隔离和网络限制来维护安全性。 **沙盒化**:命令在最小化的 Alpine Linux 容器(`alpine:latest`)中执行,具有: - **网络隔离**:默认为 `--network=none`(无互联网访问) - **文件系统隔离**:仅可访问配置的挂载点 - **最小化基础**:仅安装 `bash`、`grep`、`sed`、`awk`、`curl`、`jq` - **只读根目录**:容器文件系统是只读的,挂载路径除外 **挂载配置**:挂载点指定主机路径和访问模式: - **路径映射**:主机目录 → 容器目录 - **访问模式**:`ro`(只读)或 `rw`(读写) - **范围限制**:仅可访问显式挂载的路径 **未来增强**:针对不同用例的专用容器镜像: - `exec-dev`:开发工具(git、make、编译器) - `exec-python`:Python 运行时和常用包 - `exec-node`:Node.js 运行时和 npm ## 快速开始 ### 1. 安装 ``` git clone https://github.com/creel-ai/creel.git cd creel ```bash # macOS brew install pyenv uv age # Linux curl https://pyenv.run | bash # then follow shell setup instructions curl -LsSf https://astral.sh/uv/install.sh | sh sudo apt install age # or: brew install age ``` ``` pyenv install 3.12.12 # if not already installed uv venv source .venv/bin/activate uv pip install -e ".[dev, guardian]" # 设置 age 用于 secrets 加密(一次性) mkdir -p ~/.age age-keygen -o ~/.age/key.txt 2> ~/.age/key.pub # 列出可用任务 creel list # 验证任务定义 creel validate weather_check ``` ### 2. 添加你的 Anthropic key ``` export ANTHROPIC_API_KEY=sk-ant-... # or use ANTHROPIC_AUTH_TOKEN ``` ### 3. 启动守护进程并聊天 ``` creel daemon start # start the background agent creel attach # open the rich TUI ``` 就是这样 —— 你正在与你的 agent 聊天。在 TUI 中输入 `/help` 查看命令。 ### 更多使用方式 ``` # 单次消息(无 TUI) creel send "What's the weather today?" # 流式传输响应 creel send "Summarize my calendar" --stream # 运行计划任务 creel run weather_check # 启动时运行 creel daemon install # 查询安全审计日志 creel audit --blocked --tail 50 ``` ## Vercel 部署 该仓库部署两个独立的 Vercel 项目: - `site/`(营销网站) - `docs/`(基于 `../mkdocs.yml` 构建的 MkDocs 文档) ### 构建设置 对于 `site/`: - Framework Preset:`Other` - Install Command:*(空)* - Build Command:*(空)* - Output Directory:`.`(或留空) 对于 `docs/`: - Framework Preset:`Other` - Install Command:*(空)* - Build Command:`bash build-vercel.sh` - Output Directory:`.vercel-static` ### 部署行为 - 在 Vercel 中启用 Git 集成,以便对 pull request 进行预览部署和从 `main` 进行生产部署。 - 不需要 GitHub Actions 部署工作流。 ### 文档规范 URL 在 docs Vercel 项目环境中将 `MKDOCS_SITE_URL` 设置为你的文档域(例如 `https://docs.creel-ai.com/`)。 ## 身份验证 Runner 支持两种方式向 Anthropic API 进行身份验证: | 方法 | 环境变量 | 如何获取 | |--------|---------|---------------| | API key | `ANTHROPIC_API_KEY` | [console.anthropic.com](https://console.anthropic.com/) | | Claude Code setup token | `ANTHROPIC_AUTH_TOKEN` | `claude setup-token` | 如果两者都设置了,`ANTHROPIC_AUTH_TOKEN` 优先。 ### 使用 API key ``` export ANTHROPIC_API_KEY=sk-ant-... creel run weather_check ``` ### 使用 Claude Code setup token Claude Code 可以生成与 Anthropic API 配合使用的 OAuth token: ``` # 生成设置令牌(需要 Claude Code CLI) claude setup-token # 复制 sk-ant-oat01-... 值 export ANTHROPIC_AUTH_TOKEN=sk-ant-oat01-... creel run weather_check ``` ### 在密钥文件中存储凭证 任一变量都可以放入经过 age 加密的密钥文件中: ``` # 创建明文 .env echo 'ANTHROPIC_AUTH_TOKEN=sk-ant-oat01-...' > secrets/anthropic.env # 加密并删除明文 ./scripts/encrypt-secret.sh secrets/anthropic.env rm secrets/anthropic.env ``` 然后在你的任务 YAML 的 `llm.secrets` 下引用它。 ### 根 `.env` 文件 Runner 在启动时加载根 `.env` 文件(gitignored),用于非密钥配置,如电话号码: ``` # .env(项目根目录 — gitignored,永不提交) PHONE=+1234567890 ``` 值作为环境变量可用,并可以在任务 YAML 中使用 `$VAR` 语法引用: ``` output: type: imessage to: "$PHONE" ``` 实际环境变量优先于 `.env` 值。 ## 任务定义 任务是 `tasks/` 中的 YAML 文件。每个文件定义要获取的数据、如何提示 LLM 以及将结果发送到何处。 ``` # tasks/morning_briefing.yaml name: morning_briefing schedule: "0 7 * * *" # 7am daily fetch: calendar: image: executor-gcal:latest secrets: secrets/gcal.env.enc args: range: today weather: image: executor-weather:latest args: location: denver prompt: | You're my personal assistant. Give me a quick rundown of my day. Today: {date} Weather: {weather} Calendar: {calendar} Keep it under 150 words. Flag any early meetings or conflicts. output: type: imessage to: "$PHONE" llm: model: claude-sonnet-4-20250514 max_tokens: 300 secrets: secrets/anthropic.env.enc ``` ### 任务字段 | 字段 | 必需 | 描述 | |-------|----------|-------------| | `name` | 是 | 唯一任务标识符 | | `schedule` | 是 | 5 部分 cron 表达式 | | `fetch` | 是 | 执行器名称到配置的映射 | | `fetch..image` | 是 | 容器化模式的 Docker 镜像 | | `fetch..secrets` | 否 | age 加密的 .env 文件路径 | | `fetch..args` | 否 | 传递给执行器的键值参数 | | `prompt` | 是 | 带有 `{name}` 占位符的提示模板 | | `output.type` | 是 | `imessage`、`stdout` 或 `file` | | `output.to` | 是 | 电话号码、空字符串或文件路径 | | `llm.model` | 否 | Anthropic 模型 ID(默认值:`claude-sonnet-4-20250514`) | | `llm.max_tokens` | 否 | 最大响应 token 数(默认值:300) | | `llm.secrets` | 否 | 包含 `ANTHROPIC_AUTH_TOKEN` 或 `ANTHROPIC_API_KEY` 的 age 加密 .env 路径 | `{date}` 占位符始终可用,并解析为当前日期。 ### Agent 模式任务 任务可以通过设置 `mode: agent` 使用 agent 循环进行多步工具调用: ``` # tasks/email_triage.yaml name: email_triage schedule: "0 8 * * *" mode: agent fetch: gmail: image: executor-gmail:latest secrets: secrets/gmail.env.enc args: query: "is:unread newer_than:1d" tools: trash_email: executor: gmail_modify secrets: secrets/gmail_modify.env.enc description: "Move an email to trash" parameters: message_id: type: string description: "Gmail message ID" required: true fixed_args: action: "trash" agent: max_turns: 10 prompt: | Triage my unread emails. Trash spam. Summarize what you did. {gmail} output: type: imessage to: "$PHONE" llm: model: claude-sonnet-4-20250514 max_tokens: 1024 secrets: secrets/anthropic.env.enc ``` Agent 模式任务字段(除标准字段外): | 字段 | 必需 | 描述 | |-------|----------|-------------| | `mode` | 否 | `simple`(默认)或 `agent` | | `tools` | 否 | 工具名称到工具配置的映射 | | `tools..executor` | 是 | 要执行的执行器(例如 `gmail_modify`) | | `tools..secrets` | 否 | age 加密的 .env 文件路径 | | `tools..description` | 是 | 向 LLM 展示的描述 | | `tools..parameters` | 否 | LLM 可以提供的参数 | | `tools..fixed_args` | 否 | 始终传递给执行器的参数(覆盖 LLM 输入) | | `agent.max_turns` | 否 | 最大 agent 循环迭代次数(默认值:10) | ## Agent 配置 全局 agent 配置(`agent.yaml`)定义了交互式聊天模式的工具、LLM 设置、会话行为、通道、工作区记忆和 Guardian 设置: ``` system_prompt: | You are a personal assistant. Be concise and helpful. Today is {date}. tools: check_weather: executor: weather description: "Get current weather and forecast" parameters: location: type: string description: "City name or coordinates" required: true check_email: executor: gmail_readonly secrets: secrets/gmail.env.enc description: "Search Gmail for emails" parameters: query: type: string description: "Gmail search query" required: true # ... see agent.yaml for all tools (calendar, drive, iMessage, etc.) llm: model: claude-sonnet-4-20250514 max_tokens: 1024 secrets: secrets/anthropic.env.enc agent: max_turns: 15 session: sessions_dir: sessions max_history: 50 summarize_on_trim: true workspace: path: workspace timezone: "America/Denver" memory_days: 2 memory_max_chars: 5000 max_chars_per_file: 20000 channels: imessage: listen_to: "$PHONE" poll_interval: 3 bluebubbles: server_url: "$BLUEBUBBLES_URL" password: "$BLUEBUBBLES_PASSWORD" listen_to: - "$PHONE" poll_interval: 3 ``` 会话作为 JSON 文件存储在 `sessions/`(gitignored)中,并在交互之间保留对话历史。当启用 `summarize_on_trim` 时,旧消息在被修剪之前会被摘要。 **工作区记忆** 提供跨会话的基于文件的记忆: - `workspace/memory/YYYY-MM-DD.md` —— agent 的 `remember` 工具写入的每日仅追加日志 - `workspace/MEMORY.md` —— 精选的长期记忆 最近的每日日志和长期记忆会自动注入到系统提示中。 ### 静默时段 Creel 支持静默时段,以在配置的时间段(例如夜间、工作时间)内抑制主动通知。静默时段在 `agent.yaml` 中配置,仅抑制出站通知 —— 对用户消息的直接回复永远不会被抑制。 ``` quiet_hours: enabled: true start: "22:00" # 10 PM end: "08:00" # 8 AM timezone: "America/Denver" ``` ## 执行器 ### 天气 使用 [wttr.in](https://wttr.in) - 不需要 API key。 ``` weather: image: executor-weather:latest args: location: denver # city name or coordinates ``` ### Google 日历 需要一次性 OAuth 设置: ``` # 1. 创建 GCP project,启用 Calendar API,下载 OAuth credentials # 2. 运行安装脚本(--encrypt 自动加密并删除明文) python scripts/setup-google-oauth.py gcal --encrypt ``` 执行器使用只读 scope(`calendar.readonly`)并使用 refresh token 进行身份验证。 ### Gmail(读取) 读取与 Gmail 搜索查询匹配的电子邮件。支持列出电子邮件和按 ID 读取单个邮件。需要一次性 OAuth 设置: ``` # 同一个 GCP project — 启用 Gmail API python scripts/setup-google-oauth.py gmail --encrypt ``` 执行器使用只读 scope(`gmail.readonly`)。配置: ``` gmail_readonly: image: executor-gmail-readonly:latest secrets: secrets/gmail.env.enc args: query: "is:unread newer_than:1d" # Gmail search syntax max_results: "20" # max messages to fetch message_id: "" # set to read a specific email by ID ``` 当按 ID 读取单个邮件时,返回完整的解码(优先使用 `text/plain`,通过 BeautifulSoup 回退到 HTML 剥离)。 ### Google 日历(写入) 创建日历事件。需要使用 `calendar.events` scope 进行一次性 OAuth 设置: ``` python scripts/setup-google-oauth.py gcal_write --encrypt ``` 配置: ``` gcal_write: image: executor-gcal-write:latest secrets: secrets/gcal_write.env.enc args: summary: "Team standup" start: "2025-01-15T09:00:00-07:00" # ISO 8601 end: "2025-01-15T09:30:00-07:00" description: "Daily sync" # optional location: "Room 42" # optional ``` ### Gmail(发送) 发送电子邮件。需要使用 `gmail.send` scope 进行一次性 OAuth 设置: ``` python scripts/setup-google-oauth.py gmail_send --encrypt ``` 配置: ``` gmail_send: image: executor-gmail-send:latest secrets: secrets/gmail_send.env.enc args: to: "recipient@example.com" subject: "Daily report" body: "Here is today's summary..." ``` ### Gmail(修改) 修改、删除或永久删除 Gmail 邮件。需要使用 `gmail.modify` scope 进行一次性 OAuth 设置: ``` python scripts/setup-google-oauth.py gmail_modify --encrypt ``` 配置: ``` gmail_modify: image: executor-gmail-modify:latest secrets: secrets/gmail_modify.env.enc args: action: "modify" # modify, trash, or delete message_id: "18f1a2b3c4d5e6f" # Gmail message ID add_labels: "STARRED" # comma-separated label IDs (modify only) remove_labels: "UNREAD,INBOX" # comma-separated label IDs (modify only) ``` ### Google Drive(读取) 列出和读取 Google Drive 中的文件。需要使用 `drive.readonly` scope 进行一次性 OAuth 设置: ``` python scripts/setup-google-oauth.py drive --encrypt ``` 配置: ``` drive: image: executor-drive:latest secrets: secrets/drive.env.enc args: query: "mimeType='application/pdf'" # Drive search query (optional) max_results: "20" ``` ### Google Drive(写入) 将文件上传到 Google Drive。需要使用 `drive.file` scope 进行一次性 OAuth 设置: ``` python scripts/setup-google-oauth.py drive_write --encrypt ``` 配置: ``` drive_write: image: executor-drive-write:latest secrets: secrets/drive_write.env.enc args: name: "report.txt" content: "File contents here..." mime_type: "text/plain" # optional, defaults to text/plain folder_id: "" # optional Drive folder ID ``` ### BlueBubbles(iMessage) 通过 [BlueBubbles](https://bluebubbles.app/) 服务器发送和读取 iMessage。需要运行中的 BlueBubbles 实例。 ``` bluebubbles: image: executor-bluebubbles:latest secrets: secrets/bluebubbles.env.enc args: action: "get_recent_messages" # get_recent_messages, send_message, send_reaction, get_chats chat_id: "chat123" limit: "25" ``` 内置安全机制:消息数量硬性上限(50)、消息长度(2000 字符)、发送速率限制(10/分钟)以及收件人白名单强制执行。 ### Brave Search 通过 [Brave Search API](https://brave.com/search/api/) 进行网络搜索。 ``` brave_search: image: executor-brave-search:latest secrets: secrets/brave_search.env.enc args: query: "latest news on AI safety" count: "5" # max 20 ``` ### Fetch URL 使用 BeautifulSoup 从网页提取文本内容。剥离脚本、导航和样板内容。 ``` fetch_url: image: executor-fetch-url:latest args: url: "https://example.com/article" max_chars: "10000" ``` 不需要 API key。 ### Apple Notes 通过 host bridge 在 Notes.app 中读取和创建笔记。使用 `memo` CLI 工具进行 macOS 集成。 ``` apple_notes: args: action: "list_notes" # list_notes, search_notes, read_note, create_note folder: "Notes" limit: "25" ``` ### Apple Reminders 通过 host bridge 在 Reminders.app 中读取和创建提醒事项。使用 `remindctl` CLI 工具进行 macOS 集成。 ``` apple_reminders: args: action: "list_reminders" # list_reminders, create_reminder, complete_reminder, get_lists list_name: "Reminders" ``` ### Things 3 通过 host bridge 管理 Things 3 中的任务。使用 `things` CLI 工具进行 macOS 集成。 ``` things: args: action: "list_tasks" # list_tasks, create_task, complete_task, search_tasks area: "Personal" limit: "25" ``` ### iMessage Bridge 通过 host bridge 发送和读取 iMessage。使用 `imsg` CLI 工具进行 macOS 集成,提供 BlueBubbles 执行器的替代方案。 ``` imessage_bridge: args: action: "get_recent" # get_recent, send_message, get_chats limit: "25" chat_id: "chat123" ``` ### Exec 在具有可配置挂载点和网络隔离的沙盒 Docker 容器中执行 shell 命令。 ``` exec: args: command: "ls -la /workspace" workdir: "/workspace" mounts: - path: "/Users/user/docs" target: "/workspace" mode: "ro" ``` ## Google 服务设置 所有 Google 服务使用相同的 OAuth2 客户端凭证(来自单个 GCP 项目的客户端 ID + 客户端密钥),但获取具有不同 scope 的 **独立 refresh token**。这意味着: - 一个 `client_secret.json` 文件适用于所有服务 - 每个服务有自己的 `.env` 文件(例如 `secrets/gcal.env`、`secrets/gmail_send.env`) - 每个 refresh token 仅限于单个 API 权限 - 撤销一个 token 不影响其他 token ``` # 一次性设置所有服务(自动加密) python scripts/setup-google-oauth.py --all # 或设置特定服务 python scripts/setup-google-oauth.py gmail gcal # 每次 OAuth 流程后使用 age 加密 python scripts/setup-google-oauth.py gmail --encrypt # 加密 secrets/ 中所有现有 .env 文件(无 OAuth 流程) python scripts/setup-google-oauth.py --encrypt-all ``` 可用服务:`gcal`、`gcal_write`、`gmail`、`gmail_send`、`gmail_modify`、`drive`、`drive_write`。 ## 密钥管理 密钥使用 [age](https://github.com/FiloSottile/age) 进行静态加密。Python 端使用 [pyrage](https://pypi.org/project/pyrage/) 进行解密。 ``` # 生成 age 密钥对(一次性) mkdir -p ~/.age age-keygen -o ~/.age/key.txt 2> ~/.age/key.pub # 加密 .env 文件 ./scripts/encrypt-secret.sh secrets/anthropic.env # 解密密钥路径默认为 ~/.age/key.txt # 通过 AGE_IDENTITY 环境变量覆盖 export AGE_IDENTITY=/path/to/key.txt ``` `.env` 格式支持 `KEY=value`、带引号的值和注释: ``` ANTHROPIC_API_KEY=sk-ant-... GOOGLE_CREDENTIALS_JSON='{"refresh_token": "...", "client_id": "...", "client_secret": "..."}' ``` ## 容器模式 对于生产用途,执行器和 LLM runner 在具有受限功能的隔离 Docker 容器中执行。`--containers` 标志适用于任务运行、调度程序作业和守护程序运行时: ``` # 构建容器镜像 docker build -t executor-weather:latest executors/weather/ docker build -t executor-gcal:latest executors/gcal/ docker build -t executor-gcal-write:latest executors/gcal_write/ docker build -t executor-gmail-readonly:latest executors/gmail_readonly/ docker build -t executor-gmail-send:latest executors/gmail_send/ docker build -t executor-gmail-modify:latest executors/gmail_modify/ docker build -t executor-drive:latest executors/drive/ docker build -t executor-drive-write:latest executors/drive_write/ docker build -t executor-bluebubbles:latest executors/bluebubbles/ docker build -t executor-brave-search:latest executors/brave_search/ docker build -t executor-fetch-url:latest executors/fetch_url/ docker build -t llm-runner:latest llm/ # 使用容器运行任务 creel --containers run morning_briefing # 带有容器的 Scheduler creel --containers schedule # 带有容器化 executors 的 Daemon runtime creel --containers daemon start ``` 容器运行时具有: - `--read-only` 文件系统 - `--cap-drop=ALL` - `--security-opt=no-new-privileges` - 内存和 CPU 限制(`256m`、`0.5` CPU) - 60 秒超时 - 仅包含每个容器所需的密钥 在守护模式下,agent 循环在主机上运行,而每个工具调用(执行器)在其自己的隔离容器中执行。这保留了信任边界:LLM 永远不会看到凭证,执行器代码在沙盒中运行。 ## CLI 参考 ``` creel [options] Commands: run Run a task immediately schedule Start cron scheduler for all tasks list List available tasks validate Validate a task YAML file daemon ... Manage daemon lifecycle (start|stop|status|install|uninstall) attach Attach TUI client to running daemon send Send one message via daemon API audit Query the guardian audit log Global options: -v, --verbose Enable verbose/debug output --containers Run executors/LLM in Docker containers (all commands) --tasks-dir PATH Tasks directory (default: tasks/) --agent-config PATH Path to agent.yaml (default: agent.yaml) --json-logs Output structured JSON log lines (for production) --no-judge Disable the LLM judge to save API calls during development Run options: --dry Render prompt only, skip LLM and output Daemon start options: --socket-path PATH Unix socket path (default: ~/.creel/daemon.sock) --pid-file PATH PID file path (default: ~/.creel/daemon.pid) --log-file PATH Daemon log file (default: ~/.creel/daemon.log) --channel TYPE Channel plugin: none, imessage, bluebubbles --no-scheduler Disable scheduler in daemon runtime --label NAME launchd label (default: com.creel.daemon) --plist-path PATH launchd plist path Attach options: --sender-id ID Sender ID/session namespace (default: cli) --new Start and attach to a new session --resume ID Attach and resume a specific session --socket-path PATH Unix socket path (default: ~/.creel/daemon.sock) Send options: --sender-id ID Sender ID/session namespace (default: cli) --session-id ID Resume and send into a specific session --socket-path PATH Unix socket path (default: ~/.creel/daemon.sock) --stream Stream response events from daemon SSE endpoint Audit options: --tail N Show last N entries (default: 20) --all Show all entries --blocked Show only blocked input events --denied Show only denied action events --event TYPE Filter by event type (screen_input, validate_action, tool_result) --tool NAME Filter by tool name --since DATE Show entries since date (YYYY-MM-DD or YYYY-MM-DDTHH:MM:SS) ``` ## OpenClaw 迁移脚本 使用迁移脚本将 OpenClaw 工作区/配置/历史导入此仓库。 ``` # 完整 dry-run(默认阶段 1-2) python scripts/migrate-openclaw.py --source /path/to/openclaw # Apply migration python scripts/migrate-openclaw.py --source /path/to/openclaw --apply # 显式包含 skills migration(阶段 3) python scripts/migrate-openclaw.py --source /path/to/openclaw --phases 1,2,3 --apply # 仅运行一个阶段 python scripts/migrate-openclaw-phase1.py --source /path/to/openclaw --apply python scripts/migrate-openclaw-phase2.py --source /path/to/openclaw --apply python scripts/migrate-openclaw-phase3.py --source /path/to/openclaw --apply ``` 关键标志: - `--phases 1,2` 选择要运行的阶段(阶段 3 是可选的) - `--apply` 写入文件(不加此项为 dry-run) - `--overwrite/--no-overwrite` 文件冲突行为 - `--apply-agent-config` 将合并的 `agent.migrated.yaml` 写回 `agent.yaml` - `--report-json ` 写入机器可读的迁移报告 ## 项目结构 ``` creel/ ├── agent.yaml # Global agent config (tools, channels, sessions, guardian) ├── pyproject.toml ├── tasks/ # Task definitions (YAML) ├── policies/ # Guardian policy rules ├── secrets/ # Encrypted .env files (gitignored) ├── scripts/ # Utility scripts (encryption, OAuth setup) ├── src/ │ ├── creel/ │ │ ├── cli.py # creel CLI entrypoint │ │ ├── daemon/ # Daemon service, API, client contracts │ │ ├── chat.py # Agent/session router │ │ ├── tui.py # Textual TUI client │ │ ├── scheduler.py # APScheduler integration │ │ ├── session.py # JSON file-backed conversation sessions │ │ └── channels/ # iMessage + BlueBubbles channel plugins │ ├── bridge/ │ │ └── server.py # Host bridge HTTP API for macOS-native tools │ ├── executors/ # Tool executors (weather, Google, Notes, etc.) │ ├── guardian/ # Prompt-injection + policy protection pipeline │ └── llm/ # Containerized LLM runner ├── tests/ ├── approvals/ # Pending approval queue (gitignored) └── workspace/ # Agent workspace memory (gitignored) ``` ## 开发 需要 [pyenv](https://github.com/pyenv/pyenv) 和 [uv](https://github.com/astral-sh/uv)。 ``` # 首次设置 pyenv install 3.12.12 # .python-version pins this uv venv # creates .venv using pyenv's Python source .venv/bin/activate uv pip install -e ".[dev]" # 启用共享 git hooks(pre-commit 时运行 ruff lint + format) git config core.hooksPath scripts/hooks # 可选:live ONNX export + classifier smoke tests 所需 uv pip install -e ".[guardian]" # 运行测试 pytest ```
标签:AI 代理, Anthropic, CIS基准, CLI 工具, CNCF毕业项目, DLL 劫持, DNS 反向解析, Docker, iMessage 集成, Python, Web报告查看器, 个人助理, 人工智能, 任务编排, 任务自动化, 凭证隔离, 大语言模型, 安全代理, 安全防御评估, 容器隔离, 提示注入防御, 无后门, 智能体安全, 最小权限原则, 沙箱技术, 源代码安全, 用户模式Hook绕过, 端侧 AI, 网络安全, 自托管, 请求响应过滤, 请求拦截, 逆向工具, 隐私保护, 零信任架构, 零日漏洞检测