feat(browser_list): añade campo headless por master
browser_list ahora reporta si cada Chromium master se lanzo en modo headless, detectado por el flag de arranque (--headless / --headless=new / --headless=old) leido del cmdline. Una sola llamada devuelve navegadores activos + CDP + headless, sin tener que conectar a cada pagina para fingerprintear. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+16
-1
@@ -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."),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user