feat: agent 本机 SimAdmin 登录与会话维持;中继设备侧限流与 agent Retry-After

- runtime.NodeAuth:agent 自查 /api/auth/status,需要时自行 POST /api/auth/login 并持
  simadmin_session Cookie 转发;错误密码进入 60s 冷却(对齐控制台 ensureAuthenticated),
  节点回 401/403 即失效重探,SimAdmin 重启无需重启 agent
- 节点密码进 agent 配置(nodePassword),永不经中继、永不下发到手机;登录路径仍被策略
  挡在电话可达面之外(TestAuthPathsAreUnreachable)
- 新拒绝分类 node_auth_failed:与 upstream_error 区分,手机可提示'该节点需重新配置'
- 中继:/v1/request 每设备固定窗口限流(默认 30/分钟),未鉴权调用不消耗预算
- transport:解析 Retry-After 并入退避;main loop 对 429 直接遵从中继指示
- contracts/envelope.schema.json 补 InnerResponse.outcome 字段
- 设计文档与 README 同步:WSS 改为长轮询的实现修订、响应方向已临时×临时、P2/P3 状态
This commit is contained in:
chick
2026-09-16 09:15:25 +08:00
parent 54d9696a4d
commit 443af20e0a
9 changed files with 645 additions and 12 deletions
+6
View File
@@ -67,6 +67,10 @@ const (
OutcomeRateLimited = "rate_limited"
OutcomeTooLarge = "payload_too_large"
OutcomeUpstream = "upstream_error"
// OutcomeNodeAuth means the agent's own login to the local SimAdmin failed. The phone shows it
// as "this node needs its agent reconfigured", not "your command failed", because nothing the
// user does on the phone can fix a wrong node password.
OutcomeNodeAuth = "node_auth_failed"
)
// Agent owns the loop over one node.
@@ -208,6 +212,8 @@ func statusFor(class string) int {
return 429
case runtime.ClassTooLarge:
return 413
case runtime.ClassNodeAuth:
return 503
case runtime.ClassUpstream:
return 502
default: