Files
cremote/main.go
Josh at WLTechBlog a3c782eb24 bump
2025-09-30 14:11:27 -05:00

957 lines
38 KiB
Go

package main
import (
"flag"
"fmt"
"os"
"sort"
"git.teamworkapps.com/shortcut/cremote/client"
)
const Version = "2.0.0"
var (
// Global flags
daemonHost = flag.String("host", "localhost", "Daemon host")
daemonPort = flag.Int("port", 8989, "Daemon port")
)
func main() {
// Define subcommands
openTabCmd := flag.NewFlagSet("open-tab", flag.ExitOnError)
loadURLCmd := flag.NewFlagSet("load-url", flag.ExitOnError)
fillFormCmd := flag.NewFlagSet("fill-form", flag.ExitOnError)
uploadFileCmd := flag.NewFlagSet("upload-file", flag.ExitOnError)
submitFormCmd := flag.NewFlagSet("submit-form", flag.ExitOnError)
getSourceCmd := flag.NewFlagSet("get-source", flag.ExitOnError)
getElementCmd := flag.NewFlagSet("get-element", flag.ExitOnError)
clickElementCmd := flag.NewFlagSet("click-element", flag.ExitOnError)
closeTabCmd := flag.NewFlagSet("close-tab", flag.ExitOnError)
waitNavCmd := flag.NewFlagSet("wait-navigation", flag.ExitOnError)
evalJsCmd := flag.NewFlagSet("eval-js", flag.ExitOnError)
switchIframeCmd := flag.NewFlagSet("switch-iframe", flag.ExitOnError)
switchMainCmd := flag.NewFlagSet("switch-main", flag.ExitOnError)
screenshotCmd := flag.NewFlagSet("screenshot", flag.ExitOnError)
statusCmd := flag.NewFlagSet("status", flag.ExitOnError)
listTabsCmd := flag.NewFlagSet("list-tabs", flag.ExitOnError)
disableCacheCmd := flag.NewFlagSet("disable-cache", flag.ExitOnError)
enableCacheCmd := flag.NewFlagSet("enable-cache", flag.ExitOnError)
clearCacheCmd := flag.NewFlagSet("clear-cache", flag.ExitOnError)
clearAllSiteDataCmd := flag.NewFlagSet("clear-all-site-data", flag.ExitOnError)
clearCookiesCmd := flag.NewFlagSet("clear-cookies", flag.ExitOnError)
clearStorageCmd := flag.NewFlagSet("clear-storage", flag.ExitOnError)
dragAndDropCmd := flag.NewFlagSet("drag-and-drop", flag.ExitOnError)
dragAndDropCoordinatesCmd := flag.NewFlagSet("drag-and-drop-coordinates", flag.ExitOnError)
dragAndDropOffsetCmd := flag.NewFlagSet("drag-and-drop-offset", flag.ExitOnError)
// Advanced mouse operations
rightClickCmd := flag.NewFlagSet("right-click", flag.ExitOnError)
doubleClickCmd := flag.NewFlagSet("double-click", flag.ExitOnError)
middleClickCmd := flag.NewFlagSet("middle-click", flag.ExitOnError)
hoverCmd := flag.NewFlagSet("hover", flag.ExitOnError)
mouseMoveCmd := flag.NewFlagSet("mouse-move", flag.ExitOnError)
scrollWheelCmd := flag.NewFlagSet("scroll-wheel", flag.ExitOnError)
// Keyboard operations
keyCombinationCmd := flag.NewFlagSet("key-combination", flag.ExitOnError)
specialKeyCmd := flag.NewFlagSet("special-key", flag.ExitOnError)
modifierClickCmd := flag.NewFlagSet("modifier-click", flag.ExitOnError)
// Note: Touch operations and advanced scrolling/text operations are planned for future implementation
// touchTapCmd := flag.NewFlagSet("touch-tap", flag.ExitOnError)
// touchLongPressCmd := flag.NewFlagSet("touch-long-press", flag.ExitOnError)
// touchSwipeCmd := flag.NewFlagSet("touch-swipe", flag.ExitOnError)
// pinchZoomCmd := flag.NewFlagSet("pinch-zoom", flag.ExitOnError)
// scrollElementCmd := flag.NewFlagSet("scroll-element", flag.ExitOnError)
// scrollToCoordinatesCmd := flag.NewFlagSet("scroll-to-coordinates", flag.ExitOnError)
// selectTextCmd := flag.NewFlagSet("select-text", flag.ExitOnError)
// selectAllTextCmd := flag.NewFlagSet("select-all-text", flag.ExitOnError)
// Define flags for each subcommand
// open-tab flags
openTabTimeout := openTabCmd.Int("timeout", 5, "Timeout in seconds for opening the tab")
openTabHost := openTabCmd.String("host", "localhost", "Daemon host")
openTabPort := openTabCmd.Int("port", 8989, "Daemon port")
// load-url flags
loadURLTabID := loadURLCmd.String("tab", "", "Tab ID to load URL in (optional, uses current tab if not specified)")
loadURLTarget := loadURLCmd.String("url", "", "URL to load")
loadURLTimeout := loadURLCmd.Int("timeout", 5, "Timeout in seconds for loading the URL")
loadURLHost := loadURLCmd.String("host", "localhost", "Daemon host")
loadURLPort := loadURLCmd.Int("port", 8989, "Daemon port")
// fill-form flags
fillFormTabID := fillFormCmd.String("tab", "", "Tab ID to fill form in (optional, uses current tab if not specified)")
fillFormSelector := fillFormCmd.String("selector", "", "CSS selector for the input field")
fillFormValue := fillFormCmd.String("value", "", "Value to fill in the form field")
fillFormTimeout := fillFormCmd.Int("timeout", 5, "Timeout in seconds for the fill operation")
fillFormHost := fillFormCmd.String("host", "localhost", "Daemon host")
fillFormPort := fillFormCmd.Int("port", 8989, "Daemon port")
// upload-file flags
uploadFileTabID := uploadFileCmd.String("tab", "", "Tab ID to upload file in (optional, uses current tab if not specified)")
uploadFileSelector := uploadFileCmd.String("selector", "", "CSS selector for the file input")
uploadFilePath := uploadFileCmd.String("file", "", "Path to the file to upload")
uploadFileTimeout := uploadFileCmd.Int("timeout", 5, "Timeout in seconds for the upload operation")
uploadFileHost := uploadFileCmd.String("host", "localhost", "Daemon host")
uploadFilePort := uploadFileCmd.Int("port", 8989, "Daemon port")
// submit-form flags
submitFormTabID := submitFormCmd.String("tab", "", "Tab ID to submit form in (optional, uses current tab if not specified)")
submitFormSelector := submitFormCmd.String("selector", "", "CSS selector for the form")
submitFormTimeout := submitFormCmd.Int("timeout", 5, "Timeout in seconds for the submit operation")
submitFormHost := submitFormCmd.String("host", "localhost", "Daemon host")
submitFormPort := submitFormCmd.Int("port", 8989, "Daemon port")
// get-source flags
getSourceTabID := getSourceCmd.String("tab", "", "Tab ID to get source from (optional, uses current tab if not specified)")
getSourceTimeout := getSourceCmd.Int("timeout", 5, "Timeout in seconds for getting the page source")
getSourceHost := getSourceCmd.String("host", "localhost", "Daemon host")
getSourcePort := getSourceCmd.Int("port", 8989, "Daemon port")
// get-element flags
getElementTabID := getElementCmd.String("tab", "", "Tab ID to get element from (optional, uses current tab if not specified)")
getElementSelector := getElementCmd.String("selector", "", "CSS selector for the element")
getElementTimeout := getElementCmd.Int("timeout", 5, "Timeout in seconds for finding the element")
getElementHost := getElementCmd.String("host", "localhost", "Daemon host")
getElementPort := getElementCmd.Int("port", 8989, "Daemon port")
// click-element flags
clickElementTabID := clickElementCmd.String("tab", "", "Tab ID to click element in (optional, uses current tab if not specified)")
clickElementSelector := clickElementCmd.String("selector", "", "CSS selector for the element to click")
clickElementTimeout := clickElementCmd.Int("timeout", 5, "Timeout in seconds for the click operation")
clickElementHost := clickElementCmd.String("host", "localhost", "Daemon host")
clickElementPort := clickElementCmd.Int("port", 8989, "Daemon port")
// close-tab flags
closeTabID := closeTabCmd.String("tab", "", "Tab ID to close (optional, uses current tab if not specified)")
closeTabTimeout := closeTabCmd.Int("timeout", 5, "Timeout in seconds for closing the tab")
closeTabHost := closeTabCmd.String("host", "localhost", "Daemon host")
closeTabPort := closeTabCmd.Int("port", 8989, "Daemon port")
// wait-navigation flags
waitNavTabID := waitNavCmd.String("tab", "", "Tab ID to wait for navigation (optional, uses current tab if not specified)")
waitNavTimeout := waitNavCmd.Int("timeout", 5, "Timeout in seconds")
waitNavHost := waitNavCmd.String("host", "localhost", "Daemon host")
waitNavPort := waitNavCmd.Int("port", 8989, "Daemon port")
// eval-js flags
evalJsTabID := evalJsCmd.String("tab", "", "Tab ID to execute JavaScript in (optional, uses current tab if not specified)")
evalJsCode := evalJsCmd.String("code", "", "JavaScript code to execute")
evalJsTimeout := evalJsCmd.Int("timeout", 5, "Timeout in seconds for JavaScript execution")
evalJsHost := evalJsCmd.String("host", "localhost", "Daemon host")
evalJsPort := evalJsCmd.Int("port", 8989, "Daemon port")
// switch-iframe flags
switchIframeTabID := switchIframeCmd.String("tab", "", "Tab ID to switch iframe context in (optional, uses current tab if not specified)")
switchIframeSelector := switchIframeCmd.String("selector", "", "CSS selector for the iframe element")
switchIframeTimeout := switchIframeCmd.Int("timeout", 5, "Timeout in seconds for iframe switching")
switchIframeHost := switchIframeCmd.String("host", "localhost", "Daemon host")
switchIframePort := switchIframeCmd.Int("port", 8989, "Daemon port")
// switch-main flags
switchMainTabID := switchMainCmd.String("tab", "", "Tab ID to switch back to main context (optional, uses current tab if not specified)")
switchMainTimeout := switchMainCmd.Int("timeout", 5, "Timeout in seconds for switching back to main context")
switchMainHost := switchMainCmd.String("host", "localhost", "Daemon host")
switchMainPort := switchMainCmd.Int("port", 8989, "Daemon port")
// screenshot flags
screenshotTabID := screenshotCmd.String("tab", "", "Tab ID to take screenshot of (optional, uses current tab if not specified)")
screenshotOutput := screenshotCmd.String("output", "", "Output file path for the screenshot (PNG format)")
screenshotFullPage := screenshotCmd.Bool("full-page", false, "Capture full page screenshot (default: viewport only)")
screenshotTimeout := screenshotCmd.Int("timeout", 5, "Timeout in seconds for taking the screenshot")
screenshotHost := screenshotCmd.String("host", "localhost", "Daemon host")
screenshotPort := screenshotCmd.Int("port", 8989, "Daemon port")
// status flags
statusHost := statusCmd.String("host", "localhost", "Daemon host")
statusPort := statusCmd.Int("port", 8989, "Daemon port")
// list-tabs flags
listTabsHost := listTabsCmd.String("host", "localhost", "Daemon host")
listTabsPort := listTabsCmd.Int("port", 8989, "Daemon port")
// disable-cache flags
disableCacheTabID := disableCacheCmd.String("tab", "", "Tab ID to disable cache for (optional, uses current tab if not specified)")
disableCacheTimeout := disableCacheCmd.Int("timeout", 5, "Timeout in seconds for disabling cache")
disableCacheHost := disableCacheCmd.String("host", "localhost", "Daemon host")
disableCachePort := disableCacheCmd.Int("port", 8989, "Daemon port")
// enable-cache flags
enableCacheTabID := enableCacheCmd.String("tab", "", "Tab ID to enable cache for (optional, uses current tab if not specified)")
enableCacheTimeout := enableCacheCmd.Int("timeout", 5, "Timeout in seconds for enabling cache")
enableCacheHost := enableCacheCmd.String("host", "localhost", "Daemon host")
enableCachePort := enableCacheCmd.Int("port", 8989, "Daemon port")
// clear-cache flags
clearCacheTabID := clearCacheCmd.String("tab", "", "Tab ID to clear cache for (optional, uses current tab if not specified)")
clearCacheTimeout := clearCacheCmd.Int("timeout", 5, "Timeout in seconds for clearing cache")
clearCacheHost := clearCacheCmd.String("host", "localhost", "Daemon host")
clearCachePort := clearCacheCmd.Int("port", 8989, "Daemon port")
// clear-all-site-data flags
clearAllSiteDataTabID := clearAllSiteDataCmd.String("tab", "", "Tab ID to clear all site data for (optional, uses current tab if not specified)")
clearAllSiteDataTimeout := clearAllSiteDataCmd.Int("timeout", 5, "Timeout in seconds for clearing all site data")
clearAllSiteDataHost := clearAllSiteDataCmd.String("host", "localhost", "Daemon host")
clearAllSiteDataPort := clearAllSiteDataCmd.Int("port", 8989, "Daemon port")
// clear-cookies flags
clearCookiesTabID := clearCookiesCmd.String("tab", "", "Tab ID to clear cookies for (optional, uses current tab if not specified)")
clearCookiesTimeout := clearCookiesCmd.Int("timeout", 5, "Timeout in seconds for clearing cookies")
clearCookiesHost := clearCookiesCmd.String("host", "localhost", "Daemon host")
clearCookiesPort := clearCookiesCmd.Int("port", 8989, "Daemon port")
// clear-storage flags
clearStorageTabID := clearStorageCmd.String("tab", "", "Tab ID to clear storage for (optional, uses current tab if not specified)")
clearStorageTimeout := clearStorageCmd.Int("timeout", 5, "Timeout in seconds for clearing storage")
clearStorageHost := clearStorageCmd.String("host", "localhost", "Daemon host")
clearStoragePort := clearStorageCmd.Int("port", 8989, "Daemon port")
// drag-and-drop flags
dragAndDropTabID := dragAndDropCmd.String("tab", "", "Tab ID to perform drag and drop on (optional, uses current tab if not specified)")
dragAndDropSource := dragAndDropCmd.String("source", "", "CSS selector for the source element to drag")
dragAndDropTarget := dragAndDropCmd.String("target", "", "CSS selector for the target element to drop on")
dragAndDropTimeout := dragAndDropCmd.Int("timeout", 5, "Timeout in seconds for drag and drop operation")
dragAndDropHost := dragAndDropCmd.String("host", "localhost", "Daemon host")
dragAndDropPort := dragAndDropCmd.Int("port", 8989, "Daemon port")
// drag-and-drop-coordinates flags
dragAndDropCoordinatesTabID := dragAndDropCoordinatesCmd.String("tab", "", "Tab ID to perform drag and drop on (optional, uses current tab if not specified)")
dragAndDropCoordinatesSource := dragAndDropCoordinatesCmd.String("source", "", "CSS selector for the source element to drag")
dragAndDropCoordinatesX := dragAndDropCoordinatesCmd.Int("x", 0, "Target X coordinate")
dragAndDropCoordinatesY := dragAndDropCoordinatesCmd.Int("y", 0, "Target Y coordinate")
dragAndDropCoordinatesTimeout := dragAndDropCoordinatesCmd.Int("timeout", 5, "Timeout in seconds for drag and drop operation")
dragAndDropCoordinatesHost := dragAndDropCoordinatesCmd.String("host", "localhost", "Daemon host")
dragAndDropCoordinatesPort := dragAndDropCoordinatesCmd.Int("port", 8989, "Daemon port")
// drag-and-drop-offset flags
dragAndDropOffsetTabID := dragAndDropOffsetCmd.String("tab", "", "Tab ID to perform drag and drop on (optional, uses current tab if not specified)")
dragAndDropOffsetSource := dragAndDropOffsetCmd.String("source", "", "CSS selector for the source element to drag")
dragAndDropOffsetX := dragAndDropOffsetCmd.Int("offset-x", 0, "Horizontal offset in pixels")
dragAndDropOffsetY := dragAndDropOffsetCmd.Int("offset-y", 0, "Vertical offset in pixels")
dragAndDropOffsetTimeout := dragAndDropOffsetCmd.Int("timeout", 5, "Timeout in seconds for drag and drop operation")
dragAndDropOffsetHost := dragAndDropOffsetCmd.String("host", "localhost", "Daemon host")
dragAndDropOffsetPort := dragAndDropOffsetCmd.Int("port", 8989, "Daemon port")
// right-click flags
rightClickTabID := rightClickCmd.String("tab", "", "Tab ID to perform right-click on (optional, uses current tab if not specified)")
rightClickSelector := rightClickCmd.String("selector", "", "CSS selector for the element to right-click")
rightClickTimeout := rightClickCmd.Int("timeout", 5, "Timeout in seconds for right-click operation")
rightClickHost := rightClickCmd.String("host", "localhost", "Daemon host")
rightClickPort := rightClickCmd.Int("port", 8989, "Daemon port")
// double-click flags
doubleClickTabID := doubleClickCmd.String("tab", "", "Tab ID to perform double-click on (optional, uses current tab if not specified)")
doubleClickSelector := doubleClickCmd.String("selector", "", "CSS selector for the element to double-click")
doubleClickTimeout := doubleClickCmd.Int("timeout", 5, "Timeout in seconds for double-click operation")
doubleClickHost := doubleClickCmd.String("host", "localhost", "Daemon host")
doubleClickPort := doubleClickCmd.Int("port", 8989, "Daemon port")
// middle-click flags
middleClickTabID := middleClickCmd.String("tab", "", "Tab ID to perform middle-click on (optional, uses current tab if not specified)")
middleClickSelector := middleClickCmd.String("selector", "", "CSS selector for the element to middle-click")
middleClickTimeout := middleClickCmd.Int("timeout", 5, "Timeout in seconds for middle-click operation")
middleClickHost := middleClickCmd.String("host", "localhost", "Daemon host")
middleClickPort := middleClickCmd.Int("port", 8989, "Daemon port")
// hover flags
hoverTabID := hoverCmd.String("tab", "", "Tab ID to perform hover on (optional, uses current tab if not specified)")
hoverSelector := hoverCmd.String("selector", "", "CSS selector for the element to hover over")
hoverTimeout := hoverCmd.Int("timeout", 5, "Timeout in seconds for hover operation")
hoverHost := hoverCmd.String("host", "localhost", "Daemon host")
hoverPort := hoverCmd.Int("port", 8989, "Daemon port")
// mouse-move flags
mouseMoveTabID := mouseMoveCmd.String("tab", "", "Tab ID to move mouse on (optional, uses current tab if not specified)")
mouseMoveX := mouseMoveCmd.Int("x", 0, "X coordinate to move mouse to")
mouseMoveY := mouseMoveCmd.Int("y", 0, "Y coordinate to move mouse to")
mouseMoveTimeout := mouseMoveCmd.Int("timeout", 5, "Timeout in seconds for mouse move operation")
mouseMoveHost := mouseMoveCmd.String("host", "localhost", "Daemon host")
mouseMovePort := mouseMoveCmd.Int("port", 8989, "Daemon port")
// scroll-wheel flags
scrollWheelTabID := scrollWheelCmd.String("tab", "", "Tab ID to scroll on (optional, uses current tab if not specified)")
scrollWheelX := scrollWheelCmd.Int("x", 0, "X coordinate for scroll wheel")
scrollWheelY := scrollWheelCmd.Int("y", 0, "Y coordinate for scroll wheel")
scrollWheelDeltaX := scrollWheelCmd.Int("delta-x", 0, "Horizontal scroll delta (negative = left, positive = right)")
scrollWheelDeltaY := scrollWheelCmd.Int("delta-y", 0, "Vertical scroll delta (negative = up, positive = down)")
scrollWheelTimeout := scrollWheelCmd.Int("timeout", 5, "Timeout in seconds for scroll wheel operation")
scrollWheelHost := scrollWheelCmd.String("host", "localhost", "Daemon host")
scrollWheelPort := scrollWheelCmd.Int("port", 8989, "Daemon port")
// key-combination flags
keyCombinationTabID := keyCombinationCmd.String("tab", "", "Tab ID to send key combination to (optional, uses current tab if not specified)")
keyCombinationKeys := keyCombinationCmd.String("keys", "", "Key combination to send (e.g., 'Ctrl+C', 'Alt+Tab', 'Shift+Enter')")
keyCombinationTimeout := keyCombinationCmd.Int("timeout", 5, "Timeout in seconds for key combination operation")
keyCombinationHost := keyCombinationCmd.String("host", "localhost", "Daemon host")
keyCombinationPort := keyCombinationCmd.Int("port", 8989, "Daemon port")
// special-key flags
specialKeyTabID := specialKeyCmd.String("tab", "", "Tab ID to send special key to (optional, uses current tab if not specified)")
specialKeyKey := specialKeyCmd.String("key", "", "Special key to send (e.g., 'Enter', 'Escape', 'Tab', 'F1', 'ArrowUp')")
specialKeyTimeout := specialKeyCmd.Int("timeout", 5, "Timeout in seconds for special key operation")
specialKeyHost := specialKeyCmd.String("host", "localhost", "Daemon host")
specialKeyPort := specialKeyCmd.Int("port", 8989, "Daemon port")
// modifier-click flags
modifierClickTabID := modifierClickCmd.String("tab", "", "Tab ID to perform modifier click on (optional, uses current tab if not specified)")
modifierClickSelector := modifierClickCmd.String("selector", "", "CSS selector for the element to click")
modifierClickModifiers := modifierClickCmd.String("modifiers", "", "Modifier keys (e.g., 'Ctrl', 'Shift', 'Alt', 'Ctrl+Shift')")
modifierClickTimeout := modifierClickCmd.Int("timeout", 5, "Timeout in seconds for modifier click operation")
modifierClickHost := modifierClickCmd.String("host", "localhost", "Daemon host")
modifierClickPort := modifierClickCmd.Int("port", 8989, "Daemon port")
// Note: Touch operations flags will be added when the functionality is implemented
// touchTapTabID := touchTapCmd.String("tab", "", "Tab ID to perform touch tap on (optional, uses current tab if not specified)")
// ... (other touch operation flags)
// Check if a subcommand is provided
if len(os.Args) < 2 {
printUsage()
os.Exit(1)
}
// Parse the appropriate subcommand
switch os.Args[1] {
case "version":
fmt.Printf("cremote CLI version %s\n", Version)
// Also get daemon version if possible
c := client.NewClient("localhost", 8989)
daemonVersion, err := c.GetVersion()
if err != nil {
fmt.Printf("Daemon: Unable to connect (%v)\n", err)
} else {
fmt.Printf("Daemon version %s\n", daemonVersion)
}
return
case "open-tab":
openTabCmd.Parse(os.Args[2:])
// Create a client
c := client.NewClient(*openTabHost, *openTabPort)
// Open a new tab
tabID, err := c.OpenTab(*openTabTimeout)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
// Print the tab ID
fmt.Println(tabID)
case "load-url":
loadURLCmd.Parse(os.Args[2:])
if *loadURLTarget == "" {
fmt.Println("Error: url flag is required")
loadURLCmd.PrintDefaults()
os.Exit(1)
}
// Create a client
c := client.NewClient(*loadURLHost, *loadURLPort)
// Load the URL
err := c.LoadURL(*loadURLTabID, *loadURLTarget, *loadURLTimeout)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
fmt.Println("URL loaded successfully")
case "fill-form":
fillFormCmd.Parse(os.Args[2:])
if *fillFormSelector == "" || *fillFormValue == "" {
fmt.Println("Error: selector and value flags are required")
fillFormCmd.PrintDefaults()
os.Exit(1)
}
// Create a client
c := client.NewClient(*fillFormHost, *fillFormPort)
// Fill the form field
err := c.FillFormField(*fillFormTabID, *fillFormSelector, *fillFormValue, *fillFormTimeout)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
fmt.Println("Form field filled successfully")
case "upload-file":
uploadFileCmd.Parse(os.Args[2:])
if *uploadFileSelector == "" || *uploadFilePath == "" {
fmt.Println("Error: selector and file flags are required")
uploadFileCmd.PrintDefaults()
os.Exit(1)
}
// Create a client
c := client.NewClient(*uploadFileHost, *uploadFilePort)
// Check if the local file exists
if _, err := os.Stat(*uploadFilePath); os.IsNotExist(err) {
fmt.Fprintf(os.Stderr, "Error: Local file does not exist: %s\n", *uploadFilePath)
os.Exit(1)
}
// First, transfer the file to the daemon container
fmt.Printf("Transferring file to daemon container: %s\n", *uploadFilePath)
containerPath, err := c.UploadFileToContainer(*uploadFilePath, "")
if err != nil {
fmt.Fprintf(os.Stderr, "Error transferring file to container: %v\n", err)
os.Exit(1)
}
fmt.Printf("File transferred to container: %s\n", containerPath)
// Then upload the file to the web form using the container path
err = c.UploadFile(*uploadFileTabID, *uploadFileSelector, containerPath, *uploadFileTimeout)
if err != nil {
fmt.Fprintf(os.Stderr, "Error uploading file to web form: %v\n", err)
os.Exit(1)
}
fmt.Println("File uploaded to web form successfully")
case "submit-form":
submitFormCmd.Parse(os.Args[2:])
if *submitFormSelector == "" {
fmt.Println("Error: selector flag is required")
submitFormCmd.PrintDefaults()
os.Exit(1)
}
// Create a client
c := client.NewClient(*submitFormHost, *submitFormPort)
// Submit the form
err := c.SubmitForm(*submitFormTabID, *submitFormSelector, *submitFormTimeout)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
fmt.Println("Form submitted successfully")
case "get-source":
getSourceCmd.Parse(os.Args[2:])
// Create a client
c := client.NewClient(*getSourceHost, *getSourcePort)
// Get the page source
source, err := c.GetPageSource(*getSourceTabID, *getSourceTimeout)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
// Print the source
fmt.Println(source)
case "get-element":
getElementCmd.Parse(os.Args[2:])
if *getElementSelector == "" {
fmt.Println("Error: selector flag is required")
getElementCmd.PrintDefaults()
os.Exit(1)
}
// Create a client
c := client.NewClient(*getElementHost, *getElementPort)
// Get the element HTML
html, err := c.GetElementHTML(*getElementTabID, *getElementSelector, *getElementTimeout)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
// Print the HTML
fmt.Println(html)
case "click-element":
clickElementCmd.Parse(os.Args[2:])
if *clickElementSelector == "" {
fmt.Println("Error: selector flag is required")
clickElementCmd.PrintDefaults()
os.Exit(1)
}
// Create a client
c := client.NewClient(*clickElementHost, *clickElementPort)
// Click the element
err := c.ClickElement(*clickElementTabID, *clickElementSelector, *clickElementTimeout)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
fmt.Println("Element clicked successfully")
case "close-tab":
closeTabCmd.Parse(os.Args[2:])
// Create a client
c := client.NewClient(*closeTabHost, *closeTabPort)
// Close the tab
err := c.CloseTab(*closeTabID, *closeTabTimeout)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
fmt.Println("Tab closed successfully")
case "wait-navigation":
waitNavCmd.Parse(os.Args[2:])
// Create a client
c := client.NewClient(*waitNavHost, *waitNavPort)
// Wait for navigation
err := c.WaitNavigation(*waitNavTabID, *waitNavTimeout)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
fmt.Println("Navigation completed")
case "eval-js":
evalJsCmd.Parse(os.Args[2:])
if *evalJsCode == "" {
fmt.Println("Error: code flag is required")
evalJsCmd.PrintDefaults()
os.Exit(1)
}
// Create a client
c := client.NewClient(*evalJsHost, *evalJsPort)
// Execute JavaScript
result, err := c.EvalJS(*evalJsTabID, *evalJsCode, *evalJsTimeout)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
// Print the result if there is one
if result != "" {
fmt.Println(result)
}
case "switch-iframe":
switchIframeCmd.Parse(os.Args[2:])
if *switchIframeSelector == "" {
fmt.Println("Error: selector flag is required")
switchIframeCmd.PrintDefaults()
os.Exit(1)
}
// Create a client
c := client.NewClient(*switchIframeHost, *switchIframePort)
// Switch to iframe
err := c.SwitchToIframe(*switchIframeTabID, *switchIframeSelector, *switchIframeTimeout)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
fmt.Println("Switched to iframe context")
case "switch-main":
switchMainCmd.Parse(os.Args[2:])
// Create a client
c := client.NewClient(*switchMainHost, *switchMainPort)
// Switch back to main page
err := c.SwitchToMain(*switchMainTabID, *switchMainTimeout)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
fmt.Println("Switched to main page context")
case "screenshot":
screenshotCmd.Parse(os.Args[2:])
if *screenshotOutput == "" {
fmt.Println("Error: output flag is required")
screenshotCmd.PrintDefaults()
os.Exit(1)
}
// Create a client
c := client.NewClient(*screenshotHost, *screenshotPort)
// Take screenshot
err := c.TakeScreenshot(*screenshotTabID, *screenshotOutput, *screenshotFullPage, *screenshotTimeout)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
fmt.Printf("Screenshot saved to: %s\n", *screenshotOutput)
case "status":
statusCmd.Parse(os.Args[2:])
// Create a client
c := client.NewClient(*statusHost, *statusPort)
// Check daemon status
running, err := c.CheckStatus()
if err != nil {
// If we can't connect, the daemon is not running
fmt.Println("Daemon is not running")
os.Exit(0)
}
if running {
fmt.Println("Daemon is running")
} else {
fmt.Println("Daemon is not running")
os.Exit(0)
}
case "list-tabs":
listTabsCmd.Parse(os.Args[2:])
// Create a client
c := client.NewClient(*listTabsHost, *listTabsPort)
// List tabs
tabs, err := c.ListTabs()
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
// Sort tabs by history index (most recent first)
sort.Slice(tabs, func(i, j int) bool {
return tabs[i].HistoryIndex > tabs[j].HistoryIndex
})
// Print the tabs
if len(tabs) == 0 {
fmt.Println("No tabs open")
} else {
fmt.Println("Open tabs (in order of recent use):")
for _, tab := range tabs {
currentMarker := " "
if tab.IsCurrent {
currentMarker = "*"
}
fmt.Printf("%s %s: %s\n", currentMarker, tab.ID, tab.URL)
}
}
case "disable-cache":
disableCacheCmd.Parse(os.Args[2:])
// Create a client
c := client.NewClient(*disableCacheHost, *disableCachePort)
// Disable cache
err := c.DisableCache(*disableCacheTabID, *disableCacheTimeout)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
fmt.Println("Cache disabled successfully")
case "enable-cache":
enableCacheCmd.Parse(os.Args[2:])
// Create a client
c := client.NewClient(*enableCacheHost, *enableCachePort)
// Enable cache
err := c.EnableCache(*enableCacheTabID, *enableCacheTimeout)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
fmt.Println("Cache enabled successfully")
case "clear-cache":
clearCacheCmd.Parse(os.Args[2:])
// Create a client
c := client.NewClient(*clearCacheHost, *clearCachePort)
// Clear cache
err := c.ClearCache(*clearCacheTabID, *clearCacheTimeout)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
fmt.Println("Cache cleared successfully")
case "clear-all-site-data":
clearAllSiteDataCmd.Parse(os.Args[2:])
// Create a client
c := client.NewClient(*clearAllSiteDataHost, *clearAllSiteDataPort)
// Clear all site data
err := c.ClearAllSiteData(*clearAllSiteDataTabID, *clearAllSiteDataTimeout)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
fmt.Println("All site data cleared successfully")
case "clear-cookies":
clearCookiesCmd.Parse(os.Args[2:])
// Create a client
c := client.NewClient(*clearCookiesHost, *clearCookiesPort)
// Clear cookies
err := c.ClearCookies(*clearCookiesTabID, *clearCookiesTimeout)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
fmt.Println("Cookies cleared successfully")
case "clear-storage":
clearStorageCmd.Parse(os.Args[2:])
// Create a client
c := client.NewClient(*clearStorageHost, *clearStoragePort)
// Clear storage
err := c.ClearStorage(*clearStorageTabID, *clearStorageTimeout)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
fmt.Println("Storage cleared successfully")
case "drag-and-drop":
dragAndDropCmd.Parse(os.Args[2:])
// Validate required parameters
if *dragAndDropSource == "" {
fmt.Fprintf(os.Stderr, "Error: --source parameter is required\n")
os.Exit(1)
}
if *dragAndDropTarget == "" {
fmt.Fprintf(os.Stderr, "Error: --target parameter is required\n")
os.Exit(1)
}
// Create a client
c := client.NewClient(*dragAndDropHost, *dragAndDropPort)
// Perform drag and drop
err := c.DragAndDrop(*dragAndDropTabID, *dragAndDropSource, *dragAndDropTarget, *dragAndDropTimeout)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
fmt.Println("Drag and drop completed successfully")
case "drag-and-drop-coordinates":
dragAndDropCoordinatesCmd.Parse(os.Args[2:])
// Validate required parameters
if *dragAndDropCoordinatesSource == "" {
fmt.Fprintf(os.Stderr, "Error: --source parameter is required\n")
os.Exit(1)
}
// Create a client
c := client.NewClient(*dragAndDropCoordinatesHost, *dragAndDropCoordinatesPort)
// Perform drag and drop to coordinates
err := c.DragAndDropToCoordinates(*dragAndDropCoordinatesTabID, *dragAndDropCoordinatesSource, *dragAndDropCoordinatesX, *dragAndDropCoordinatesY, *dragAndDropCoordinatesTimeout)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
fmt.Printf("Drag and drop to coordinates (%d, %d) completed successfully\n", *dragAndDropCoordinatesX, *dragAndDropCoordinatesY)
case "drag-and-drop-offset":
dragAndDropOffsetCmd.Parse(os.Args[2:])
// Validate required parameters
if *dragAndDropOffsetSource == "" {
fmt.Fprintf(os.Stderr, "Error: --source parameter is required\n")
os.Exit(1)
}
// Create a client
c := client.NewClient(*dragAndDropOffsetHost, *dragAndDropOffsetPort)
// Perform drag and drop by offset
err := c.DragAndDropByOffset(*dragAndDropOffsetTabID, *dragAndDropOffsetSource, *dragAndDropOffsetX, *dragAndDropOffsetY, *dragAndDropOffsetTimeout)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
fmt.Printf("Drag and drop by offset (%d, %d) completed successfully\n", *dragAndDropOffsetX, *dragAndDropOffsetY)
case "right-click":
rightClickCmd.Parse(os.Args[2:])
c := client.NewClient(*rightClickHost, *rightClickPort)
err := c.RightClick(*rightClickTabID, *rightClickSelector, *rightClickTimeout)
if err != nil {
fmt.Printf("Error: %v\n", err)
os.Exit(1)
}
fmt.Printf("Right-click completed successfully\n")
case "double-click":
doubleClickCmd.Parse(os.Args[2:])
c := client.NewClient(*doubleClickHost, *doubleClickPort)
err := c.DoubleClick(*doubleClickTabID, *doubleClickSelector, *doubleClickTimeout)
if err != nil {
fmt.Printf("Error: %v\n", err)
os.Exit(1)
}
fmt.Printf("Double-click completed successfully\n")
case "middle-click":
middleClickCmd.Parse(os.Args[2:])
c := client.NewClient(*middleClickHost, *middleClickPort)
err := c.MiddleClick(*middleClickTabID, *middleClickSelector, *middleClickTimeout)
if err != nil {
fmt.Printf("Error: %v\n", err)
os.Exit(1)
}
fmt.Printf("Middle-click completed successfully\n")
case "hover":
hoverCmd.Parse(os.Args[2:])
c := client.NewClient(*hoverHost, *hoverPort)
err := c.Hover(*hoverTabID, *hoverSelector, *hoverTimeout)
if err != nil {
fmt.Printf("Error: %v\n", err)
os.Exit(1)
}
fmt.Printf("Hover completed successfully\n")
case "mouse-move":
mouseMoveCmd.Parse(os.Args[2:])
c := client.NewClient(*mouseMoveHost, *mouseMovePort)
err := c.MouseMove(*mouseMoveTabID, *mouseMoveX, *mouseMoveY, *mouseMoveTimeout)
if err != nil {
fmt.Printf("Error: %v\n", err)
os.Exit(1)
}
fmt.Printf("Mouse move to (%d, %d) completed successfully\n", *mouseMoveX, *mouseMoveY)
case "scroll-wheel":
scrollWheelCmd.Parse(os.Args[2:])
c := client.NewClient(*scrollWheelHost, *scrollWheelPort)
err := c.ScrollWheel(*scrollWheelTabID, *scrollWheelX, *scrollWheelY, *scrollWheelDeltaX, *scrollWheelDeltaY, *scrollWheelTimeout)
if err != nil {
fmt.Printf("Error: %v\n", err)
os.Exit(1)
}
fmt.Printf("Scroll wheel at (%d, %d) with delta (%d, %d) completed successfully\n", *scrollWheelX, *scrollWheelY, *scrollWheelDeltaX, *scrollWheelDeltaY)
case "key-combination":
keyCombinationCmd.Parse(os.Args[2:])
c := client.NewClient(*keyCombinationHost, *keyCombinationPort)
err := c.KeyCombination(*keyCombinationTabID, *keyCombinationKeys, *keyCombinationTimeout)
if err != nil {
fmt.Printf("Error: %v\n", err)
os.Exit(1)
}
fmt.Printf("Key combination '%s' sent successfully\n", *keyCombinationKeys)
case "special-key":
specialKeyCmd.Parse(os.Args[2:])
c := client.NewClient(*specialKeyHost, *specialKeyPort)
err := c.SpecialKey(*specialKeyTabID, *specialKeyKey, *specialKeyTimeout)
if err != nil {
fmt.Printf("Error: %v\n", err)
os.Exit(1)
}
fmt.Printf("Special key '%s' sent successfully\n", *specialKeyKey)
case "modifier-click":
modifierClickCmd.Parse(os.Args[2:])
c := client.NewClient(*modifierClickHost, *modifierClickPort)
err := c.ModifierClick(*modifierClickTabID, *modifierClickSelector, *modifierClickModifiers, *modifierClickTimeout)
if err != nil {
fmt.Printf("Error: %v\n", err)
os.Exit(1)
}
fmt.Printf("Modifier click with '%s' completed successfully\n", *modifierClickModifiers)
default:
printUsage()
os.Exit(1)
}
}
func printUsage() {
fmt.Println("Usage: cremote <command> [options]")
fmt.Println("Commands:")
fmt.Println(" version Show version information for CLI and daemon")
fmt.Println(" open-tab Open a new tab and return its ID")
fmt.Println(" load-url Load a URL in a tab")
fmt.Println(" fill-form Fill a form field with a value")
fmt.Println(" upload-file Transfer a file to daemon container and upload to a file input")
fmt.Println(" submit-form Submit a form")
fmt.Println(" get-source Get the source code of a page")
fmt.Println(" get-element Get the HTML of an element")
fmt.Println(" click-element Click on an element")
fmt.Println(" close-tab Close a tab")
fmt.Println(" wait-navigation Wait for a navigation event")
fmt.Println(" eval-js Execute JavaScript code in a tab")
fmt.Println(" switch-iframe Switch to iframe context for subsequent commands")
fmt.Println(" switch-main Switch back to main page context")
fmt.Println(" screenshot Take a screenshot of the current page")
fmt.Println(" list-tabs List all open tabs")
fmt.Println(" disable-cache Disable browser cache for a tab")
fmt.Println(" enable-cache Enable browser cache for a tab")
fmt.Println(" clear-cache Clear browser cache for a tab")
fmt.Println(" clear-all-site-data Clear all site data (cookies, storage, cache, etc.)")
fmt.Println(" clear-cookies Clear cookies for a tab")
fmt.Println(" clear-storage Clear web storage (localStorage, sessionStorage, etc.)")
fmt.Println(" drag-and-drop Drag and drop from source element to target element")
fmt.Println(" drag-and-drop-coordinates Drag and drop from source element to specific coordinates")
fmt.Println(" drag-and-drop-offset Drag and drop from source element by relative offset")
fmt.Println(" right-click Right-click on an element")
fmt.Println(" double-click Double-click on an element")
fmt.Println(" middle-click Middle-click on an element")
fmt.Println(" hover Hover over an element")
fmt.Println(" mouse-move Move mouse to specific coordinates")
fmt.Println(" scroll-wheel Scroll with mouse wheel at specific coordinates")
fmt.Println(" key-combination Send key combinations (Ctrl+C, Alt+Tab, etc.)")
fmt.Println(" special-key Send special keys (Enter, Escape, Tab, F1, etc.)")
fmt.Println(" modifier-click Click with modifier keys (Ctrl+click, Shift+click)")
fmt.Println(" status Check if the daemon is running")
fmt.Println("\nRun 'cremote <command> -h' for more information on a command")
fmt.Println("\nBefore using this tool, make sure the daemon is running:")
fmt.Println(" cremotedaemon")
fmt.Println("\nThe daemon requires Chromium/Chrome to be running with remote debugging enabled:")
fmt.Println(" chromium --remote-debugging-port=9222 --user-data-dir=/tmp/chromium-debug")
fmt.Println("\nNote: Most commands can use the current tab if you don't specify a tab ID.")
}