Chore: linter complaints

This commit is contained in:
Grail Finder
2026-03-03 07:38:57 +03:00
parent cad1bd46c1
commit c05b93299c

View File

@@ -3,8 +3,9 @@ package main
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"log/slog"
"os" "os"
"strconv"
"strings"
"sync" "sync"
"github.com/playwright-community/playwright-go" "github.com/playwright-community/playwright-go"
@@ -13,7 +14,6 @@ import (
) )
var ( var (
browserLogger *slog.Logger
pw *playwright.Playwright pw *playwright.Playwright
browser playwright.Browser browser playwright.Browser
browserStarted bool browserStarted bool
@@ -26,30 +26,20 @@ func checkPlaywright() {
var err error var err error
pw, err = playwright.Run() pw, err = playwright.Run()
if err != nil { if err != nil {
if browserLogger != nil { logger.Warn("playwright not available", "error", err)
browserLogger.Warn("playwright not available", "error", err)
}
return return
} }
browserAvailable = true browserAvailable = true
if browserLogger != nil { logger.Info("playwright tools available")
browserLogger.Info("playwright tools available")
}
} }
func pwStart(args map[string]string) []byte { func pwStart(args map[string]string) []byte {
browserStartMu.Lock() browserStartMu.Lock()
defer browserStartMu.Unlock() defer browserStartMu.Unlock()
if browserStarted { if browserStarted {
return []byte(`{"error": "Browser already started"}`) return []byte(`{"error": "Browser already started"}`)
} }
headless := cfg == nil || cfg.PlaywrightHeadless
headless := true
if cfg != nil && !cfg.PlaywrightHeadless {
headless = false
}
var err error var err error
browser, err = pw.Chromium.Launch(playwright.BrowserTypeLaunchOptions{ browser, err = pw.Chromium.Launch(playwright.BrowserTypeLaunchOptions{
Headless: playwright.Bool(headless), Headless: playwright.Bool(headless),
@@ -57,13 +47,11 @@ func pwStart(args map[string]string) []byte {
if err != nil { if err != nil {
return []byte(fmt.Sprintf(`{"error": "failed to launch browser: %s"}`, err.Error())) return []byte(fmt.Sprintf(`{"error": "failed to launch browser: %s"}`, err.Error()))
} }
page, err = browser.NewPage() page, err = browser.NewPage()
if err != nil { if err != nil {
browser.Close() browser.Close()
return []byte(fmt.Sprintf(`{"error": "failed to create page: %s"}`, err.Error())) return []byte(fmt.Sprintf(`{"error": "failed to create page: %s"}`, err.Error()))
} }
browserStarted = true browserStarted = true
return []byte(`{"success": true, "message": "Browser started"}`) return []byte(`{"success": true, "message": "Browser started"}`)
} }
@@ -71,11 +59,9 @@ func pwStart(args map[string]string) []byte {
func pwStop(args map[string]string) []byte { func pwStop(args map[string]string) []byte {
browserStartMu.Lock() browserStartMu.Lock()
defer browserStartMu.Unlock() defer browserStartMu.Unlock()
if !browserStarted { if !browserStarted {
return []byte(`{"success": true, "message": "Browser was not running"}`) return []byte(`{"success": true, "message": "Browser was not running"}`)
} }
if page != nil { if page != nil {
page.Close() page.Close()
page = nil page = nil
@@ -84,7 +70,6 @@ func pwStop(args map[string]string) []byte {
browser.Close() browser.Close()
browser = nil browser = nil
} }
browserStarted = false browserStarted = false
return []byte(`{"success": true, "message": "Browser stopped"}`) return []byte(`{"success": true, "message": "Browser stopped"}`)
} }
@@ -101,16 +86,13 @@ func pwNavigate(args map[string]string) []byte {
if !ok || url == "" { if !ok || url == "" {
return []byte(`{"error": "url not provided"}`) return []byte(`{"error": "url not provided"}`)
} }
if !browserStarted || page == nil { if !browserStarted || page == nil {
return []byte(`{"error": "Browser not started. Call pw_start first."}`) return []byte(`{"error": "Browser not started. Call pw_start first."}`)
} }
_, err := page.Goto(url) _, err := page.Goto(url)
if err != nil { if err != nil {
return []byte(fmt.Sprintf(`{"error": "failed to navigate: %s"}`, err.Error())) return []byte(fmt.Sprintf(`{"error": "failed to navigate: %s"}`, err.Error()))
} }
title, _ := page.Title() title, _ := page.Title()
pageURL := page.URL() pageURL := page.URL()
return []byte(fmt.Sprintf(`{"success": true, "title": "%s", "url": "%s"}`, title, pageURL)) return []byte(fmt.Sprintf(`{"success": true, "title": "%s", "url": "%s"}`, title, pageURL))
@@ -121,31 +103,29 @@ func pwClick(args map[string]string) []byte {
if !ok || selector == "" { if !ok || selector == "" {
return []byte(`{"error": "selector not provided"}`) return []byte(`{"error": "selector not provided"}`)
} }
if !browserStarted || page == nil { if !browserStarted || page == nil {
return []byte(`{"error": "Browser not started. Call pw_start first."}`) return []byte(`{"error": "Browser not started. Call pw_start first."}`)
} }
index := 0 index := 0
if args["index"] != "" { if args["index"] != "" {
fmt.Sscanf(args["index"], "%d", &index) if i, err := strconv.Atoi(args["index"]); err != nil {
logger.Warn("failed to parse index", "value", args["index"], "error", err)
} else {
index = i
}
} }
locator := page.Locator(selector) locator := page.Locator(selector)
count, err := locator.Count() count, err := locator.Count()
if err != nil { if err != nil {
return []byte(fmt.Sprintf(`{"error": "failed to find elements: %s"}`, err.Error())) return []byte(fmt.Sprintf(`{"error": "failed to find elements: %s"}`, err.Error()))
} }
if index >= count { if index >= count {
return []byte(fmt.Sprintf(`{"error": "Element not found at index %d (found %d elements)"}`, index, count)) return []byte(fmt.Sprintf(`{"error": "Element not found at index %d (found %d elements)"}`, index, count))
} }
err = locator.Nth(index).Click() err = locator.Nth(index).Click()
if err != nil { if err != nil {
return []byte(fmt.Sprintf(`{"error": "failed to click: %s"}`, err.Error())) return []byte(fmt.Sprintf(`{"error": "failed to click: %s"}`, err.Error()))
} }
return []byte(`{"success": true, "message": "Clicked element"}`) return []byte(`{"success": true, "message": "Clicked element"}`)
} }
@@ -154,36 +134,33 @@ func pwFill(args map[string]string) []byte {
if !ok || selector == "" { if !ok || selector == "" {
return []byte(`{"error": "selector not provided"}`) return []byte(`{"error": "selector not provided"}`)
} }
text := args["text"] text := args["text"]
if text == "" { if text == "" {
text = "" text = ""
} }
if !browserStarted || page == nil { if !browserStarted || page == nil {
return []byte(`{"error": "Browser not started. Call pw_start first."}`) return []byte(`{"error": "Browser not started. Call pw_start first."}`)
} }
index := 0 index := 0
if args["index"] != "" { if args["index"] != "" {
fmt.Sscanf(args["index"], "%d", &index) if i, err := strconv.Atoi(args["index"]); err != nil {
logger.Warn("failed to parse index", "value", args["index"], "error", err)
} else {
index = i
}
} }
locator := page.Locator(selector) locator := page.Locator(selector)
count, err := locator.Count() count, err := locator.Count()
if err != nil { if err != nil {
return []byte(fmt.Sprintf(`{"error": "failed to find elements: %s"}`, err.Error())) return []byte(fmt.Sprintf(`{"error": "failed to find elements: %s"}`, err.Error()))
} }
if index >= count { if index >= count {
return []byte(fmt.Sprintf(`{"error": "Element not found at index %d"}`, index)) return []byte(fmt.Sprintf(`{"error": "Element not found at index %d"}`, index))
} }
err = locator.Nth(index).Fill(text) err = locator.Nth(index).Fill(text)
if err != nil { if err != nil {
return []byte(fmt.Sprintf(`{"error": "failed to fill: %s"}`, err.Error())) return []byte(fmt.Sprintf(`{"error": "failed to fill: %s"}`, err.Error()))
} }
return []byte(`{"success": true, "message": "Filled input"}`) return []byte(`{"success": true, "message": "Filled input"}`)
} }
@@ -192,29 +169,24 @@ func pwExtractText(args map[string]string) []byte {
if selector == "" { if selector == "" {
selector = "body" selector = "body"
} }
if !browserStarted || page == nil { if !browserStarted || page == nil {
return []byte(`{"error": "Browser not started. Call pw_start first."}`) return []byte(`{"error": "Browser not started. Call pw_start first."}`)
} }
locator := page.Locator(selector) locator := page.Locator(selector)
count, err := locator.Count() count, err := locator.Count()
if err != nil { if err != nil {
return []byte(fmt.Sprintf(`{"error": "failed to find elements: %s"}`, err.Error())) return []byte(fmt.Sprintf(`{"error": "failed to find elements: %s"}`, err.Error()))
} }
if count == 0 { if count == 0 {
return []byte(`{"error": "No elements found"}`) return []byte(`{"error": "No elements found"}`)
} }
if selector == "body" { if selector == "body" {
text, err := page.TextContent("body") text, err := page.Locator("body").TextContent()
if err != nil { if err != nil {
return []byte(fmt.Sprintf(`{"error": "failed to get text: %s"}`, err.Error())) return []byte(fmt.Sprintf(`{"error": "failed to get text: %s"}`, err.Error()))
} }
return []byte(fmt.Sprintf(`{"text": "%s"}`, text)) return []byte(fmt.Sprintf(`{"text": "%s"}`, text))
} }
var texts []string var texts []string
for i := 0; i < count; i++ { for i := 0; i < count; i++ {
text, err := locator.Nth(i).TextContent() text, err := locator.Nth(i).TextContent()
@@ -223,31 +195,27 @@ func pwExtractText(args map[string]string) []byte {
} }
texts = append(texts, text) texts = append(texts, text)
} }
return []byte(fmt.Sprintf(`{"text": "%s"}`, joinLines(texts))) return []byte(fmt.Sprintf(`{"text": "%s"}`, joinLines(texts)))
} }
func joinLines(lines []string) string { func joinLines(lines []string) string {
result := "" var sb strings.Builder
for i, line := range lines { for i, line := range lines {
if i > 0 { if i > 0 {
result += "\n" sb.WriteString("\n")
} }
result += line sb.WriteString(line)
} }
return result return sb.String()
} }
func pwScreenshot(args map[string]string) []byte { func pwScreenshot(args map[string]string) []byte {
selector := args["selector"] selector := args["selector"]
fullPage := args["full_page"] == "true" fullPage := args["full_page"] == "true"
if !browserStarted || page == nil { if !browserStarted || page == nil {
return []byte(`{"error": "Browser not started. Call pw_start first."}`) return []byte(`{"error": "Browser not started. Call pw_start first."}`)
} }
path := fmt.Sprintf("/tmp/pw_screenshot_%d.png", os.Getpid()) path := fmt.Sprintf("/tmp/pw_screenshot_%d.png", os.Getpid())
var err error var err error
if selector != "" && selector != "body" { if selector != "" && selector != "body" {
locator := page.Locator(selector) locator := page.Locator(selector)
@@ -260,24 +228,19 @@ func pwScreenshot(args map[string]string) []byte {
FullPage: playwright.Bool(fullPage), FullPage: playwright.Bool(fullPage),
}) })
} }
if err != nil { if err != nil {
return []byte(fmt.Sprintf(`{"error": "failed to take screenshot: %s"}`, err.Error())) return []byte(fmt.Sprintf(`{"error": "failed to take screenshot: %s"}`, err.Error()))
} }
return []byte(fmt.Sprintf(`{"path": "%s"}`, path)) return []byte(fmt.Sprintf(`{"path": "%s"}`, path))
} }
func pwScreenshotAndView(args map[string]string) []byte { func pwScreenshotAndView(args map[string]string) []byte {
selector := args["selector"] selector := args["selector"]
fullPage := args["full_page"] == "true" fullPage := args["full_page"] == "true"
if !browserStarted || page == nil { if !browserStarted || page == nil {
return []byte(`{"error": "Browser not started. Call pw_start first."}`) return []byte(`{"error": "Browser not started. Call pw_start first."}`)
} }
path := fmt.Sprintf("/tmp/pw_screenshot_%d.png", os.Getpid()) path := fmt.Sprintf("/tmp/pw_screenshot_%d.png", os.Getpid())
var err error var err error
if selector != "" && selector != "body" { if selector != "" && selector != "body" {
locator := page.Locator(selector) locator := page.Locator(selector)
@@ -290,16 +253,13 @@ func pwScreenshotAndView(args map[string]string) []byte {
FullPage: playwright.Bool(fullPage), FullPage: playwright.Bool(fullPage),
}) })
} }
if err != nil { if err != nil {
return []byte(fmt.Sprintf(`{"error": "failed to take screenshot: %s"}`, err.Error())) return []byte(fmt.Sprintf(`{"error": "failed to take screenshot: %s"}`, err.Error()))
} }
dataURL, err := models.CreateImageURLFromPath(path) dataURL, err := models.CreateImageURLFromPath(path)
if err != nil { if err != nil {
return []byte(fmt.Sprintf(`{"error": "failed to create image URL: %s"}`, err.Error())) return []byte(fmt.Sprintf(`{"error": "failed to create image URL: %s"}`, err.Error()))
} }
resp := models.MultimodalToolResp{ resp := models.MultimodalToolResp{
Type: "multimodal_content", Type: "multimodal_content",
Parts: []map[string]string{ Parts: []map[string]string{
@@ -319,23 +279,24 @@ func pwWaitForSelector(args map[string]string) []byte {
if !ok || selector == "" { if !ok || selector == "" {
return []byte(`{"error": "selector not provided"}`) return []byte(`{"error": "selector not provided"}`)
} }
if !browserStarted || page == nil { if !browserStarted || page == nil {
return []byte(`{"error": "Browser not started. Call pw_start first."}`) return []byte(`{"error": "Browser not started. Call pw_start first."}`)
} }
timeout := 30000 timeout := 30000
if args["timeout"] != "" { if args["timeout"] != "" {
fmt.Sscanf(args["timeout"], "%d", &timeout) if t, err := strconv.Atoi(args["timeout"]); err != nil {
logger.Warn("failed to parse timeout", "value", args["timeout"], "error", err)
} else {
timeout = t
} }
}
_, err := page.WaitForSelector(selector, playwright.PageWaitForSelectorOptions{ locator := page.Locator(selector)
err := locator.WaitFor(playwright.LocatorWaitForOptions{
Timeout: playwright.Float(float64(timeout)), Timeout: playwright.Float(float64(timeout)),
}) })
if err != nil { if err != nil {
return []byte(fmt.Sprintf(`{"error": "element not found: %s"}`, err.Error())) return []byte(fmt.Sprintf(`{"error": "element not found: %s"}`, err.Error()))
} }
return []byte(`{"success": true, "message": "Element found"}`) return []byte(`{"success": true, "message": "Element found"}`)
} }
@@ -344,58 +305,63 @@ func pwDrag(args map[string]string) []byte {
if !ok { if !ok {
return []byte(`{"error": "x1 not provided"}`) return []byte(`{"error": "x1 not provided"}`)
} }
y1, ok := args["y1"] y1, ok := args["y1"]
if !ok { if !ok {
return []byte(`{"error": "y1 not provided"}`) return []byte(`{"error": "y1 not provided"}`)
} }
x2, ok := args["x2"] x2, ok := args["x2"]
if !ok { if !ok {
return []byte(`{"error": "x2 not provided"}`) return []byte(`{"error": "x2 not provided"}`)
} }
y2, ok := args["y2"] y2, ok := args["y2"]
if !ok { if !ok {
return []byte(`{"error": "y2 not provided"}`) return []byte(`{"error": "y2 not provided"}`)
} }
if !browserStarted || page == nil { if !browserStarted || page == nil {
return []byte(`{"error": "Browser not started. Call pw_start first."}`) return []byte(`{"error": "Browser not started. Call pw_start first."}`)
} }
var fx1, fy1, fx2, fy2 float64 var fx1, fy1, fx2, fy2 float64
fmt.Sscanf(x1, "%f", &fx1) if parsedX1, err := strconv.ParseFloat(x1, 64); err != nil {
fmt.Sscanf(y1, "%f", &fy1) logger.Warn("failed to parse x1", "value", x1, "error", err)
fmt.Sscanf(x2, "%f", &fx2) } else {
fmt.Sscanf(y2, "%f", &fy2) fx1 = parsedX1
}
if parsedY1, err := strconv.ParseFloat(y1, 64); err != nil {
logger.Warn("failed to parse y1", "value", y1, "error", err)
} else {
fy1 = parsedY1
}
if parsedX2, err := strconv.ParseFloat(x2, 64); err != nil {
logger.Warn("failed to parse x2", "value", x2, "error", err)
} else {
fx2 = parsedX2
}
if parsedY2, err := strconv.ParseFloat(y2, 64); err != nil {
logger.Warn("failed to parse y2", "value", y2, "error", err)
} else {
fy2 = parsedY2
}
mouse := page.Mouse() mouse := page.Mouse()
err := mouse.Move(fx1, fy1) err := mouse.Move(fx1, fy1)
if err != nil { if err != nil {
return []byte(fmt.Sprintf(`{"error": "failed to move mouse: %s"}`, err.Error())) return []byte(fmt.Sprintf(`{"error": "failed to move mouse: %s"}`, err.Error()))
} }
err = mouse.Down() err = mouse.Down()
if err != nil { if err != nil {
return []byte(fmt.Sprintf(`{"error": "failed to mouse down: %s"}`, err.Error())) return []byte(fmt.Sprintf(`{"error": "failed to mouse down: %s"}`, err.Error()))
} }
err = mouse.Move(fx2, fy2) err = mouse.Move(fx2, fy2)
if err != nil { if err != nil {
return []byte(fmt.Sprintf(`{"error": "failed to move mouse: %s"}`, err.Error())) return []byte(fmt.Sprintf(`{"error": "failed to move mouse: %s"}`, err.Error()))
} }
err = mouse.Up() err = mouse.Up()
if err != nil { if err != nil {
return []byte(fmt.Sprintf(`{"error": "failed to mouse up: %s"}`, err.Error())) return []byte(fmt.Sprintf(`{"error": "failed to mouse up: %s"}`, err.Error()))
} }
return []byte(fmt.Sprintf(`{"success": true, "message": "Dragged from (%s,%s) to (%s,%s)"}`, x1, y1, x2, y2)) return []byte(fmt.Sprintf(`{"success": true, "message": "Dragged from (%s,%s) to (%s,%s)"}`, x1, y1, x2, y2))
} }
func init() { func init() {
browserLogger = logger.With("component", "browser") logger = logger.With("component", "browser")
checkPlaywright() checkPlaywright()
} }