Documentation
¶
Overview ¶
backlog.go —— follow 建连前「积压对账」的线格式与计算。
职责:
- 定义摘要行的线格式 BacklogSummary(stdout 每行一条,与事件行共用一条通道)
- 从 agentd 的权威快照(AttachInfo)算出摘要:错过多少条、其中多少已失效、 当前还欠哪些工单
边界:
- 无 I/O、无网络:快照怎么拿是 reconcileBacklog 的事,本文件只做纯计算
- 不决定摘要吐不吐、cursor 推不推——那是 FollowEvents 的编排
- 不打日志:本文件是纯计算,可观测性由调用方 reconcileBacklog 承担 (它拿同一份结果打一行带全部计数的 Info)
Package client 是 handoff 协调者侧对 agentd 的唯一拨号方:任务列表、attach 现场恢复、 ticket 应答(reply)、wait 事件等待(WS + cursor 断线续拉)与审阅命令(diff/fetch/run)。
职责:
- 封装 agentd 的全部 HTTP API 与 WS 事件流的调用(Bearer token 鉴权)
- WaitEvent 按 task 自存 cursor(<游标根>/cursors/<agentd>/<task>,见 cursordir.go) 实现「事件不丢不重」:重连时携带最后交付事件的 seq,从服务端补拉断线期间产生的事件
- 断线指数退避重连(1s→2s→…→60s),覆盖本机 agentd 重启、网络抖动等场景
边界:
- 无业务判断:不解析事件 payload 语义、不做审批决策——「答什么」由协调者(人/上层) 决定后经 Reply 原样透传,审批策略在协调者脑中,本包只保证传输可靠与语义透明
- 不持久化除 cursor 外的任何状态:任务/事件/工单数据全部实时向 agentd 查询
cursordir.go —— 协调者侧游标目录的解析、降级与命名空间折算。
职责:
- 解析游标根:~/.handoff → <cwd>/.handoff 两级确定性降级,都不可写则报错
- 把 agentd 地址折算成可作路径段的命名空间名
边界:
- 不读写游标内容(那是 client.go 的 readCursor/writeCursor)
- 不做回收(那是 cursorgc.go)
- 不认识 --target:命名空间按 agentd 地址而非本机别名,见 cursorNamespace 的 why
cursorgc.go —— 协调者侧游标的回收。
职责:
- 任务归档时删掉它的游标(DropCursor)
- 按 TTL 清扫超期游标与遗留的写入临时文件(sweepCursors)
- 一次性清除旧平铺布局遗留的 cursor-* 文件(purgeLegacyFlatCursors)
边界:
- 不判断任务是否真的终结:那是调用方(观察到 archived 事件 / done 成功)的事
- 不解析游标根:复用 cursordir.go 的 cursorRootDir
- 全部回收动作都是尽力而为,失败只记 Debug,绝不影响游标读写的成败
update.go —— 换版接口的客户端侧:推送二进制、触发重启、轮询确认上线。
职责:
- PushUpdate / RestartAgentd:调 POST /api/update 的两种模式
- WaitVersion:换版后轮询 status 直到新版本上线或超时
边界:
- 不下载、不校验资产:那是 internal/release 的职责,本层只搬字节
- 不做重试:换版是有副作用的动作,失败了要让操作者看见并决定
Index ¶
- Constants
- Variables
- type AttachInfo
- type BacklogSummary
- type Client
- func (c *Client) Attach(ctx context.Context, taskID string) (*AttachInfo, error)
- func (c *Client) Continue(ctx context.Context, taskID, instructions string) error
- func (c *Client) Diff(ctx context.Context, taskID, base string) (string, error)
- func (c *Client) Dispatch(ctx context.Context, opts DispatchOpts) (*proto.Task, error)
- func (c *Client) Done(ctx context.Context, taskID, note string) (bool, error)
- func (c *Client) DropCursor(taskID string)
- func (c *Client) Fetch(ctx context.Context, taskID, relPath string) (string, error)
- func (c *Client) FollowEvents(ctx context.Context, taskID string, all bool, idle time.Duration, ...) error
- func (c *Client) Footprint(ctx context.Context) (*proto.FootprintResp, error)
- func (c *Client) ListTasks(ctx context.Context) ([]proto.TaskView, error)
- func (c *Client) ProjectAdd(ctx context.Context, opts ProjectAddOpts) (*proto.ProjectLocation, error)
- func (c *Client) ProjectList(ctx context.Context) ([]proto.ProjectLocation, error)
- func (c *Client) ProjectRemove(ctx context.Context, name string) error
- func (c *Client) PushUpdate(ctx context.Context, tag, sum string, tgz []byte, force bool) (*proto.UpdateResp, error)
- func (c *Client) Reclaim(ctx context.Context, taskID string, force bool) (*proto.ReclaimResp, error)
- func (c *Client) ReclaimList(ctx context.Context) (*proto.ReclaimListResp, error)
- func (c *Client) RenderStream(ctx context.Context, taskID string, offset, tail int64, follow bool) (io.ReadCloser, int64, error)
- func (c *Client) Reply(ctx context.Context, taskID, ticketID, answer string) error
- func (c *Client) RestartAgentd(ctx context.Context, force bool) (*proto.UpdateResp, error)
- func (c *Client) Resume(ctx context.Context, taskID string, force bool) (string, error)
- func (c *Client) Run(ctx context.Context, taskID, cmd string) (stdout string, exitCode int, err error)
- func (c *Client) Status(ctx context.Context) (*proto.StatusResp, error)
- func (c *Client) Stop(ctx context.Context, taskID string) (worktreeRemoved bool, err error)
- func (c *Client) WaitEvent(ctx context.Context, taskID string, all bool) (*proto.Event, error)
- func (c *Client) WaitVersion(ctx context.Context, want string, timeout, interval time.Duration) error
- type DispatchOpts
- type ProjectAddOpts
- type ReclaimRejected
- type UpdateRejected
Constants ¶
const BacklogSummaryType = "backlog_summary"
BacklogSummaryType 是摘要行的 type 取值。
为什么复用 type 这个 key 而不另起 kind:stdout 的既有契约是「每行一个带 type 的 JSON 对象」,上层按行解析。沿用 type 能让既有解析器读到一个不认识的取值 就跳过;换个 key 则会让它们撞上一个缺字段的对象。
注意:这是**客户端合成**的行,agentd 从不存这个事件类型——不要去 proto.EventType 里找它。
Variables ¶
var ErrFootprintUnsupported = errors.New("对端 agentd 不支持足迹体检")
ErrFootprintUnsupported 表示对端 agentd 太旧,没有 /api/footprint。
与 ErrStatusUnsupported 分开而不复用:调用方要给出的处置建议不同 (那条说「升级后才能看状态」,这条说「升级后才能看进程足迹,眼下只能上机器 ps」)
var ErrIdleTimeout = errors.New("空闲超时:期间未收到任何帧")
ErrIdleTimeout 表示 follow 期间空闲超过约定时长——期间**一帧都没收到** (含被过滤掉的 progress)。
为什么它值得一个独立哨兵:它与「任务停滞」不是一回事。任务停滞由 agentd 的 看门狗诊断并作为 stalled 事件送达(带 last_seq 与 idle 时长);本错误只说明 连接侧一片死寂,第一嫌疑是 agentd 失联而不是任务卡住。
var ErrReclaimUnsupported = errors.New("对端 agentd 不支持 worktree 回收")
ErrReclaimUnsupported 表示对端 agentd 太旧,没有 worktree 回收端点。
与 ErrStatusUnsupported / ErrFootprintUnsupported 分开:处置建议不同 (这条说「升级后才能远程回收,眼下只能上机器 git worktree remove」)
var ErrStatusUnsupported = errors.New("对端 agentd 不支持 /api/status")
ErrStatusUnsupported 表示对端 agentd 不认识 /api/status(版本早于该端点引入)。
why(必须是可判别的哨兵):这是唯一一个「HTTP 失败但结论是成功」的分支—— 能收到 404 说明 TCP 通、HTTP 正常、Bearer 已经通过,三件事都被证明了。 CLI 据此输出降级结论并退 0,而不是把一台完全能用的机器判成失败。
var ErrUnreachable = errors.New("对端 agentd 够不着")
ErrUnreachable 表示这次请求**一个 HTTP 响应都没拿到**——TCP 拨不通、连接被拒、 DNS 解析失败或读写中断,对端在不在都无从判断。
why(必须是可判别的哨兵):调用方要区分「对端不在」与「对端拒绝了这次请求」。 后者(400/409/500)拿到了响应,说明 agentd 在、Bearer 通过、语义上真的冲突了, 绝不能当成「机器不在」咽下去——那是往登记表里写脏数据。这个区分只有 client 知道;让调用方去 grep 错误文本里的 "connection refused" 是把 Go 的错误措辞与 平台差异变成契约。同 ErrStatusUnsupported 的理由。
**不包含 ctx 取消与超时**(见 do 里的注释)。
var ErrUpdateUnsupported = errors.New("对端 agentd 不支持 /api/update")
ErrUpdateUnsupported 表示对端 agentd 不认识 /api/update(v0.1.0 及更早)。
与 ErrStatusUnsupported 同一条纪律:这是一条**有用的结论**——对端过旧, 这一跳必须手工做(spec §8),不是一个含糊的失败。
Functions ¶
This section is empty.
Types ¶
type AttachInfo ¶
type AttachInfo struct {
Task proto.TaskView `json:"task"`
PendingTickets []proto.Ticket `json:"pending_tickets"`
RecentEvents []proto.Event `json:"recent_events"`
}
AttachInfo 是 attach 命令的完整现场快照:任务 + 待办工单 + 最近事件。 与 agentd GET /api/tasks/{id} 的响应线格式一一对应,协调者恢复现场的关键数据源。
type BacklogSummary ¶
type BacklogSummary struct {
Type string `json:"type"`
TaskID string `json:"task_id"`
FromSeq int64 `json:"from_seq"`
ToSeq int64 `json:"to_seq"`
State proto.TaskState `json:"state"`
// Missed 是间隙内可交付事件的条数。MissedTruncated 为 true 时语义降级为
// 「至少 Missed 条」——快照的事件窗口没能覆盖到 cursor,剩下的数不出来。
Missed int `json:"missed"`
MissedTruncated bool `json:"missed_truncated"`
// Stale 是间隙内工单已被消费(审批链答掉或被作废)的事件条数——补 reply 会 404。
Stale int `json:"stale"`
// Actionable 是当前仍待处置的工单**全量**,每张带完整 Request 原文,协调者
// 可直接据此 reply --ticket <id>。
//
// 注意它**不限于间隙内**:断网前你就看见过、但一直没答的工单也在里面。
// 那正是最需要知道的一类,也是 Stale 不能用减法算出来的原因。
Actionable []proto.Ticket `json:"actionable"`
}
BacklogSummary 是 follow 建立连接前对账得出的「你错过了什么」。
线格式(单行 JSON,stdout):{"type":"backlog_summary","task_id":..,"from_seq":.., "to_seq":..,"state":..,"missed":..,"missed_truncated":..,"stale":..,"actionable":[..]}
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client 是 agentd 的 HTTP/WS 客户端,持有服务地址与 Bearer 令牌。
并发安全:baseURL/token/hc 与 WS 节奏字段构造后只读;游标根由 cursorRootOnce 保护,首次调用解析、后续读缓存,可被多个 goroutine 同时使用。
func New ¶
New 创建 agentd 客户端。
参数:
- addr: agentd 地址(如 http://127.0.0.1:7777);缺少 scheme 时自动补 http://
- token: Bearer 访问令牌;为空时请求不带 Authorization 头
注意:
- 仅做地址归一化,不做任何网络请求,连接在首次调用时建立
func NewWithWSTiming ¶
NewWithWSTiming 是 New 的 WS 重连节奏可注入变体:测试注入毫秒级退避与 健康门槛,让「连接活够了才复位退避」的断言不必真等 1s..60s;生产一律走 New。
参数:
- initial/max: 断线重连的初始/封顶退避
- stableAfter: 连接存活多久才算健康、才复位退避(见 WaitEvent)
func (*Client) Attach ¶
Attach 获取任务的完整现场快照(任务 + 待办工单 + 最近事件), 是协调者恢复会话现场(pending_tickets)的数据源。
参数:
- taskID: 任务 ID;任务不存在时返回 404 错误
func (*Client) Continue ¶
Continue 向任务续发修改指令(要求任务处于 waiting_review,指令原样透传 executor)。
注意:
- 任务不存在返回 404 错误;状态不允许续接返回 409 错误
func (*Client) Diff ¶
Diff 获取任务分支相对基准分支的审阅素材(git diff + 提交列表)。
参数:
- base: 基准分支名;传空串时由 agentd 按仓库默认分支推导(origin/HEAD → main → master)
func (*Client) Dispatch ¶
Dispatch 派发一个新任务到 agentd 执行。
参数:
- opts: 派发参数(仓库/计划/执行者/分支/工作区等,见 DispatchOpts)
返回:
- 创建后的任务(state=running);服务端启动 executor 失败时返回错误
func (*Client) Done ¶
Done 归档任务,可携带一句完成说明。
参数:
- taskID: 待归档任务 ID
- note: 完成说明;空串=不留说明(服务端照常归档并照常发 archived 事件)
返回:
- noteSaved: 响应体 note_saved 如实回传——true=说明已落库;false=本次没带 说明,**或对端是不支持该字段的旧版 agentd**。响应体缺字段按 false 处理, 与 Stop 的 worktree_removed 同一模式:宁可多告警一次,也不让「说明悄悄 丢了」变成哑失败。调用方据此决定是否提示,不猜
func (*Client) DropCursor ¶
DropCursor 删除某任务的游标,幂等。
参数:taskID 为已终结(归档)的任务 ID
注意:
- 文件不存在不是错误:本函数有两条调用通道(观察到 archived 事件、done 成功 返回),两条都可能先到,必须能重复调用
- 任何失败只记 Debug:回收是卫生工作,失败不影响任何正确性
func (*Client) Fetch ¶
Fetch 读取任务仓库内相对路径文件的内容(协调者取上下文用)。
注意:
- 路径逃出仓库(如 ../ 前缀或绝对路径)返回错误;文件不存在返回 404 错误
func (*Client) FollowEvents ¶
func (c *Client) FollowEvents(ctx context.Context, taskID string, all bool, idle time.Duration, onEvent func(*proto.Event) error, onBacklog func(*BacklogSummary) error) error
FollowEvents 持续订阅任务事件流,逐条交给 onEvent,直到任务终结或出错。
与 WaitEvent 的区别只有一条:不在首个事件后返回。这条区别是本设计的全部理由 ——一事件一退出意味着每两个事件之间必然有一段无人订阅的真空,而「回合结束后 记得重挂」是需要每轮重做的人工动作,漏一次即永久断链。
参数:
- all: false 时过滤 progress(与 WaitEvent 同义)
- idle: 空闲上限,0 表示不设。**空闲以「收到任何帧」为准,包含被过滤掉的 progress**——一个健康的长跑任务可以数小时只有 progress,用可交付事件计时 会让它周期性无故超时。这个计时跨重连累计
- onEvent: 每条**可交付**事件调用一次;返回非 nil 立即终止跟随并原样返回该错误
- onBacklog: 每次建连前对账出的积压摘要的消费者。**传 nil 表示完全跳过对账**, 行为与改动前逐字一致——不能只是丢弃摘要却照样跳过积压,那会让事件无声消失
返回:
- nil: 任务终结(收到 failed 事件,或对端归档关闭连接)
- ErrIdleTimeout: 空闲超过 idle
- ctx.Err() / 永久失败(401、任务不存在): 原样返回
cursor 语义(与 WaitEvent 的差别,取舍已在 spec §2.4 记录并接受):
- cursor 仍只在**交付**事件时推进,但「交付」不再等价于「协调者看过了」 ——事件可能在协调者正忙时流入。此刻会话若崩溃,该事件不会再重放
- 接受这个回退的理由:事件流本就不是权威,工单在 agentd 侧持久, pending_tickets 才是权威清单。醒来先 show 这条纪律因此从建议变成必须
- 断线续拉起点(fromSeq)则按**任何帧**推进:已经收到的帧没有再补发的必要, 它与 cursor 的分叉是有意的,且分叉方向安全(cursor 永远更保守)
func (*Client) Footprint ¶
Footprint 拉取对端全部任务的进程足迹体检结果。
返回:
- 体检结果;404(对端 agentd 过旧、没有这个端点)返回 ErrFootprintUnsupported
- 请求失败或响应非法时返回错误
注意:这是慢命令——对端要遍历全部历史任务目录,调用方应给足超时。
func (*Client) ListTasks ¶
ListTasks 查询全部任务(created_at 降序)。
返回:
- 任务列表;服务端保证空库时返回空切片而非 nil
- 请求失败或响应非法时返回错误
func (*Client) ProjectAdd ¶
func (c *Client) ProjectAdd(ctx context.Context, opts ProjectAddOpts) (*proto.ProjectLocation, error)
ProjectAdd 在目标 agentd 上登记一个项目位置(必要时先克隆)。
注意:
- 路径不是 git 仓库/没有 origin/克隆失败返回 400 错误(报文含 git 原文)
- 路径上是另一个项目返回 400 错误(报文同时给出两边的 origin)
- 项目/名字/路径已被登记、克隆落点已存在返回 409 错误
func (*Client) ProjectList ¶
ProjectList 列出目标 agentd 上的全部项目位置(含实际状态)。
func (*Client) ProjectRemove ¶
ProjectRemove 注销一条项目位置。
注意:
- 只删登记,**不删磁盘上的代码**
- 登记不存在返回 404 错误;项目仓库仍被活跃任务占用返回 409 错误
func (*Client) PushUpdate ¶
func (c *Client) PushUpdate(ctx context.Context, tag, sum string, tgz []byte, force bool) (*proto.UpdateResp, error)
PushUpdate 把 tar.gz 资产原文推给对端 agentd 并触发换版重启。
参数:
- tag: 目标版本,agentd 用它做自检比对(新二进制 version 首行必须等于它)
- sum: 资产的 sha256(十六进制小写),来自 release 的 checksums.txt
- tgz: **tar.gz 原文**,不是解包后的裸二进制——这样三处校验比的是同一个 来自 release 的声明,传输两端不会互相背书
- force: 越过闸一(活跃任务)。**不越过闸二(非托管)**
返回:
- 成功响应(含 Prev:旧二进制留存路径,回滚要用)
- *UpdateRejected(两道闸)/ ErrUpdateUnsupported(对端过旧)/ 其他错误
func (*Client) Reclaim ¶
func (c *Client) Reclaim(ctx context.Context, taskID string, force bool) (*proto.ReclaimResp, error)
Reclaim 回收指定终态任务残留的 managed worktree。
参数:
- taskID: 目标任务
- force: 对脏工作树强删(丢弃未提交改动)
返回:
- 回收结果
- ErrReclaimUnsupported: 对端过旧
- *ReclaimRejected: 被拒(409),带机器码与改动清单
- 其余错误:连不上、401、5xx、任务不存在
注意(404 消歧):老 agentd 没有这条路由,POST 打过去也是 404——与「任务 不存在」撞码。照直翻译会对着一台好机器报「任务不存在」,把人引向完全错误 的方向。因此收到 404 时补打一次 GET /api/reclaim:它也 404 才是老 agentd, 它 200 说明任务是真不存在。只在错误路径上多一次往返,换一个不靠猜的结论
func (*Client) ReclaimList ¶
ReclaimList 拉取对端全部终态任务的 worktree 残留体检结果。
返回:
- 体检结果;404(对端过旧)返回 ErrReclaimUnsupported
- 请求失败或响应非法时返回错误
func (*Client) RenderStream ¶
func (c *Client) RenderStream(ctx context.Context, taskID string, offset, tail int64, follow bool) (io.ReadCloser, int64, error)
RenderStream 打开任务实况(render.log)的流式读取。
参数:
- taskID: 目标任务
- offset: 起始字节偏移;>0 时优先于 tail(用于断线续传)
- tail: 从尾部回溯的字节数(offset<=0 时生效;两者都为 0 时由服务端取默认值)
- follow: 是否在到达文件尾后继续等待增量
返回:
- 流(调用方负责 Close)、响应开始时的文件字节数、错误
注意:
- 本方法**不设读超时**:follow 模式下长时间无输出是正常的(模型在思考)。 取消靠 ctx——CLI 把 Ctrl+C 接到 ctx 上
- 非 200 一律转成错误并读走响应体,避免连接泄漏
func (*Client) Reply ¶
Reply 回答一个工单(权限门批准/拒绝、提问的答案)。
参数:
- taskID: 工单所属任务 ID
- ticketID: 待回答的工单 ID
- answer: 应答原文,原样透传给 agentd(如 "allow" / "deny: 原因" / 任意文本), 语义由上层(协调者/manager)决定,本包不做解释
注意:
- 工单不存在、已回答(不可重复回答)或不属于该任务时返回错误
func (*Client) RestartAgentd ¶
RestartAgentd 让对端 agentd 重启但不换版(body 为空,spec D8)。
用于本机:二进制由 CLI 直接换掉了,但正在跑的 agentd 仍是旧进程。
func (*Client) Resume ¶
Resume 显式恢复卡死的任务:让 agentd 重投「已落库但未送达 executor」的应答, 并(B38)对断连窗口内丢失的回合终态做会话对账。
参数:
- taskID: 任务 ID
- force: 为真时即使对账判不出(executor 不支持对账 / 回合确实还在忙 / 查询失败)仍把任务强制收口到 waiting_review,使 continue/done 可用; 收口保住 executor 会话,与 stop 不同(stop 会杀会话并落 failed)
返回:
- 恢复结果 JSON 原文(重投条数、对账结果、executor 是否已不在、收尾状态与结论), 原样输出给协调者
- executor 仍不可用(502)或任务已终结(409)等情况返回错误;502 时响应体 里仍带着本次已重投成功的条数,错误信息中包含它
func (*Client) Run ¶
func (c *Client) Run(ctx context.Context, taskID, cmd string) (stdout string, exitCode int, err error)
Run 在任务仓库执行一条审阅命令(sh -c,10min 超时),返回合并输出与退出码。
注意:
- 命令非零退出不返回错误,退出码经 exitCode 表达;超时被杀时 exitCode=124
- 只有执行未发生(启动失败/超时/请求失败)才返回错误
func (*Client) Status ¶
Status 查询 agentd 的可用性与身份信息(handoff status 的数据源)。
返回:
- StatusResp:版本、监听地址、DataDir、执行者清单、任务计数、活跃任务
- ErrStatusUnsupported:对端是老 agentd(404),调用方应走降级输出
- 其余错误:连不上、401、5xx 等真失败
func (*Client) Stop ¶
Stop 主动中止任务:停 executor、作废挂起工单、任务落 failed。
参数:
- taskID: 待中止的任务 ID
返回:
- worktreeRemoved: 响应体 worktree_removed 如实回传——true=本次删除了 managed worktree,false=用户自带 worktree / 原地模式(没删);响应体缺字段 (旧版 agentd)按 false 处理。CLI 据此打印与行为一致的提示,不猜
- 任务不存在(404)或已是终态(409)时返回错误
func (*Client) WaitEvent ¶
WaitEvent 阻塞等待任务的下一个事件:跳过 progress(除非 all=true), 拿到首个可动作事件即返回并把 cursor 写盘;断线指数退避 1s→2s→…→60s 无限重连,ctx 取消才退出。
cursor 语义(事件不丢不重的根基):
- 每次调用开始时从 <游标根>/cursors/<agentd>/<task> 读取上次交付事件的 seq, 连接 WS 时以 from_seq=cursor 补拉断线期间产生的事件
- 返回首个可动作事件时把 cursor 原子写盘为该事件的 seq;被跳过的 progress 事件不推进 cursor(下次调用会重新收到并再次跳过,重复跳过无副作用)
- 因此每条可动作事件恰好交付一次(不重),cursor 之后的事件断线后一条不丢(不丢)
为什么 progress 不唤醒:progress 是高频、无需人工动作的状态播报(如「正在运行」), 若用它唤醒,wait 会在每次进度变化时把协调者叫醒做无意义的一次「看-忽略」; 协调者只需在真正需要决策的事件(question/permission_request/completed/failed/stalled) 到达时被唤醒。需要全量事件流时显式传 all=true。
永久性失败(不重试):握手 400/401/403(配置错误)与任务不存在(PolicyViolation close)立即返回错误——退避重连只为瞬时故障设计,见 isPermanent 的 why。
参数:
- taskID: 要等待的任务 ID
- all: true 时不做类型过滤,第一个到达的事件即返回
返回:
- 首个可动作事件;ctx 取消时返回 ctx.Err()(context.Canceled/DeadlineExceeded); 永久性失败时返回对应的错误(不做退避)
func (*Client) WaitVersion ¶
func (c *Client) WaitVersion(ctx context.Context, want string, timeout, interval time.Duration) error
WaitVersion 轮询 status 直到对端版本变成 want,或超时。
参数:
- want: 期望的版本号(形如 v0.1.1)
- timeout / interval: 等待时限与轮询间隔(生产取 60s / 2s)
注意:
- **轮询期间的失败一律忽略继续等**。重启窗口里连接被拒、502、503 都是 过程而不是结论;第一次 dial 失败就放弃,等于把每一次正常换版都报成失败
- 超时返回错误。不确认就报成功是主张不是事实,而 agentd 起不来恰恰是最 需要立刻知道的时刻
type DispatchOpts ¶
type DispatchOpts struct {
// ProjectID 是项目身份,由 CLI 从 cwd 的 origin 离线算出;与 ProjectName 二选一。
ProjectID string
// ProjectName 是 --project <名字> 的取值,仅在 cwd 不是目标项目时使用。
ProjectName string
PlanB64 string
PlanName string
Target string
Prompt string
Name string
Executor string
Model string
Branch string
NewBranch string
Base string
Worktree string
NewWorktree bool
// BaseCommit 是协调者本地 HEAD 的提交号,随请求上送让 agentd 校验任务仓库
// 不落后于本地(空=不校验)。
BaseCommit string
}
DispatchOpts 是 Dispatch 的入参,与 agentd POST /api/tasks 的请求体键一一对应。
PlanB64 与 Prompt 至少其一:PlanB64 是 base64 的 plan 文件内容(附 plan 名归档), Prompt 是直接指令(prompt-only 派发);两者都传时 Prompt 作为附加指令拼接在 plan 之后。Branch/NewBranch、Worktree/NewWorktree 各自二选一,空=自动分支/原地。
type ProjectAddOpts ¶
ProjectAddOpts 是 ProjectAdd 的参数。
两种形态由 Path 是否为空决定:
- Path 非空:目标 agentd 所在机器上已经有一份代码,登记它(agentd 现读 它的 origin 校验一致)
- Path 为空:让 agentd 自己 clone 到 repo_root/<Name>
type ReclaimRejected ¶
type ReclaimRejected struct {
Reason proto.ReclaimReason
Msg string
Dirty []proto.DirtyFile
}
ReclaimRejected 是一次被拒的回收,带机器码与(脏树时的)改动清单。
为什么不做成一堆哨兵:四种拒绝共用 409,调用方要的是「哪一种 + 细节」, 一个带 Reason 字段的类型比四个哨兵加一次类型断言更直白
func (*ReclaimRejected) Error ¶
func (e *ReclaimRejected) Error() string
type UpdateRejected ¶
type UpdateRejected struct {
Reason string // proto.UpdateReasonBusy / proto.UpdateReasonUnmanaged / ""
Msg string
}
UpdateRejected 是被两道闸拒绝时的错误。
为什么要带 Reason 而不只是一句话:busy 与 unmanaged 的处置**完全不同** ——前者能 --force 越过,后者不能。把它压成字符串,调用方就只能靠 strings.Contains 猜,而猜错的代价是给用户一条注定失败的命令。
func (*UpdateRejected) Error ¶
func (e *UpdateRejected) Error() string