Merge quick/browser-list-headless: campo headless en browser_list

This commit is contained in:
2026-06-16 20:05:51 +02:00
2 changed files with 44 additions and 1 deletions
+16 -1
View File
@@ -55,6 +55,7 @@ type chromiumMaster struct {
UserDataDir string `json:"user_data_dir"` // value of --user-data-dir
CDPPort string `json:"cdp_port"` // value of --remote-debugging-port ("" if none)
HasCDP bool `json:"has_cdp"`
Headless bool `json:"headless"` // true if launched with --headless / --headless=new / --headless=old
}
// parseCmdline turns the raw bytes of /proc/<pid>/cmdline into argv.
@@ -154,9 +155,23 @@ func parseChromiumMaster(pid int, args []string) (chromiumMaster, bool) {
UserDataDir: udd,
CDPPort: port,
HasCDP: hasCDP,
Headless: isHeadless(args),
}, true
}
// isHeadless reports whether the process was launched in headless mode. Chromium
// spells it "--headless", "--headless=new" or "--headless=old"; matching the
// "--headless" prefix covers all three. There is no current Chromium flag that
// starts with "--headless" but means something else, so the prefix is safe.
func isHeadless(args []string) bool {
for _, a := range args {
if a == "--headless" || strings.HasPrefix(a, "--headless=") {
return true
}
}
return false
}
// firstNonEmpty returns the flag value or "" if absent.
func firstNonEmpty(args []string, name string) string {
v, _ := flagValue(args, name)
@@ -256,7 +271,7 @@ type browserListArgs struct{}
func browserListTool() mcp.Tool {
return mcp.NewTool("browser_list",
mcp.WithDescription("List the running Chromium MASTER processes (one per user-data-dir master, NOT zygote/gpu/renderer children). For each: pid, profile (--profile-directory value), user_data_dir, cdp_port (--remote-debugging-port value, empty if none), has_cdp. Returns a JSON array. Read-only."),
mcp.WithDescription("List the running Chromium MASTER processes (one per user-data-dir master, NOT zygote/gpu/renderer children). For each: pid, profile (--profile-directory value), user_data_dir, cdp_port (--remote-debugging-port value, empty if none), has_cdp, headless (true if launched with --headless). Returns a JSON array. Read-only."),
)
}
+28
View File
@@ -195,3 +195,31 @@ func TestParseCmdline(t *testing.T) {
t.Errorf("cmdline vacio debe devolver nil")
}
}
// TestIsHeadless valida la deteccion de modo headless por el flag de lanzamiento:
// --headless, --headless=new y --headless=old cuentan; su ausencia es headed.
func TestIsHeadless(t *testing.T) {
cases := []struct {
name string
args []string
want bool
}{
{"sin flag (headed)", []string{"/usr/lib/chromium/chromium", "--user-data-dir=/tmp/x"}, false},
{"--headless legacy", []string{"/usr/lib/chromium/chromium", "--headless", "--user-data-dir=/tmp/x"}, true},
{"--headless=new", []string{"/usr/lib/chromium/chromium", "--headless=new"}, true},
{"--headless=old", []string{"/usr/lib/chromium/chromium", "--headless=old"}, true},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
if got := isHeadless(c.args); got != c.want {
t.Errorf("isHeadless(%v) = %v, want %v", c.args, got, c.want)
}
})
}
// El master headed real (cmdline colapsado por espacios) debe reportar headless=false.
headed := parseCmdline([]byte("/usr/lib/chromium/chromium --remote-debugging-port=9333 --user-data-dir=/tmp/browser_mcp_userdata"))
if m, ok := parseChromiumMaster(1, headed); !ok || m.Headless {
t.Errorf("master headed: ok=%v headless=%v, want ok=true headless=false", ok, m.Headless)
}
}