Files
auto_control/doc/DEVELOPMENT.md
T
butubb 98cdc39224 chore: 删除抖音养号任务类型,平台只保留 generic_steps
tasks/douyin/(task_type=douyin_nurture)整体删除,唯一任务类型是 generic_steps。
所有默认值/文档/接口示例同步改成 generic_steps:

- tasks/:删 douyin 包;__init__ 只注册 generic;base.py/generic 注释改为照 generic 抄
- 默认值:core/models.py(列默认 + 旧 JSON 迁移默认)、core/task_manager.py TaskJob、
  web/tasks_api.py 建任务默认、static/admin/tasks.js 新建任务默认
- core/task_manager.py:去掉 douyin 专属的"清理废弃 comment 参数"迁移块,改为**启动时告警**
  仍残留已删类型的任务(只告警不改数据);run_job_now 对已删类型直接返回明确错误,
  不再"报已触发、线程里静默失败"
- 清理残留:douyin_running 状态位(无任何读取方)、core/__init__、core/actions/*、
  core/logger.py 注释里的抖音示例
- 文档:README(特性/目录树/类型表/参数表/示例)、TASK_DEV(目录树/注册说明/模板引用)、
  ARCHITECTURE(注册示例/action 注册表示例)、API.md(task_types 与任务 JSON 示例)、
  AI_TASK_GEN(P1 去掉 douyin 预设)、DEVELOPMENT
- 注:示例里"抖音"作为**App 名**(MCP 列应用、AI 建任务的需求举例)保留,与任务类型无关

自测(全部通过):类型列表只剩 generic_steps;建任务不传类型默认 generic_steps;传
douyin_nurture 被 400 拒;库里塞残留旧类型任务 → 启动日志告警 + 执行返回明确错误 +
不自动删用户数据;前端新建任务下拉 1 项且默认选中、界面建任务成功;监控页卡片两个按钮 +
覆盖设备正常。临时任务/数据验完已清理,任务集合复原。
2026-09-10 21:43:08 +08:00

15 KiB
Raw Blame History

开发手册(DEVELOPMENT)

面向本项目开发者:开发流程、git 工作流、环境说明、技术红线、本地开发、常见开发任务。


1. 开发流程与 git 工作流

1.1 分支策略

分支 用途
dev 开发分支,所有新功能/修复都在这里开发
main 生产分支(主分支),只放已确认的稳定版本

生产环境 = 部署机 192.168.20.220 的 /mnt/data/openstf/auto_control(python-app 容器运行 web_server.py)。

1.2 git 操作铁律(重要)

所有 git 操作都必须先经项目负责人明确确认后才能执行,包括但不限于:

  • commit / push(即使 push 到 dev 也要确认)
  • merge(dev → main)
  • revert / checkout / reset / branch -D 等

标准流程:

1. 在 dev 分支开发、本地测试
2. 完成改动 → 把改动清单 + 建议 commit 信息 列给负责人
3. 负责人确认 → 才能 commit + push dev
4. 需要发布 → 负责人确认后再合并到 main
5. 部署生产 → 负责人明确指示后才 pull 到 220

不允许"开发完顺手就 commit/push"。即使是一次性小改动,也要先确认。


2. 环境说明

环境 位置 说明
开发机 本机(192.168.20.57) .venv + 本地运行 web_server.py
生产机 部署机 220 的 auto_control python-app 容器,network_mode: host
STF 服务 192.168.20.220:7100 已停用(代码已摘除依赖)。注意:此处"已停用"与 STF_REMOVAL.md 的"待人工确认"项矛盾(220 侧是否已 docker stop stf 未核实),需以 220 实际为准
adb 容器 220 上 adb(host 网络 5037) USB 设备远程 adb server;网络设备补连用
设备 Tailscale 100.100.10.x:5555 Xiaomi 舰队,本机 100.100.10.2 在 tailnet 内
uiautodev 本机 20242 元素抓取服务(web_server 自动拉起)

2.1 adb key(关键)

  • 本机 ~/.android/adbkey 沿用历史 key(原取自 STF adb 容器,全部设备都信任)
  • 不要随意更换 key——设备会变 unauthorized 连不上
  • 旧 key 备份在 ~/.android/adbkey.local.bak
  • 生产环境的容器也需要用这把 key(部署时处理)

2.2 设备连接方式

  • 设备 serial 是 IP:5555(Tailscale 地址),直连优先(只 connect、绝不 disconnect)
  • USB 设备(serial 无冒号):插本机走本地 adb;插 220 走远程 adb server(USB_ADB_HOST:5037)
  • 本机已在 tailnet 内,直连可靠且快(<1s)
  • 设备加入/退出平台:工具 → 设备池管理(SQLite 清单,自动连接 + 型号采集)

2.3 配置键速查(config.py / .env)

.env 加载方式:项目根目录逐行解析、os.environ.setdefault(环境变量已设则不覆盖)。以下默认值以 config.py 为准:

键 默认值 说明
WEB_HOST / WEB_PORT 0.0.0.0 / 18050 Web 后台监听(18050 避开 Windows 动态端口范围)
DATA_DIR / APK_DIR data/ / data/apks/ 持久化数据目录 / APK 存储目录
BACKUP_DIR / RESTORE_STAGING_DIR / RESTORE_PENDING_DIR data/backups / data/restore_staging / data/restore_pending 系统备份:导出 zip、导入暂存、待重启生效的恢复目录
ADB_PATH bin/adb/adb(Windows 为 adb.exe) 按平台自动识别,代码只拼路径
USB_ADB_HOST / USB_ADB_PORT 100.100.10.1 / 5037 220 的 adb 容器(host 网络),驱动远程 USB 设备
DISCOVERY_PORT / DISCOVERY_SUBNETS / DISCOVERY_INTERVAL 5555 / 局域网+Tailscale 网段 / 60 设备自动发现;可在工具页设备池面板改,存 app_meta discovery_* 覆盖默认
TAILSCALE_API_KEY / TAILSCALE_TAILNET 空 / 空 工具页 Tailscale 管理(官方 API v2)
WEB_SECRET_KEY 未配置则随机生成 会话密钥(web_server 读 .env;不配则重启登录态失效)
STF_URL / STF_TOKEN / STF_SSH_* 废弃 STF 摘除后仅历史保留,代码不再使用

3. 技术红线(开发限制)—— 违反会打断共享 adb transport,需人工恢复

这些是踩过坑后总结的,任何修改都不能引入。违反任何一条都会导致设备连接被全部重建(历史原因:STF provider 共享同一 adb transport,摘除 STF 后仍保留此约束):

  1. 绝不 adb kill-server

    • 会断开所有设备的 adb transport,全部设备连接被重建,运行中任务中断
    • 见 core/adb_helper.py
  2. 绝不对 IP:5555 设备 adb disconnect

    • 该地址的 adb transport 是共享的(历史与 STF provider 共用),disconnect 会断掉全部相关连接
    • 直连模式下 release() 不 disconnect
    • 见 core/device_worker.py STFDevice.release()
  3. 空闲设备扫描不主动 connect/disconnect

    • IP:5555 的 transport 由多方共享(历史与 STF provider 共用),外部 connect/disconnect 会扰动共享连接
    • _ForegroundScanner._scan_free 对空闲设备直接返回"空闲",不碰 adb
    • 见 core/task_manager.py
  4. adb key 保持历史 key 不变(见 2.1,设备信任该 key)

  5. 直连优先,不引入第三方桥接(见 2.2)

3.1 其他开发限制

  • web_server.py 以 debug=False 运行,不热重载:改 core/、tasks/、templates/、static/ 后必须重启 web_server(前端 HTML 改完强刷浏览器)
  • 任务参数放各自 tasks/<app>/task.py 顶部,不放 config.py
  • 生产环境(220)默认只读:任何写操作(改文件/重启容器/部署)都必须先经负责人确认
  • 数据库是 SQLite(data/users.db,WAL 模式):运行时数据不提交 git
  • 前端 JS 已拆分多文件(均位于 static/admin/):monitor.html 按 base.js → markdown.js → list.js → monitor.js → editor.js → tasks.js → tools.js → apps.js → admin.js → agent.js → system.js 的顺序用 <script src> 加载(markdown.js 提供 renderMarkdown(),AI 控制台回答渲染用;依赖 base.js 的 esc(),故排在其后、agent.js 之前)
  • 监控页/大列表已加分页:100 台设备也只渲染 10 行/页,不要移除分页逻辑
  • 任务批量触发已错峰(_START_STAGGER_SEC):避免大量设备同时启动造成 adb 连接风暴,不要移除

4. 本地开发手册

4.1 首次安装

# 用 Python 3.12 建 venv(README 推荐版本)
/opt/homebrew/bin/python3.12 -m venv .venv
.venv/bin/pip install -r requirements.txt

# macOS:adb 用系统自带的(bin/adb/adb 是被 gitignore 的符号链接)
ln -sf /opt/homebrew/bin/adb bin/adb/adb

# 确认 adb key(必须是 STF 容器的 key,否则设备连不上)
ls -la ~/.android/adbkey

4.2 启动

.venv/bin/python web_server.py
# 访问 http://localhost:18050/  账号 admin/admin123

启动日志看到以下即成功:

[INFO] [core.worker] 心跳看门狗已启动
[INFO] [web] 管理后台: http://localhost:18050/ (admin/admin123)
[INFO] [web] 启动服务: http://localhost:18050/

4.3 测试

  • 后端逻辑:直接 .venv/bin/python -c "..." 调用(如 tasks/、core/ 的函数)
  • 前端 UI:Playwright(系统 python3 已装),脚本示例见下
  • 浏览器冒烟:切 6 个 tab、开任务编辑器,确认无 JS 错误
# Playwright 冒烟示例(python3 运行)
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
    b = p.chromium.launch(headless=True)
    pg = b.new_page()
    pg.goto("http://127.0.0.1:18050/login")
    pg.fill("input[name=username]", "admin")
    pg.fill("input[name=password]", "admin123")
    pg.click("input[type=submit], button[type=submit]")
    pg.wait_for_load_state("networkidle")
    # ... 检查各 tab、编辑器
    b.close()

4.4 常用调试

  • 看日志:logs/ 下 core.log / task.log / web.log / action.log(10MB 滚动,保留 5 份)
  • 看设备/任务状态:浏览器监控页,或 GET /api/status、GET /api/health
  • 停止设备/清异常:监控页工具条用"停止全部 / 停止选中 / 清除全部异常"(AUTO_RELEASE_STALE_OCCUPY 等 STF occupy 残留清理配置代码已不再读取);崩溃残留的 worker 状态重启即清零
  • 打包项目:python scripts/pack.py

5. 常见开发任务

5.1 新增 App 任务类型

参照 tasks/generic/ 结构,详见 doc/TASK_DEV.md(6 步模板)。 注意:当前平台只保留 generic_steps 一种任务类型;大多数 App 操作直接用步骤编辑器 编排即可,不必新增任务类型。

tasks/<app>/
  __init__.py     # from . import task
  task.py         # DEFAULT_PARAMS + Worker + @register_task
  actions/        # 专属操作(可选)

5.2 新增专属操作

在 tasks/<app>/actions/ 建 .py,继承 BaseAction + @register_action(ACTIONS),在 __init__.py import。

5.3 修改前端

文件 内容
templates/admin/monitor.html HTML 结构 + CSS + <script src> 引用
static/admin/(base/markdown/list/monitor/editor/tasks/tools/apps/admin/agent/system.js) 前端 JS(按 monitor.html 中 <script src> 顺序拆分加载,功能归属见各文件;markdown.js = 轻量 Markdown 渲染器,AI 控制台回答用)

改完强刷浏览器(Cmd/Ctrl+Shift+R),必要时重启 web_server。

5.4 新增 API

路由按功能域放在 web/ 蓝图包(web/__init__.py 的 register_blueprints(app) 统一注册 10 个蓝图:auth / monitor / tasks / admin / tools / devices / apks / tailscale / agent / system)。

  • 新增 API:在对应功能域的 web/xxx_api.py 里加 @bp.route(...) + 权限装饰器(如 admin_required)
  • 新建蓝图:需在 web/__init__.py 里 import 并加进 register_blueprints 的注册元组
  • web_server.py 只做 app 装配(初始化、register_blueprints(app)、常驻线程启动),一般不改
  • 最后更新 doc/API.md

5.5 新增数据库字段/表

  • 模型改 core/models.py,首次建表用 create_all();SQLAlchemy 模型未写 __tablename__ 时默认表名 = 小写类名
  • 已有数据的老库:在 core/models.py 的 SCHEMA_MIGRATIONS 里加迁移(版本号递增 + SQL)
  • app_meta(KV 配置表)由 core/models.py 的 _migrate_schema() 建表并维护 schema_version
  • agent_conversation / agent_experience / experience_audit / agent_action(动作经验库)由 web/agent_api.py 内的原生 CREATE TABLE IF NOT EXISTS 幂等创建(模块内首次用时执行),不经 SCHEMA_MIGRATIONS,无版本管理

5.6 改动必须同步文档

任何功能/配置/接口/页面改动,须与代码同一 commit 同步更新对应文档:

改动类型 对应文档
HTTP 接口 API.md
数据表 / schema ARCHITECTURE.md §3.6
config.py / .env 键增删 本文档 §2 + .env.example
页面 Tab / 子分栏 / 前端拆分 ARCHITECTURE.md §5
任务 / 步骤 TASK_DEV.md
常驻线程 / 进程与装配 ARCHITECTURE.md §7
MCP 工具 MCP.md 与 MCP_DESIGN.md
对外接入 / 数字员工知识库 staffdeck/KNOWLEDGE_BASE.md 与 staffdeck/JOB_SPEC.md

备份覆盖红线(2026-09-10 新增):新增任何持久化表(业务数据)时,必须同步把它登记进 core/system_backup.py 的 SUMMARY_TABLES(并在 TABLE_LABELS 给中文名)+ 更新 DEPLOY.md §3.5 的覆盖清单。理由:清单漏登记 → 导出预览看不到该表 → 会被误判为 "没有备份"(动作库 agent_action 就踩过)。导出侧有覆盖自检、导入侧有未登记表反向告警。

注:STF_REMOVAL.md 是历史迁移记录,不改写。


6. 发布流程(团队约定,2026-09-10 更新)

每个改动都走分支,确认后再并 dev;dev 整体就绪后才并 main 上生产。

① 本机新建分支          fix/xxx 或 feat/xxx(从 dev 切出)
② 分支上开发 + 自测     本机跑通(服务/接口/页面)
③ 交负责人确认          ★ 未经确认不合 dev
④ 合并到 dev            确认通过后(fast-forward 或 merge)
⑤ dev 整体就绪          dev 上功能齐全、验证完毕
⑥ 合并到 main           ★ 负责人确认后
⑦ 生产 220 部署         git pull → 重启 python-app 容器 → 验证

生产 220 部署(目录 /mnt/data/openstf/auto_control,容器 python-app 挂到 /app):

cd /mnt/data/openstf/auto_control
git fetch origin && git checkout main && git pull --ff-only origin main
docker restart python-app          # 入口 scripts/start.sh:依赖守卫 → 拉起 MCP → exec web_server
# 验证:curl -s http://127.0.0.1:18050/api/health ; ss -ltnp | grep -E ':(18050|8033|20242)'

发布前检查:生产容器 adb key、依赖(新增依赖看 requirements.txt)、数据库迁移(create_all 自动补新表)、 以及"新增持久化表是否已登记进备份覆盖清单"(见 §5.6 红线)。

数据迁移:用平台自带「系统 → 数据备份导出/导入」;导入后需重启容器才生效(见 DEPLOY.md §3.5)。


7. 文档索引

文档 内容
README 项目总览、快速上手
DEVELOPMENT.md 本文档:流程/准则/限制/手册
TASK_DEV.md 任务开发指南(新增 App 任务模板)
ARCHITECTURE.md 架构详解(分层、数据流、设计决策)
API.md 全部 HTTP 接口说明
DEPLOY.md 部署指南(环境、生产、故障排查)
STF_REMOVAL.md 摘除 STF 的历史迁移记录(2026-08,不随现状改写)
MCP.md MCP 手机控制使用手册(工具清单/用法)
MCP_DESIGN.md MCP 架构与演进设计
AI_TASK_GEN.md AI 建任务设计文档
staffdeck/KNOWLEDGE_BASE.md 给 StaffDeck 数字员工的知识库(MCP 接入/工具/约定/红线)
staffdeck/JOB_SPEC.md 数字员工岗位说明(岗位描述/看板摘要/执行约束)
backlog/TODO.md 待完成项(已确认但暂缓的功能/优化,完成时移出并同步文档)