feat: dual execution model (SSH CLI + HTTP pull)

- ExecutionMode enum: SshCli (orchestrator dispatches) | HttpPull (agent pulls)
- SSH CLI executor: spawn remote agents via ssh + CLI template
- Local subprocess as SSH special case (localhost)
- HostConfig with capability matching and load-based selection
- Dispatch loop: scan created tasks → select host → execute → update
- CliAdapterConfig: CLI templates for Codex and Claude Code
- Structured prompt construction (Issue → goal/constraints/validation)
- Output parsers: Codex JSON, Claude Code JSON, raw fallback
- TaskStatus::ReviewPending + review_count loop limit
- Forgejo webhook: pull_request (opened→review_pending, merged→completed)
- Forgejo webhook: push events (task/* branch → last_activity_at)
- HTTP API: dequeue only returns http_pull tasks
- HTTP API: status update only for http_pull mode
- Token auth config for http_pull agents
- Adapter module rewritten: AgentAdapter trait removed → config-driven CLI templates
- New fields: execution_mode, assigned_host, branch_name, pr_title, last_activity_at, review_count
- 30/30 tests pass
This commit is contained in:
Zer4tul 2026-05-12 14:07:56 +08:00
parent 1bc7580ecc
commit e39a16498c
34 changed files with 2541 additions and 1555 deletions

View file

@ -0,0 +1,83 @@
## Context
`adapter-cross-machine-revision` change 设计了纯 Pull 模型Agent 主动调 HTTP API但分析后发现 Pull 模型不是 subprocess CLI 的跨机等价——控制权、上下文传递、生命周期管理都不同。
真正的跨机等价是 **SSH + CLI**Orchestrator 通过 SSH 在远程主机上 spawn Agent CLI与本地 subprocess 完全一致的执行模型。
同时HTTP Pull 对外部 AgentOpenClaw/Jeeves、Hermes仍有价值。因此需要双执行模型。
## Goals / Non-Goals
**Goals:**
- SSH + CLI 作为主执行模式Orchestrator 主动调度、构造上下文、控制生命周期)
- HTTP API 保留给外部 Agent 自主接入
- 每种 Agent 类型定义为 CLI 模板 + 输出解析器
- 远程主机管理SSH 连接、CLI 可用性检查)
**Non-Goals:**
- 不实现 Agent daemonPhase 1 用 SSH + CLI 足够)
- 不实现动态主机发现Phase 1 静态配置)
- 不实现容器化执行(不用 Docker/Kubernetes
## Decisions
### Decision 1: SSH + CLI 是 subprocess 的跨机等价
**选择**: Orchestrator 通过 SSH 执行远程 Agent CLI
**理由**:
- 控制流与本地 subprocess 完全一致
- 上下文由 Orchestrator 构造,通过 CLI 参数传入
- Agent 不需要预运行,不需要 daemon
- 唯一前提SSH 免密登录 + 目标机器装了 CLI
**替代方案**:
- HTTP Pull控制权反转上下文传递难解决
- Agent daemon复杂度高每台机器多一个服务
- 容器化:更复杂,需要 container runtime
### Decision 2: HTTP Pull 作为补充模式
**选择**: 保留 HTTP API 给外部 Agent
**理由**:
- OpenClaw/Jeeves、Hermes 有自己的调度,不需要 Orchestrator 启动
- 它们只需要查询/更新任务状态
- 两种模式通过 `execution_mode` 字段区分
### Decision 3: Adapter = CLI 模板 + 输出解析器
**选择**: 不实现 AgentAdapter trait而是配置驱动
**理由**:
- CLI 模板可以通过配置文件定义,无需编译
- 不同 Agent 的差异主要在 CLI 参数和输出格式
- 新增 Agent 类型只需要加配置,不需要写代码
### Decision 4: 本地执行作为 SSH 的特例
**选择**: Orchestrator 所在机器的 Agent 用本地 subprocess不经过 SSH
**理由**:
- 避免 SSH loopback 的开销和配置
- subprocess 是 SSH 的本地特例,逻辑统一
## Risks / Trade-offs
- **[SSH 密钥管理] 需要配置免密 SSH** → 用 SSH agent forwarding 或 deploy key标准运维实践
- **[长时间运行] SSH 连接可能超时** → 用 `ssh -o ServerAliveInterval=60`,或改用 SSH multiplexing
- **[CLI 版本差异] 不同机器可能装不同版本** → health check 时验证版本
## Migration Plan
1. 新增 `src/execution/` 模块SSH executor、CLI template、output parser
2. Task 模型添加 `execution_mode``assigned_host` 字段
3. 新增 `[hosts]` 配置 section
4. 实现 Orchestrator dispatch loopssh_cli → SSH 执行http_pull → 等待 Agent dequeue
5. 保留并调整 HTTP API 端点dequeue 仅限 http_pull 任务)
6. 更新测试
## Open Questions
- SSH 库选择:`ssh2` crate vs `tokio::process::Command` + 系统 `ssh` 命令?
- 是否需要支持 SSH jump host通过跳板机连接目标机器