mirror of
https://github.com/YspCoder/clawgo.git
synced 2026-04-27 20:37:31 +08:00
tighten device action args validation and normalize media payload schema
This commit is contained in:
@@ -49,6 +49,7 @@
|
||||
- 可在 `NodeInfo` 中配置 `token`,relay 会自动附加 `Authorization: Bearer <token>`
|
||||
- `nodes` 工具支持设备快捷参数:`facing`、`duration_ms`、`command`
|
||||
- 设备动作响应统一:`ok/code/error/payload`(code 示例:`ok` `unsupported_action` `transport_error`)
|
||||
- 设备 `payload` 规范字段:`media_type` `storage` `url|path|image` `meta`
|
||||
|
||||
实现位置:
|
||||
- `pkg/nodes/types.go`
|
||||
|
||||
@@ -49,6 +49,7 @@ A `nodes` tool control-plane PoC is now available:
|
||||
- `NodeInfo.token` is supported; relay automatically sets `Authorization: Bearer <token>`
|
||||
- `nodes` tool supports device shortcuts: `facing`, `duration_ms`, `command`
|
||||
- unified device response envelope: `ok/code/error/payload` (code examples: `ok`, `unsupported_action`, `transport_error`)
|
||||
- device `payload` normalized fields: `media_type` `storage` `url|path|image` `meta`
|
||||
|
||||
Implementation:
|
||||
- `pkg/nodes/types.go`
|
||||
|
||||
@@ -93,17 +93,17 @@ func NewAgentLoop(cfg *config.Config, msgBus *bus.MessageBus, provider providers
|
||||
}
|
||||
return nodes.Response{OK: true, Code: "ok", Node: "local", Action: req.Action, Payload: payload}
|
||||
case "camera_snap":
|
||||
return nodes.Response{OK: true, Code: "ok", Node: "local", Action: req.Action, Payload: map[string]interface{}{"transport": "relay-local", "media_type": "image", "facing": req.Args["facing"], "simulated": true}}
|
||||
return nodes.Response{OK: true, Code: "ok", Node: "local", Action: req.Action, Payload: map[string]interface{}{"transport": "relay-local", "media_type": "image", "storage": "inline", "facing": req.Args["facing"], "simulated": true, "meta": map[string]interface{}{"width": 1280, "height": 720}}}
|
||||
case "camera_clip":
|
||||
return nodes.Response{OK: true, Code: "ok", Node: "local", Action: req.Action, Payload: map[string]interface{}{"transport": "relay-local", "media_type": "video", "duration_ms": req.Args["duration_ms"], "simulated": true}}
|
||||
return nodes.Response{OK: true, Code: "ok", Node: "local", Action: req.Action, Payload: map[string]interface{}{"transport": "relay-local", "media_type": "video", "storage": "path", "path": "/tmp/camera_clip.mp4", "duration_ms": req.Args["duration_ms"], "simulated": true, "meta": map[string]interface{}{"fps": 30}}}
|
||||
case "screen_snapshot":
|
||||
return nodes.Response{OK: true, Code: "ok", Node: "local", Action: req.Action, Payload: map[string]interface{}{"transport": "relay-local", "media_type": "image", "simulated": true}}
|
||||
return nodes.Response{OK: true, Code: "ok", Node: "local", Action: req.Action, Payload: map[string]interface{}{"transport": "relay-local", "media_type": "image", "storage": "inline", "simulated": true, "meta": map[string]interface{}{"width": 1920, "height": 1080}}}
|
||||
case "screen_record":
|
||||
return nodes.Response{OK: true, Code: "ok", Node: "local", Action: req.Action, Payload: map[string]interface{}{"transport": "relay-local", "media_type": "video", "duration_ms": req.Args["duration_ms"], "simulated": true}}
|
||||
return nodes.Response{OK: true, Code: "ok", Node: "local", Action: req.Action, Payload: map[string]interface{}{"transport": "relay-local", "media_type": "video", "storage": "path", "path": "/tmp/screen_record.mp4", "duration_ms": req.Args["duration_ms"], "simulated": true, "meta": map[string]interface{}{"fps": 30}}}
|
||||
case "location_get":
|
||||
return nodes.Response{OK: true, Code: "ok", Node: "local", Action: req.Action, Payload: map[string]interface{}{"transport": "relay-local", "lat": 0.0, "lng": 0.0, "accuracy": "simulated"}}
|
||||
return nodes.Response{OK: true, Code: "ok", Node: "local", Action: req.Action, Payload: map[string]interface{}{"transport": "relay-local", "lat": 0.0, "lng": 0.0, "accuracy": "simulated", "meta": map[string]interface{}{"provider": "simulated"}}}
|
||||
case "canvas_snapshot":
|
||||
return nodes.Response{OK: true, Code: "ok", Node: "local", Action: req.Action, Payload: map[string]interface{}{"transport": "relay-local", "image": "data:image/png;base64,<simulated>", "simulated": true}}
|
||||
return nodes.Response{OK: true, Code: "ok", Node: "local", Action: req.Action, Payload: map[string]interface{}{"transport": "relay-local", "image": "data:image/png;base64,<simulated>", "media_type": "image", "storage": "inline", "simulated": true, "meta": map[string]interface{}{"width": 1280, "height": 720}}}
|
||||
case "canvas_action":
|
||||
return nodes.Response{OK: true, Code: "ok", Node: "local", Action: req.Action, Payload: map[string]interface{}{"transport": "relay-local", "applied": true, "simulated": true, "args": req.Args}}
|
||||
default:
|
||||
|
||||
@@ -146,5 +146,36 @@ func (s *HTTPRelayTransport) Send(ctx context.Context, req Request) (Response, e
|
||||
resp.Code = "remote_error"
|
||||
}
|
||||
}
|
||||
resp.Payload = normalizeDevicePayload(resp.Action, resp.Payload)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func normalizeDevicePayload(action string, payload map[string]interface{}) map[string]interface{} {
|
||||
if payload == nil {
|
||||
payload = map[string]interface{}{}
|
||||
}
|
||||
a := strings.ToLower(strings.TrimSpace(action))
|
||||
switch a {
|
||||
case "camera_snap", "screen_snapshot", "canvas_snapshot":
|
||||
if _, ok := payload["media_type"]; !ok {
|
||||
payload["media_type"] = "image"
|
||||
}
|
||||
case "camera_clip", "screen_record":
|
||||
if _, ok := payload["media_type"]; !ok {
|
||||
payload["media_type"] = "video"
|
||||
}
|
||||
}
|
||||
if _, ok := payload["storage"]; !ok {
|
||||
if _, hasURL := payload["url"]; hasURL {
|
||||
payload["storage"] = "url"
|
||||
} else if _, hasPath := payload["path"]; hasPath {
|
||||
payload["storage"] = "path"
|
||||
} else if _, hasInline := payload["image"]; hasInline {
|
||||
payload["storage"] = "inline"
|
||||
}
|
||||
}
|
||||
if _, ok := payload["meta"]; !ok {
|
||||
payload["meta"] = map[string]interface{}{}
|
||||
}
|
||||
return payload
|
||||
}
|
||||
|
||||
@@ -95,6 +95,11 @@ func (t *NodesTool) Execute(ctx context.Context, args map[string]interface{}) (s
|
||||
}
|
||||
reqArgs["duration_ms"] = di
|
||||
}
|
||||
if action == "canvas_action" {
|
||||
if act, _ := reqArgs["action"].(string); strings.TrimSpace(act) == "" {
|
||||
return "", fmt.Errorf("invalid_args: canvas_action requires args.action")
|
||||
}
|
||||
}
|
||||
resp, err := t.router.Dispatch(ctx, nodes.Request{Action: action, Node: nodeID, Args: reqArgs}, mode)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
||||
Reference in New Issue
Block a user