526 lines
18 KiB
Go
526 lines
18 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)
|
|
|
|
// 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")
|
|
|
|
// 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)
|
|
}
|
|
}
|
|
|
|
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(" 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.")
|
|
}
|