real-browser-cli 0.15.5__tar.gz → 0.16.2__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.
Files changed (232) hide show
  1. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/PKG-INFO +96 -62
  2. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/README.md +94 -61
  3. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/auth/__init__.py +6 -0
  4. real_browser_cli-0.16.2/browser_cli/auth/keys.py +125 -0
  5. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/cli.py +1 -19
  6. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/client/__init__.py +2 -0
  7. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/client/core.py +203 -8
  8. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/client/targets.py +5 -0
  9. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/command_security.py +16 -2
  10. real_browser_cli-0.16.2/browser_cli/commands/__init__.py +155 -0
  11. real_browser_cli-0.16.2/browser_cli/commands/auth.py +436 -0
  12. real_browser_cli-0.16.2/browser_cli/commands/clients.py +106 -0
  13. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/commands/doctor.py +3 -20
  14. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/commands/groups.py +9 -23
  15. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/commands/install.py +38 -17
  16. real_browser_cli-0.16.2/browser_cli/commands/raw.py +21 -0
  17. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/commands/remote.py +18 -8
  18. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/commands/rendering/__init__.py +2 -0
  19. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/commands/rendering/common.py +53 -0
  20. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/commands/script.py +5 -7
  21. real_browser_cli-0.16.2/browser_cli/commands/search.py +28 -0
  22. real_browser_cli-0.16.2/browser_cli/commands/serve.py +194 -0
  23. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/commands/serve_http.py +8 -7
  24. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/commands/session.py +10 -14
  25. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/commands/tabs.py +4 -7
  26. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/commands/windows.py +4 -7
  27. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/compat/auth.py +8 -2
  28. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/constants.py +20 -2
  29. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/models.py +20 -23
  30. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/native/host.py +6 -4
  31. real_browser_cli-0.16.2/browser_cli/remote/pool.py +103 -0
  32. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/remote/socket.py +7 -2
  33. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/remote/transport.py +28 -2
  34. real_browser_cli-0.16.2/browser_cli/sdk/factories.py +124 -0
  35. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/sdk/navigation.py +1 -1
  36. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/sdk/routing.py +28 -11
  37. real_browser_cli-0.16.2/browser_cli/search/__init__.py +1 -0
  38. real_browser_cli-0.16.2/browser_cli/search/engines.py +53 -0
  39. real_browser_cli-0.16.2/browser_cli/serve/control.py +126 -0
  40. real_browser_cli-0.16.2/browser_cli/serve/logging.py +24 -0
  41. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/serve/proxy.py +5 -4
  42. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/serve/runtime.py +87 -6
  43. real_browser_cli-0.16.2/browser_cli/serve/security.py +115 -0
  44. real_browser_cli-0.16.2/browser_cli/version_manager.py +33 -0
  45. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/manifest.json +1 -1
  46. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/src/classes/CommandRegistry.ts +19 -3
  47. real_browser_cli-0.16.2/extension/test/paging.test.ts +41 -0
  48. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/justfile +12 -0
  49. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/pyproject.toml +2 -1
  50. real_browser_cli-0.16.2/tests/conftest.py +58 -0
  51. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/tests/test_api.py +51 -16
  52. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/tests/test_cli.py +174 -18
  53. real_browser_cli-0.16.2/tests/test_client.py +684 -0
  54. real_browser_cli-0.16.2/tests/test_native_host.py +440 -0
  55. real_browser_cli-0.16.2/tests/test_new_feature_commands.py +405 -0
  56. real_browser_cli-0.16.2/tests/test_remote_pool.py +52 -0
  57. real_browser_cli-0.16.2/tests/test_remote_protocol_matrix.py +277 -0
  58. real_browser_cli-0.16.2/tests/test_serve.py +652 -0
  59. real_browser_cli-0.16.2/tests/test_serve_security.py +163 -0
  60. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/uv.lock +36 -1
  61. real_browser_cli-0.15.5/browser_cli/auth/keys.py +0 -59
  62. real_browser_cli-0.15.5/browser_cli/commands/__init__.py +0 -81
  63. real_browser_cli-0.15.5/browser_cli/commands/auth.py +0 -157
  64. real_browser_cli-0.15.5/browser_cli/commands/clients.py +0 -173
  65. real_browser_cli-0.15.5/browser_cli/commands/raw.py +0 -23
  66. real_browser_cli-0.15.5/browser_cli/commands/search.py +0 -79
  67. real_browser_cli-0.15.5/browser_cli/commands/serve.py +0 -117
  68. real_browser_cli-0.15.5/browser_cli/sdk/factories.py +0 -117
  69. real_browser_cli-0.15.5/browser_cli/serve/control.py +0 -67
  70. real_browser_cli-0.15.5/browser_cli/serve/logging.py +0 -16
  71. real_browser_cli-0.15.5/browser_cli/version_manager.py +0 -17
  72. real_browser_cli-0.15.5/tests/conftest.py +0 -52
  73. real_browser_cli-0.15.5/tests/test_client.py +0 -559
  74. real_browser_cli-0.15.5/tests/test_native_host.py +0 -420
  75. real_browser_cli-0.15.5/tests/test_new_feature_commands.py +0 -188
  76. real_browser_cli-0.15.5/tests/test_remote_protocol_matrix.py +0 -213
  77. real_browser_cli-0.15.5/tests/test_serve.py +0 -530
  78. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/.claude/settings.local.json +0 -0
  79. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/.gitea/workflows/package-extension.yml +0 -0
  80. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/.gitea/workflows/publish.yml +0 -0
  81. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/.gitea/workflows/testing.yml +0 -0
  82. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/.gitignore +0 -0
  83. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/.python-version +0 -0
  84. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/CLAUDE.md +0 -0
  85. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/LICENSE +0 -0
  86. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/PRIVACY.md +0 -0
  87. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/__init__.py +0 -0
  88. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/async_sdk.py +0 -0
  89. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/auth/agent.py +0 -0
  90. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/auth/pq.py +0 -0
  91. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/auth/signing.py +0 -0
  92. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/client/auth.py +0 -0
  93. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/client/messages.py +0 -0
  94. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/commands/completion.py +0 -0
  95. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/commands/dom.py +0 -0
  96. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/commands/events.py +0 -0
  97. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/commands/extension.py +0 -0
  98. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/commands/extract.py +0 -0
  99. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/commands/navigate.py +0 -0
  100. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/commands/page.py +0 -0
  101. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/commands/perf.py +0 -0
  102. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/commands/rendering/labels.py +0 -0
  103. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/commands/rendering/tabs_tree.py +0 -0
  104. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/commands/rendering/windows_tree.py +0 -0
  105. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/commands/storage.py +0 -0
  106. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/commands/watch.py +0 -0
  107. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/commands/workspace.py +0 -0
  108. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/compat/__init__.py +0 -0
  109. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/compat/commands.py +0 -0
  110. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/endpoints.py +0 -0
  111. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/errors.py +0 -0
  112. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/framing.py +0 -0
  113. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/local_transport.py +0 -0
  114. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/markdown/__init__.py +0 -0
  115. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/markdown/html.py +0 -0
  116. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/markdown/render.py +0 -0
  117. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/native/__init__.py +0 -0
  118. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/native/local_server.py +0 -0
  119. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/native/protocol.py +0 -0
  120. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/platform.py +0 -0
  121. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/registry.py +0 -0
  122. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/remote/__init__.py +0 -0
  123. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/remote/auth.py +0 -0
  124. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/remote/registry.py +0 -0
  125. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/sdk/__init__.py +0 -0
  126. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/sdk/base.py +0 -0
  127. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/sdk/browser_data.py +0 -0
  128. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/sdk/decorators.py +0 -0
  129. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/sdk/dom.py +0 -0
  130. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/sdk/extension.py +0 -0
  131. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/sdk/groups.py +0 -0
  132. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/sdk/perf.py +0 -0
  133. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/sdk/session.py +0 -0
  134. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/sdk/tabs.py +0 -0
  135. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/sdk/windows.py +0 -0
  136. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/sdk/workflow_decorators.py +0 -0
  137. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/serve/__init__.py +0 -0
  138. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/serve/auth.py +0 -0
  139. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/serve/challenge.py +0 -0
  140. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/transport/__init__.py +0 -0
  141. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/transport/binary.py +0 -0
  142. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/browser_cli/transport/codecs.py +0 -0
  143. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/examples/demo.py +0 -0
  144. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/examples/demo.sh +0 -0
  145. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/content.js +0 -0
  146. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/icon.svg +0 -0
  147. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/icons/icon-128.png +0 -0
  148. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/icons/icon-16.png +0 -0
  149. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/icons/icon-32.png +0 -0
  150. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/icons/icon-48.png +0 -0
  151. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/src/browser-api.ts +0 -0
  152. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/src/classes/CommandGroup.ts +0 -0
  153. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/src/classes/JobManager.ts +0 -0
  154. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/src/classes/NativeConnection.ts +0 -0
  155. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/src/commands/autosave.ts +0 -0
  156. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/src/commands/browser-data.ts +0 -0
  157. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/src/commands/dom.ts +0 -0
  158. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/src/commands/extension.ts +0 -0
  159. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/src/commands/groups.ts +0 -0
  160. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/src/commands/injected.ts +0 -0
  161. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/src/commands/navigation.ts +0 -0
  162. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/src/commands/perf.ts +0 -0
  163. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/src/commands/session-snapshot.ts +0 -0
  164. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/src/commands/session.ts +0 -0
  165. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/src/commands/tabs-query.ts +0 -0
  166. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/src/commands/tabs.ts +0 -0
  167. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/src/commands/windows.ts +0 -0
  168. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/src/content/dispatch.ts +0 -0
  169. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/src/content/dom-ops.ts +0 -0
  170. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/src/content/extract.ts +0 -0
  171. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/src/content/markdown/code.ts +0 -0
  172. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/src/content/markdown/index.ts +0 -0
  173. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/src/content/markdown/renderer.ts +0 -0
  174. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/src/content/markdown/root.ts +0 -0
  175. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/src/content/markdown/utils.ts +0 -0
  176. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/src/content/page-info.ts +0 -0
  177. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/src/content-dispatch.ts +0 -0
  178. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/src/core/errors.ts +0 -0
  179. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/src/core/group-helpers.ts +0 -0
  180. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/src/core/index.ts +0 -0
  181. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/src/core/scripting.ts +0 -0
  182. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/src/core/storage.ts +0 -0
  183. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/src/core/tab-groups.ts +0 -0
  184. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/src/core/tab-helpers.ts +0 -0
  185. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/src/core/throttle.ts +0 -0
  186. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/src/index.ts +0 -0
  187. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/src/types/command-args.ts +0 -0
  188. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/src/types/index.ts +0 -0
  189. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/src/types/jobs.ts +0 -0
  190. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/src/types/json.ts +0 -0
  191. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/src/types/messages.ts +0 -0
  192. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/src/types/session.ts +0 -0
  193. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/src/types/tabs.ts +0 -0
  194. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/src/types/webextension.ts +0 -0
  195. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/test/autosave.test.ts +0 -0
  196. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/test/browser-api.test.ts +0 -0
  197. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/test/chrome-mock.ts +0 -0
  198. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/test/clients.test.ts +0 -0
  199. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/test/jobs.test.ts +0 -0
  200. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/extension/test/navigation.test.ts +0 -0
  201. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/package-lock.json +0 -0
  202. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/package.json +0 -0
  203. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/scripts/gen_extension_key.py +0 -0
  204. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/scripts/package_extension.py +0 -0
  205. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/scripts/package_verified_crx.sh +0 -0
  206. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/scripts/setup_verified_crx_key.sh +0 -0
  207. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/shell.nix +0 -0
  208. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/tests/test_auth.py +0 -0
  209. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/tests/test_commands_cli.py +0 -0
  210. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/tests/test_dom.py +0 -0
  211. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/tests/test_extension_error_page_handling.py +0 -0
  212. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/tests/test_extension_packaging.py +0 -0
  213. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/tests/test_extract.py +0 -0
  214. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/tests/test_framing.py +0 -0
  215. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/tests/test_groups.py +0 -0
  216. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/tests/test_nav.py +0 -0
  217. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/tests/test_page.py +0 -0
  218. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/tests/test_perf.py +0 -0
  219. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/tests/test_performance_integration.py +0 -0
  220. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/tests/test_platform.py +0 -0
  221. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/tests/test_refactor_boundaries.py +0 -0
  222. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/tests/test_registry.py +0 -0
  223. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/tests/test_rendering.py +0 -0
  224. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/tests/test_session.py +0 -0
  225. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/tests/test_storage.py +0 -0
  226. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/tests/test_tabs.py +0 -0
  227. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/tests/test_transport.py +0 -0
  228. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/tests/test_windows.py +0 -0
  229. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/tsconfig.json +0 -0
  230. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/webstore-assets/screenshots/screenshot-1.png +0 -0
  231. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/webstore-assets/screenshots/screenshot-2.png +0 -0
  232. {real_browser_cli-0.15.5 → real_browser_cli-0.16.2}/webstore-assets/screenshots/screenshot-3.png +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: real-browser-cli
3
- Version: 0.15.5
3
+ Version: 0.16.2
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
@@ -86,28 +86,30 @@ Requires-Python: >=3.10
86
86
  Requires-Dist: click>=8
87
87
  Requires-Dist: cryptography>=48
88
88
  Requires-Dist: msgpack>=1
89
+ Requires-Dist: questionary>=2
89
90
  Requires-Dist: rich>=13
90
91
  Provides-Extra: fast
91
92
  Requires-Dist: zstandard>=0.22; extra == 'fast'
92
93
  Description-Content-Type: text/markdown
93
94
 
94
95
  # browser-cli
95
- Control your real, running browser from the terminal or the Python SDK — no headless browser, no Playwright, no virtual display. Your actual open tabs, windows, and tab groups respond to your commands.
96
+ Control your real, running browser from the terminal, Python SDK, or a trusted remote client — no headless browser, no Playwright, no virtual display. Your actual open tabs, windows, and tab groups respond to your commands.
96
97
 
97
98
  ---
98
99
 
99
100
  ## What it does
100
101
  You have 40 tabs open. You want to close all the duplicates, group the GitHub ones, save your session before a meeting, and open a few URLs into a specific group — all from a script. That is what browser-cli is for.
101
102
 
102
- It works by pairing a small browser extension with a Python package that provides both a CLI and SDK. The extension has full access to your browser's tabs, windows, groups, and page DOM. The CLI and SDK talk to it in real time over a local IPC channel.
103
+ It works by pairing a small browser extension with a Python package that provides both a CLI and SDK. The extension has full access to your browser's tabs, windows, groups, and page DOM. The CLI and SDK talk to it in real time over a local IPC channel, or through the optional authenticated TCP remote bridge.
103
104
 
104
105
  ---
105
106
 
106
107
  ## How it works
107
108
  ```
108
- terminal / python script
109
+ terminal / python script / remote client
109
110
  │
110
111
  │ Local IPC (Unix socket on Linux/macOS, named pipe on Windows)
112
+ │ or TCP remote bridge (Ed25519 auth, optional compression)
111
113
  ▼
112
114
  Native Messaging Host (Python process, launched by the browser)
113
115
  │
@@ -126,7 +128,7 @@ terminal / python script
126
128
  4. CLI commands connect to that socket, send a JSON command, and wait for the result.
127
129
  5. The native host relays the command to the extension via stdout, receives the result via stdin, and sends it back to the CLI.
128
130
 
129
- No server needs to be running beforehand. The browser manages the native host's lifecycle.
131
+ No local server needs to be running beforehand. The browser manages the native host's lifecycle. For cross-machine control, `browser-cli serve` starts an explicit TCP listener protected by Ed25519 public-key authentication unless you opt out with `--no-auth`.
130
132
 
131
133
  **Message format**
132
134
 
@@ -145,8 +147,10 @@ Every response:
145
147
 
146
148
  **Requirements:** Python 3.10+, [uv](https://github.com/astral-sh/uv), Chrome, Chromium, Brave, Edge, Vivaldi, or Firefox
147
149
 
150
+ browser-cli has two parts: the **CLI / native host** (a Python package) and the **browser extension** (published on the public stores).
151
+
148
152
  ### Install with uv
149
- Once published on PyPI, install the CLI as a uv tool:
153
+ Install the CLI from PyPI as a uv tool, then register the native host:
150
154
 
151
155
  ```sh
152
156
  uv tool install real-browser-cli
@@ -168,21 +172,31 @@ To upgrade later:
168
172
  uv tool upgrade real-browser-cli
169
173
  ```
170
174
 
175
+ ### Add the browser extension
176
+ Install the extension from its public store listing (the `install` command prints the right link for you):
177
+
178
+ - Chrome / Chromium / Brave / Edge / Vivaldi — [Chrome Web Store](https://chromewebstore.google.com/detail/browser-cli/hekaebjhbhhdbmakimmaklbblbmccahp)
179
+ - Firefox — [Firefox Add-ons](https://addons.mozilla.org/firefox/addon/browser-cli/)
180
+
181
+ The native host manifest trusts both the published store ID and the unpacked development ID, so the store extension works out of the box. If you are hacking on the extension yourself, run `browser-cli install <browser> --dev` for the unpacked / temporary-add-on load steps instead.
182
+
171
183
  ### Install from source
172
184
  ```sh
173
185
  git clone <repo>
174
186
  cd browser-cli
175
187
  uv sync
176
- uv run browser-cli install brave # or: chrome, chromium, edge, vivaldi, firefox
188
+ npm ci && npm run build:extension # build the unpacked extension bundles
189
+ uv run browser-cli install brave --dev # --dev prints unpacked-load steps; or: chrome, chromium, edge, vivaldi, firefox
177
190
  ```
178
191
 
192
+ Omit `--dev` to be pointed at the public store listing instead of loading the unpacked build.
193
+
179
194
  The `install` command will:
180
- 1. Ask you to load the browser-specific extension package
181
- 2. Show the stable extension ID used by that browser family
182
- 3. Write the native messaging manifest to your OS so the browser can find the host
183
- 4. Copy the native host into an internal `libexec` directory and create a small wrapper outside your `PATH`
195
+ 1. Write the native messaging manifest to your OS so the browser can find the host
196
+ 2. Copy the native host into an internal `libexec` directory and create a small wrapper outside your `PATH`
197
+ 3. Print the public store link for installing the extension (or, with `--dev`, the unpacked / temporary-add-on load steps)
184
198
 
185
- After install, **fully restart your browser** (Quit and reopen — not just close the window). The extension will connect to the native host automatically on startup.
199
+ After install, add the extension from the store link above and **fully restart your browser** (Quit and reopen — not just close the window). The extension will connect to the native host automatically on startup.
186
200
 
187
201
  Only the `browser-cli` command needs to be on your `PATH`. The browser launches the native host wrapper directly from its absolute path in the native messaging manifest, and that wrapper imports the installed `browser_cli.native.host` entry point. On Windows the install command also registers the host in the current user's Registry for the selected browser.
188
202
 
@@ -193,62 +207,42 @@ Only the `browser-cli` command needs to be on your `PATH`. The browser launches
193
207
  ```text
194
208
  browser-cli/
195
209
  ├── browser_cli/
196
- │ ├── __init__.py # Python SDK — BrowserCLI class and SDK entry point
197
- │ ├── cli.py # Click CLI entry point
198
- │ ├── client/ # Client-side command routing used by CLI and SDK
199
- │ │ ├── core.py # send_command and remote command routing
200
- │ │ ├── targets.py # Browser target discovery and socket resolution
201
- │ │ ├── auth.py # Remote auth fields and key lookup
202
- │ │ └── messages.py # Request/response helpers
203
- │ ├── models.py # Tab and Group helper models
204
- │ ├── native/ # Native messaging host internals
205
- │ │ ├── host.py # Browser-launched native host entry point
206
- │ │ ├── local_server.py # Local CLI IPC server
207
- │ │ └── protocol.py # Chrome Native Messaging framing
208
- │ ├── remote/ # Client-side remote browser support
209
- │ │ ├── transport.py # TCP/TLS remote transport
210
- │ │ └── registry.py # Saved remote endpoints/keys
211
- │ └── commands/
212
- │ ├── navigate.py # nav open/reload/back/forward/focus
213
- │ ├── search.py # search engine shortcuts
214
- │ ├── tabs.py # tab management
215
- │ ├── groups.py # tab group management
216
- │ ├── windows.py # window management
217
- │ ├── dom.py # DOM querying and interaction
218
- │ ├── extract.py # content extraction
219
- │ └── session.py # session save/load
210
+ │ ├── __init__.py # Public sync SDK: BrowserCLI and namespace wiring
211
+ │ ├── async_sdk.py # AsyncBrowserCLI
212
+ │ ├── cli.py # Click root command and native-host entry point
213
+ │ ├── client/ # send_command path, local/remote routing, message helpers
214
+ │ ├── sdk/ # SDK namespaces: nav, tabs, groups, windows, dom, session, ...
215
+ │ ├── commands/ # CLI presentation layer over the SDK namespaces
216
+ │ ├── native/ # Browser-launched Native Messaging host + local IPC server
217
+ │ ├── remote/ # TCP remote client transport and saved endpoint registry
218
+ │ ├── serve/ # Authenticated TCP server runtime
219
+ │ ├── transport/ # JSON/msgpack response encoding and compression helpers
220
+ │ ├── markdown/ # HTML-to-Markdown extraction helpers
221
+ │ ├── auth/ # Ed25519 keys, signing, SSH-agent/YubiKey helpers, PQ KEX
222
+ │ └── models.py # Tab, Group, BrowserCounts dataclasses
220
223
  ├── extension/
221
- │ ├── manifest.json # MV3 extension manifest
222
- │ ├── content.js # Content-script helpers
223
- │ └── src/ # TypeScript source split by command area
224
- │ ├── index.ts # Builds generated extension/background.js
225
- │ └── content/ # Builds generated extension/content-dispatch.js
226
- ├── examples/
227
- │ ├── demo.py # Python SDK walkthrough
228
- │ └── demo.sh # Bash CLI walkthrough
229
- ├── tests/
230
- │ ├── conftest.py # shared pytest fixtures
231
- │ ├── test_api.py
232
- │ ├── test_cli.py
233
- │ ├── test_dom.py
234
- │ ├── test_extract.py
235
- │ ├── test_groups.py
236
- │ ├── test_nav.py
237
- │ ├── test_session.py
238
- │ ├── test_tabs.py
239
- │ └── test_windows.py
240
- ├── com.browsercli.host.json # native messaging manifest template
241
- ├── pyproject.toml # package metadata and CLI entry point
242
- └── uv.lock # locked dependencies for uv
224
+ │ ├── manifest.json # Chromium MV3 manifest
225
+ │ └── src/ # TypeScript WebExtension source
226
+ │ ├── index.ts # Background/service-worker bundle entry
227
+ │ ├── content-dispatch.ts
228
+ │ ├── commands/ # Browser-side command implementations
229
+ │ ├── content/ # DOM/extract/Markdown logic injected into pages
230
+ │ └── core/ # Shared extension helpers
231
+ ├── examples/ # Python and shell walkthroughs
232
+ ├── scripts/ # Packaging and release helper scripts
233
+ ├── tests/ # pytest suite
234
+ ├── package.json # Extension build/test/package scripts
235
+ ├── pyproject.toml # Python package metadata
236
+ └── uv.lock # locked Python dependencies
243
237
  ```
244
238
 
245
239
  ---
246
240
 
247
241
  ## CLI reference
248
242
 
249
- All commands are run with `uv run browser-cli [--browser ALIAS] <command>`.
243
+ 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`.
250
244
 
251
- If exactly one browser instance is connected, commands auto-target it. Use `--browser ALIAS` when multiple browser instances are connected. `tabs list`, `tabs count`, `groups list`, `groups count`, `windows list`, and `session list` aggregate across all active browsers when `--browser` is omitted; in that mode they show the source browser alias or UUID. You can inspect the active instances with `browser-cli clients` and assign a persistent profile alias from inside the target browser with `browser-cli clients rename --browser <current-alias> <new-alias>`. Closed browsers are removed from the client registry automatically.
245
+ If exactly one browser instance is connected, commands auto-target it. Use `--browser ALIAS` when multiple browser instances are connected. `tabs list`, `tabs count`, `groups list`, `groups count`, `windows list`, and `session list` aggregate across all active browsers when `--browser` is omitted; in that mode they show the source browser alias or UUID. When local and saved remote browsers are mixed, tables group rows by source (`local` or the remote endpoint) and indent the browser profile below that group. You can inspect active instances with `browser-cli clients` and assign a persistent profile alias from inside the target browser with `browser-cli clients rename --browser <current-alias> <new-alias>`. Closed browsers are removed from the client registry automatically.
252
246
 
253
247
  Important: profile aliases are browser-instance aliases, not window aliases. Window aliases created with `windows rename` are only for targeting windows in commands like `nav open --window work`. If a browser instance has no explicit profile alias set, the native host gives it a generated UUID alias so multiple unaliased browsers stay distinct.
254
248
 
@@ -408,6 +402,44 @@ browser-cli completion zsh # print setup instructions
408
402
  browser-cli completion zsh --script # output raw completion script
409
403
  ```
410
404
 
405
+ ### Remote control, auth, and gateways
406
+
407
+ ```sh
408
+ # On the machine with the browser
409
+ browser-cli auth keygen --output ~/.config/browser-cli/client.key
410
+ PUBKEY=$(browser-cli auth show --key ~/.config/browser-cli/client.key | tail -n1)
411
+ browser-cli auth trust "$PUBKEY"
412
+ browser-cli serve --host 0.0.0.0 --port 8765 --authorized-keys ~/.config/browser-cli/authorized_keys
413
+
414
+ # Allow remote browser control (navigation, clicks); safe-only otherwise
415
+ browser-cli serve --authorized-keys ~/.config/browser-cli/authorized_keys --allow-control
416
+
417
+ # Per-key authorization (inline in authorized_keys) + a tighter rate limit
418
+ browser-cli auth trust "$PUBKEY" --name ci-bot --allow-read-page --allow-control
419
+ browser-cli serve --authorized-keys ~/.config/browser-cli/authorized_keys --rate-limit 20
420
+
421
+ # From another machine
422
+ browser-cli --remote browser-host.example:8765 --key ~/.config/browser-cli/client.key tabs list
423
+ browser-cli remote trust browser-host.example:8765 ~/.config/browser-cli/client.key
424
+ browser-cli --remote browser-host.example:8765 clients
425
+
426
+ # Local HTTP JSON gateway for small integrations
427
+ browser-cli serve-http --port 8766
428
+ curl -H "Authorization: Bearer <token>" http://127.0.0.1:8766/tabs
429
+ ```
430
+
431
+ Remote auth uses Ed25519 challenge/response. `--remote` domains default to port 443; explicit `host:port` endpoints are also supported. Saved remote endpoints participate in aggregate list/count commands, where output is grouped by endpoint.
432
+
433
+ #### Security model
434
+
435
+ - **`serve` (TCP)** authenticates every connection with an Ed25519 signature over a fresh server nonce and, for modern clients, wraps the transport in an ML-KEM-768 (post-quantum) AEAD channel. Commands are gated by a **safe-only policy by default** — even a trusted key can only run read-only status/listing commands until you open more with `--allow-read-page`, `--allow-control`, `--allow-dangerous`, or `--allow-all` (full control, including `dom.eval`/`storage.*`). `--no-auth` is rejected on non-loopback hosts.
436
+ - **Per-key authorization:** a key in `authorized_keys` can carry an optional `allow:` token (`<pubkey> <name> allow:read-page,control`) listing its categories (`all`, `safe`, `read-page`, `control`, `dangerous`). That key uses its own policy, overriding the server-wide `--allow-*` default; keys without a token fall back to the default. Set it with `auth trust <pubkey> --allow-control …` (works locally and over `--remote`); `auth keys` shows each key's policy.
437
+ - **Rate limiting:** `--rate-limit N` caps commands/second per client key (token bucket, default `100`, `0` disables) so a compromised key can't hammer the browser.
438
+ - **Audit logging:** request logs include the acting key (its name from `authorized_keys` plus a short pubkey), not just the client address.
439
+ - **`serve-http`** is a convenience gateway with the inverse trade-off: commands are gated by the same `--allow-*` policy (safe-only by default), but the bearer token travels in **clear text over plain HTTP**. It binds to loopback by default; `--no-auth` is only permitted there. If you must expose it beyond loopback, put it behind a TLS-terminating reverse proxy — never send the token over an untrusted network unencrypted.
440
+
441
+ For low latency, an authenticated encrypted remote connection is kept open and reused for further commands in the same process — so SDK scripts and multi-browser fan-out avoid repeating the TCP/TLS/challenge handshake on every command. Aggregate commands also fan out to remote targets concurrently. Both degrade gracefully against older servers that handle one command per connection.
442
+
411
443
  ---
412
444
 
413
445
  ## Python SDK
@@ -418,7 +450,7 @@ from browser_cli import AsyncBrowserCLI, BrowserCLI
418
450
  b = BrowserCLI()
419
451
  ```
420
452
 
421
- Commands are grouped into namespaces on the client (`b.tabs`, `b.dom`, `b.session`, ...). Each sync call blocks until the browser responds and returns the data directly as a Python object. For asyncio programs, `AsyncBrowserCLI` exposes the same namespaces as native awaitable methods over async Unix/TCP transport.
453
+ Commands are grouped into namespaces on the client (`b.tabs`, `b.dom`, `b.session`, ...). Each sync call blocks until the browser responds and returns the data directly as a Python object. Create `BrowserCLI(remote="host:8765", key="client.key")` to target a remote server. For asyncio programs, `AsyncBrowserCLI` exposes the same namespaces as native awaitable methods over async Unix/TCP transport.
422
454
 
423
455
  ```python
424
456
  # Navigation ── b.nav
@@ -573,6 +605,7 @@ counts = b.tabs.count()
573
605
  if isinstance(counts, BrowserCounts):
574
606
  print(counts.total)
575
607
  print(counts.by_browser)
608
+ print(counts.browser_groups) # e.g. {"local:work": "local", "remote:work": "remote"}
576
609
  ```
577
610
 
578
611
  ---
@@ -603,11 +636,12 @@ nix-shell # automatically runs npm ci when node_modules is missing/outdated
603
636
  npm run check:extension
604
637
  ```
605
638
 
606
- The extension source lives in `extension/src/`. `extension/background.js` and `extension/content-dispatch.js` are generated and ignored by git. Run `npm run build:extension` before using `Load unpacked` with `extension/`. On NixOS, use `nix-shell` first if npm is not installed globally.
639
+ The extension source lives in `extension/src/`. `extension/background.js` and `extension/content-dispatch.js` are generated and ignored by git. Run `npm run build:extension` before loading the unpacked `extension/` directory; `browser-cli install <browser> --dev` prints the per-browser load steps. On NixOS, use `nix-shell` first if npm is not installed globally.
607
640
 
608
641
  Packaging:
609
642
 
610
643
  ```bash
644
+ just publish # build to /tmp/dist-browser-cli and publish with .env credentials
611
645
  npm run package:extension # testing/unpacked zip, keeps manifest.key for stable Chromium native-messaging ID
612
646
  npm run package:extension:webstore # Chrome Web Store zip, strips manifest.key
613
647
  npm run package:extension:webstore:verified # Chrome Web Store CRX signed for verified uploads
@@ -1,20 +1,21 @@
1
1
  # browser-cli
2
- Control your real, running browser from the terminal or the Python SDK — no headless browser, no Playwright, no virtual display. Your actual open tabs, windows, and tab groups respond to your commands.
2
+ Control your real, running browser from the terminal, Python SDK, or a trusted remote client — no headless browser, no Playwright, no virtual display. Your actual open tabs, windows, and tab groups respond to your commands.
3
3
 
4
4
  ---
5
5
 
6
6
  ## What it does
7
7
  You have 40 tabs open. You want to close all the duplicates, group the GitHub ones, save your session before a meeting, and open a few URLs into a specific group — all from a script. That is what browser-cli is for.
8
8
 
9
- It works by pairing a small browser extension with a Python package that provides both a CLI and SDK. The extension has full access to your browser's tabs, windows, groups, and page DOM. The CLI and SDK talk to it in real time over a local IPC channel.
9
+ It works by pairing a small browser extension with a Python package that provides both a CLI and SDK. The extension has full access to your browser's tabs, windows, groups, and page DOM. The CLI and SDK talk to it in real time over a local IPC channel, or through the optional authenticated TCP remote bridge.
10
10
 
11
11
  ---
12
12
 
13
13
  ## How it works
14
14
  ```
15
- terminal / python script
15
+ terminal / python script / remote client
16
16
  │
17
17
  │ Local IPC (Unix socket on Linux/macOS, named pipe on Windows)
18
+ │ or TCP remote bridge (Ed25519 auth, optional compression)
18
19
  ▼
19
20
  Native Messaging Host (Python process, launched by the browser)
20
21
  │
@@ -33,7 +34,7 @@ terminal / python script
33
34
  4. CLI commands connect to that socket, send a JSON command, and wait for the result.
34
35
  5. The native host relays the command to the extension via stdout, receives the result via stdin, and sends it back to the CLI.
35
36
 
36
- No server needs to be running beforehand. The browser manages the native host's lifecycle.
37
+ No local server needs to be running beforehand. The browser manages the native host's lifecycle. For cross-machine control, `browser-cli serve` starts an explicit TCP listener protected by Ed25519 public-key authentication unless you opt out with `--no-auth`.
37
38
 
38
39
  **Message format**
39
40
 
@@ -52,8 +53,10 @@ Every response:
52
53
 
53
54
  **Requirements:** Python 3.10+, [uv](https://github.com/astral-sh/uv), Chrome, Chromium, Brave, Edge, Vivaldi, or Firefox
54
55
 
56
+ browser-cli has two parts: the **CLI / native host** (a Python package) and the **browser extension** (published on the public stores).
57
+
55
58
  ### Install with uv
56
- Once published on PyPI, install the CLI as a uv tool:
59
+ Install the CLI from PyPI as a uv tool, then register the native host:
57
60
 
58
61
  ```sh
59
62
  uv tool install real-browser-cli
@@ -75,21 +78,31 @@ To upgrade later:
75
78
  uv tool upgrade real-browser-cli
76
79
  ```
77
80
 
81
+ ### Add the browser extension
82
+ Install the extension from its public store listing (the `install` command prints the right link for you):
83
+
84
+ - Chrome / Chromium / Brave / Edge / Vivaldi — [Chrome Web Store](https://chromewebstore.google.com/detail/browser-cli/hekaebjhbhhdbmakimmaklbblbmccahp)
85
+ - Firefox — [Firefox Add-ons](https://addons.mozilla.org/firefox/addon/browser-cli/)
86
+
87
+ The native host manifest trusts both the published store ID and the unpacked development ID, so the store extension works out of the box. If you are hacking on the extension yourself, run `browser-cli install <browser> --dev` for the unpacked / temporary-add-on load steps instead.
88
+
78
89
  ### Install from source
79
90
  ```sh
80
91
  git clone <repo>
81
92
  cd browser-cli
82
93
  uv sync
83
- uv run browser-cli install brave # or: chrome, chromium, edge, vivaldi, firefox
94
+ npm ci && npm run build:extension # build the unpacked extension bundles
95
+ uv run browser-cli install brave --dev # --dev prints unpacked-load steps; or: chrome, chromium, edge, vivaldi, firefox
84
96
  ```
85
97
 
98
+ Omit `--dev` to be pointed at the public store listing instead of loading the unpacked build.
99
+
86
100
  The `install` command will:
87
- 1. Ask you to load the browser-specific extension package
88
- 2. Show the stable extension ID used by that browser family
89
- 3. Write the native messaging manifest to your OS so the browser can find the host
90
- 4. Copy the native host into an internal `libexec` directory and create a small wrapper outside your `PATH`
101
+ 1. Write the native messaging manifest to your OS so the browser can find the host
102
+ 2. Copy the native host into an internal `libexec` directory and create a small wrapper outside your `PATH`
103
+ 3. Print the public store link for installing the extension (or, with `--dev`, the unpacked / temporary-add-on load steps)
91
104
 
92
- After install, **fully restart your browser** (Quit and reopen — not just close the window). The extension will connect to the native host automatically on startup.
105
+ After install, add the extension from the store link above and **fully restart your browser** (Quit and reopen — not just close the window). The extension will connect to the native host automatically on startup.
93
106
 
94
107
  Only the `browser-cli` command needs to be on your `PATH`. The browser launches the native host wrapper directly from its absolute path in the native messaging manifest, and that wrapper imports the installed `browser_cli.native.host` entry point. On Windows the install command also registers the host in the current user's Registry for the selected browser.
95
108
 
@@ -100,62 +113,42 @@ Only the `browser-cli` command needs to be on your `PATH`. The browser launches
100
113
  ```text
101
114
  browser-cli/
102
115
  ├── browser_cli/
103
- │ ├── __init__.py # Python SDK — BrowserCLI class and SDK entry point
104
- │ ├── cli.py # Click CLI entry point
105
- │ ├── client/ # Client-side command routing used by CLI and SDK
106
- │ │ ├── core.py # send_command and remote command routing
107
- │ │ ├── targets.py # Browser target discovery and socket resolution
108
- │ │ ├── auth.py # Remote auth fields and key lookup
109
- │ │ └── messages.py # Request/response helpers
110
- │ ├── models.py # Tab and Group helper models
111
- │ ├── native/ # Native messaging host internals
112
- │ │ ├── host.py # Browser-launched native host entry point
113
- │ │ ├── local_server.py # Local CLI IPC server
114
- │ │ └── protocol.py # Chrome Native Messaging framing
115
- │ ├── remote/ # Client-side remote browser support
116
- │ │ ├── transport.py # TCP/TLS remote transport
117
- │ │ └── registry.py # Saved remote endpoints/keys
118
- │ └── commands/
119
- │ ├── navigate.py # nav open/reload/back/forward/focus
120
- │ ├── search.py # search engine shortcuts
121
- │ ├── tabs.py # tab management
122
- │ ├── groups.py # tab group management
123
- │ ├── windows.py # window management
124
- │ ├── dom.py # DOM querying and interaction
125
- │ ├── extract.py # content extraction
126
- │ └── session.py # session save/load
116
+ │ ├── __init__.py # Public sync SDK: BrowserCLI and namespace wiring
117
+ │ ├── async_sdk.py # AsyncBrowserCLI
118
+ │ ├── cli.py # Click root command and native-host entry point
119
+ │ ├── client/ # send_command path, local/remote routing, message helpers
120
+ │ ├── sdk/ # SDK namespaces: nav, tabs, groups, windows, dom, session, ...
121
+ │ ├── commands/ # CLI presentation layer over the SDK namespaces
122
+ │ ├── native/ # Browser-launched Native Messaging host + local IPC server
123
+ │ ├── remote/ # TCP remote client transport and saved endpoint registry
124
+ │ ├── serve/ # Authenticated TCP server runtime
125
+ │ ├── transport/ # JSON/msgpack response encoding and compression helpers
126
+ │ ├── markdown/ # HTML-to-Markdown extraction helpers
127
+ │ ├── auth/ # Ed25519 keys, signing, SSH-agent/YubiKey helpers, PQ KEX
128
+ │ └── models.py # Tab, Group, BrowserCounts dataclasses
127
129
  ├── extension/
128
- │ ├── manifest.json # MV3 extension manifest
129
- │ ├── content.js # Content-script helpers
130
- │ └── src/ # TypeScript source split by command area
131
- │ ├── index.ts # Builds generated extension/background.js
132
- │ └── content/ # Builds generated extension/content-dispatch.js
133
- ├── examples/
134
- │ ├── demo.py # Python SDK walkthrough
135
- │ └── demo.sh # Bash CLI walkthrough
136
- ├── tests/
137
- │ ├── conftest.py # shared pytest fixtures
138
- │ ├── test_api.py
139
- │ ├── test_cli.py
140
- │ ├── test_dom.py
141
- │ ├── test_extract.py
142
- │ ├── test_groups.py
143
- │ ├── test_nav.py
144
- │ ├── test_session.py
145
- │ ├── test_tabs.py
146
- │ └── test_windows.py
147
- ├── com.browsercli.host.json # native messaging manifest template
148
- ├── pyproject.toml # package metadata and CLI entry point
149
- └── uv.lock # locked dependencies for uv
130
+ │ ├── manifest.json # Chromium MV3 manifest
131
+ │ └── src/ # TypeScript WebExtension source
132
+ │ ├── index.ts # Background/service-worker bundle entry
133
+ │ ├── content-dispatch.ts
134
+ │ ├── commands/ # Browser-side command implementations
135
+ │ ├── content/ # DOM/extract/Markdown logic injected into pages
136
+ │ └── core/ # Shared extension helpers
137
+ ├── examples/ # Python and shell walkthroughs
138
+ ├── scripts/ # Packaging and release helper scripts
139
+ ├── tests/ # pytest suite
140
+ ├── package.json # Extension build/test/package scripts
141
+ ├── pyproject.toml # Python package metadata
142
+ └── uv.lock # locked Python dependencies
150
143
  ```
151
144
 
152
145
  ---
153
146
 
154
147
  ## CLI reference
155
148
 
156
- All commands are run with `uv run browser-cli [--browser ALIAS] <command>`.
149
+ 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`.
157
150
 
158
- If exactly one browser instance is connected, commands auto-target it. Use `--browser ALIAS` when multiple browser instances are connected. `tabs list`, `tabs count`, `groups list`, `groups count`, `windows list`, and `session list` aggregate across all active browsers when `--browser` is omitted; in that mode they show the source browser alias or UUID. You can inspect the active instances with `browser-cli clients` and assign a persistent profile alias from inside the target browser with `browser-cli clients rename --browser <current-alias> <new-alias>`. Closed browsers are removed from the client registry automatically.
151
+ If exactly one browser instance is connected, commands auto-target it. Use `--browser ALIAS` when multiple browser instances are connected. `tabs list`, `tabs count`, `groups list`, `groups count`, `windows list`, and `session list` aggregate across all active browsers when `--browser` is omitted; in that mode they show the source browser alias or UUID. When local and saved remote browsers are mixed, tables group rows by source (`local` or the remote endpoint) and indent the browser profile below that group. You can inspect active instances with `browser-cli clients` and assign a persistent profile alias from inside the target browser with `browser-cli clients rename --browser <current-alias> <new-alias>`. Closed browsers are removed from the client registry automatically.
159
152
 
160
153
  Important: profile aliases are browser-instance aliases, not window aliases. Window aliases created with `windows rename` are only for targeting windows in commands like `nav open --window work`. If a browser instance has no explicit profile alias set, the native host gives it a generated UUID alias so multiple unaliased browsers stay distinct.
161
154
 
@@ -315,6 +308,44 @@ browser-cli completion zsh # print setup instructions
315
308
  browser-cli completion zsh --script # output raw completion script
316
309
  ```
317
310
 
311
+ ### Remote control, auth, and gateways
312
+
313
+ ```sh
314
+ # On the machine with the browser
315
+ browser-cli auth keygen --output ~/.config/browser-cli/client.key
316
+ PUBKEY=$(browser-cli auth show --key ~/.config/browser-cli/client.key | tail -n1)
317
+ browser-cli auth trust "$PUBKEY"
318
+ browser-cli serve --host 0.0.0.0 --port 8765 --authorized-keys ~/.config/browser-cli/authorized_keys
319
+
320
+ # Allow remote browser control (navigation, clicks); safe-only otherwise
321
+ browser-cli serve --authorized-keys ~/.config/browser-cli/authorized_keys --allow-control
322
+
323
+ # Per-key authorization (inline in authorized_keys) + a tighter rate limit
324
+ browser-cli auth trust "$PUBKEY" --name ci-bot --allow-read-page --allow-control
325
+ browser-cli serve --authorized-keys ~/.config/browser-cli/authorized_keys --rate-limit 20
326
+
327
+ # From another machine
328
+ browser-cli --remote browser-host.example:8765 --key ~/.config/browser-cli/client.key tabs list
329
+ browser-cli remote trust browser-host.example:8765 ~/.config/browser-cli/client.key
330
+ browser-cli --remote browser-host.example:8765 clients
331
+
332
+ # Local HTTP JSON gateway for small integrations
333
+ browser-cli serve-http --port 8766
334
+ curl -H "Authorization: Bearer <token>" http://127.0.0.1:8766/tabs
335
+ ```
336
+
337
+ Remote auth uses Ed25519 challenge/response. `--remote` domains default to port 443; explicit `host:port` endpoints are also supported. Saved remote endpoints participate in aggregate list/count commands, where output is grouped by endpoint.
338
+
339
+ #### Security model
340
+
341
+ - **`serve` (TCP)** authenticates every connection with an Ed25519 signature over a fresh server nonce and, for modern clients, wraps the transport in an ML-KEM-768 (post-quantum) AEAD channel. Commands are gated by a **safe-only policy by default** — even a trusted key can only run read-only status/listing commands until you open more with `--allow-read-page`, `--allow-control`, `--allow-dangerous`, or `--allow-all` (full control, including `dom.eval`/`storage.*`). `--no-auth` is rejected on non-loopback hosts.
342
+ - **Per-key authorization:** a key in `authorized_keys` can carry an optional `allow:` token (`<pubkey> <name> allow:read-page,control`) listing its categories (`all`, `safe`, `read-page`, `control`, `dangerous`). That key uses its own policy, overriding the server-wide `--allow-*` default; keys without a token fall back to the default. Set it with `auth trust <pubkey> --allow-control …` (works locally and over `--remote`); `auth keys` shows each key's policy.
343
+ - **Rate limiting:** `--rate-limit N` caps commands/second per client key (token bucket, default `100`, `0` disables) so a compromised key can't hammer the browser.
344
+ - **Audit logging:** request logs include the acting key (its name from `authorized_keys` plus a short pubkey), not just the client address.
345
+ - **`serve-http`** is a convenience gateway with the inverse trade-off: commands are gated by the same `--allow-*` policy (safe-only by default), but the bearer token travels in **clear text over plain HTTP**. It binds to loopback by default; `--no-auth` is only permitted there. If you must expose it beyond loopback, put it behind a TLS-terminating reverse proxy — never send the token over an untrusted network unencrypted.
346
+
347
+ For low latency, an authenticated encrypted remote connection is kept open and reused for further commands in the same process — so SDK scripts and multi-browser fan-out avoid repeating the TCP/TLS/challenge handshake on every command. Aggregate commands also fan out to remote targets concurrently. Both degrade gracefully against older servers that handle one command per connection.
348
+
318
349
  ---
319
350
 
320
351
  ## Python SDK
@@ -325,7 +356,7 @@ from browser_cli import AsyncBrowserCLI, BrowserCLI
325
356
  b = BrowserCLI()
326
357
  ```
327
358
 
328
- Commands are grouped into namespaces on the client (`b.tabs`, `b.dom`, `b.session`, ...). Each sync call blocks until the browser responds and returns the data directly as a Python object. For asyncio programs, `AsyncBrowserCLI` exposes the same namespaces as native awaitable methods over async Unix/TCP transport.
359
+ Commands are grouped into namespaces on the client (`b.tabs`, `b.dom`, `b.session`, ...). Each sync call blocks until the browser responds and returns the data directly as a Python object. Create `BrowserCLI(remote="host:8765", key="client.key")` to target a remote server. For asyncio programs, `AsyncBrowserCLI` exposes the same namespaces as native awaitable methods over async Unix/TCP transport.
329
360
 
330
361
  ```python
331
362
  # Navigation ── b.nav
@@ -480,6 +511,7 @@ counts = b.tabs.count()
480
511
  if isinstance(counts, BrowserCounts):
481
512
  print(counts.total)
482
513
  print(counts.by_browser)
514
+ print(counts.browser_groups) # e.g. {"local:work": "local", "remote:work": "remote"}
483
515
  ```
484
516
 
485
517
  ---
@@ -510,11 +542,12 @@ nix-shell # automatically runs npm ci when node_modules is missing/outdated
510
542
  npm run check:extension
511
543
  ```
512
544
 
513
- The extension source lives in `extension/src/`. `extension/background.js` and `extension/content-dispatch.js` are generated and ignored by git. Run `npm run build:extension` before using `Load unpacked` with `extension/`. On NixOS, use `nix-shell` first if npm is not installed globally.
545
+ The extension source lives in `extension/src/`. `extension/background.js` and `extension/content-dispatch.js` are generated and ignored by git. Run `npm run build:extension` before loading the unpacked `extension/` directory; `browser-cli install <browser> --dev` prints the per-browser load steps. On NixOS, use `nix-shell` first if npm is not installed globally.
514
546
 
515
547
  Packaging:
516
548
 
517
549
  ```bash
550
+ just publish # build to /tmp/dist-browser-cli and publish with .env credentials
518
551
  npm run package:extension # testing/unpacked zip, keeps manifest.key for stable Chromium native-messaging ID
519
552
  npm run package:extension:webstore # Chrome Web Store zip, strips manifest.key
520
553
  npm run package:extension:webstore:verified # Chrome Web Store CRX signed for verified uploads
@@ -17,11 +17,14 @@ from browser_cli.auth.agent import (
17
17
  )
18
18
  from browser_cli.auth.keys import (
19
19
  add_authorized_key,
20
+ format_authorized_line,
20
21
  generate_keypair,
21
22
  load_authorized_keys,
22
23
  load_authorized_keys_with_names,
24
+ load_authorized_keys_with_policies,
23
25
  load_private_key,
24
26
  public_key_hex,
27
+ set_authorized_key_policy,
25
28
  )
26
29
  from browser_cli.auth.pq import (
27
30
  new_nonce,
@@ -51,9 +54,11 @@ __all__ = [
51
54
  "agent_list_keys",
52
55
  "agent_sign_raw",
53
56
  "canonical_payload",
57
+ "format_authorized_line",
54
58
  "generate_keypair",
55
59
  "load_authorized_keys",
56
60
  "load_authorized_keys_with_names",
61
+ "load_authorized_keys_with_policies",
57
62
  "load_private_key",
58
63
  "new_nonce",
59
64
  "pq_decrypt",
@@ -62,6 +67,7 @@ __all__ = [
62
67
  "pq_kex_server_decapsulate",
63
68
  "pq_kex_server_keypair",
64
69
  "public_key_hex",
70
+ "set_authorized_key_policy",
65
71
  "sign",
66
72
  "verify",
67
73
  ]