☁️ 云机管家

这是一个云手机远程控制服务。POST 请求需要 HMAC-SHA256 签名。

📋 快速连接(3步)

步骤 1:获取 Token
Token 由手机端 App 生成,启动后自动复制到剪贴板
步骤 2:获取签名客户端脚本
curl "https://YOUR_URL/api/client?token=YOUR_TOKEN" -o cloudmanager.py

脚本里自动填入了 URL、Token、签名密钥,可以直接运行。

步骤 3:运行脚本
python3 cloudmanager.py

或在自己的代码里 import:

from cloudmanager import CloudManagerClient
c = CloudManagerClient()
print(c.status())           # 查看状态
print(c.shell("id"))        # 执行命令
c.tap(540, 1170)            # 点击屏幕
c.keyboard_text("hello")    # 键盘输入
c.screenshot()              # 截图

🔐 签名算法(v33 更新)

POST 请求必须带签名头。签名密钥从 token 派生,无需额外接口获取。

import hmac, hashlib, time, secrets

# 1. 从 token 派生签名密钥 (无需请求服务端)
sign_key = hmac.new(
    YOUR_TOKEN.encode(),
    b"cloudmanager-signing-v1",
    hashlib.sha256
).hexdigest()

# 2. 每次 POST 请求生成签名
timestamp = str(int(time.time() * 1000))  # 毫秒级时间戳
nonce = secrets.token_hex(16)              # 随机数
body = '{"cmd": "echo hi"}'               # 请求体 JSON 字符串
body_hash = hashlib.sha256(body.encode()).hexdigest()
sign_data = f"POST{path}{timestamp}{nonce}{body_hash}"
# path 不含 query string,如 "/api/shell"
signature = hmac.new(sign_key.encode(), sign_data.encode(), hashlib.sha256).hexdigest()

# 3. 发送请求 (注意: 不需要 X-Body-Hash 头,服务端自己算 sha256)
headers = {
    "Authorization": "Bearer YOUR_TOKEN",
    "Content-Type": "application/json",
    "X-Timestamp": timestamp,
    "X-Nonce": nonce,
    "X-Signature": signature,
}
POST /api/shell with body

📡 API 端点

GET  /api/status              公开,返回在线状态
GET  /api/devices             需要 token,列出 ADB 设备
POST /api/shell               需要 token + 签名,执行 root shell 命令
POST /api/command             需要 token + 签名,执行 ADB 命令
POST /api/screenshot          需要 token + 签名,截图 (PNG base64)
POST /api/input               需要 token + 签名,输入操作 (tap/swipe/text/keyevent)
POST /text                    需要 token + 签名,中文输入 (剪贴板)
POST /gesture                 需要 token + 签名,手势 (scroll/fling)
POST /multi                   需要 token + 签名,多点触控
POST /file/upload             需要 token + 签名,上传文件 (base64)
POST /file/download           需要 token + 签名,下载文件 (自动分块 >600KB)
POST /file/download/chunk     需要 token + 签名,下载分块

🤖 AI 会话 + 聊天 API (v49+)

POST /api/ai/connect          需要 token + 签名,AI 接入 (自动分配 codename)
  body: {"clientInfo":"my-ai-bot"}
  返回: {"sessionId":"ai-1","codename":"AI-1","heartbeatTimeoutSec":90}
POST /api/ai/heartbeat        保活, 每 25s 调用一次, 90s 不调自动断开
POST /api/ai/disconnect       主动断开
POST /api/ai/status           查询所有 AI 会话
POST /api/ai/chat/send        AI 发消息给用户 (text/choice/task_output)
  body: {"sessionId":"ai-1","type":"text","content":"你好","choices":["选项1","选项2"]}
POST /api/ai/chat/user-send   用户发消息给 AI (App 内部用)
POST /api/ai/chat/choice      用户回复选择题
POST /api/ai/chat/history     拉取聊天历史 (轮询模式)
  body: {"sessionId":"ai-1","limit":50,"sinceId":0}
POST /api/ai/chat/clear       清空聊天记录
POST /api/ai/chat/delete      删除单条消息
POST /api/ai/chat/export      导出聊天记录
GET  /api/ai/chat/stream      SSE 长连接, 实时接收用户消息 (需 token)

🔌 MCP 客户端 API (v53+, v57 重写支持 JSON-RPC)

POST /api/mcp/connect         连接 MCP 服务 (标准 JSON-RPC initialize 握手)
  body: {"url":"http://127.0.0.1:8787/mcp"}
  或:    {"url":"builtin://local"}  (内置工具集 fallback)
  返回: {"sessionId":"...","serverInfo":{"name":"MT APK MCP","version":"0.1.0"}}
POST /api/mcp/tools           列出工具 (tools/list)
  body: {"url":"http://127.0.0.1:8787/mcp"}
POST /api/mcp/call            调用工具 (tools/call)
  body: {"url":"...","tool":"mt_apk_search","args":{"workspaceId":"xxx","query":"vip",...}}
POST /api/mcp/discover        自动发现本地 MCP 服务 (扫描 8787/3001/8080 等端口)
POST /api/mcp/list            列出已连接的 MCP 服务
POST /api/mcp/disconnect      断开 MCP 服务

安全: 只允许本地 (127.0.0.1) 和局域网 (10.x/192.168.x/172.16-31.x), 禁止公网
已验证: 可连接 MT管理器 MCP (17 个 APK 逆向工具: mt_apk_open/search/read_text/edit/build)

🛡 安全特性 (v33+, v58 日志脱敏)

- HMAC-SHA256 签名验证 (所有 POST 请求)
- 时间戳防重放 (5 分钟有效期)
- Nonce 防重放 (5 分钟去重)
- IP 频率限制 (60/min, unknown 180/min)
- IP 黑名单 (失败 10 次封禁 30 分钟)
- Token 加密存储 (EncryptedSharedPreferences)
- signKey 从 Token 派生 (HKDF), 不持久化
- v58: 日志脱敏 (LogSanitizer)
  - token/secret/password/apikey → ***REDACTED***
  - Bearer xxx → Bearer ***REDACTED***
  - --network-secret 'xxx' → --network-secret '***'
  - /api/logs 输出前再次脱敏 (兜底)

🌐 隧道模式 (4 种)

1. SSH (serveo.net)        默认, 免配置, 随机域名
2. Tailscale Funnel        固定域名 cloudphone.tail7369aa.ts.net
3. Cloudflare Tunnel       最快, 随机域名
4. EasyTier (推荐)          去中心化 mesh VPN, 延迟最低
  POST /api/easytier/start {"auto":true}  一键自动配置
  POST /api/easytier/connect-info          获取连接信息
  AI 端: easytier-core --no-tun --network-name 'xxx' --network-secret 'yyy' \
         -p 'tcp://39.98.83.46:51010' --socks5 1080
  然后: curl --socks5 127.0.0.1:1080 http://10.144.144.100:3000/api/status

📱 聊天功能 (App 内)

- App 聊天 Tab: 和 AI 实时对话
- AI 接入后显示在 "在线" 列表
- 支持: 文本/选择题/任务输出/系统消息
- 长按消息: 删除
- 导出聊天记录 (通过 API)
- v59: 修复输入框第一字符重复 bug (TextFieldValue + composition 严格检测)
- v58.1: 修复中文消息发送阻塞 bug (字节流读取)

📜 日志端点 (v34 新增)

GET  /api/logs                需要 token,被动拉取日志
  参数: limit (默认 200, max 1000)
        since (毫秒时间戳,只返回此时间之后的)
        level (DEBUG/INFO/WARN/ERROR)
        tag (按 tag 过滤,如 HttpServer/Security)
  响应: {"ok":true,"data":{"logs":[...],"count":N}}

GET  /api/logs/stream         需要 token,SSE 主动推送 (长连接)
  参数: 同 /api/logs + follow (默认 true,false=只推历史后断开)
  响应: text/event-stream,每条日志一个 data: 行
  示例:
    curl -N -H "Authorization: Bearer TOKEN" \\
      "https://URL/api/logs/stream?level=WARN&follow=true"

🔌 插件系统 (v34 新增, v63 大幅增强)

v63: AI 自由编写 10 种语言脚本, 永久保存, 标签分类, 版本管理, 安全沙箱, 内置工具包。

统一存储路径: /data/local/tmp/cm_plugins/

  scripts/    脚本文件
  meta/       元数据 (JSON)
  versions/   旧版本备份
  sandbox/    沙箱工作目录
  builtin/    内置通用工具

支持的语言 (12 种)

shell   python   js   ruby   lua   php   perl   go   rust   java   frida(动态分析)   c

API 端点

POST /api/plugins/install     安装/更新插件 (含标签+沙箱+版本)
  body: {"name":"vip_unlock","type":"python","description":"VIP解锁模板",
         "code":"#!/usr/bin/env python\n...","author":"glm-4.6",
         "tags":["逆向","vip","通用"],"sandbox":true}
  响应: {"ok":true,"data":{"name":"vip_unlock","version":1,"tags":[...],"sandbox":true}}

POST /api/plugins/list        列出所有插件 + 内置工具
  响应: {"ok":true,"data":{"plugins":[...],"builtins":[...],"count":N,"builtin_count":M}}

POST /api/plugins/get         获取插件详情 (含代码)
  body: {"name":"vip_unlock"}

POST /api/plugins/run         运行插件 (自动沙箱)
  body: {"name":"vip_unlock","args":{"package":"com.xxx"},"timeout":30000}
  响应: {"ok":true,"data":{"exitCode":0,"stdout":"...","sandbox_dir":"..."}}

POST /api/plugins/delete      删除插件 (含版本+沙箱)
POST /api/plugins/clear       清空所有插件
POST /api/plugins/clear_sandbox  清空沙箱目录

POST /api/plugins/search      按标签搜索
  body: {"tag":"逆向"}
  响应: {"ok":true,"data":{"plugins":[...],"count":N}}

POST /api/plugins/tags        列出所有标签
  响应: {"ok":true,"data":{"tags":["逆向","vip","内存","抓包"],"count":4}}

POST /api/plugins/versions    列出插件所有版本
  body: {"name":"vip_unlock"}
  响应: {"ok":true,"data":{"versions":[{"version":1,"current":false},{"version":2,"current":true}]}}

POST /api/plugins/rollback    回滚到旧版本
  body: {"name":"vip_unlock","version":1}
  响应: {"ok":true,"data":{"rolled_back_to":1,"backup_of":2}}

POST /api/plugins/diff        对比两个版本
  body: {"name":"vip_unlock","v1":1,"v2":2}
  响应: {"ok":true,"data":{"diff":"..."}}

内置通用工具包 (6 个)

apk_dump_dex      dump 加固 APK 的真实 dex (frida-dexdump)
  args: APK_PATH=/data/app/xxx.apk
memory_scan       内存搜索 (找 VIP 标志位)
  args: PID=12345 PATTERN=isVip
smali_patcher     smali 修改重打包 (提示用 MT管理器 MCP)
  args: APK=xxx.apk METHOD=isVIP NEW_CODE="const/4 v0, 0x1"
frida_tracer      Frida 自动 trace 关键方法
  args: PACKAGE=com.xxx METHODS=isVip,isPremium
crypto_toolkit    加解密 (md5/sha256/aes_enc/aes_dec)
  args: ACTION=md5 DATA=hello [KEY=xxx]
network_capture   网络抓包 (tcpdump)
  args: DURATION=30 PORT=443

安全特性

- 名称校验: ^[a-zA-Z][a-zA-Z0-9_]{1,63}$ (防路径穿越)
- 代码限制: max 1MB
- 路径校验: script_path 必须在 scripts/ 或 builtin/ 下
- 沙箱: 默认在 sandbox// 运行, 隔离文件操作
- 超时: 1s ~ 5 分钟 (可配)
- 参数传递: env CM_ARG_ (base64 解码, 防 shell 注入)
- 审计日志: 所有插件调用记录到 LogBuffer
- v65 智能解释器查找: 自动检测 4 个路径
  /system/bin → /system/xbin → /data/local/tmp/tbin → /data/data/com.termux/files/usr/bin
  C 语言: gcc 优先, clang 备选, cc 兜底
  不存在时返回友好错误 + 安装提示

🚀 高级 API (v35 新增 - 28 个)

封装常见操作,一行调用完成,返回结构化 JSON,不用拼 shell 命令。

1. 进程管理

POST /api/process/list       列出所有进程 (PID/PPID/USER/RSS/STAT/NAME)
  body: {"filter":"frida"}   可选,按名字过滤
  响应: {"ok":true,"data":{"processes":[{"pid":123,"name":"frida-server",...}],"count":1}}

POST /api/process/kill       杀进程
  body: {"pid":123,"signal":"KILL"}   signal 可选: TERM/KILL/HUP/INT (默认 TERM)

2. 应用管理

POST /api/apps/list          列出应用
  body: {"type":"third"}     type 可选: third/system/all (默认 all)

POST /api/apps/info          应用详情 (version/uid/dataDir/apkPath)
  body: {"package":"com.tencent.mm"}

POST /api/apps/launch        启动应用
  body: {"package":"com.tencent.mm","activity":"com.tencent.mm.ui.LauncherUI"}
  activity 可选,不填用默认 LAUNCHER

POST /api/apps/force-stop    强制停止
  body: {"package":"com.tencent.mm"}

POST /api/apps/uninstall     卸载
  body: {"package":"com.tencent.mm"}

POST /api/apps/clear-data    清除应用数据
  body: {"package":"com.tencent.mm"}

3. 文件管理

POST /api/file/ls            列目录 (返回 JSON 结构)
  body: {"path":"/data/local/tmp","hidden":false}

POST /api/file/cp            复制  body: {"src":"...","dst":"..."}
POST /api/file/mv            移动  body: {"src":"...","dst":"..."}
POST /api/file/rm            删除  body: {"path":"...","recursive":true}
POST /api/file/mkdir         建目录 body: {"path":"..."}
POST /api/file/chmod         改权限 body: {"path":"...","mode":"755"}

4. 网络管理

POST /api/net/connections    所有 TCP 连接 (含 PID/进程名)
POST /api/net/listen-ports   监听端口
POST /api/net/interfaces     网络接口 (IP/MAC/状态)
POST /api/net/routes         路由表

5. 系统控制

POST /api/system/reboot      重启
POST /api/system/shutdown    关机
POST /api/system/lock        锁屏
POST /api/system/brightness  亮度  body: {"level":128}   (0-255)
POST /api/system/volume      音量  body: {"stream":"media","level":8}
                              stream: media/ring/notification/alarm/system
POST /api/system/battery      电池状态 (level/temp/voltage/charging)
POST /api/system/info         系统信息 (model/android/abi/build/meminfo/disk)

6. UI 自动化 (智能找元素)

POST /api/ui/dump            dump UI 树,返回所有可见元素 (text/resourceId/bounds)
  响应: {"ok":true,"data":{"elements":[{"text":"登录","bounds":{"cx":540,"cy":1200},...}]}}

POST /api/ui/click-text      按文本点击 (自动找元素+点中心)
  body: {"text":"登录","partial":true}
  partial=true 表示包含匹配 (默认),false=精确匹配

POST /api/ui/wait-text       等待文本出现 (轮询)
  body: {"text":"加载完成","timeout":10000}
  返回: {"found":true,"elapsed_ms":2340}

7. 环境准备 (一键装工具)

POST /api/setup/tools        下载预编译工具到 /data/local/tmp/tools/
  body: {"tools":["frida-server","nmap","sqlmap","mitmproxy"]}
  用 App 内置 HttpURLConnection 下载,不依赖 curl/wget
  自动解压 .xz 文件
  支持的工具:
    - frida-server (Frida Hook 服务端)
    - nmap (网络扫描)
    - sqlmap (SQL 注入)
    - mitmproxy (HTTPS MITM 代理)

v35 高级 API 使用示例

# 之前 (要拼 shell 命令,效率低):
POST /api/shell
body: 用 ps + grep + awk + xargs kill 拼命令

# 现在 (一行 API 调用,返回结构化 JSON):
POST /api/process/kill
body: {"pid":1234,"signal":"KILL"}

# 之前 (找按钮点击要 dump + 算坐标):
POST /api/shell
body: 用 uiautomator dump + 解析 XML + 算坐标 + input tap

# 现在 (自动找元素+点击):
POST /api/ui/click-text
body: {"text":"登录"}

# 之前 (装工具要找 curl/wget):
POST /api/shell
body: 用 curl -o 下载 (但手机没 curl)

# 现在 (App 内置下载):
POST /api/setup/tools
body: {"tools":["frida-server"]}

插件示例

1. 安装 tcpdump 抓包插件:
   POST /api/plugins/install
   body: {"name":"tcpdump_capture","type":"shell",
          "description":"抓包 30s 到 /data/local/tmp/cap.pcap",
          "code":"#!/system/bin/sh\\ntcpdump -i any -w /data/local/tmp/cap.pcap -s 0 -G 30 -W 1"}

2. 运行插件:
   POST /api/plugins/run
   body: {"name":"tcpdump_capture","timeout":60000}

3. 下载抓包文件:
   POST /file/download
   body: {"path":"/data/local/tmp/cap.pcap"}

插件可用环境变量

所有插件运行时,通过 args 传入的参数会变成环境变量:
  CM_ARG_ = 

例如: args={"url":"https://api.example.com","count":"3"}
脚本里读: CM_ARG_URL / CM_ARG_COUNT (Bourne shell 语法)

🐧 微缩 Linux 环境 (v68 新增, v74.2 修复)

云机管家内置 Alpine Linux chroot,可执行完整 Linux 命令 (python3/gcc/node/sqlite 等)。

POST /api/linux/setup       初始化 Alpine rootfs (首次约 30s, 3MB 下载)
  自动: 下载 Alpine 3.19.7 aarch64 mini rootfs + SHA256 校验 + 解压到 /data/local/tmp/cm_linux/
  自动: mount /proc /sys /dev (用完 umount)

POST /api/linux/exec       在 chroot 内执行 Linux 命令
  body: {"cmd":"python3 -c 'import sys; print(sys.version)'","timeout":15000}
  响应: {"ok":true,"data":{"exitCode":0,"stdout":"Python 3.x.x","stderr":"","timedOut":false}}

POST /api/linux/install    用 apk 包管理器安装包 (apk add)
  body: {"packages":"python3 py3-requests gcc nodejs-current"}
  响应: {"ok":true,"data":{"exitCode":0,"stdout":"Installing..."}}

POST /api/linux/status     查看 chroot 状态 (ready/rootfs路径/已装工具)
POST /api/linux/destroy    销毁 rootfs (清理 ~50MB 空间)

示例:
  # 1. 初始化
  POST /api/linux/setup {}
  # 2. 执行 python3
  POST /api/linux/exec {"cmd":"python3 -c 'import requests; print(requests.get(\"https://api.github.com\").status_code)'"}
  # 3. 装 sqlite3
  POST /api/linux/install {"packages":"sqlite"}
  # 4. 用 sqlite3 操作数据库
  POST /api/linux/exec {"cmd":"sqlite3 /tmp/test.db 'CREATE TABLE t(id INTEGER); INSERT INTO t VALUES(1); SELECT * FROM t;'"}

特性:
  - chroot 隔离, 不影响 Android 系统
  - mount/umount 严格配对 (防止泄露)
  - 命令通过临时 .sh 文件传递 (v74.2 修复, 避免引号嵌套问题)
  - PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin
  - DNS 自动从 Android 复制
  - 配置文件 chmod 600, root 可读

📱 Termux 环境 (v67 新增,智能解释器查找)

如果手机装了 Termux,可以用 Termux 的解释器 (无需 chroot)。

POST /api/termux/setup      检查/初始化 Termux (装 bash/coreutils)
POST /api/termux/exec       在 Termux 环境执行命令
  body: {"command":"pip install requests","timeout":30000}
  响应: {"ok":true,"data":{"exitCode":0,"stdout":"...","stderr":"","timedOut":false}}

POST /api/termux/pkg-install  装 Termux 包 (pkg install)
  body: {"packages":"python nodejs ruby"}
POST /api/termux/status     查看 Termux 是否安装

智能解释器查找 (v65):
  按顺序查找: /system/bin → /system/xbin → /data/local/tmp/tbin → Termux usr/bin → Linux chroot
  C 语言: gcc 优先, clang 备选, cc 兜底
  不存在时返回友好错误 + 安装提示

🔧 Frida 动态分析 (v36+v37)

完整 Frida 工具链: 启动 frida-server, 写脚本, 运行, 枚举 Java 类。

POST /api/frida/start       启动 frida-server (后台守护进程)
POST /api/frida/stop        停止 frida-server
POST /api/frida/status      查看状态 (running/port/binaryPath)

POST /api/frida/script/write   写 Frida 脚本到 /data/local/tmp/frida_scripts/
  body: {"name":"hook_isvip","code":"Java.perform(()=>{...})"}

POST /api/frida/script/list    列出所有已保存脚本
POST /api/frida/script/run     运行脚本 (附加到指定 PID 的进程)
  body: {"name":"hook_isvip","pid":1234}
  注: 字段是 "name" (脚本名) + "pid" (进程 ID), 不是 scriptName/package
  先用 /api/shell {"cmd":"pidof com.xxx"} 获取目标进程 PID

POST /api/frida/enumerate      枚举 Java 类 (附加到指定 PID)
  body: {"pid":1234}
  注: 字段是 "pid", 不是 "type"
  返回该进程加载的所有 Java 类列表

Frida Inject (v37, 不需要 Python!):
POST /api/frida2/setup         下载/检查 frida-inject 二进制
POST /api/frida2/inject        注入 .so 或脚本到指定 PID
  body: {"pid":1234,"script":"/data/local/tmp/hook.js","timeout":15}
  注: 字段是 "pid" + "script" + "timeout" (秒)
POST /api/frida2/hook-so       hook native 函数
  body: {"pid":1234,"so":"libnative-lib.so","func":"Java_com_xxx_isVip","action":"trace"}
  注: 字段是 "pid" + "so" (库名) + "func" (函数名) + "action" (trace/replace)
POST /api/frida2/exports       列 .so 导出符号
  body: {"pid":1234,"so":"libnative-lib.so"}
  注: 字段是 "pid" + "so"
POST /api/frida2/classes       列 Java 类 (Java.choose)
  body: {"pid":1234,"filter":"com.xxx."}
  注: 字段是 "pid" + "filter" (可选, 前缀过滤)

完整使用流程:
  # 1. 启动 frida-server
  POST /api/frida/start {}
  # 2. 找目标进程 PID
  POST /api/shell {"cmd":"pidof com.tencent.mm"}
  # 3. 写 Frida 脚本
  POST /api/frida/script/write
    {"name":"hook_login","code":"Java.perform(()=>{var L=Java.use('com.xxx.Login');L.isVip.implementation=function(){return true;};});"}
  # 4. 运行脚本 (用第 2 步获取的 PID)
  POST /api/frida/script/run {"name":"hook_login","pid":12345}
  # 5. 枚举 Java 类 (查看可 hook 的类)
  POST /api/frida/enumerate {"pid":12345}
  # 6. 停止 frida-server
  POST /api/frida/stop {}

🧠 内存工具 (v36+v37, GameGuardian 替代)

搜索/读写内存,支持 native heap (LibGDX/Unity 游戏)。

POST /api/mem/read           读内存 (/proc/pid/mem)
  body: {"pid":1234,"addr":"0x7fff1234","size":64}
  响应: {"ok":true,"data":{"pid":1234,"addr":"0x7fff1234","size":64,"data":"base64...","hex":"ab cd ef..."}}

POST /api/mem/write          写内存 (改游戏数值)
  body: {"pid":1234,"addr":"0x7fff1234","data":"base64编码的数据"}

POST /api/mem/search         搜索 dalvik heap (int32/int16/int8/float)
  body: {"pid":1234,"value":9999,"type":"int32","maxResults":100}
  响应: {"ok":true,"data":{"addresses":["0x...","0x..."],"totalMatches":2}}

POST /api/mem/dump           dump 内存块 (大块,1MB 内)
  body: {"pid":1234,"addr":"0x7fff0000","size":65536}

POST /api/mem/search-all     搜索所有 rw-p 区域 (含 native heap, scudo)
  body: {"pid":1234,"value":9999,"type":"int32","maxResults":100}

POST /api/mem/regions        列出所有内存区域 (按大小排序)
  body: {"pid":1234}
  响应: {"ok":true,"data":{"regions":[{"sizeMB":64,"start":"0x...","desc":"[anon:scudo:secondary]"}]}}

POST /api/mem/search-string  搜索字符串 (ASCII + UTF-16)
  body: {"pid":1234,"keyword":"isVip","maxResults":50}

安全 (v74 修复):
  - addrHex 必须匹配 ^(0x)?[0-9a-fA-F]+\$ (防 shell 注入)
  - data 必须是合法 base64
  - pid 必须 > 0 且 < 4194304

📦 反编译工具 (v36)

POST /api/decompile/apk      反编译 APK (unzip + 提取 dex/so + 找游戏引擎)
  body: {"package":"com.xxx"}
  响应: 自动识别 Unity/Cocos2d/LibGDX, 列 dex 文件, strings 搜游戏关键字

POST /api/decompile/dex      分析 dex (strings + 关键字搜索)
  body: {"path":"/data/app/com.xxx/base.apk","keyword":"vip"}
  响应: {"ok":true,"data":{"results":["com.xxx.VipUtils","isVip:Z"],"count":2}}

安全 (v74 修复):
  - dexPath 校验 (只允许 [a-zA-Z0-9._/-], 禁止 ..)
  - keyword 校验 (禁止 shell 元字符, 单引号转义)

🌐 网络工具 (v36)

POST /api/capture            抓包 (tcpdump)
  body: {"duration":30,"filter":"port 443"}
  响应: {"ok":true,"data":{"pcapPath":"/data/local/tmp/cap_xxx.pcap","output":"..."}}
  filter 是 BPF 过滤器 (host/port/tcp/udp/src/dst 等)

POST /api/scan               端口扫描 (用 bash /dev/tcp, 不依赖 nmap)
  body: {"host":"192.168.1.1","portStart":1,"portEnd":1000}
  响应: {"ok":true,"data":{"openPorts":[22,80,443,8080],"count":4}}

安全 (v74 修复):
  - filter 必须是合法 BPF (禁止 shell 元字符, 允许字母数字 () * : <> !=)
  - host 校验 (只允许 [a-zA-Z0-9.-])

🔐 AES Body 加密 (v73 新增, --no-tun 模式推荐)

EasyTier --no-tun + socks5 模式下, HTTP body 默认明文。 加 X-AES-Encrypted: 1 头启用 AES-256-CBC 端到端加密。

算法:
  密钥: SHA-256("aes-body-v1:" + token)  → 32 字节 (AES-256)
  IV: 每次请求随机 16 字节
  模式: AES/CBC/PKCS5Padding
  body = base64(IV(16) + ciphertext)

请求头:
  Authorization: Bearer TOKEN
  Content-Type: application/octet-stream
  X-Timestamp: 毫秒时间戳
  X-Nonce: 32 字符 hex
  X-Signature: HMAC-SHA256(signKey, "POST" + path + ts + nonce + sha256(明文body))
  X-AES-Encrypted: 1

body: base64(IV + AES-256-CBC(明文 JSON))

注意:
  - 签名基于明文 body (服务端先 AES 解密再验签)
  - 响应也是 AES 加密的 (同样的 IV+ciphertext 格式)
  - 错误响应也可能加密 (401/403 等)

Python 示例:
  from Crypto.Cipher import AES
  from Crypto.Util.Padding import pad, unpad
  import hashlib, base64, hmac, secrets, json, time

  aes_key = hashlib.sha256(("aes-body-v1:" + TOKEN).encode()).digest()
  iv = secrets.token_bytes(16)
  cipher = AES.new(aes_key, AES.MODE_CBC, iv)
  plain = json.dumps({"cmd": "echo aes_test"})
  ct = cipher.encrypt(pad(plain.encode(), 16))
  aes_body = base64.b64encode(iv + ct).decode()

  # 签名基于明文
  body_hash = hashlib.sha256(plain.encode()).hexdigest()
  sign_data = f"POST/api/shell{ts}{nonce}{body_hash}"
  sig = hmac.new(signKey.encode(), sign_data.encode(), hashlib.sha256).hexdigest()

🛡 EasyTier 加强安全 (v74.2)

v74.2 起云手机端 EasyTier 默认用 AES-256-GCM 加密 mesh 流量。

云手机端配置 (自动生成):
  [flags]
  encryption_algorithm = "aes-256-gcm"   ← v74.2 加强
  no_tun = true                          ← 默认 socks5 模式

network-secret:
  v74.2 升级为 64 位随机字母数字 (旧版 32 位)
  生成器: SecureRandom, 字符集 [a-zA-Z0-9]

AI 端连接命令 (必须加 --encryption-algorithm aes-256-gcm, 否则连不上):
  easytier-core --no-tun -i 10.144.144.1 \
    --network-name 'cm-xxx' \
    --network-secret '64位secret' \
    -p 'tcp://39.98.83.46:51010' \
    --socks5 1080 \
    --encryption-algorithm aes-256-gcm

⚠ 重要: 两端必须用相同加密算法, 否则连接被拒绝 (Connection closed)
  - aes-gcm (AES-128-GCM) — EasyTier 默认
  - aes-256-gcm (AES-256-GCM) — v74.2 推荐
  - chacha20 — 性能优先 (无 AES-NI 时)
  - xor — 仅测试用, 不安全

双重加密 (最强):
  1. EasyTier mesh 隧道层: aes-256-gcm (网络包加密)
  2. HTTP body 应用层: AES-256-CBC (token 派生密钥)
  攻击者即使拿到 mesh 节点, 也解不开 HTTP body

📡 多点触控 + 手势 (完整)

POST /multi                  多点触控 (批量动作)
  body: {
    "actions": [
      {"type":"swipe","x1":300,"y1":800,"x2":400,"y2":700,"duration":500},
      {"type":"tap","x":900,"y":600,"delay":200},
      {"type":"long_press","x":800,"y":500,"duration":1000,"delay":0}
    ]
  }
  支持类型: tap / swipe / long_press
  delay: 动作间延迟 (毫秒)

POST /gesture                单手势 (简单版)
  body: {"type":"scroll","x":540,"y":1000,"dy":-300}    滚动
  body: {"type":"fling","x1":540,"y1":1500,"x2":540,"y2":500}  快速滑动

POST /api/input              标准输入 (input tap/swipe/keyevent/text)
  body: {"action":"tap","x":540,"y":1170}
  body: {"action":"swipe","x1":100,"y1":100,"x2":200,"y2":200,"duration":300}
  body: {"action":"keyevent","keycode":3}    ← HOME 键 (整数, 不是字符串)
  body: {"action":"text","text":"hello"}

POST /text                   中文输入 (Unicode 转义, 不依赖 IME)
  body: {"text":"你好世界"}
  原理: input text "\u4f60\u597d\u4e16\u754c"

🔑 签名客户端 (Python) 生成

用以下 Python 代码生成签名客户端 (无需服务端 /api/client 端点):

import hmac, hashlib, time, secrets, json, urllib.request

TOKEN = "your_token"
BASE = "http://10.144.144.100:3000"

# 1. 派生 signKey (和服务端相同算法)
signKey = hmac.new(TOKEN.encode(), b"cloudmanager-signing-v1", hashlib.sha256).hexdigest()

# 2. POST 请求 (带签名)
def post(path, body_dict):
    body = json.dumps(body_dict).encode()
    ts = str(int(time.time() * 1000))
    nonce = secrets.token_hex(16)
    body_hash = hashlib.sha256(body).hexdigest()
    sign_data = f"POST{path}{ts}{nonce}{body_hash}"
    sig = hmac.new(signKey.encode(), sign_data.encode(), hashlib.sha256).hexdigest()
    headers = {
        "Authorization": f"Bearer {TOKEN}",
        "Content-Type": "application/json",
        "X-Timestamp": ts, "X-Nonce": nonce, "X-Signature": sig,
    }
    req = urllib.request.Request(BASE + path, data=body, method="POST", headers=headers)
    with urllib.request.urlopen(req, timeout=30) as resp:
        return resp.read().decode()

# 3. 调用 API
print(post("/api/shell", {"cmd": "id"}))
print(post("/api/apps/list", {}))
print(post("/api/screenshot", {}))

📊 完整 API 端点列表 (113 个)

GET 公开:
  /                  连接指南 (本文档)
  /api/status        服务状态 (version/adb/tunnel)
  /remote            远程桌面 (HTML)

GET 需 Token:
  /api/devices       ADB 设备列表
  /api/logs          日志 (支持 limit/since/level/tag 过滤)
  /api/logs/stream   SSE 日志流 (长连接)
  /api/ai/chat/stream  SSE 聊天流 (AI 接收用户消息)

POST 需 Token + 签名 (113 个端点):
  Shell:    /api/shell /api/command /shell
  Screenshot: /api/screenshot
  Input:    /api/input /text /gesture /multi /input
  File:     /file/upload /file/download /file/download/chunk
            /api/file/ls /api/file/cp /api/file/mv /api/file/rm
            /api/file/mkdir /api/file/chmod
  Apps:     /api/apps/list /api/apps/info /api/apps/launch
            /api/apps/force-stop /api/apps/uninstall /api/apps/clear-data
  Process:  /api/process/list /api/process/kill
  System:   /api/system/reboot /api/system/shutdown /api/system/lock
            /api/system/brightness /api/system/volume
            /api/system/battery /api/system/info
  Net:      /api/net/connections /api/net/listen-ports
            /api/net/interfaces /api/net/routes
  UI:       /api/ui/dump /api/ui/click-text /api/ui/wait-text
  Memory:   /api/mem/read /api/mem/write /api/mem/search /api/mem/dump
            /api/mem/search-all /api/mem/regions /api/mem/search-string
  Decompile: /api/decompile/apk /api/decompile/dex
  Network:  /api/capture /api/scan
  Frida:    /api/frida/start /api/frida/stop /api/frida/status
            /api/frida/script/write /api/frida/script/list /api/frida/script/run
            /api/frida/enumerate
            /api/frida2/setup /api/frida2/inject /api/frida2/hook-so
            /api/frida2/exports /api/frida2/classes
  Termux:   /api/termux/setup /api/termux/exec /api/termux/pkg-install /api/termux/status
  Linux:    /api/linux/setup /api/linux/exec /api/linux/install
            /api/linux/status /api/linux/destroy
  EasyTier: /api/easytier/start /api/easytier/stop
            /api/easytier/status /api/easytier/connect-info
  AI:       /api/ai/connect /api/ai/disconnect /api/ai/heartbeat /api/ai/status
            /api/ai/chat/send /api/ai/chat/user-send /api/ai/chat/choice
            /api/ai/chat/history /api/ai/chat/clear /api/ai/chat/delete
            /api/ai/chat/export
  Plugins:  /api/plugins/list /api/plugins/install /api/plugins/get
            /api/plugins/run /api/plugins/delete /api/plugins/clear
            /api/plugins/search /api/plugins/tags /api/plugins/rollback
            /api/plugins/diff /api/plugins/versions /api/plugins/clear_sandbox
  MCP:      /api/mcp/list /api/mcp/connect /api/mcp/discover
            /api/mcp/tools /api/mcp/call /api/mcp/disconnect
  Setup:    /api/setup/tools

📚 使用方法 (完整指南)

1. 初次使用 (5 步)

1. 在云手机打开「云机管家」App, 主页显示状态
2. 切换到「隧道」Tab, 点击「启动 EasyTier」(默认推荐模式)
3. App 自动生成 NetworkName/NetworkSecret/Token, 复制到剪贴板
4. 在本地终端用 EasyTier 连接 (见下方命令)
5. 用 socks5 代理访问 App 的 HTTP API (http://10.144.144.100:3000)

2. AI 端连接命令 (推荐)

# 1. 启动 EasyTier (用 App 给的 NetworkName/NetworkSecret)
easytier-core --no-tun -i 10.144.144.1 \
  --network-name 'cm-XXXXXX' \
  --network-secret '64位secret' \
  -p 'tcp://39.98.83.46:51010' \
  --socks5 1080 \
  --encryption-algorithm aes-256-gcm

# 2. 测试连通性 (无需签名, GET /api/status 公开)
curl --socks5 127.0.0.1:1080 http://10.144.144.100:3000/api/status
# 期望: {"ok":true,"data":{"version":"3.42.x",...}}

# 3. 用 Python 客户端调 API (自动签名)
#    从 App 的「/」首页下载: curl "http://10.144.144.100:3000/api/client?token=YOUR_TOKEN"
#    或按本页"签名客户端 (Python) 生成"章节自己写

3. App 主要功能 (5 个 Tab)

🏠 主页:
  - 显示云手机状态 (系统/CPU/内存/电池)
  - 隧道连接状态 (EasyTier/SSH/Tailscale/Cloudflare)
  - 快捷操作 (重启/锁屏/截图/录屏)
  - App 自启/保活设置

💬 聊天:
  - 与 AI 实时对话 (双向)
  - 支持文本/选择题/任务输出/系统消息
  - AI 接入后显示在「在线」列表
  - 长按消息可删除
  - v59 修复输入框第一字符重复 bug
  - v75.4 修复 IME 重复字符 (KeyboardType.Uri)

🧠 Cairn (通用问题求解引擎):
  - 点击「安装」部署 Cairn (Alpine chroot + Python 3.11 + FastAPI)
  - 启动 Server (端口 8000) + Dispatcher (任务调度)
  - 新建项目: 填写标题/目标/已知信息, AI 自动探索
  - 项目列表: 查看进度/状态/事实数/Intent数/Hint数
  - 每个项目支持: 图谱(查看进度)/Hint(给AI提示)/停止/删除
  - 设置: 调整 intent_timeout/reason_timeout 等
  - 日志: 查看 server/dispatcher/setup 日志
  - v75.21 修复: setup 并发竞态 + proxyToServer 错误响应被当成功
  - v75.22 修复: addHint 缺 creator 字段 + prompt_group 默认改 mock
  - 详细使用方法见下方「🧠 Cairn 详细使用方法」

🛠 工具:
  - ADB Shell (执行 root 命令)
  - 文件管理 (ls/cp/mv/rm/mkdir/chmod)
  - 应用管理 (列表/启动/强制停止/卸载/清数据)
  - 进程管理 (列表/kill)
  - 系统控制 (亮度/音量/重启/锁屏)
  - 网络管理 (连接/端口/接口/路由)
  - UI 自动化 (dump/click-text/wait-text)
  - 截图 (全屏/区域选择)
  - 录屏 (MP4, 可选时长)

⚙️ 设置:
  - 隧道模式切换 (EasyTier/SSH/Tailscale/Cloudflare)
  - EasyTier 配置 (Peer/NetworkName/Secret)
  - 安全设置 (Token 重置/AES Body 加密)
  - 网络黑名单/IP 限流配置

4. AI 接入流程 (开发者)

Step 1: AI 通过 /api/ai/connect 接入
  POST /api/ai/connect
  body: {"clientInfo":"my-ai-bot"}
  返回: {"sessionId":"ai-1","codename":"AI-1","heartbeatTimeoutSec":90}

Step 2: 每 25s 发心跳 (90s 不发自动断开)
  POST /api/ai/heartbeat
  body: {"sessionId":"ai-1"}

Step 3: AI 主动发消息给用户 (App 聊天界面会显示)
  POST /api/ai/chat/send
  body: {"sessionId":"ai-1","type":"text","content":"你好"}
  type 可选: text / choice (附 choices 数组) / task_output / system

Step 4: 接收用户消息 (两种模式)
  模式 A (轮询): POST /api/ai/chat/history {"sessionId":"ai-1","sinceId":0}
  模式 B (SSE 长连接): GET /api/ai/chat/stream (需 token)
  SSE 推荐长连接, 实时性更好

Step 5: 用完断开
  POST /api/ai/disconnect {"sessionId":"ai-1"}

5. 常用 API 调用示例 (Python)

# 完整 Python 客户端示例 (自己实现签名)
import hmac, hashlib, time, secrets, json, urllib.request

TOKEN = "your_token_here"
BASE = "http://10.144.144.100:3000"
signKey = hmac.new(TOKEN.encode(), b"cloudmanager-signing-v1", hashlib.sha256).hexdigest()

def post(path, body_dict):
    body = json.dumps(body_dict).encode()
    ts = str(int(time.time() * 1000))
    nonce = secrets.token_hex(16)
    bh = hashlib.sha256(body).hexdigest()
    sig = hmac.new(signKey.encode(), f"POST{path}{ts}{nonce}{bh}".encode(), hashlib.sha256).hexdigest()
    req = urllib.request.Request(BASE + path, data=body, method="POST", headers={
        "Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json",
        "X-Timestamp": ts, "X-Nonce": nonce, "X-Signature": sig,
    })
    with urllib.request.urlopen(req, timeout=30) as r:
        return json.loads(r.read().decode())

# 截图
r = post("/api/screenshot", {})
print(r["data"]["image"][:50])  # base64 PNG

# 点击屏幕
post("/api/input", {"action":"tap","x":540,"y":1170})

# 输入文字 (英文)
post("/api/input", {"action":"text","text":"hello"})

# 中文输入 (Unicode 转义)
post("/text", {"text":"你好世界"})

# 执行 shell (root)
post("/api/shell", {"cmd":"id"})

# 列出应用
post("/api/apps/list", {"type":"third"})

# 启动应用
post("/api/apps/launch", {"package":"com.tencent.mm"})

# UI 自动化 (按文本点击)
post("/api/ui/click-text", {"text":"登录","partial":true})

# 文件列表
post("/api/file/ls", {"path":"/data/local/tmp"})

6. 故障排查

Q: curl --socks5 连不上?
A: 1) 检查 EasyTier 两端 encryption_algorithm 是否一致 (默认 aes-256-gcm)
   2) 检查 NetworkName/NetworkSecret 是否完全一致
   3) App 端 EasyTier 是否在运行 (主页看状态)
   4) 端口 1080 是否被占用 (lsof -i:1080)

Q: API 返回 401 / "Invalid or missing token"?
A: 1) 检查 Authorization: Bearer TOKEN 头是否带
   2) Token 在 App 设置里查看, 区分大小写
   3) Token 重置后旧 Token 立即失效

Q: API 返回 403 / "Invalid signature"?
A: 1) signKey 算法: HMAC-SHA256(TOKEN, "cloudmanager-signing-v1").hexdigest()
   2) 签名数据格式: "POST" + path + timestamp + nonce + sha256(body)
   3) path 不含 query string, 如 "/api/shell" 不是 "/api/shell?foo=1"
   4) body 是原始 JSON 字符串的 sha256, 不是 dict

Q: API 返回 429 / "Rate limit"?
A: IP 限流 60/min (unknown IP 180/min), 失败 10 次封禁 30 分钟
   等待或换 IP

Q: /api/shell 返回 timeout?
A: 1) 命令执行超过 timeout (默认 30s)
   2) RootManager shell 进程被卡 (重启 App)
   3) 命令等待输入 (确保命令非交互式)

Q: Cairn Tab 显示「未部署」?
A: 1) 点「安装」按钮, 等待 5 步完成 (~5 分钟)
   2) 失败看「日志」Tab 的 setup 日志
   3) v75.21 修复了并发安装竞态, 不再会损坏 rootfs

Q: EasyTier 启动失败?
A: 1) 看主页状态栏错误提示
   2) 网络不通: 试 SSH/Tailscale 模式
   3) Peer 不可达: 改用其他 Peer (39.98.83.46:51010)

Q: 聊天 Tab 输入框第一字符重复 (如「我我说」)?
A: v59 已修复 (TextFieldValue + composition 严格检测)
   v75.4 进一步修复 IME 重复 (KeyboardType.Uri)
   仍有问题: 切换系统输入法 (建议 Gboard)

Q: AI 接入后 90s 自动断开?
A: 必须每 25s 发一次 /api/ai/heartbeat
   或用 SSE 长连接 (GET /api/ai/chat/stream), 心跳自动维持

🔒 安全最佳实践

1. Token 安全:
   - 32 位随机字符 (v33 用 SecureRandom, 不是 Random)
   - 加密存储 (EncryptedSharedPreferences, AES-256-GCM)
   - 日志自动脱敏 (token/secret/password → ***REDACTED***)
   - 错误 10 次自动封禁 IP 30 分钟

2. 通信安全:
   - POST 强制 HMAC-SHA256 签名 (防篡改)
   - 时间戳 5 分钟容差 (防重放)
   - Nonce 5 分钟去重 (防重放)
   - IP 频率限制 60/min (unknown IP 180/min)
   - 路径白名单 (防穿越)
   - shell 命令注入防护 (v74 修复 7 处)

3. EasyTier 安全 (v74.2):
   - network-secret 64 位随机 (升级, 旧版 32 位)
   - AES-256-GCM 加密 mesh 流量 (升级, 旧版默认 aes-gcm)
   - secret 不出现在命令行 (用 -c config.toml)
   - 配置文件 chmod 600 (只 root 可读)
   - 停止时清理所有残留文件 (config/log/state)

4. AES Body 加密 (v73):
   - AES-256-CBC, 密钥从 token 派生 (SHA-256("aes-body-v1:" + token))
   - 每次请求随机 IV (16 字节)
   - 签名基于明文 body (服务端先解密再验签)
   - 响应也加密 (X-AES-Encrypted: 1 时)

5. 推荐配置 (最高安全):
   - 隧道: EasyTier --encryption-algorithm aes-256-gcm
   - 应用: X-AES-Encrypted: 1 (AES-256-CBC body)
   - 攻击者即使破解 mesh, 也解不开 HTTP body

🧠 Cairn 详细使用方法

1. Cairn 是什么

Cairn 是一个通用问题求解引擎 (General Purpose Problem Solving Engine),
设计思路类似 ReAct/Tree-of-Thoughts + 工具调用:
- 你给它一个目标和已知信息 (origin)
- 它通过 Intent (意图) → Fact (事实) 的迭代逐步逼近目标
- 每个 Intent 由 Worker (AI) 执行, 产出 Fact
- Fact 累积到一定程度后, Worker 判定目标达成 → 输出 complete

架构:
  App (Android, 端口 3000)  ←─签名的 HTTP API─→ AI/用户
    ↓
  Cairn Server (chroot, 端口 8000)  ← FastAPI + SQLite 存储项目/facts/intents
    ↓
  Dispatcher (chroot, 单独进程)  ← 调度 worker 执行 intent
    ↓
  Worker (mock / claudecode / codex / pi)  ← 实际执行 AI 任务

注意: 默认只有 mock-worker, 它只输出假数据用于测试调度链路.
要真正执行任务需要配置 claudecode/codex/pi worker (需对应 CLI 工具).

2. App 内使用流程 (5 步)

Step 1: 打开 Cairn Tab, 点击「安装 Cairn」(首次 ~5 分钟)
  - 下载 Alpine 3.19.7 rootfs (3MB)
  - 安装 Python 3.11 + pip + pydantic-core + docker 等依赖
  - 解压 Cairn 源码到 /opt/cairn/Cairn-main/
  - 生成 /etc/cairn-dispatch.yaml 配置

Step 2: 启动 Server (端口 8000)
  - App 内点「启动 Server」按钮
  - 后台启动 uvicorn (FastAPI)
  - 5s 后刷新状态确认 serverRunning=true

Step 3: 启动 Dispatcher
  - App 内点「启动 Dispatcher」按钮
  - 后台启动 cairn dispatch --config /etc/cairn-dispatch.yaml
  - 3s 后刷新状态确认 dispatcherRunning=true

Step 4: 新建项目
  - 点「➕ 新建」按钮
  - 填写:
    标题: 简短描述 (如「渗透测试网站 A」)
    目标 Goal: 想达成什么 (如「获取 admin 权限」)
    已知信息 Origin: 已知条件 (如「目标是 https://example.com」)
  - 勾选「启用 Bootstrap」(推荐, 让 AI 先理解任务)

Step 5: 跟踪进度
  - 项目列表显示: 状态/事实数/Intent数/Hint数
  - 点「图谱」: 查看事实和意图的关系图 (YAML 格式)
  - 点「Hint」: 给 AI 提供额外提示 (如「试试 SQL 注入」)
  - 点「停止」: 暂停项目 (可重新打开)
  - 点「删除」: 删除项目
  - 点「📋」: 查看 dispatcher/server/setup 日志

3. API 调用示例 (Python)

import hmac, hashlib, time, secrets, json, urllib.request

TOKEN = "your_token"
BASE = "https://your-app-url"
signKey = hmac.new(TOKEN.encode(), b"cloudmanager-signing-v1", hashlib.sha256).hexdigest()

def post(path, body_dict):
    body = json.dumps(body_dict).encode()
    ts = str(int(time.time() * 1000))
    nonce = secrets.token_hex(16)
    bh = hashlib.sha256(body).hexdigest()
    sig = hmac.new(signKey.encode(), f"POST{path}{ts}{nonce}{bh}".encode(), hashlib.sha256).hexdigest()
    req = urllib.request.Request(BASE + path, data=body, method="POST", headers={
        "Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json",
        "X-Timestamp": ts, "X-Nonce": nonce, "X-Signature": sig,
    })
    with urllib.request.urlopen(req, timeout=30) as r:
        return json.loads(r.read().decode())

# 1. 检查状态
print(post("/api/cairn/status", {}))
# {"deployed":true, "serverRunning":true, "dispatcherRunning":true, ...}

# 2. 部署 (首次)
print(post("/api/cairn/setup", {}))

# 3. 启动 Server + Dispatcher
print(post("/api/cairn/start-server", {}))
print(post("/api/cairn/start-dispatcher", {}))

# 4. 新建项目
r = post("/api/cairn/projects/create", {
    "title": "渗透测试 example.com",
    "goal": "找出 example.com 的真实可利用漏洞, 输出 PoC",
    "origin": "目标是 https://example.com, 已知是 WordPress 5.8"
})
project_id = r["data"]["project"]["id"]
print(f"Project ID: {project_id}")

# 5. 查询项目进度
print(post("/api/cairn/projects/get", {"projectId": project_id}))

# 6. 给项目添加提示
print(post("/api/cairn/projects/hint", {
    "projectId": project_id,
    "content": "试试 wp-content/plugins/ 目录扫描"
}))

# 7. 查看图谱 (事实-意图关系)
print(post("/api/cairn/projects/graph", {"projectId": project_id}))

# 8. 停止/删除项目
print(post("/api/cairn/projects/stop", {"projectId": project_id}))
print(post("/api/cairn/projects/delete", {"projectId": project_id}))

4. 配置 Worker (真实 AI 执行)

默认 cairn-dispatch.yaml 只配了 mock-worker-1, 它只输出假数据.
要真正执行任务, 需要编辑 /data/local/tmp/cm_linux/etc/cairn-dispatch.yaml:

workers:
  # mock worker (默认, 用于测试)
  - name: "mock-worker-1"
    type: "mock"
    task_types: [reason, explore, bootstrap]
    max_running: 1
    priority: 0

  # Claude Code worker (需 claude CLI)
  - name: "claude-worker-1"
    type: "claudecode"
    task_types: [reason, explore, bootstrap]
    max_running: 1
    priority: 10  # 优先级更高
    env:
      ANTHROPIC_BASE_URL: "https://api.anthropic.com"
      ANTHROPIC_AUTH_TOKEN: "sk-ant-xxx"
      ANTHROPIC_MODEL: "claude-sonnet-4-5-20250929"

  # OpenAI Codex worker (需 codex CLI)
  - name: "codex-worker-1"
    type: "codex"
    task_types: [reason, explore, bootstrap]
    max_running: 1
    priority: 5
    env:
      OPENAI_API_KEY: "sk-xxx"
      OPENAI_MODEL: "gpt-5"

修改后重启 dispatcher:
  POST /api/cairn/stop-dispatcher {}
  POST /api/cairn/start-dispatcher {}

5. 关键概念

Project (项目):
  - 一个独立的问题求解任务
  - 状态: active (运行中) / stopped (已停止) / completed (已完成)
  - 包含 origin (已知信息) + goal (目标) + facts + intents + hints

Fact (事实):
  - Worker 执行 Intent 后产出的确认结果
  - 例: 「找到 SQL 注入点: /api/users?id=1」
  - 是项目知识的累积

Intent (意图):
  - 一个待执行的任务步骤
  - 从一个或多个 Fact 出发, 目标是产出新 Fact
  - 状态: open (待执行) / claimed (worker认领) / concluded (已完成)
  - 类型: bootstrap (初始理解) / reason (推理) / explore (探索)

Hint (提示):
  - 用户给 AI 的额外指导
  - 例: 「试试 SQL 注入」「检查 /admin 路径」
  - 不强制 AI 执行, 但会影响后续推理

Bootstrap (引导):
  - 项目创建后第一个 Intent
  - AI 理解 origin + goal, 决定如何开始
  - 完成后产出第一个 Fact

Worker (执行者):
  - 实际执行 Intent 的实体
  - 类型: mock (假数据) / claudecode (Claude) / codex (GPT) / pi (本地)
  - 配置在 cairn-dispatch.yaml

6. 实战示例: 用 Cairn 做渗透测试

场景: 对 https://target.example.com 做黑盒渗透

Step 1: 创建项目
  POST /api/cairn/projects/create
  {
    "title": "渗透测试 target.example.com",
    "goal": "对 https://target.example.com 做黑盒渗透, 找出真实可利用漏洞
            (SQL注入/XSS/SSRF/路径穿越/认证绕过等), 输出可复现 PoC",
    "origin": "目标: https://target.example.com
              已知: Web 服务, 可能用 Nginx + PHP
              限制: 仅授权测试, 不破坏数据"
  }

Step 2: 等待 bootstrap 完成 (Dispatcher 自动调度)
  轮询 GET /api/cairn/projects/get
  等 intents[0].concluded_at != null

Step 3: 给 AI 工具访问权限
  - Cairn Worker 在 chroot 内运行, 有 curl/python3/gcc 等工具
  - 但访问外网需要 chroot 内能 DNS 解析
  - 一般 Cairn Server 自动配置 DNS (从 Android 复制 /etc/resolv.conf)

Step 4: 添加 Hint 引导 AI
  POST /api/cairn/projects/hint
  {"projectId": "xxx", "content": "先用 nmap 扫端口, 再用 nuclei 扫漏洞"}

Step 5: 等待完成
  - 项目状态变 completed 时, 最终 Fact 包含漏洞报告
  - 用 /api/cairn/projects/graph 查看完整推理过程

注意: 实际执行能力取决于 Worker 类型:
  - mock: 不执行任何真实操作, 只输出假数据
  - claudecode/codex: 真实调用 AI API, AI 决定执行什么命令
  - 当前云手机默认只配 mock, 要真实渗透需配置 AI Worker

7. Cairn API 完整端点

状态/管理:
  POST /api/cairn/status            查看部署/Server/Dispatcher 状态
  POST /api/cairn/setup             部署 Cairn (首次)
  POST /api/cairn/progress          查看安装进度
  POST /api/cairn/start-server      启动 Server
  POST /api/cairn/stop-server       停止 Server
  POST /api/cairn/start-dispatcher  启动 Dispatcher
  POST /api/cairn/stop-dispatcher   停止 Dispatcher

项目管理:
  POST /api/cairn/projects          列出所有项目
  POST /api/cairn/projects/create   新建项目
  POST /api/cairn/projects/get      查询项目详情
  POST /api/cairn/projects/delete   删除项目
  POST /api/cairn/projects/stop     停止项目
  POST /api/cairn/projects/complete 标记完成
  POST /api/cairn/projects/reopen   重新打开
  POST /api/cairn/projects/title    修改标题
  POST /api/cairn/projects/hint     添加提示
  POST /api/cairn/projects/graph    查看图谱 (YAML)

日志:
  POST /api/cairn/logs              查看日志
    body: {"which": "server|dispatcher|setup", "lines": 100}

设置:
  POST /api/cairn/settings          查看/修改设置

8. 常见问题

Q: Cairn Tab 显示「未部署」?
A: 点「安装」按钮, 等 5 步完成 (~5 分钟)
   失败看「日志」Tab 的 setup 日志
   v75.21 修复了并发安装竞态

Q: bootstrap 一直失败, 日志报 "mock setup failed: Expecting value"?
A: v75.22 修复: prompt_group 应为 "mock" (之前默认 "default" 导致 prompt 不匹配)
   手动修复: 编辑 /data/local/tmp/cm_linux/etc/cairn-dispatch.yaml
   把 prompt_group: "default" 改为 prompt_group: "mock"
   重启 Dispatcher

Q: 项目一直 active 不完成?
A: mock-worker 默认 bootstrap 后会 complete, 但 reason/explore 阶段会无限循环
   要真正完成项目需要配置真实 AI Worker (claudecode/codex)

Q: 怎么让 Cairn 真的执行命令?
A: 1) 配置真实 Worker (claudecode/codex/pi)
   2) Worker 在 chroot 内运行, 有 curl/python3 等工具
   3) Worker 输出 JSON: {"accepted":true,"data":{"fact":{"description":"..."},"complete":{"description":"..."}}}

Q: addHint 返回 422 "Field required"?
A: v75.22 修复: addHint 缺 creator 字段 (Cairn Server 要求)
   之前 v75.21 的 proxyToServer bug 把 422 当成功, 暴露了这个 bug

Q: /api/cairn/projects/delete 返回错误?
A: DELETE 请求 Cairn Server 返回 204 空响应
   v75.21 的 proxyToServer 已修复: 不再把空响应当成功
   但代码可能仍报 "解析 Cairn 响应失败" (因为空 body 不是合法 JSON)
   实际项目已删除, 可忽略错误