93 lines
3.4 KiB
Go
93 lines
3.4 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"math"
|
|
"strings"
|
|
)
|
|
|
|
// Optional filters must remain optional across JSON-schema clients. In
|
|
// particular, null means unspecified; zero is not a wildcard process ID.
|
|
func windowQuery(args obj) (obj, error) {
|
|
query := obj{}
|
|
for key, value := range args {
|
|
if value != nil {
|
|
query[key] = value
|
|
}
|
|
}
|
|
if value, ok := query["app"]; ok {
|
|
app, valid := value.(string)
|
|
if !valid {
|
|
return nil, errors.New("app must be an application name or null")
|
|
}
|
|
if app = strings.TrimSpace(app); app == "" {
|
|
delete(query, "app")
|
|
} else {
|
|
query["app"] = app
|
|
}
|
|
}
|
|
if value, ok := query["pid"]; ok {
|
|
valid := num(value) > 0 && num(value) <= math.MaxUint32
|
|
switch n := value.(type) {
|
|
case float64:
|
|
valid = valid && n == math.Trunc(n)
|
|
case int, int64:
|
|
default:
|
|
valid = false
|
|
}
|
|
if !valid {
|
|
return nil, errors.New("pid must be a positive Windows process ID from a matching list_windows result. If unknown, omit pid or use null and search by app, e.g. {\"app\":\"Brave\",\"on_screen_only\":true}. Never use 0 or a different app's foreground PID")
|
|
}
|
|
}
|
|
return query, nil
|
|
}
|
|
|
|
// Upstream's empty-result hint names the unrelated foreground process. Do not
|
|
// present that PID as a recovery candidate. Keep the exact filters and provide
|
|
// a separate suggested query; never silently widen the caller's PID scope.
|
|
func windowListing(result obj, query obj) obj {
|
|
if result["isError"] == true {
|
|
return result
|
|
}
|
|
data := object(result["structuredContent"])
|
|
windows, ok := data["windows"].([]any)
|
|
if !ok {
|
|
return failure("Driver returned no window list; this is an observation error, not evidence that the application is absent", "not_started", "none", "invalid_window_list")
|
|
}
|
|
lookup := obj{"status": "matched", "filters": query, "count": len(windows)}
|
|
lines := []string{fmt.Sprintf("Found %d matching window(s).", len(windows))}
|
|
for _, value := range windows {
|
|
window := object(value)
|
|
lines = append(lines, fmt.Sprintf("- %s (pid %d), window_id %d, title %q", str(window["app_name"]), num(window["pid"]), num(window["window_id"]), str(window["title"])))
|
|
}
|
|
if len(windows) == 0 {
|
|
lookup["status"] = "no_match"
|
|
lines = append(lines, "No windows match these filters. This alone does not prove the application is unavailable or screenshot capture is broken. Do not substitute another application's foreground PID.")
|
|
if str(query["app"]) != "" && query["pid"] != nil {
|
|
next := obj{}
|
|
for key, value := range query {
|
|
if key != "pid" {
|
|
next[key] = value
|
|
}
|
|
}
|
|
lookup["suggested_arguments"] = next
|
|
lines = append(lines, "app and pid are combined filters. Retry list_windows for the same app with pid omitted; use only a PID returned for that app.")
|
|
} else if query["on_screen_only"] == true {
|
|
next := obj{}
|
|
for key, value := range query {
|
|
next[key] = value
|
|
}
|
|
next["on_screen_only"] = false
|
|
lookup["suggested_arguments"] = next
|
|
lines = append(lines, "Retry the same filters with on_screen_only=false to check for minimized/off-screen windows. A listed window still needs a fresh get_window_state before interaction.")
|
|
} else {
|
|
lines = append(lines, "Verify the requested application name or PID. When the PID is unknown, search by the known app name with pid omitted. Do not guess a PID or operate an unrelated window.")
|
|
}
|
|
}
|
|
data["lookup"] = lookup
|
|
result["structuredContent"] = data
|
|
result["content"] = []any{textBlock(strings.Join(lines, "\n"))}
|
|
return result
|
|
}
|