real-browser-cli 0.16.4__tar.gz → 0.16.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/CLAUDE.md +1 -1
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/PKG-INFO +92 -2
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/README.md +88 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/client/core.py +11 -8
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/client/messages.py +2 -2
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/commands/extract.py +25 -19
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/commands/page.py +5 -4
- real_browser_cli-0.16.8/browser_cli/mcp/__init__.py +5 -0
- real_browser_cli-0.16.8/browser_cli/mcp/naming.py +39 -0
- real_browser_cli-0.16.8/browser_cli/mcp/serialization.py +19 -0
- real_browser_cli-0.16.8/browser_cli/mcp/server.py +292 -0
- real_browser_cli-0.16.8/browser_cli/mcp/targets.py +16 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/remote/pool.py +23 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/remote/registry.py +26 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/sdk/dom.py +25 -23
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/serve/security.py +30 -1
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/manifest.json +1 -1
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/src/classes/JobManager.ts +10 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/src/commands/dom.ts +2 -2
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/src/commands/perf.ts +15 -11
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/src/commands/windows.ts +29 -2
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/test/jobs.test.ts +16 -1
- real_browser_cli-0.16.8/extension/test/windows.test.ts +69 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/n8n-nodes-browser-cli/package-lock.json +1385 -292
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/n8n-nodes-browser-cli/package.json +6 -6
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/n8n-nodes-browser-cli/tsconfig.json +2 -2
- real_browser_cli-0.16.8/package-lock.json +917 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/package.json +3 -3
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/pyproject.toml +5 -1
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/tests/conftest.py +12 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/tests/test_api.py +14 -3
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/tests/test_cli.py +52 -13
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/tests/test_client.py +19 -17
- real_browser_cli-0.16.8/tests/test_mcp.py +305 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/tests/test_remote_pool.py +33 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/tests/test_remote_registry.py +17 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/tests/test_serve_security.py +17 -0
- real_browser_cli-0.16.8/uv.lock +1559 -0
- real_browser_cli-0.16.4/package-lock.json +0 -556
- real_browser_cli-0.16.4/uv.lock +0 -690
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/.claude/settings.local.json +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/.gitea/workflows/package-extension.yml +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/.gitea/workflows/publish.yml +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/.gitea/workflows/testing.yml +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/.gitignore +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/.python-version +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/LICENSE +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/PRIVACY.md +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/__init__.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/async_sdk.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/auth/__init__.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/auth/agent.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/auth/keys.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/auth/pq.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/auth/server_identity.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/auth/signing.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/cli.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/client/__init__.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/client/auth.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/client/targets.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/command_security.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/commands/__init__.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/commands/auth.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/commands/clients.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/commands/completion.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/commands/doctor.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/commands/dom.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/commands/events.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/commands/extension.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/commands/groups.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/commands/install.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/commands/navigate.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/commands/perf.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/commands/raw.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/commands/remote.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/commands/rendering/__init__.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/commands/rendering/common.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/commands/rendering/labels.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/commands/rendering/tabs_tree.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/commands/rendering/windows_tree.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/commands/script.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/commands/search.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/commands/serve.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/commands/serve_http.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/commands/session.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/commands/storage.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/commands/tabs.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/commands/watch.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/commands/windows.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/commands/workspace.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/compat/__init__.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/compat/auth.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/compat/commands.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/constants.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/endpoints.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/errors.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/framing.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/local_transport.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/markdown/__init__.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/markdown/html.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/markdown/render.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/models.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/native/__init__.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/native/host.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/native/local_server.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/native/protocol.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/platform.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/registry.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/remote/__init__.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/remote/auth.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/remote/known_hosts.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/remote/socket.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/remote/transport.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/sdk/__init__.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/sdk/base.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/sdk/browser_data.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/sdk/decorators.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/sdk/extension.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/sdk/factories.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/sdk/groups.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/sdk/navigation.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/sdk/perf.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/sdk/routing.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/sdk/session.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/sdk/tabs.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/sdk/windows.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/sdk/workflow_decorators.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/search/__init__.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/search/engines.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/serve/__init__.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/serve/auth.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/serve/challenge.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/serve/control.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/serve/logging.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/serve/proxy.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/serve/runtime.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/transport/__init__.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/transport/binary.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/transport/codecs.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/browser_cli/version_manager.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/examples/demo.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/examples/demo.sh +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/content.js +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/icon.svg +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/icons/icon-128.png +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/icons/icon-16.png +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/icons/icon-32.png +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/icons/icon-48.png +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/src/browser-api.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/src/classes/CommandGroup.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/src/classes/CommandRegistry.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/src/classes/NativeConnection.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/src/commands/autosave.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/src/commands/browser-data.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/src/commands/extension.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/src/commands/groups.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/src/commands/injected.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/src/commands/navigation.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/src/commands/session-snapshot.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/src/commands/session.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/src/commands/tabs-query.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/src/commands/tabs.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/src/content/dispatch.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/src/content/dom-ops.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/src/content/extract.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/src/content/markdown/code.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/src/content/markdown/index.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/src/content/markdown/renderer.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/src/content/markdown/root.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/src/content/markdown/utils.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/src/content/page-info.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/src/content-dispatch.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/src/core/errors.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/src/core/group-helpers.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/src/core/index.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/src/core/scripting.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/src/core/storage.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/src/core/tab-groups.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/src/core/tab-helpers.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/src/core/throttle.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/src/index.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/src/types/command-args.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/src/types/index.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/src/types/jobs.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/src/types/json.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/src/types/messages.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/src/types/session.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/src/types/tabs.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/src/types/webextension.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/test/autosave.test.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/test/browser-api.test.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/test/chrome-mock.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/test/clients.test.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/test/navigation.test.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/test/paging.test.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/extension/test/url-match.test.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/justfile +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/n8n-nodes-browser-cli/.claude/settings.local.json +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/n8n-nodes-browser-cli/.gitignore +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/n8n-nodes-browser-cli/README.md +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/n8n-nodes-browser-cli/credentials/BrowserCliApi.credentials.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/n8n-nodes-browser-cli/nodes/BrowserCli/BrowserCli.node.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/n8n-nodes-browser-cli/nodes/BrowserCli/browserCli.svg +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/n8n-nodes-browser-cli/nodes/BrowserCli/protocol.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/n8n-nodes-browser-cli/nodes/BrowserCli/request.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/n8n-nodes-browser-cli/nodes/BrowserCli/serveClient.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/n8n-nodes-browser-cli/scripts/copy-assets.mjs +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/n8n-nodes-browser-cli/scripts/run-tests.mjs +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/n8n-nodes-browser-cli/test/protocol.test.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/n8n-nodes-browser-cli/test/request.test.ts +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/n8n-nodes-browser-cli/test-dist/protocol.test.mjs +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/n8n-nodes-browser-cli/test-dist/request.test.mjs +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/scripts/gen_extension_key.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/scripts/package_extension.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/scripts/package_verified_crx.sh +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/scripts/setup_verified_crx_key.sh +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/shell.nix +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/tests/test_auth.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/tests/test_commands_cli.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/tests/test_compat.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/tests/test_dom.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/tests/test_extension_error_page_handling.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/tests/test_extension_packaging.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/tests/test_extract.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/tests/test_framing.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/tests/test_groups.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/tests/test_known_hosts.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/tests/test_markdown_security.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/tests/test_native_host.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/tests/test_nav.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/tests/test_new_feature_commands.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/tests/test_page.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/tests/test_perf.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/tests/test_performance_integration.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/tests/test_platform.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/tests/test_refactor_boundaries.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/tests/test_registry.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/tests/test_remote_protocol_matrix.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/tests/test_rendering.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/tests/test_serve.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/tests/test_session.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/tests/test_storage.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/tests/test_tabs.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/tests/test_transport.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/tests/test_windows.py +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/tsconfig.json +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/webstore-assets/screenshots/screenshot-1.png +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/webstore-assets/screenshots/screenshot-2.png +0 -0
- {real_browser_cli-0.16.4 → real_browser_cli-0.16.8}/webstore-assets/screenshots/screenshot-3.png +0 -0
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
## Memory laden
|
|
4
4
|
Beim Start dieser Session: Memory-Datei lesen für vollständigen Codebase-Kontext:
|
|
5
5
|
```
|
|
6
|
-
|
|
6
|
+
03 Bereiche/Open Source & Side Projects/browser-cli.md
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
Memory nach Änderungen aktualisieren (neue Commands, Architektur-Änderungen, neue Patterns, Version-Bumps).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: real-browser-cli
|
|
3
|
-
Version: 0.16.
|
|
3
|
+
Version: 0.16.8
|
|
4
4
|
Summary: Control your real running browser from the terminal or Python SDK
|
|
5
5
|
Project-URL: Homepage, https://git.yiprawr.dev/Automatisation/browser-cli
|
|
6
6
|
Project-URL: Repository, https://git.yiprawr.dev/Automatisation/browser-cli
|
|
@@ -90,6 +90,8 @@ Requires-Dist: questionary>=2
|
|
|
90
90
|
Requires-Dist: rich>=13
|
|
91
91
|
Provides-Extra: fast
|
|
92
92
|
Requires-Dist: zstandard>=0.22; extra == 'fast'
|
|
93
|
+
Provides-Extra: mcp
|
|
94
|
+
Requires-Dist: mcp<3,>=2; extra == 'mcp'
|
|
93
95
|
Description-Content-Type: text/markdown
|
|
94
96
|
|
|
95
97
|
# browser-cli
|
|
@@ -164,6 +166,11 @@ For better remote-response compression, install the optional `fast` extra:
|
|
|
164
166
|
uv tool install "real-browser-cli[fast]"
|
|
165
167
|
```
|
|
166
168
|
|
|
169
|
+
To expose the conservative MCP tool surface, install the optional `mcp` extra:
|
|
170
|
+
```sh
|
|
171
|
+
uv tool install "real-browser-cli[mcp]"
|
|
172
|
+
```
|
|
173
|
+
|
|
167
174
|
To upgrade later:
|
|
168
175
|
|
|
169
176
|
```sh
|
|
@@ -235,6 +242,89 @@ browser-cli/
|
|
|
235
242
|
|
|
236
243
|
---
|
|
237
244
|
|
|
245
|
+
## Stateless MCP server
|
|
246
|
+
The optional MCP adapter exposes a small, typed subset of the Python SDK for
|
|
247
|
+
MCP hosts such as Claude Desktop, Claude Code, Cursor, or VS Code. It controls
|
|
248
|
+
the same real browser; it does not launch a headless browser or duplicate the
|
|
249
|
+
browser command implementation.
|
|
250
|
+
|
|
251
|
+
Install and run the local stdio server:
|
|
252
|
+
```sh
|
|
253
|
+
uv tool install "real-browser-cli[mcp]"
|
|
254
|
+
browser-cli-mcp
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Example MCP host configuration:
|
|
258
|
+
```json
|
|
259
|
+
{
|
|
260
|
+
"mcpServers": {
|
|
261
|
+
"browser-cli": {
|
|
262
|
+
"command": "browser-cli-mcp"
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
The server is stateless at the MCP layer. Every tool call creates a fresh
|
|
269
|
+
`BrowserCLI` SDK client, and browser state remains in the real browser. Pass
|
|
270
|
+
`browser`, `remote`, and `key` on a tool call when a specific local profile or
|
|
271
|
+
authenticated browser-cli remote is required.
|
|
272
|
+
|
|
273
|
+
`browser_navigate`, `browser_tabs_close`, and `browser_screenshot` take an
|
|
274
|
+
optional `tab_id` and act on the active tab when it is omitted, so a caller
|
|
275
|
+
does not need a preceding `browser_tabs_list` round trip. `browser_tabs_close`
|
|
276
|
+
reports the tab it closed.
|
|
277
|
+
|
|
278
|
+
Available tools:
|
|
279
|
+
- `browser_tabs_list`, `browser_tabs_open`, `browser_tabs_close`
|
|
280
|
+
- `browser_navigate`, `browser_page_info`
|
|
281
|
+
- `browser_extract_text`, `browser_extract_markdown`
|
|
282
|
+
- `browser_dom_query`, `browser_dom_click`, `browser_dom_type`
|
|
283
|
+
- `browser_screenshot`
|
|
284
|
+
|
|
285
|
+
Generic JavaScript evaluation, raw browser commands, storage writes, and
|
|
286
|
+
session import are intentionally not exposed.
|
|
287
|
+
|
|
288
|
+
### Pinning one browser and naming tools
|
|
289
|
+
Set `BROWSER_CLI_PROFILE` to pin every call from an MCP server to one browser,
|
|
290
|
+
so tools do not have to pass `browser` and listing tools do not fan out across
|
|
291
|
+
all connected browsers:
|
|
292
|
+
```json
|
|
293
|
+
{
|
|
294
|
+
"mcpServers": {
|
|
295
|
+
"browser-cli-testing": {
|
|
296
|
+
"command": "browser-cli-mcp",
|
|
297
|
+
"env": {
|
|
298
|
+
"BROWSER_CLI_PROFILE": "testing",
|
|
299
|
+
"BROWSER_CLI_MCP_TOOL_PREFIX": ""
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
`BROWSER_CLI_REMOTE` and `BROWSER_CLI_KEY` pin an authenticated remote the same
|
|
306
|
+
way. Explicit `browser`, `remote`, and `key` tool arguments still win.
|
|
307
|
+
|
|
308
|
+
Tool names carry a `browser_` prefix by default so they stay unambiguous in
|
|
309
|
+
hosts that expose raw MCP tool names. Hosts that already prefix tools with the
|
|
310
|
+
server name produce stutter such as `browser_cli_testing_browser_tabs_list`;
|
|
311
|
+
setting `BROWSER_CLI_MCP_TOOL_PREFIX` to an empty string drops the built-in
|
|
312
|
+
prefix and yields `browser_cli_testing_tabs_list`. Any other value replaces the
|
|
313
|
+
prefix.
|
|
314
|
+
|
|
315
|
+
For local development and testing, Streamable HTTP is also available:
|
|
316
|
+
```sh
|
|
317
|
+
browser-cli-mcp --transport streamable-http --port 8000
|
|
318
|
+
# endpoint: http://127.0.0.1:8000/mcp
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
HTTP uses stateless JSON responses and intentionally refuses non-loopback bind
|
|
322
|
+
addresses because this MCP endpoint has no independent authentication. For a
|
|
323
|
+
browser on another machine, keep MCP local and pass an authenticated
|
|
324
|
+
browser-cli `remote` target to each tool call.
|
|
325
|
+
|
|
326
|
+
---
|
|
327
|
+
|
|
238
328
|
## CLI reference
|
|
239
329
|
During source development, commands are usually run as `uv run browser-cli [--browser ALIAS] <command>`. After tool installation, use `browser-cli ...` directly. Add `--remote HOST[:PORT]` and optionally `--key PATH` to target a browser exposed by `browser-cli serve`.
|
|
240
330
|
|
|
@@ -70,6 +70,11 @@ For better remote-response compression, install the optional `fast` extra:
|
|
|
70
70
|
uv tool install "real-browser-cli[fast]"
|
|
71
71
|
```
|
|
72
72
|
|
|
73
|
+
To expose the conservative MCP tool surface, install the optional `mcp` extra:
|
|
74
|
+
```sh
|
|
75
|
+
uv tool install "real-browser-cli[mcp]"
|
|
76
|
+
```
|
|
77
|
+
|
|
73
78
|
To upgrade later:
|
|
74
79
|
|
|
75
80
|
```sh
|
|
@@ -141,6 +146,89 @@ browser-cli/
|
|
|
141
146
|
|
|
142
147
|
---
|
|
143
148
|
|
|
149
|
+
## Stateless MCP server
|
|
150
|
+
The optional MCP adapter exposes a small, typed subset of the Python SDK for
|
|
151
|
+
MCP hosts such as Claude Desktop, Claude Code, Cursor, or VS Code. It controls
|
|
152
|
+
the same real browser; it does not launch a headless browser or duplicate the
|
|
153
|
+
browser command implementation.
|
|
154
|
+
|
|
155
|
+
Install and run the local stdio server:
|
|
156
|
+
```sh
|
|
157
|
+
uv tool install "real-browser-cli[mcp]"
|
|
158
|
+
browser-cli-mcp
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Example MCP host configuration:
|
|
162
|
+
```json
|
|
163
|
+
{
|
|
164
|
+
"mcpServers": {
|
|
165
|
+
"browser-cli": {
|
|
166
|
+
"command": "browser-cli-mcp"
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
The server is stateless at the MCP layer. Every tool call creates a fresh
|
|
173
|
+
`BrowserCLI` SDK client, and browser state remains in the real browser. Pass
|
|
174
|
+
`browser`, `remote`, and `key` on a tool call when a specific local profile or
|
|
175
|
+
authenticated browser-cli remote is required.
|
|
176
|
+
|
|
177
|
+
`browser_navigate`, `browser_tabs_close`, and `browser_screenshot` take an
|
|
178
|
+
optional `tab_id` and act on the active tab when it is omitted, so a caller
|
|
179
|
+
does not need a preceding `browser_tabs_list` round trip. `browser_tabs_close`
|
|
180
|
+
reports the tab it closed.
|
|
181
|
+
|
|
182
|
+
Available tools:
|
|
183
|
+
- `browser_tabs_list`, `browser_tabs_open`, `browser_tabs_close`
|
|
184
|
+
- `browser_navigate`, `browser_page_info`
|
|
185
|
+
- `browser_extract_text`, `browser_extract_markdown`
|
|
186
|
+
- `browser_dom_query`, `browser_dom_click`, `browser_dom_type`
|
|
187
|
+
- `browser_screenshot`
|
|
188
|
+
|
|
189
|
+
Generic JavaScript evaluation, raw browser commands, storage writes, and
|
|
190
|
+
session import are intentionally not exposed.
|
|
191
|
+
|
|
192
|
+
### Pinning one browser and naming tools
|
|
193
|
+
Set `BROWSER_CLI_PROFILE` to pin every call from an MCP server to one browser,
|
|
194
|
+
so tools do not have to pass `browser` and listing tools do not fan out across
|
|
195
|
+
all connected browsers:
|
|
196
|
+
```json
|
|
197
|
+
{
|
|
198
|
+
"mcpServers": {
|
|
199
|
+
"browser-cli-testing": {
|
|
200
|
+
"command": "browser-cli-mcp",
|
|
201
|
+
"env": {
|
|
202
|
+
"BROWSER_CLI_PROFILE": "testing",
|
|
203
|
+
"BROWSER_CLI_MCP_TOOL_PREFIX": ""
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
`BROWSER_CLI_REMOTE` and `BROWSER_CLI_KEY` pin an authenticated remote the same
|
|
210
|
+
way. Explicit `browser`, `remote`, and `key` tool arguments still win.
|
|
211
|
+
|
|
212
|
+
Tool names carry a `browser_` prefix by default so they stay unambiguous in
|
|
213
|
+
hosts that expose raw MCP tool names. Hosts that already prefix tools with the
|
|
214
|
+
server name produce stutter such as `browser_cli_testing_browser_tabs_list`;
|
|
215
|
+
setting `BROWSER_CLI_MCP_TOOL_PREFIX` to an empty string drops the built-in
|
|
216
|
+
prefix and yields `browser_cli_testing_tabs_list`. Any other value replaces the
|
|
217
|
+
prefix.
|
|
218
|
+
|
|
219
|
+
For local development and testing, Streamable HTTP is also available:
|
|
220
|
+
```sh
|
|
221
|
+
browser-cli-mcp --transport streamable-http --port 8000
|
|
222
|
+
# endpoint: http://127.0.0.1:8000/mcp
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
HTTP uses stateless JSON responses and intentionally refuses non-loopback bind
|
|
226
|
+
addresses because this MCP endpoint has no independent authentication. For a
|
|
227
|
+
browser on another machine, keep MCP local and pass an authenticated
|
|
228
|
+
browser-cli `remote` target to each tool call.
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
144
232
|
## CLI reference
|
|
145
233
|
During source development, commands are usually run as `uv run browser-cli [--browser ALIAS] <command>`. After tool installation, use `browser-cli ...` directly. Add `--remote HOST[:PORT]` and optionally `--key PATH` to target a browser exposed by `browser-cli serve`.
|
|
146
234
|
|
|
@@ -151,21 +151,25 @@ def active_browser_targets(*, include_remotes: bool = True, key=None, suppress_p
|
|
|
151
151
|
targets.extend(_remote_browser_targets(key=key, suppress_pq_warning=suppress_pq_warning))
|
|
152
152
|
return targets
|
|
153
153
|
|
|
154
|
-
def _cached_client_row(target: BrowserTarget) -> dict | None:
|
|
154
|
+
def _cached_client_row(target: BrowserTarget, *, scoped: bool = False) -> dict | None:
|
|
155
155
|
"""Build a clients row from a target's discovery data, skipping a roundtrip.
|
|
156
156
|
|
|
157
157
|
Returns None when the remote didn't advertise its version (older serve), so
|
|
158
|
-
callers fall back to an explicit ``clients.list`` query.
|
|
158
|
+
callers fall back to an explicit ``clients.list`` query. When *scoped* is
|
|
159
|
+
true, the caller already selected one remote host, so render profile-only
|
|
160
|
+
labels instead of adding a host group header.
|
|
159
161
|
"""
|
|
160
162
|
if target.version is None and target.extension_version is None:
|
|
161
163
|
return None
|
|
162
|
-
|
|
163
|
-
"profile": target.display_name,
|
|
164
|
-
"profileGroup": target.display_group,
|
|
164
|
+
row = {
|
|
165
|
+
"profile": target.profile if scoped else target.display_name,
|
|
165
166
|
"name": target.browser_name or "",
|
|
166
167
|
"version": target.version or "",
|
|
167
168
|
"extensionVersion": target.extension_version or "",
|
|
168
169
|
}
|
|
170
|
+
if target.display_group and not scoped:
|
|
171
|
+
row["profileGroup"] = target.display_group
|
|
172
|
+
return row
|
|
169
173
|
|
|
170
174
|
def _rows_from_result(result, label: str, profile_group: str | None) -> list[dict]:
|
|
171
175
|
rows = []
|
|
@@ -249,18 +253,17 @@ def collect_browser_clients(
|
|
|
249
253
|
if targets:
|
|
250
254
|
uncached = []
|
|
251
255
|
for target in targets:
|
|
252
|
-
cached = _cached_client_row(target)
|
|
256
|
+
cached = _cached_client_row(target, scoped=True)
|
|
253
257
|
if cached is not None:
|
|
254
258
|
rows.append(cached)
|
|
255
259
|
else:
|
|
256
260
|
uncached.append(target)
|
|
257
261
|
results = _run_concurrent([
|
|
258
262
|
(lambda t=t: _client_rows_async(
|
|
259
|
-
t.
|
|
263
|
+
t.profile,
|
|
260
264
|
profile=t.profile,
|
|
261
265
|
remote=remote,
|
|
262
266
|
key=key,
|
|
263
|
-
profile_group=t.display_group,
|
|
264
267
|
))
|
|
265
268
|
for t in uncached
|
|
266
269
|
])
|
|
@@ -5,8 +5,8 @@ import uuid
|
|
|
5
5
|
from typing import Any
|
|
6
6
|
|
|
7
7
|
from browser_cli import transport
|
|
8
|
-
from browser_cli.endpoints import _normalize_endpoint
|
|
9
8
|
from browser_cli.errors import BrowserNotConnected
|
|
9
|
+
from browser_cli.remote.registry import resolve_remote_endpoint
|
|
10
10
|
|
|
11
11
|
def base_message(command: str, args: dict | None) -> dict:
|
|
12
12
|
return {"id": str(uuid.uuid4()), "command": command, "args": args or {}}
|
|
@@ -14,7 +14,7 @@ def base_message(command: str, args: dict | None) -> dict:
|
|
|
14
14
|
def requested_target(profile: str | None, remote: str | None) -> tuple[str | None, str | None]:
|
|
15
15
|
requested_profile = profile or os.environ.get("BROWSER_CLI_PROFILE")
|
|
16
16
|
remote_endpoint = remote or os.environ.get("BROWSER_CLI_REMOTE")
|
|
17
|
-
return requested_profile,
|
|
17
|
+
return requested_profile, resolve_remote_endpoint(remote_endpoint) if remote_endpoint else None
|
|
18
18
|
|
|
19
19
|
def encode_payload(msg: dict) -> bytes:
|
|
20
20
|
return json.dumps(msg).encode("utf-8")
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import json
|
|
2
2
|
|
|
3
3
|
import click
|
|
4
|
-
from browser_cli.commands import client_from_ctx, handle_errors
|
|
4
|
+
from browser_cli.commands import client_from_ctx, handle_errors, tab_option
|
|
5
5
|
from rich.console import Console
|
|
6
6
|
from rich.table import Table
|
|
7
7
|
|
|
@@ -12,10 +12,11 @@ def extract_group():
|
|
|
12
12
|
"""Extract content from the active tab."""
|
|
13
13
|
|
|
14
14
|
@extract_group.command("links")
|
|
15
|
+
@tab_option
|
|
15
16
|
@handle_errors
|
|
16
|
-
def extract_links():
|
|
17
|
-
"""Extract all links from the active tab."""
|
|
18
|
-
links = client_from_ctx().extract.links()
|
|
17
|
+
def extract_links(tab_id):
|
|
18
|
+
"""Extract all links from the active tab or --tab."""
|
|
19
|
+
links = client_from_ctx().extract.links(tab_id)
|
|
19
20
|
if not links:
|
|
20
21
|
console.print("[yellow]No links found[/yellow]")
|
|
21
22
|
return
|
|
@@ -27,10 +28,11 @@ def extract_links():
|
|
|
27
28
|
console.print(table)
|
|
28
29
|
|
|
29
30
|
@extract_group.command("images")
|
|
31
|
+
@tab_option
|
|
30
32
|
@handle_errors
|
|
31
|
-
def extract_images():
|
|
32
|
-
"""Extract all images from the active tab."""
|
|
33
|
-
images = client_from_ctx().extract.images()
|
|
33
|
+
def extract_images(tab_id):
|
|
34
|
+
"""Extract all images from the active tab or --tab."""
|
|
35
|
+
images = client_from_ctx().extract.images(tab_id)
|
|
34
36
|
if not images:
|
|
35
37
|
console.print("[yellow]No images found[/yellow]")
|
|
36
38
|
return
|
|
@@ -42,29 +44,33 @@ def extract_images():
|
|
|
42
44
|
console.print(table)
|
|
43
45
|
|
|
44
46
|
@extract_group.command("text")
|
|
47
|
+
@tab_option
|
|
45
48
|
@handle_errors
|
|
46
|
-
def extract_text():
|
|
47
|
-
"""Extract all visible text from the active tab."""
|
|
48
|
-
console.print(client_from_ctx().extract.text())
|
|
49
|
+
def extract_text(tab_id):
|
|
50
|
+
"""Extract all visible text from the active tab or --tab."""
|
|
51
|
+
console.print(client_from_ctx().extract.text(tab_id))
|
|
49
52
|
|
|
50
53
|
@extract_group.command("json")
|
|
51
54
|
@click.argument("selector")
|
|
55
|
+
@tab_option
|
|
52
56
|
@handle_errors
|
|
53
|
-
def extract_json(selector):
|
|
54
|
-
"""Parse and pretty-print JSON content inside SELECTOR."""
|
|
55
|
-
data = client_from_ctx().extract.json(selector)
|
|
57
|
+
def extract_json(selector, tab_id):
|
|
58
|
+
"""Parse and pretty-print JSON content inside SELECTOR in the active tab or --tab."""
|
|
59
|
+
data = client_from_ctx().extract.json(selector, tab_id)
|
|
56
60
|
console.print_json(json.dumps(data))
|
|
57
61
|
|
|
58
62
|
@extract_group.command("html")
|
|
63
|
+
@tab_option
|
|
59
64
|
@handle_errors
|
|
60
|
-
def extract_html():
|
|
61
|
-
"""Print the full HTML of the active tab to stdout."""
|
|
62
|
-
click.echo(client_from_ctx().extract.html())
|
|
65
|
+
def extract_html(tab_id):
|
|
66
|
+
"""Print the full HTML of the active tab or --tab to stdout."""
|
|
67
|
+
click.echo(client_from_ctx().extract.html(tab_id))
|
|
63
68
|
|
|
64
69
|
@extract_group.command("markdown")
|
|
65
70
|
@click.option("--selector", help="Extract only the DOM subtree matching this CSS selector.")
|
|
71
|
+
@tab_option
|
|
66
72
|
@handle_errors
|
|
67
|
-
def extract_markdown(selector):
|
|
68
|
-
"""Extract the page's main content as Markdown."""
|
|
69
|
-
markdown = client_from_ctx().extract.markdown(selector)
|
|
73
|
+
def extract_markdown(selector, tab_id):
|
|
74
|
+
"""Extract the page's main content as Markdown from the active tab or --tab."""
|
|
75
|
+
markdown = client_from_ctx().extract.markdown(selector, tab_id)
|
|
70
76
|
click.echo(markdown or "", nl=not (markdown or "").endswith("\n"))
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import click
|
|
2
|
-
from browser_cli.commands import client_from_ctx, handle_errors
|
|
2
|
+
from browser_cli.commands import client_from_ctx, handle_errors, tab_option
|
|
3
3
|
from rich.console import Console
|
|
4
4
|
from rich.table import Table
|
|
5
5
|
|
|
@@ -10,10 +10,11 @@ def page_group():
|
|
|
10
10
|
"""Inspect current page metadata."""
|
|
11
11
|
|
|
12
12
|
@page_group.command("info")
|
|
13
|
+
@tab_option
|
|
13
14
|
@handle_errors
|
|
14
|
-
def page_info():
|
|
15
|
-
"""Show title, URL, readyState, language, and meta tags of the active tab."""
|
|
16
|
-
info = client_from_ctx().page.info()
|
|
15
|
+
def page_info(tab_id):
|
|
16
|
+
"""Show title, URL, readyState, language, and meta tags of the active tab or --tab."""
|
|
17
|
+
info = client_from_ctx().page.info(tab_id)
|
|
17
18
|
table = Table(show_header=False)
|
|
18
19
|
table.add_column("Field", style="bold cyan", no_wrap=True)
|
|
19
20
|
table.add_column("Value")
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""Tool-name prefixing for the MCP surface.
|
|
2
|
+
|
|
3
|
+
Hosts differ in how they namespace MCP tools. Hosts that already prefix tool
|
|
4
|
+
names with the server name turn the built-in ``browser_`` prefix into stutter
|
|
5
|
+
(``browser_cli_testing_browser_tabs_list``), while hosts that expose raw names
|
|
6
|
+
need the prefix to keep ``navigate`` or ``screenshot`` unambiguous. The prefix
|
|
7
|
+
is therefore configurable per MCP server process.
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import os
|
|
12
|
+
import re
|
|
13
|
+
|
|
14
|
+
TOOL_PREFIX_ENV = "BROWSER_CLI_MCP_TOOL_PREFIX"
|
|
15
|
+
DEFAULT_TOOL_PREFIX = "browser_"
|
|
16
|
+
|
|
17
|
+
_VALID_PREFIX = re.compile(r"\A[a-z][a-z0-9_]*\Z")
|
|
18
|
+
|
|
19
|
+
def resolve_tool_prefix(environ: dict[str, str] | None = None) -> str:
|
|
20
|
+
"""Return the configured tool-name prefix, defaulting to ``browser_``.
|
|
21
|
+
|
|
22
|
+
An empty value disables prefixing for hosts that namespace tools themselves.
|
|
23
|
+
"""
|
|
24
|
+
configured = (environ if environ is not None else os.environ).get(TOOL_PREFIX_ENV)
|
|
25
|
+
if configured is None:
|
|
26
|
+
return DEFAULT_TOOL_PREFIX
|
|
27
|
+
prefix = configured.strip()
|
|
28
|
+
if not prefix:
|
|
29
|
+
return ""
|
|
30
|
+
if not _VALID_PREFIX.match(prefix):
|
|
31
|
+
raise ValueError(
|
|
32
|
+
f"{TOOL_PREFIX_ENV} must be lowercase letters, digits, and underscores starting "
|
|
33
|
+
f"with a letter, or empty to disable prefixing; got {configured!r}"
|
|
34
|
+
)
|
|
35
|
+
return prefix if prefix.endswith("_") else f"{prefix}_"
|
|
36
|
+
|
|
37
|
+
def tool_name(base: str, prefix: str) -> str:
|
|
38
|
+
"""Apply *prefix* to a bare tool name such as ``tabs_list``."""
|
|
39
|
+
return f"{prefix}{base}"
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Convert browser-cli SDK models into MCP structured-output values."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from dataclasses import fields, is_dataclass
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
def structured(value: Any) -> Any:
|
|
8
|
+
"""Return JSON-compatible data without private SDK binding fields."""
|
|
9
|
+
if is_dataclass(value) and not isinstance(value, type):
|
|
10
|
+
return {
|
|
11
|
+
field.name: structured(getattr(value, field.name))
|
|
12
|
+
for field in fields(value)
|
|
13
|
+
if not field.name.startswith("_")
|
|
14
|
+
}
|
|
15
|
+
if isinstance(value, dict):
|
|
16
|
+
return {str(key): structured(item) for key, item in value.items()}
|
|
17
|
+
if isinstance(value, (list, tuple, set)):
|
|
18
|
+
return [structured(item) for item in value]
|
|
19
|
+
return value
|