- 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
3.2 KiB
3.2 KiB
Context
adapter-cross-machine-revision change 设计了纯 Pull 模型(Agent 主动调 HTTP API),但分析后发现 Pull 模型不是 subprocess CLI 的跨机等价——控制权、上下文传递、生命周期管理都不同。
真正的跨机等价是 SSH + CLI:Orchestrator 通过 SSH 在远程主机上 spawn Agent CLI,与本地 subprocess 完全一致的执行模型。
同时,HTTP Pull 对外部 Agent(OpenClaw/Jeeves、Hermes)仍有价值。因此需要双执行模型。
Goals / Non-Goals
Goals:
- SSH + CLI 作为主执行模式(Orchestrator 主动调度、构造上下文、控制生命周期)
- HTTP API 保留给外部 Agent 自主接入
- 每种 Agent 类型定义为 CLI 模板 + 输出解析器
- 远程主机管理(SSH 连接、CLI 可用性检查)
Non-Goals:
- 不实现 Agent daemon(Phase 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
- 新增
src/execution/模块(SSH executor、CLI template、output parser) - Task 模型添加
execution_mode、assigned_host字段 - 新增
[hosts]配置 section - 实现 Orchestrator dispatch loop(ssh_cli → SSH 执行,http_pull → 等待 Agent dequeue)
- 保留并调整 HTTP API 端点(dequeue 仅限 http_pull 任务)
- 更新测试
Open Questions
- SSH 库选择:
ssh2crate vstokio::process::Command+ 系统ssh命令? - 是否需要支持 SSH jump host(通过跳板机连接目标机器)?