12 KiB
name, version, description, metadata
| name | version | description | metadata | ||||
|---|---|---|---|---|---|---|---|
| uncaged-dev | 1.0.0 | Uncaged 项目的完整开发 skill。涵盖:项目架构、开发流程、build/deploy、 测试验证、调试排查。装这一个 skill 就获得所有 Uncaged 开发能力。 |
|
Uncaged Dev
Uncaged 项目开发的一站式 skill。
项目概览
Uncaged — Sigil-native AI Agent 平台,运行在 Cloudflare Workers 上。
uncaged/
├── packages/
│ ├── core/ # 共享核心:LLM、Memory、Sigil、Tool Registry
│ ├── worker/ # CF Worker(后端 API + routing)
│ ├── web/ # React 19 + Tailwind v4 前端(SPA)
│ └── runner/ # Runner 客户端(连设备到 Agent)
├── tests/e2e/ # 场景化测试用例
├── scripts/ # 部署脚本
└── .github/workflows/ # CI/CD
仓库: oc-xiaoju/uncaged
线上: https://uncaged.shazhou.work
CI/CD: push main → GitHub Actions 自动部署
开发流程
原则:Issue 驱动,协调者不写代码,Cursor Agent 干活。
需求/Bug → 开 Issue → 创建分支 → Cursor Agent 编码 →
验证(build + diff)→ Commit(closes #N)→ 合并 main → 自动部署
1. 开 Issue
gh issue create --repo oc-xiaoju/uncaged \
--title "fix/feat: 简短描述" \
--body "## Problem\n...\n## Plan\n...\n## Acceptance\n..."
2. 创建分支
cd <uncaged-repo>
git checkout main && git pull
git checkout -b fix/descriptive-name # 或 feat/
3. 用 Cursor Agent 编码
CURSOR_API_KEY="$(secret get CURSOR_API_KEY)" \
bash ~/.openclaw/workspace/skills/cursor-agent-cn/scripts/run.sh \
<uncaged-repo> "<任务描述>" auto ask # 先 review
# 确认后
... auto write # 再 apply
中国区必须用 auto model。两步走:先 ask review,再 write apply。
4. 验证
# Build(三步)
cd packages/core && rm -rf dist && npx tsc
cd ../web && npm run build
cd ../worker && npx tsc --noEmit
# Diff 检查
git diff --stat
5. 提交 + 合并
git add -A
git commit -m "fix: description (closes #N)"
git checkout main && git merge <branch> --no-ff
git push origin main # 触发 CI/CD 自动部署
Build & Deploy
线上(自动)
Push main → GitHub Actions → build core → build web → wrangler deploy
开发环境(手动)
每人一个独立 Worker,互不影响:
bash scripts/deploy-dev.sh xingyue # → uncaged-xingyue.shazhou.work
bash scripts/deploy-dev.sh xiaoju # → uncaged-xiaoju.shazhou.work
bash scripts/deploy-dev.sh xiaomooo # → uncaged-xiaomooo.shazhou.work
bash scripts/deploy-dev.sh aobing # → uncaged-aobing.shazhou.work
脚本自动:build core → build web → wrangler deploy --env
手动部署线上(紧急)
cd packages/core && rm -rf dist && npx tsc
cd ../web && npm run build
cd ../worker
CLOUDFLARE_API_TOKEN="$(secret get CLOUDFLARE_API_TOKEN)" \
CLOUDFLARE_ACCOUNT_ID="$(secret get CLOUDFLARE_ACCOUNT_ID)" \
npx wrangler deploy
常见 Build 问题
| 问题 | 原因 | 解决 |
|---|---|---|
| core dist 为空 | tsconfig 缺 noEmitOnError: false |
检查 packages/core/tsconfig.json |
| wrangler can't resolve @uncaged/core/* | core 没 build | 先 cd packages/core && npx tsc |
| POST 请求返回 SPA HTML | wrangler.toml 缺 run_worker_first = true |
检查 [assets] 配置 |
| 路由 404 | normalizeApiPath strip 了 /api/v1/ |
路由匹配用 strip 后路径 |
测试
测试分层
| 层级 | 工具 | 位置 | 用途 |
|---|---|---|---|
| Unit Test | Vitest | packages/*/src/*.test.ts |
纯逻辑,mock 外部依赖,秒级 |
| UI 组件测试 | Vitest + @testing-library/react | packages/web/src/**/*.test.tsx |
React 组件交互,mock fetch |
| E2E API | curl 脚本 | tests/e2e/scripts/run-tests.sh |
真实 HTTP 请求打线上 API |
| E2E UI | Playwright | tests/e2e/ui/*.spec.ts |
浏览器自动化,端到端流程 |
跑测试的顺序: UT → UI 组件 → build → E2E(先快后慢,先本地后线上)
Unit Test
# 跑全部 UT
cd packages/core && npx vitest run
cd packages/worker && npx vitest run
cd packages/web && npx vitest run # UI 组件测试也在这
# 跑单个文件
npx vitest run src/agent-loop.test.ts
# Watch 模式(开发时)
npx vitest --watch
写 UT 的规范:
- 文件放在被测源码旁边:
foo.ts→foo.test.ts - Mock 外部依赖(KV、D1、fetch),参考
core/src/__mocks__/cf-bindings.ts - 测试文件不要 import 真实 Cloudflare bindings
describe按功能分组,test名字说清楚测的是什么行为- 验收:
npx vitest run全绿 + 无 type error
现有 UT 覆盖(13 文件 134 tests):
agent-loop— loop 流程、tool call、maxRounds、nudge、streamingpipeline— model 选择、context 压缩、orphan tool 清理baton— 创建、状态流转、子 baton 事件tool-registry— 注册、查询、user-invocable 过滤chat-store+chat-store-kv— 消息存储/读取identity/soul/codegen/embedding/chat-key/short-id/slug-resolver
UI 组件测试
cd packages/web && npx vitest run
写 UI 组件测试的规范:
- 用
@testing-library/react+jsdom环境 - 文件放在组件旁边:
chat-input.tsx→chat-input.test.tsx - Mock
fetch/authedFetch,不打真实 API - 优先测用户交互(输入、点击、键盘事件)和渲染结果
- 使用
data-testid(#71 已添加)定位元素 - 不测样式/CSS,测行为和 DOM 结构
Playwright UI 测试(9 个用例,28 秒)
cd <uncaged-repo>
npx playwright test # 跑全部
npx playwright test -g "tool search" # 跑单个
npx playwright test --reporter=html # 生成 HTML 报告
覆盖:token 登录、聊天发消息、工具搜索浮层、ESC 关闭、主题切换、JS 错误检测、手机端布局。
首次使用需安装浏览器: npx playwright install chromium
指定目标环境: UNCAGED_URL=https://uncaged-xingyue.shazhou.work npx playwright test
指定 token: TOKEN_NAME=UNCAGED_AGENT_TOKEN_XIAOJU npx playwright test
API 回归(12 个用例,curl 脚本)
bash tests/e2e/scripts/run-tests.sh UNCAGED_AGENT_TOKEN_XINGYUE
场景验证(给 subagent 用)
场景文件在 tests/e2e/scenes/:
| 场景 | 文件 |
|---|---|
| 认证 | scenes/auth.md |
| 聊天 | scenes/chat.md |
| 流式 | scenes/streaming.md |
| Tool Gateway | scenes/tool-gateway.md |
| 工具搜索 | scenes/tool-search.md |
| 异常处理 | scenes/error-handling.md |
派 subagent 验证:
读 <uncaged-repo>/tests/e2e/scenes/tool-gateway.md,按描述验证。
失败了收集 logs 开 bug issue。
Token 登录(测试用)
TOKEN=$(secret get UNCAGED_AGENT_TOKEN_XINGYUE)
curl -s -X POST "https://uncaged.shazhou.work/auth/token" \
-H "Content-Type: application/json" \
-d "{\"token\": \"$TOKEN\"}" \
-c /tmp/uncaged-cookies.txt
uncaged-cli 工具
安装:
npm install -g @uncaged/cli # 官方版本
# 或
npm install -g uncaged-cli # 备用版本
基本用法:
# 初始化配置
uncaged init
# 登录(自动检测 JWT token 或生成 Admin token)
uncaged login
# 环境状态检查
uncaged status
# 数据库查询
uncaged db users --limit 10
uncaged db messages --user scott --channel web --limit 20
uncaged db agents --format table
uncaged db context-boundaries --user scott --channel telegram
# API 调试
uncaged api chat "hello world"
uncaged api history --user scott --channel web --limit 10
uncaged api debug overview
uncaged api debug stats
uncaged api debug capabilities
# 测试用户管理(Admin 权限)
uncaged admin create-user test-user-123
uncaged admin delete-user test-user-123 --cascade
uncaged admin create-token test-user-123
# Sigil 操作
uncaged sigil list
uncaged sigil inspect <capability-name>
# 配置管理
uncaged config show
uncaged config set endpoint https://uncaged-dev.shazhou.work
uncaged config set auth.method admin
多环境切换:
# 开发环境
uncaged config set endpoint https://uncaged-xingyue.shazhou.work
uncaged config set auth.method jwt
uncaged config set auth.token "$(secret get UNCAGED_AGENT_TOKEN_XINGYUE)"
# 生产环境
uncaged config set endpoint https://uncaged.shazhou.work
uncaged config set auth.method admin
uncaged config set auth.admin_token "$(secret get UNCAGED_ADMIN_TOKEN)"
# 查看当前配置
uncaged status
Channel 概念:
web— 网页聊天telegram— Telegram 消息api— 直接 API 调用
所有 db 和 api 命令都支持 --channel 过滤。
调试
Worker 日志
CF_TOKEN=$(secret get CLOUDFLARE_API_TOKEN)
CF_ACCOUNT=$(secret get CLOUDFLARE_ACCOUNT_ID)
cd <uncaged-repo>/packages/worker
CLOUDFLARE_API_TOKEN="$CF_TOKEN" CLOUDFLARE_ACCOUNT_ID="$CF_ACCOUNT" \
npx wrangler tail --format pretty
D1 查询
CLOUDFLARE_API_TOKEN="$CF_TOKEN" CLOUDFLARE_ACCOUNT_ID="$CF_ACCOUNT" \
npx wrangler d1 execute uncaged-memory --remote --json \
--command "SELECT * FROM users LIMIT 10;"
前端状态
# Session
curl -s https://uncaged.shazhou.work/auth/session -b /tmp/uncaged-cookies.txt | python3 -m json.tool
# History
curl -s https://uncaged.shazhou.work/scott/doudou/api/history -b /tmp/uncaged-cookies.txt | python3 -c "
import sys,json; [print(f' [{m[\"role\"]}] {str(m.get(\"content\",\"\"))[:80]}') for m in json.load(sys.stdin)['history'][-5:]]"
架构速查
API
| 端点 | 方法 | 说明 |
|---|---|---|
/auth/token |
POST | Token 登录 |
/auth/session |
GET | Session 检查 |
/:o/:a/api/v1/chat |
POST | 发消息 |
/:o/:a/api/v1/chat/stream |
POST | SSE 流式 |
/:o/:a/api/v1/history |
GET | 聊天历史 |
/:o/:a/api/v1/clear |
POST | 清空历史 |
/:o/:a/api/v1/tools/builtin |
GET | Builtin 工具列表 |
/:o/:a/api/v1/tools/:slug/invoke |
POST | 直接调用工具 |
关键文件
| 文件 | 作用 |
|---|---|
core/src/llm/tool-registry.ts |
SSOT — 所有 builtin tool 定义 |
core/src/llm/agent-loop.ts |
LLM agent loop + tool execution |
worker/src/index.ts |
主路由 + Worker entry |
worker/src/services/capability-service.ts |
Tool Gateway 执行层 |
web/src/hooks/use-chat.ts |
前端聊天状态 |
web/src/components/chat/chat-input.tsx |
输入框 + 工具搜索 |
web/src/components/chat/message-bubble.tsx |
消息气泡渲染 |
worker/wrangler.toml |
CF 配置(bindings + routes + envs) |
路由注意事项
- 所有前端 API 调用统一使用
/api/v1/前缀(#73 已清理) normalizeApiPath只 strip/api/v1/前缀(不再处理/api/)- 新路由要用 strip 后的路径匹配(如
/tools/:slug/invoke不是/api/v1/tools/...) [assets] run_worker_first = true确保 POST 请求到 Worker- 没有
webEnabled、hasBearer、channels/web.ts(#73 已删除)
Tool Registry(SSOT)
添加新 builtin tool 只需改 core/src/llm/tool-registry.ts 的 TOOL_REGISTRY 数组,LLM / Gateway / 前端自动同步。
Secrets
secret get CLOUDFLARE_API_TOKEN # CF 部署
secret get CLOUDFLARE_ACCOUNT_ID # CF 账户
secret get CURSOR_API_KEY # Cursor Agent
secret get UNCAGED_AGENT_TOKEN_XINGYUE # 测试登录 token