feat: 支持wings断言和自动化

This commit is contained in:
余泓铮
2025-07-09 15:45:56 +08:00
parent 5ddd51ea00
commit 813a4b7646
12 changed files with 1636 additions and 102 deletions
+193
View File
@@ -3,6 +3,7 @@ package uixt
import (
"context"
"fmt"
"time"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
@@ -393,3 +394,195 @@ func (t *ToolGetForegroundApp) Implement() server.ToolHandlerFunc {
func (t *ToolGetForegroundApp) ConvertActionToCallToolRequest(action option.MobileAction) (mcp.CallToolRequest, error) {
return BuildMCPCallToolRequest(t.Name(), map[string]any{}, action), nil
}
// ToolOpenApp implements the open_app tool call.
type ToolOpenApp struct {
// Return data fields - these define the structure of data returned by this tool
PackageName string `json:"packageName" desc:"Package name of the opened app"`
}
func (t *ToolOpenApp) Name() option.ActionName {
return option.ACTION_OpenApp
}
func (t *ToolOpenApp) Description() string {
return "Open an app on mobile device using its package name and wait for the app to load"
}
func (t *ToolOpenApp) Options() []mcp.ToolOption {
unifiedReq := &option.ActionOptions{}
return unifiedReq.GetMCPOptions(option.ACTION_OpenApp)
}
func (t *ToolOpenApp) Implement() server.ToolHandlerFunc {
return func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
arguments := request.GetArguments()
driverExt, err := setupXTDriver(ctx, arguments)
if err != nil {
return nil, fmt.Errorf("setup driver failed: %w", err)
}
unifiedReq, err := parseActionOptions(arguments)
if err != nil {
return nil, err
}
if unifiedReq.PackageName == "" {
return nil, fmt.Errorf("package_name is required")
}
// Open app action logic
err = driverExt.AppLaunch(unifiedReq.PackageName)
if err != nil {
return NewMCPErrorResponse(fmt.Sprintf("Open app failed: %s", err.Error())), err
}
message := fmt.Sprintf("Successfully opened app: %s", unifiedReq.PackageName)
returnData := ToolOpenApp{PackageName: unifiedReq.PackageName}
return NewMCPSuccessResponse(message, &returnData), nil
}
}
func (t *ToolOpenApp) ConvertActionToCallToolRequest(action option.MobileAction) (mcp.CallToolRequest, error) {
if packageName, ok := action.Params.(string); ok {
arguments := map[string]any{
"packageName": packageName,
}
return BuildMCPCallToolRequest(t.Name(), arguments, action), nil
}
return mcp.CallToolRequest{}, fmt.Errorf("invalid open app params: %v", action.Params)
}
// ToolTerminateAppNew implements the terminal_app tool call.
type ToolTerminateAppNew struct {
// Return data fields - these define the structure of data returned by this tool
PackageName string `json:"packageName" desc:"Package name of the terminated app"`
WasRunning bool `json:"wasRunning" desc:"Whether the app was actually running before termination"`
}
func (t *ToolTerminateAppNew) Name() option.ActionName {
return option.ACTION_TerminateApp
}
func (t *ToolTerminateAppNew) Description() string {
return "Terminate a running app on mobile device using its package name"
}
func (t *ToolTerminateAppNew) Options() []mcp.ToolOption {
unifiedReq := &option.ActionOptions{}
return unifiedReq.GetMCPOptions(option.ACTION_TerminateApp)
}
func (t *ToolTerminateAppNew) Implement() server.ToolHandlerFunc {
return func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
arguments := request.GetArguments()
driverExt, err := setupXTDriver(ctx, arguments)
if err != nil {
return nil, fmt.Errorf("setup driver failed: %w", err)
}
unifiedReq, err := parseActionOptions(arguments)
if err != nil {
return nil, err
}
if unifiedReq.PackageName == "" {
return nil, fmt.Errorf("package_name is required")
}
// Terminate app action logic
success, err := driverExt.AppTerminate(unifiedReq.PackageName)
if err != nil {
return NewMCPErrorResponse(fmt.Sprintf("Terminate app failed: %s", err.Error())), err
}
if !success {
log.Warn().Str("packageName", unifiedReq.PackageName).Msg("app was not running")
}
message := fmt.Sprintf("Successfully terminated app: %s", unifiedReq.PackageName)
returnData := ToolTerminateAppNew{
PackageName: unifiedReq.PackageName,
WasRunning: success,
}
return NewMCPSuccessResponse(message, &returnData), nil
}
}
func (t *ToolTerminateAppNew) ConvertActionToCallToolRequest(action option.MobileAction) (mcp.CallToolRequest, error) {
if packageName, ok := action.Params.(string); ok {
arguments := map[string]any{
"packageName": packageName,
}
return BuildMCPCallToolRequest(t.Name(), arguments, action), nil
}
return mcp.CallToolRequest{}, fmt.Errorf("invalid terminate app params: %v", action.Params)
}
// ToolColdLaunch implements the cold_launch tool call.
type ToolColdLaunch struct {
// Return data fields - these define the structure of data returned by this tool
PackageName string `json:"packageName" desc:"Package name of the cold launched app"`
}
func (t *ToolColdLaunch) Name() option.ActionName {
return option.ACTION_ColdLaunch
}
func (t *ToolColdLaunch) Description() string {
return "Perform a cold launch of an app (terminate first if running, then launch)"
}
func (t *ToolColdLaunch) Options() []mcp.ToolOption {
unifiedReq := &option.ActionOptions{}
return unifiedReq.GetMCPOptions(option.ACTION_ColdLaunch)
}
func (t *ToolColdLaunch) Implement() server.ToolHandlerFunc {
return func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
arguments := request.GetArguments()
driverExt, err := setupXTDriver(ctx, arguments)
if err != nil {
return nil, fmt.Errorf("setup driver failed: %w", err)
}
unifiedReq, err := parseActionOptions(arguments)
if err != nil {
return nil, err
}
if unifiedReq.PackageName == "" {
return nil, fmt.Errorf("package_name is required")
}
// Cold launch logic: terminate first, then launch
// First try to terminate the app (ignore errors if app is not running)
_, err = driverExt.AppTerminate(unifiedReq.PackageName)
if err != nil {
log.Warn().Str("packageName", unifiedReq.PackageName).Msg("app was not running")
return NewMCPErrorResponse(fmt.Sprintf("Cold launch failed, terminate app failed: %s", err.Error())), err
}
time.Sleep(3 * time.Second)
// Then launch the app
err = driverExt.AppLaunch(unifiedReq.PackageName)
if err != nil {
return NewMCPErrorResponse(fmt.Sprintf("Cold launch failed, launch app failed: %s", err.Error())), err
}
message := fmt.Sprintf("Successfully cold launched app: %s", unifiedReq.PackageName)
returnData := ToolColdLaunch{PackageName: unifiedReq.PackageName}
return NewMCPSuccessResponse(message, &returnData), nil
}
}
func (t *ToolColdLaunch) ConvertActionToCallToolRequest(action option.MobileAction) (mcp.CallToolRequest, error) {
if packageName, ok := action.Params.(string); ok {
arguments := map[string]any{
"packageName": packageName,
}
return BuildMCPCallToolRequest(t.Name(), arguments, action), nil
}
return mcp.CallToolRequest{}, fmt.Errorf("invalid cold launch params: %v", action.Params)
}