813 lines
19 KiB
Go
813 lines
19 KiB
Go
package client
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"mime/multipart"
|
|
"net/http"
|
|
"os"
|
|
"strconv"
|
|
)
|
|
|
|
// Client is the client for communicating with the daemon
|
|
type Client struct {
|
|
serverURL string
|
|
}
|
|
|
|
// Command represents a command sent from the client to the daemon
|
|
type Command struct {
|
|
Action string `json:"action"`
|
|
Params map[string]string `json:"params"`
|
|
}
|
|
|
|
// Response represents a response from the daemon to the client
|
|
type Response struct {
|
|
Success bool `json:"success"`
|
|
Data interface{} `json:"data,omitempty"`
|
|
Error string `json:"error,omitempty"`
|
|
}
|
|
|
|
// NewClient creates a new client
|
|
func NewClient(host string, port int) *Client {
|
|
return &Client{
|
|
serverURL: fmt.Sprintf("http://%s:%d", host, port),
|
|
}
|
|
}
|
|
|
|
// CheckStatus checks if the daemon is running
|
|
func (c *Client) CheckStatus() (bool, error) {
|
|
resp, err := http.Get(c.serverURL + "/status")
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return false, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
|
}
|
|
|
|
var response Response
|
|
err = json.NewDecoder(resp.Body).Decode(&response)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
return response.Success, nil
|
|
}
|
|
|
|
// TabInfo contains information about a tab
|
|
type TabInfo struct {
|
|
ID string `json:"id"`
|
|
URL string `json:"url"`
|
|
IsCurrent bool `json:"is_current"`
|
|
HistoryIndex int `json:"history_index"` // Position in tab history (higher = more recent)
|
|
}
|
|
|
|
// ListTabs returns a list of all open tabs
|
|
func (c *Client) ListTabs() ([]TabInfo, error) {
|
|
resp, err := http.Get(c.serverURL + "/status")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
|
}
|
|
|
|
var response Response
|
|
err = json.NewDecoder(resp.Body).Decode(&response)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to parse response: %w", err)
|
|
}
|
|
|
|
if !response.Success {
|
|
return nil, fmt.Errorf("daemon returned error: %s", response.Error)
|
|
}
|
|
|
|
// Extract the data
|
|
data, ok := response.Data.(map[string]interface{})
|
|
if !ok {
|
|
return nil, fmt.Errorf("unexpected response format")
|
|
}
|
|
|
|
// Get the tabs
|
|
tabsData, ok := data["tabs"].(map[string]interface{})
|
|
if !ok {
|
|
return nil, fmt.Errorf("unexpected tabs format")
|
|
}
|
|
|
|
// Get the current tab
|
|
currentTab, _ := data["current_tab"].(string)
|
|
|
|
// Get the tab history
|
|
tabHistoryData, ok := data["tab_history"].([]interface{})
|
|
if !ok {
|
|
tabHistoryData = []interface{}{}
|
|
}
|
|
|
|
// Create a map of tab history indices
|
|
tabHistoryIndices := make(map[string]int)
|
|
for i, idInterface := range tabHistoryData {
|
|
id, ok := idInterface.(string)
|
|
if ok {
|
|
tabHistoryIndices[id] = i
|
|
}
|
|
}
|
|
|
|
// Convert to TabInfo
|
|
tabs := make([]TabInfo, 0, len(tabsData))
|
|
for id, urlInterface := range tabsData {
|
|
url, ok := urlInterface.(string)
|
|
if !ok {
|
|
url = "<unknown>"
|
|
}
|
|
|
|
// Get the history index (default to -1 if not in history)
|
|
historyIndex, inHistory := tabHistoryIndices[id]
|
|
if !inHistory {
|
|
historyIndex = -1
|
|
}
|
|
|
|
tabs = append(tabs, TabInfo{
|
|
ID: id,
|
|
URL: url,
|
|
IsCurrent: id == currentTab,
|
|
HistoryIndex: historyIndex,
|
|
})
|
|
}
|
|
|
|
return tabs, nil
|
|
}
|
|
|
|
// SendCommand sends a command to the daemon
|
|
func (c *Client) SendCommand(action string, params map[string]string) (*Response, error) {
|
|
cmd := Command{
|
|
Action: action,
|
|
Params: params,
|
|
}
|
|
|
|
jsonData, err := json.Marshal(cmd)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
resp, err := http.Post(c.serverURL+"/command", "application/json", bytes.NewBuffer(jsonData))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
body, _ := io.ReadAll(resp.Body)
|
|
return nil, fmt.Errorf("unexpected status code: %d, body: %s", resp.StatusCode, body)
|
|
}
|
|
|
|
var response Response
|
|
err = json.NewDecoder(resp.Body).Decode(&response)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &response, nil
|
|
}
|
|
|
|
// OpenTab opens a new tab
|
|
// timeout is in seconds, 0 means no timeout
|
|
func (c *Client) OpenTab(timeout int) (string, error) {
|
|
params := map[string]string{}
|
|
|
|
// Add timeout if specified
|
|
if timeout > 0 {
|
|
params["timeout"] = strconv.Itoa(timeout)
|
|
}
|
|
|
|
resp, err := c.SendCommand("open-tab", params)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if !resp.Success {
|
|
return "", fmt.Errorf("failed to open tab: %s", resp.Error)
|
|
}
|
|
|
|
tabID, ok := resp.Data.(string)
|
|
if !ok {
|
|
return "", fmt.Errorf("unexpected response data type")
|
|
}
|
|
|
|
return tabID, nil
|
|
}
|
|
|
|
// LoadURL loads a URL in a tab
|
|
// If tabID is empty, the current tab will be used
|
|
// timeout is in seconds, 0 means no timeout
|
|
func (c *Client) LoadURL(tabID, url string, timeout int) error {
|
|
params := map[string]string{
|
|
"url": url,
|
|
}
|
|
|
|
// Only include tab ID if it's provided
|
|
if tabID != "" {
|
|
params["tab"] = tabID
|
|
}
|
|
|
|
// Add timeout if specified
|
|
if timeout > 0 {
|
|
params["timeout"] = strconv.Itoa(timeout)
|
|
}
|
|
|
|
resp, err := c.SendCommand("load-url", params)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !resp.Success {
|
|
return fmt.Errorf("failed to load URL: %s", resp.Error)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// FillFormField fills a form field with a value
|
|
// If tabID is empty, the current tab will be used
|
|
// selectionTimeout and actionTimeout are in seconds, 0 means no timeout
|
|
func (c *Client) FillFormField(tabID, selector, value string, selectionTimeout, actionTimeout int) error {
|
|
params := map[string]string{
|
|
"selector": selector,
|
|
"value": value,
|
|
}
|
|
|
|
// Only include tab ID if it's provided
|
|
if tabID != "" {
|
|
params["tab"] = tabID
|
|
}
|
|
|
|
// Add timeouts if specified
|
|
if selectionTimeout > 0 {
|
|
params["selection-timeout"] = strconv.Itoa(selectionTimeout)
|
|
}
|
|
|
|
if actionTimeout > 0 {
|
|
params["action-timeout"] = strconv.Itoa(actionTimeout)
|
|
}
|
|
|
|
resp, err := c.SendCommand("fill-form", params)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !resp.Success {
|
|
return fmt.Errorf("failed to fill form field: %s", resp.Error)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// UploadFile uploads a file to a file input
|
|
// If tabID is empty, the current tab will be used
|
|
// selectionTimeout and actionTimeout are in seconds, 0 means no timeout
|
|
func (c *Client) UploadFile(tabID, selector, filePath string, selectionTimeout, actionTimeout int) error {
|
|
params := map[string]string{
|
|
"selector": selector,
|
|
"file": filePath,
|
|
}
|
|
|
|
// Only include tab ID if it's provided
|
|
if tabID != "" {
|
|
params["tab"] = tabID
|
|
}
|
|
|
|
// Add timeouts if specified
|
|
if selectionTimeout > 0 {
|
|
params["selection-timeout"] = strconv.Itoa(selectionTimeout)
|
|
}
|
|
|
|
if actionTimeout > 0 {
|
|
params["action-timeout"] = strconv.Itoa(actionTimeout)
|
|
}
|
|
|
|
resp, err := c.SendCommand("upload-file", params)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !resp.Success {
|
|
return fmt.Errorf("failed to upload file: %s", resp.Error)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// SubmitForm submits a form
|
|
// If tabID is empty, the current tab will be used
|
|
// selectionTimeout and actionTimeout are in seconds, 0 means no timeout
|
|
func (c *Client) SubmitForm(tabID, selector string, selectionTimeout, actionTimeout int) error {
|
|
params := map[string]string{
|
|
"selector": selector,
|
|
}
|
|
|
|
// Only include tab ID if it's provided
|
|
if tabID != "" {
|
|
params["tab"] = tabID
|
|
}
|
|
|
|
// Add timeouts if specified
|
|
if selectionTimeout > 0 {
|
|
params["selection-timeout"] = strconv.Itoa(selectionTimeout)
|
|
}
|
|
|
|
if actionTimeout > 0 {
|
|
params["action-timeout"] = strconv.Itoa(actionTimeout)
|
|
}
|
|
|
|
resp, err := c.SendCommand("submit-form", params)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !resp.Success {
|
|
return fmt.Errorf("failed to submit form: %s", resp.Error)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetPageSource gets the source code of a page
|
|
// If tabID is empty, the current tab will be used
|
|
// timeout is in seconds, 0 means no timeout
|
|
func (c *Client) GetPageSource(tabID string, timeout int) (string, error) {
|
|
params := map[string]string{}
|
|
|
|
// Only include tab ID if it's provided
|
|
if tabID != "" {
|
|
params["tab"] = tabID
|
|
}
|
|
|
|
// Add timeout if specified
|
|
if timeout > 0 {
|
|
params["timeout"] = strconv.Itoa(timeout)
|
|
}
|
|
|
|
resp, err := c.SendCommand("get-source", params)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if !resp.Success {
|
|
return "", fmt.Errorf("failed to get page source: %s", resp.Error)
|
|
}
|
|
|
|
source, ok := resp.Data.(string)
|
|
if !ok {
|
|
return "", fmt.Errorf("unexpected response data type")
|
|
}
|
|
|
|
return source, nil
|
|
}
|
|
|
|
// GetElementHTML gets the HTML of an element
|
|
// If tabID is empty, the current tab will be used
|
|
// selectionTimeout is in seconds, 0 means no timeout
|
|
func (c *Client) GetElementHTML(tabID, selector string, selectionTimeout int) (string, error) {
|
|
params := map[string]string{
|
|
"selector": selector,
|
|
}
|
|
|
|
// Only include tab ID if it's provided
|
|
if tabID != "" {
|
|
params["tab"] = tabID
|
|
}
|
|
|
|
// Add timeout if specified
|
|
if selectionTimeout > 0 {
|
|
params["selection-timeout"] = strconv.Itoa(selectionTimeout)
|
|
}
|
|
|
|
resp, err := c.SendCommand("get-element", params)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if !resp.Success {
|
|
return "", fmt.Errorf("failed to get element HTML: %s", resp.Error)
|
|
}
|
|
|
|
html, ok := resp.Data.(string)
|
|
if !ok {
|
|
return "", fmt.Errorf("unexpected response data type")
|
|
}
|
|
|
|
return html, nil
|
|
}
|
|
|
|
// CloseTab closes a tab
|
|
// If tabID is empty, the current tab will be used
|
|
// timeout is in seconds, 0 means no timeout
|
|
func (c *Client) CloseTab(tabID string, timeout int) error {
|
|
params := map[string]string{}
|
|
|
|
// Only include tab ID if it's provided
|
|
if tabID != "" {
|
|
params["tab"] = tabID
|
|
}
|
|
|
|
// Add timeout if specified
|
|
if timeout > 0 {
|
|
params["timeout"] = strconv.Itoa(timeout)
|
|
}
|
|
|
|
resp, err := c.SendCommand("close-tab", params)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !resp.Success {
|
|
return fmt.Errorf("failed to close tab: %s", resp.Error)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// WaitNavigation waits for a navigation event
|
|
// If tabID is empty, the current tab will be used
|
|
func (c *Client) WaitNavigation(tabID string, timeout int) error {
|
|
params := map[string]string{
|
|
"timeout": fmt.Sprintf("%d", timeout),
|
|
}
|
|
|
|
// Only include tab ID if it's provided
|
|
if tabID != "" {
|
|
params["tab"] = tabID
|
|
}
|
|
|
|
resp, err := c.SendCommand("wait-navigation", params)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !resp.Success {
|
|
return fmt.Errorf("failed to wait for navigation: %s", resp.Error)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// EvalJS executes JavaScript code in a tab and returns the result
|
|
// If tabID is empty, the current tab will be used
|
|
// timeout is in seconds, 0 means no timeout
|
|
func (c *Client) EvalJS(tabID, jsCode string, timeout int) (string, error) {
|
|
params := map[string]string{
|
|
"code": jsCode,
|
|
}
|
|
|
|
// Only include tab ID if it's provided
|
|
if tabID != "" {
|
|
params["tab"] = tabID
|
|
}
|
|
|
|
// Add timeout if specified
|
|
if timeout > 0 {
|
|
params["timeout"] = strconv.Itoa(timeout)
|
|
}
|
|
|
|
resp, err := c.SendCommand("eval-js", params)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if !resp.Success {
|
|
return "", fmt.Errorf("failed to execute JavaScript: %s", resp.Error)
|
|
}
|
|
|
|
// Convert result to string if it exists
|
|
if resp.Data != nil {
|
|
if result, ok := resp.Data.(string); ok {
|
|
return result, nil
|
|
}
|
|
// If it's not a string, convert it to string representation
|
|
return fmt.Sprintf("%v", resp.Data), nil
|
|
}
|
|
|
|
return "", nil
|
|
}
|
|
|
|
// TakeScreenshot takes a screenshot of a tab and saves it to a file
|
|
// If tabID is empty, the current tab will be used
|
|
// timeout is in seconds, 0 means no timeout
|
|
func (c *Client) TakeScreenshot(tabID, outputPath string, fullPage bool, timeout int) error {
|
|
params := map[string]string{
|
|
"output": outputPath,
|
|
"full-page": strconv.FormatBool(fullPage),
|
|
}
|
|
|
|
// Only include tab ID if it's provided
|
|
if tabID != "" {
|
|
params["tab"] = tabID
|
|
}
|
|
|
|
// Add timeout if specified
|
|
if timeout > 0 {
|
|
params["timeout"] = strconv.Itoa(timeout)
|
|
}
|
|
|
|
resp, err := c.SendCommand("screenshot", params)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !resp.Success {
|
|
return fmt.Errorf("failed to take screenshot: %s", resp.Error)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// SwitchToIframe switches the context to an iframe for subsequent commands
|
|
// If tabID is empty, the current tab will be used
|
|
// timeout is in seconds, 0 means no timeout
|
|
func (c *Client) SwitchToIframe(tabID, selector string, timeout int) error {
|
|
params := map[string]string{
|
|
"selector": selector,
|
|
}
|
|
|
|
// Only include tab ID if it's provided
|
|
if tabID != "" {
|
|
params["tab"] = tabID
|
|
}
|
|
|
|
// Add timeout if specified
|
|
if timeout > 0 {
|
|
params["timeout"] = strconv.Itoa(timeout)
|
|
}
|
|
|
|
resp, err := c.SendCommand("switch-iframe", params)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !resp.Success {
|
|
return fmt.Errorf("failed to switch to iframe: %s", resp.Error)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// SwitchToMain switches the context back to the main page
|
|
// If tabID is empty, the current tab will be used
|
|
func (c *Client) SwitchToMain(tabID string) error {
|
|
params := map[string]string{}
|
|
|
|
// Only include tab ID if it's provided
|
|
if tabID != "" {
|
|
params["tab"] = tabID
|
|
}
|
|
|
|
resp, err := c.SendCommand("switch-main", params)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !resp.Success {
|
|
return fmt.Errorf("failed to switch to main context: %s", resp.Error)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ClickElement clicks on an element
|
|
// If tabID is empty, the current tab will be used
|
|
// selectionTimeout and actionTimeout are in seconds, 0 means no timeout
|
|
func (c *Client) ClickElement(tabID, selector string, selectionTimeout, actionTimeout int) error {
|
|
params := map[string]string{
|
|
"selector": selector,
|
|
}
|
|
|
|
// Only include tab ID if it's provided
|
|
if tabID != "" {
|
|
params["tab"] = tabID
|
|
}
|
|
|
|
// Add timeouts if specified
|
|
if selectionTimeout > 0 {
|
|
params["selection-timeout"] = strconv.Itoa(selectionTimeout)
|
|
}
|
|
|
|
if actionTimeout > 0 {
|
|
params["action-timeout"] = strconv.Itoa(actionTimeout)
|
|
}
|
|
|
|
resp, err := c.SendCommand("click-element", params)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !resp.Success {
|
|
return fmt.Errorf("failed to click element: %s", resp.Error)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// UploadFileToContainer uploads a file from the client to the container
|
|
// localPath: path to the file on the client machine
|
|
// containerPath: optional path where to store the file in the container (defaults to /tmp/filename)
|
|
func (c *Client) UploadFileToContainer(localPath, containerPath string) (string, error) {
|
|
// Open the local file
|
|
file, err := os.Open(localPath)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to open local file: %w", err)
|
|
}
|
|
defer file.Close()
|
|
|
|
// Get file info
|
|
fileInfo, err := file.Stat()
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to get file info: %w", err)
|
|
}
|
|
|
|
// Create multipart form
|
|
var body bytes.Buffer
|
|
writer := multipart.NewWriter(&body)
|
|
|
|
// Add the file field
|
|
fileWriter, err := writer.CreateFormFile("file", fileInfo.Name())
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to create form file: %w", err)
|
|
}
|
|
|
|
_, err = io.Copy(fileWriter, file)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to copy file data: %w", err)
|
|
}
|
|
|
|
// Add the path field if specified
|
|
if containerPath != "" {
|
|
err = writer.WriteField("path", containerPath)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to write path field: %w", err)
|
|
}
|
|
}
|
|
|
|
err = writer.Close()
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to close multipart writer: %w", err)
|
|
}
|
|
|
|
// Send the request
|
|
req, err := http.NewRequest("POST", c.serverURL+"/upload", &body)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to create request: %w", err)
|
|
}
|
|
req.Header.Set("Content-Type", writer.FormDataContentType())
|
|
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to send request: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
body, _ := io.ReadAll(resp.Body)
|
|
return "", fmt.Errorf("upload failed with status %d: %s", resp.StatusCode, body)
|
|
}
|
|
|
|
// Parse the response
|
|
var response Response
|
|
err = json.NewDecoder(resp.Body).Decode(&response)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to decode response: %w", err)
|
|
}
|
|
|
|
if !response.Success {
|
|
return "", fmt.Errorf("upload failed: %s", response.Error)
|
|
}
|
|
|
|
// Extract the target path from response
|
|
data, ok := response.Data.(map[string]interface{})
|
|
if !ok {
|
|
return "", fmt.Errorf("invalid response data format")
|
|
}
|
|
|
|
targetPath, ok := data["target_path"].(string)
|
|
if !ok {
|
|
return "", fmt.Errorf("target_path not found in response")
|
|
}
|
|
|
|
return targetPath, nil
|
|
}
|
|
|
|
// DownloadFileFromContainer downloads a file from the container to the client
|
|
// containerPath: path to the file in the container
|
|
// localPath: path where to save the file on the client machine
|
|
func (c *Client) DownloadFileFromContainer(containerPath, localPath string) error {
|
|
// Create the request
|
|
url := fmt.Sprintf("%s/download?path=%s", c.serverURL, containerPath)
|
|
resp, err := http.Get(url)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to send download request: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
body, _ := io.ReadAll(resp.Body)
|
|
return fmt.Errorf("download failed with status %d: %s", resp.StatusCode, body)
|
|
}
|
|
|
|
// Create the local file
|
|
localFile, err := os.Create(localPath)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create local file: %w", err)
|
|
}
|
|
defer localFile.Close()
|
|
|
|
// Copy the response body to the local file
|
|
_, err = io.Copy(localFile, resp.Body)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to save file: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetConsoleLogs retrieves console logs from a tab
|
|
// If tabID is empty, the current tab will be used
|
|
// If clear is true, the logs will be cleared after retrieval
|
|
func (c *Client) GetConsoleLogs(tabID string, clear bool) ([]map[string]interface{}, error) {
|
|
params := map[string]string{}
|
|
|
|
// Only include tab ID if it's provided
|
|
if tabID != "" {
|
|
params["tab"] = tabID
|
|
}
|
|
|
|
// Add clear flag if specified
|
|
if clear {
|
|
params["clear"] = "true"
|
|
}
|
|
|
|
resp, err := c.SendCommand("console-logs", params)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if !resp.Success {
|
|
return nil, fmt.Errorf("failed to get console logs: %s", resp.Error)
|
|
}
|
|
|
|
// Convert response data to slice of console logs
|
|
logs, ok := resp.Data.([]interface{})
|
|
if !ok {
|
|
return nil, fmt.Errorf("unexpected response data type")
|
|
}
|
|
|
|
result := make([]map[string]interface{}, len(logs))
|
|
for i, log := range logs {
|
|
logMap, ok := log.(map[string]interface{})
|
|
if !ok {
|
|
return nil, fmt.Errorf("unexpected log entry format")
|
|
}
|
|
result[i] = logMap
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// ExecuteConsoleCommand executes a command in the browser console
|
|
// If tabID is empty, the current tab will be used
|
|
// timeout is in seconds, 0 means no timeout
|
|
func (c *Client) ExecuteConsoleCommand(tabID, command string, timeout int) (string, error) {
|
|
params := map[string]string{
|
|
"command": command,
|
|
}
|
|
|
|
// Only include tab ID if it's provided
|
|
if tabID != "" {
|
|
params["tab"] = tabID
|
|
}
|
|
|
|
// Add timeout if specified
|
|
if timeout > 0 {
|
|
params["timeout"] = strconv.Itoa(timeout)
|
|
}
|
|
|
|
resp, err := c.SendCommand("console-command", params)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if !resp.Success {
|
|
return "", fmt.Errorf("failed to execute console command: %s", resp.Error)
|
|
}
|
|
|
|
result, ok := resp.Data.(string)
|
|
if !ok {
|
|
return "", fmt.Errorf("unexpected response data type")
|
|
}
|
|
|
|
return result, nil
|
|
}
|