hyperbrowser 1.12.1__tar.gz → 1.14.0__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 (145) hide show
  1. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/PKG-INFO +432 -4
  2. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/README.md +431 -3
  3. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/async_client.py +2 -0
  4. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/async_manager/sandbox.py +29 -8
  5. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/async_manager/sandboxes/sandbox_processes.py +16 -4
  6. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/async_manager/secret.py +46 -19
  7. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/async_manager/session.py +2 -0
  8. hyperbrowser-1.14.0/hyperbrowser/client/managers/async_manager/session_credentials.py +53 -0
  9. hyperbrowser-1.14.0/hyperbrowser/client/managers/async_manager/vault.py +106 -0
  10. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/sandboxes/shared.py +29 -4
  11. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/sync_manager/sandbox.py +29 -8
  12. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/sync_manager/sandboxes/sandbox_processes.py +16 -4
  13. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/sync_manager/secret.py +46 -19
  14. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/sync_manager/session.py +2 -0
  15. hyperbrowser-1.14.0/hyperbrowser/client/managers/sync_manager/session_credentials.py +53 -0
  16. hyperbrowser-1.14.0/hyperbrowser/client/managers/sync_manager/vault.py +104 -0
  17. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/sync.py +2 -0
  18. hyperbrowser-1.14.0/hyperbrowser/credentials_common.py +33 -0
  19. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/models/__init__.py +57 -0
  20. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/models/consts.py +22 -8
  21. hyperbrowser-1.14.0/hyperbrowser/models/credential.py +115 -0
  22. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/models/sandbox.py +42 -4
  23. hyperbrowser-1.14.0/hyperbrowser/models/secret.py +350 -0
  24. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/models/session.py +58 -0
  25. hyperbrowser-1.14.0/hyperbrowser/models/vault.py +107 -0
  26. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/secrets_common.py +185 -3
  27. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/tools/__init__.py +115 -1
  28. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/tools/anthropic.py +8 -0
  29. hyperbrowser-1.14.0/hyperbrowser/tools/gemini.py +55 -0
  30. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/tools/openai.py +12 -0
  31. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/tools/schema.py +97 -3
  32. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/types/__init__.py +38 -0
  33. hyperbrowser-1.14.0/hyperbrowser/types/credential.py +53 -0
  34. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/types/sandbox.py +5 -1
  35. hyperbrowser-1.14.0/hyperbrowser/types/secret.py +101 -0
  36. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/types/session.py +34 -1
  37. hyperbrowser-1.14.0/hyperbrowser/types/vault.py +44 -0
  38. hyperbrowser-1.14.0/hyperbrowser/vaults_common.py +57 -0
  39. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/pyproject.toml +1 -1
  40. hyperbrowser-1.12.1/hyperbrowser/models/secret.py +0 -173
  41. hyperbrowser-1.12.1/hyperbrowser/types/secret.py +0 -45
  42. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/LICENSE +0 -0
  43. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/__init__.py +0 -0
  44. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/build_context.py +0 -0
  45. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/_request.py +0 -0
  46. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/base.py +0 -0
  47. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/async_manager/agents/__init__.py +0 -0
  48. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/async_manager/agents/browser_use.py +0 -0
  49. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/async_manager/agents/claude_computer_use.py +0 -0
  50. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/async_manager/agents/cua.py +0 -0
  51. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/async_manager/agents/gemini_computer_use.py +0 -0
  52. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/async_manager/agents/grok_computer_use.py +0 -0
  53. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/async_manager/agents/hyper_agent.py +0 -0
  54. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/async_manager/agents/jev_computer_use.py +0 -0
  55. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/async_manager/agents/meta_computer_use.py +0 -0
  56. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/async_manager/computer_action.py +0 -0
  57. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/async_manager/crawl.py +0 -0
  58. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/async_manager/extension.py +0 -0
  59. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/async_manager/extract.py +0 -0
  60. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/async_manager/profile.py +0 -0
  61. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/async_manager/sandboxes/__init__.py +0 -0
  62. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/async_manager/sandboxes/sandbox_files.py +0 -0
  63. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/async_manager/sandboxes/sandbox_terminal.py +0 -0
  64. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/async_manager/sandboxes/sandbox_transport.py +0 -0
  65. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/async_manager/scrape.py +0 -0
  66. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/async_manager/team.py +0 -0
  67. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/async_manager/volume.py +0 -0
  68. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/async_manager/web/__init__.py +0 -0
  69. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/async_manager/web/batch_fetch.py +0 -0
  70. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/async_manager/web/crawl.py +0 -0
  71. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/async_manager/webmcp.py +0 -0
  72. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/sandboxes/__init__.py +0 -0
  73. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/sandboxes/dockerfile_analysis.py +0 -0
  74. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/sandboxes/dockerignore.py +0 -0
  75. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/sandboxes/image_build.py +0 -0
  76. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/sandboxes/image_resolution.py +0 -0
  77. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/sandboxes/process_output.py +0 -0
  78. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/sync_manager/agents/__init__.py +0 -0
  79. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/sync_manager/agents/browser_use.py +0 -0
  80. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/sync_manager/agents/claude_computer_use.py +0 -0
  81. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/sync_manager/agents/cua.py +0 -0
  82. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/sync_manager/agents/gemini_computer_use.py +0 -0
  83. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/sync_manager/agents/grok_computer_use.py +0 -0
  84. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/sync_manager/agents/hyper_agent.py +0 -0
  85. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/sync_manager/agents/jev_computer_use.py +0 -0
  86. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/sync_manager/agents/meta_computer_use.py +0 -0
  87. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/sync_manager/computer_action.py +0 -0
  88. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/sync_manager/crawl.py +0 -0
  89. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/sync_manager/extension.py +0 -0
  90. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/sync_manager/extract.py +0 -0
  91. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/sync_manager/profile.py +0 -0
  92. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/sync_manager/sandboxes/__init__.py +0 -0
  93. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/sync_manager/sandboxes/sandbox_files.py +0 -0
  94. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/sync_manager/sandboxes/sandbox_terminal.py +0 -0
  95. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/sync_manager/sandboxes/sandbox_transport.py +0 -0
  96. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/sync_manager/scrape.py +0 -0
  97. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/sync_manager/team.py +0 -0
  98. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/sync_manager/volume.py +0 -0
  99. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/sync_manager/web/__init__.py +0 -0
  100. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/sync_manager/web/batch_fetch.py +0 -0
  101. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/sync_manager/web/crawl.py +0 -0
  102. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/client/managers/sync_manager/webmcp.py +0 -0
  103. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/config.py +0 -0
  104. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/exceptions.py +0 -0
  105. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/image_builds.py +0 -0
  106. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/models/_parsers.py +0 -0
  107. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/models/agents/browser_use.py +0 -0
  108. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/models/agents/claude_computer_use.py +0 -0
  109. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/models/agents/cua.py +0 -0
  110. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/models/agents/gemini_computer_use.py +0 -0
  111. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/models/agents/grok_computer_use.py +0 -0
  112. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/models/agents/hyper_agent.py +0 -0
  113. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/models/agents/jev_computer_use.py +0 -0
  114. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/models/agents/meta_computer_use.py +0 -0
  115. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/models/computer_action.py +0 -0
  116. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/models/crawl.py +0 -0
  117. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/models/extension.py +0 -0
  118. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/models/extract.py +0 -0
  119. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/models/profile.py +0 -0
  120. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/models/scrape.py +0 -0
  121. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/models/team.py +0 -0
  122. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/models/volume.py +0 -0
  123. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/models/web/batch_fetch.py +0 -0
  124. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/models/web/branding.py +0 -0
  125. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/models/web/common.py +0 -0
  126. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/models/web/crawl.py +0 -0
  127. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/models/web/fetch.py +0 -0
  128. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/models/web/search.py +0 -0
  129. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/models/webmcp.py +0 -0
  130. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/py.typed +0 -0
  131. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/sandbox_common.py +0 -0
  132. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/transport/async_transport.py +0 -0
  133. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/transport/base.py +0 -0
  134. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/transport/sync.py +0 -0
  135. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/types/_json.py +0 -0
  136. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/types/agents.py +0 -0
  137. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/types/computer_action.py +0 -0
  138. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/types/crawl.py +0 -0
  139. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/types/extension.py +0 -0
  140. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/types/extract.py +0 -0
  141. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/types/profile.py +0 -0
  142. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/types/scrape.py +0 -0
  143. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/types/volume.py +0 -0
  144. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/types/web.py +0 -0
  145. {hyperbrowser-1.12.1 → hyperbrowser-1.14.0}/hyperbrowser/types/webmcp.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hyperbrowser
3
- Version: 1.12.1
3
+ Version: 1.14.0
4
4
  Summary: Python SDK for hyperbrowser
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -138,6 +138,356 @@ and cannot be combined with `allow_internet_access=False`, `allow_out`, or
138
138
  `deny_out`. See the
139
139
  [proxy routes guide](https://hyperbrowser.ai/docs/sessions/proxy-routes).
140
140
 
141
+ ## Fill credentials into browser sessions
142
+
143
+ Any secret with allowed origins can be typed into pages of a browser session.
144
+ A website login is usually two such secrets grouped in a vault, plus a TOTP
145
+ secret when the site asks for a one-time code. Values are encrypted on your
146
+ machine; after upload, neither the API, your code, nor an LLM sees them again.
147
+
148
+ ```python
149
+ from playwright.sync_api import sync_playwright
150
+
151
+ from hyperbrowser import Hyperbrowser
152
+
153
+ client = Hyperbrowser(api_key="test-key")
154
+
155
+ origins = ["https://github.com"]
156
+ client.secrets.create(
157
+ {"name": "github-username", "value": "ada@example.com", "allowed_origins": origins}
158
+ )
159
+ client.secrets.create(
160
+ {"name": "github-password", "value": "correct horse", "allowed_origins": origins}
161
+ )
162
+ # A TOTP seed: base32 as the site shows it, or an otpauth://totp/ URI.
163
+ client.secrets.create(
164
+ {
165
+ "name": "github-otp",
166
+ "type": "totp",
167
+ "value": "JBSW Y3DP EHPK 3PXP JBSW Y3DP EHPK 3PXP",
168
+ "allowed_origins": origins,
169
+ }
170
+ )
171
+ client.vaults.create(
172
+ {
173
+ "name": "github-login",
174
+ "entries": {
175
+ "USERNAME": {"name": "github-username"},
176
+ "PASSWORD": {"name": "github-password"},
177
+ "OTP": {"name": "github-otp"},
178
+ },
179
+ }
180
+ )
181
+
182
+ # Secrets and vaults can only be attached when the session is created.
183
+ session = client.sessions.create({"credentials": [{"vault": "github-login"}]})
184
+
185
+ with sync_playwright() as p:
186
+ browser = p.chromium.connect_over_cdp(session.ws_endpoint)
187
+ page = browser.contexts[0].pages[0]
188
+ page.goto("https://github.com/login")
189
+
190
+ # With a vault, each field names the entry to type.
191
+ result = client.sessions.credentials.fill(
192
+ session.id,
193
+ {
194
+ "vault": "github-login",
195
+ "fields": [
196
+ {"field": "USERNAME", "selector": "#login_field"},
197
+ {"field": "PASSWORD", "selector": "#password"},
198
+ ],
199
+ },
200
+ )
201
+ if all(field.outcome == "filled" for field in result.fields):
202
+ page.click("input[type=submit]") # Fill only types; submitting is up to you.
203
+
204
+ page.wait_for_selector("#app_totp")
205
+ # A TOTP entry types the code that is current at fill time.
206
+ client.sessions.credentials.fill(
207
+ session.id,
208
+ {"vault": "github-login", "fields": [{"field": "OTP", "selector": "#app_totp"}]},
209
+ )
210
+
211
+ client.sessions.stop(session.id)
212
+ ```
213
+
214
+ A single secret works without a vault: attach it by name (`"credentials":
215
+ ["github-password"]` or `[{"secret": "github-password"}]`) and fill it with
216
+ `{"credential": "github-password", "fields": [{"selector": "#password"}]}`.
217
+
218
+ `fill()` returns the filled page's `page_id` and `origin`, the `version` for a
219
+ single-secret fill, and per input its `selector`, `outcome`, `secret_id`,
220
+ `version`, `field` (vault fills), `reference` (1Password fills) and
221
+ `expires_at` (TOTP codes). Values are never returned.
222
+
223
+ | Outcome | Meaning |
224
+ | --- | --- |
225
+ | `filled` | The input now holds the value. |
226
+ | `not_found` | No input matched the selector. |
227
+ | `ambiguous` | Several visible inputs matched the selector. |
228
+ | `not_editable` | The input is disabled, read-only, hidden, or not a text input. |
229
+ | `not_accepted` | The page didn't keep the value, for example because of `maxlength`. |
230
+
231
+ Things to know:
232
+
233
+ - A secret can be filled only on its allowed origins (`scheme://host[:port]`;
234
+ `https://*.example.com` matches any subdomain but not `example.com` itself).
235
+ A secret takes 1-20 of at most 512 characters, https only (http for
236
+ `localhost`, `127.0.0.1` and `[::1]`), with no path and `*.` only on a
237
+ registrable domain such as `example.com`, never `*.com` or an IP address.
238
+ The API validates and normalizes them.
239
+ Change them with `client.secrets.set_allowed_origins("github-password",
240
+ [...])`; `None` clears them, after which it can't be filled.
241
+ `client.secrets.update()` takes `{"metadata": {"browser": None}}` too: each
242
+ metadata member sent replaces the stored one and `None` removes it.
243
+ - Secret types are `secret` (the default), `totp` and `op_token`, fixed at
244
+ creation; `client.secrets.list({"type": "totp"})` filters by type. TOTP
245
+ values are checked locally before upload: a base32 seed (spaces, hyphens, line breaks,
246
+ lowercase and trailing `=` are fine) or an `otpauth://totp/` URI with
247
+ `algorithm` SHA1/SHA256/SHA512, `digits` 6 or 8 and `period` 1-300. Seeds
248
+ shorter than 10 bytes (16 characters) are accepted but weak. The platform
249
+ can't read values, so a bad seed would otherwise only fail when used. A TOTP
250
+ secret delivered to a sandbox holds the code current at process start.
251
+ - A fill takes one of `credential`, `vault` and `one_password`, and 1-16
252
+ fields with unique selectors; `field` is required with a vault and left out
253
+ without one, and `ref` is required with `one_password` and left out without
254
+ it.
255
+ Without `page_id`, fill considers the tabs whose URL starts with `page_url`
256
+ (all tabs when it's not given). If one is left, that's the target. If several
257
+ are left, it prefers the tabs on an origin every secret allows (skipped with
258
+ `frame_url`, since a login frame's tab is usually on another origin), then
259
+ the visible one among them; if more than one still qualifies, it fails with
260
+ `CREDENTIAL_PAGE_AMBIGUOUS`. The chosen tab's origin is then checked: on any
261
+ other origin nothing is typed and the fill fails with
262
+ `CREDENTIAL_ORIGIN_NOT_ALLOWED`. Under Playwright every tab reports as visible, so pass
263
+ `page_url` (a URL prefix) or the tab's CDP target ID as `page_id`:
264
+ `page.context.new_cdp_session(page).send("Target.getTargetInfo")["targetInfo"]["targetId"]`.
265
+ For a login form in an iframe, pass `frame_url` (a URL prefix).
266
+ - Fill is safe to retry and never retried automatically. It waits at least
267
+ 30 seconds for the API (the client timeout when longer).
268
+ - Attaching secrets needs an API key with `secrets.use`; filling also needs
269
+ `browser.sessions.update`. The team needs the secrets feature. Sessions with
270
+ credentials run as Firecracker browsers with Chrome's password manager off.
271
+ A vault attaches the secrets its entries hold when the session is created.
272
+ - Fills are recorded in the session's event log as `credential_filled` and
273
+ `credential_fill_refused`. Their metadata names the `credential` or `vault`;
274
+ for 1Password fills, `onePasswordToken` (the op_token's name) and its
275
+ `secretId`.
276
+ - Errors are raised as `HyperbrowserError` with the API's `code`, `retryable`,
277
+ and `status_code`. `details` holds the API's response body, so the
278
+ candidate tabs of an ambiguous fill are in `error.details["details"]["pages"]`
279
+ (each with `pageId`, `url` and `title`). Error codes from the API:
280
+ - `CREDENTIAL_PAGE_AMBIGUOUS` (409): several tabs could be the target; pass
281
+ one's `pageId` as `page_id`, or a `page_url`. `CREDENTIAL_FRAME_AMBIGUOUS`
282
+ (409) is the same for frames.
283
+ - `CREDENTIAL_ORIGIN_NOT_ALLOWED` (403): the page's origin (in `details`)
284
+ isn't allowed; nothing was typed.
285
+ - `CREDENTIAL_NOT_ATTACHED` (403): the secret (or a 1Password fill's
286
+ op_token) wasn't attached when the session was created, or a vault entry
287
+ now points at another secret.
288
+ - `CREDENTIAL_NOT_READY` (409): the secret has no allowed origins or no
289
+ value.
290
+ - `SECRET_TYPE_MISMATCH` (409): an `op_token` can't be attached or filled
291
+ as a secret (by name, `{"secret": ...}` or a vault entry); attach it with
292
+ `{"one_password": ...}` instead. Also another type named as one.
293
+ - `SECRET_VALUE_INVALID` (422): the stored value is unusable, such as a bad
294
+ TOTP seed; add a new version.
295
+ - `CREDENTIAL_PAGE_NOT_FOUND`, `CREDENTIAL_FRAME_NOT_FOUND`,
296
+ `SECRET_NOT_FOUND`, `SECRET_VERSION_NOT_FOUND`, `VAULT_NOT_FOUND`,
297
+ `VAULT_ENTRY_NOT_FOUND` (404), `SESSION_NOT_ACTIVE` (409).
298
+ - `CREDENTIAL_FILL_TIMEOUT` (503/504, retryable) and
299
+ `CREDENTIAL_FILL_FAILED` (500/502/503, retryable as flagged).
300
+ - `CREDENTIALS_UNSUPPORTED_RUNTIME` (400), `CREDENTIAL_FILL_UNSUPPORTED`
301
+ (501) and `CREDENTIAL_VALUE_UNSUPPORTED` (501): the session's browser
302
+ can't fill this, such as 1Password fields on an older host.
303
+ - `SECRET_ONEPASSWORD_REFERENCE_FAILED` (422): a 1Password item or field
304
+ couldn't be read, so nothing was filled. `details` has the failing
305
+ `selectors` and `reasons` by selector. `SECRET_ONEPASSWORD_AUTH_FAILED`
306
+ (422): 1Password rejected the token, or the token is malformed or doesn't
307
+ sign in to `1password.com`, `.ca` or `.eu` (or a subdomain), so it wasn't
308
+ sent to 1Password. `SECRET_ONEPASSWORD_RATE_LIMITED` (429) and
309
+ `SECRET_ONEPASSWORD_FAILED` (502/503) are retryable. Each has the
310
+ token's `tokenSecretId` and `tokenVersion` in `details`.
311
+ - Local fill shape errors raise `invalid_request`; invalid secret values
312
+ raise `invalid_secret_value` before upload. Fill, origin, vault-entry and
313
+ 1Password policy errors come from the API. Inspect the status, code (when
314
+ supplied) and details; schema errors may have no machine code.
315
+
316
+ ### Fill from 1Password
317
+
318
+ A fill can also type fields of a 1Password vault, read with a 1Password
319
+ service account token stored as an `op_token` secret (see
320
+ [Manage secrets](#manage-secrets-and-pass-them-to-sandbox-processes)). Attach
321
+ the token when creating the session, then name the vault, the origins the
322
+ values may be typed on, and each input's field:
323
+
324
+ ```python
325
+ session = client.sessions.create({"credentials": [{"one_password": "op-token"}]})
326
+ # ... open https://www.scrapingcourse.com/login in the session ...
327
+ result = client.sessions.credentials.fill(
328
+ session.id,
329
+ {
330
+ "one_password": {
331
+ "token": "op-token", # the op_token secret; its latest version signs in
332
+ "vault": "Dev Vault", # the 1Password vault's name or ID
333
+ # Nothing stored says where a 1Password value belongs, so the
334
+ # request names the origins it may be typed on.
335
+ "allowed_origins": ["https://www.scrapingcourse.com"],
336
+ },
337
+ "fields": [
338
+ {"selector": "#email", "ref": {"item": "scrapingcourse login", "field": "username"}},
339
+ {"selector": "#password", "ref": {"item": "scrapingcourse login", "field": "password"}},
340
+ {
341
+ "selector": "#otp",
342
+ "ref": {"item": "scrapingcourse login", "section": "Security", "field": "otp?attribute=totp"},
343
+ },
344
+ ],
345
+ "page_url": "https://www.scrapingcourse.com/login",
346
+ },
347
+ )
348
+ for field in result.fields:
349
+ print(field.selector, field.outcome, field.reference) # op://Dev Vault/...
350
+ ```
351
+
352
+ - `token` names the op_token secret, which must be attached to the session
353
+ (`{"one_password": name}` counts toward the 20 credentials). An op_token is
354
+ never typed itself, and attaching or filling it as a plain secret fails with
355
+ `SECRET_TYPE_MISMATCH`.
356
+ - `allowed_origins` takes 1-20 origins with the same rules as a secret's.
357
+ A page on another origin fails with
358
+ `CREDENTIAL_ORIGIN_NOT_ALLOWED`, as for stored secrets.
359
+ - `ref` is `{"item", "section"?, "field"}` by name or ID, with the same rules
360
+ as a sandbox's `one_password` fields: a field may end with a query such as
361
+ `?attribute=otp`, and `op://<vault>/<item>/[<section>/]<field>` is at most
362
+ 512 bytes. Every field takes `ref` and none takes `field`.
363
+ - The result has no top-level `version`; each input's `secret_id` and
364
+ `version` are the op_token's, and `reference` is the `op://` reference it
365
+ was typed from.
366
+ - The API validates these rules. Request models still reject unknown members
367
+ and invalid shapes locally.
368
+
369
+ ### Use with LLM SDKs
370
+
371
+ `CredentialFillTool` lets a model fill a secret or vault by name while your
372
+ application binds the session ID. It doesn't offer 1Password fills: their
373
+ request names the allowed origins, which a model shouldn't choose, so call
374
+ `client.sessions.credentials.fill()` with `one_password` directly. The model chooses only `credential` or
375
+ `vault`, the fields (`selector`, and `field` with a vault), and optionally
376
+ `page_id`, `page_url` or `frame_url`; it never sees the values. `runnable()`
377
+ returns JSON text for the model: the fill result, or `{"error": {"code",
378
+ "message", "retryable", "details"}}` when the fill fails, so the model can
379
+ react (for example by choosing a `page_id`). Give the model your other
380
+ browser tools too, since it must submit the form itself. `async_runnable()`
381
+ does the same with `AsyncHyperbrowser`.
382
+
383
+ OpenAI (Chat Completions):
384
+
385
+ ```python
386
+ import json
387
+
388
+ from openai import OpenAI
389
+
390
+ from hyperbrowser.tools import CredentialFillTool
391
+
392
+ openai_client = OpenAI()
393
+ tools = [CredentialFillTool.openai_tool_definition] # plus your browser tools
394
+ messages = [{"role": "user", "content": "Sign in to GitHub with the vault named github-login."}]
395
+ while True:
396
+ response = openai_client.chat.completions.create(
397
+ model="gpt-5", messages=messages, tools=tools
398
+ )
399
+ message = response.choices[0].message
400
+ messages.append(message)
401
+ if not message.tool_calls:
402
+ break
403
+ for call in message.tool_calls:
404
+ if call.function.name == "fill_credential":
405
+ output = CredentialFillTool.runnable(
406
+ client, session.id, json.loads(call.function.arguments)
407
+ )
408
+ else:
409
+ output = run_browser_tool(call) # your click/press/navigate tools
410
+ messages.append({"role": "tool", "tool_call_id": call.id, "content": output})
411
+ ```
412
+
413
+ Anthropic (Messages):
414
+
415
+ ```python
416
+ import anthropic
417
+
418
+ from hyperbrowser.tools import CredentialFillTool
419
+
420
+ anthropic_client = anthropic.Anthropic()
421
+ tools = [CredentialFillTool.anthropic_tool_definition] # plus your browser tools
422
+ messages = [{"role": "user", "content": "Sign in to GitHub with the vault named github-login."}]
423
+ while True:
424
+ response = anthropic_client.messages.create(
425
+ model="claude-sonnet-5-5", max_tokens=1024, tools=tools, messages=messages
426
+ )
427
+ messages.append({"role": "assistant", "content": response.content})
428
+ if response.stop_reason != "tool_use":
429
+ break
430
+ results = []
431
+ for block in response.content:
432
+ if block.type != "tool_use":
433
+ continue
434
+ if block.name == "fill_credential":
435
+ output = CredentialFillTool.runnable(client, session.id, block.input)
436
+ else:
437
+ output = run_browser_tool(block)
438
+ results.append({"type": "tool_result", "tool_use_id": block.id, "content": output})
439
+ messages.append({"role": "user", "content": results})
440
+ ```
441
+
442
+ Gemini (google-genai):
443
+
444
+ ```python
445
+ from google import genai
446
+ from google.genai import types
447
+
448
+ from hyperbrowser.tools import CredentialFillTool
449
+
450
+ gemini = genai.Client()
451
+ config = types.GenerateContentConfig(
452
+ tools=[
453
+ types.Tool(function_declarations=[CredentialFillTool.gemini_tool_definition])
454
+ ] # plus your browser tools
455
+ )
456
+ contents = [
457
+ types.Content(
458
+ role="user",
459
+ parts=[types.Part.from_text(text="Sign in to GitHub with the vault named github-login.")],
460
+ )
461
+ ]
462
+ while True:
463
+ response = gemini.models.generate_content(
464
+ model="gemini-3.5-flash", contents=contents, config=config
465
+ )
466
+ contents.append(response.candidates[0].content)
467
+ if not response.function_calls:
468
+ break
469
+ parts = []
470
+ for call in response.function_calls:
471
+ if call.name == "fill_credential":
472
+ output = CredentialFillTool.runnable(client, session.id, call.args or {})
473
+ else:
474
+ output = run_browser_tool(call)
475
+ parts.append(
476
+ types.Part.from_function_response(name=call.name, response={"result": output})
477
+ )
478
+ contents.append(types.Content(role="user", parts=parts))
479
+ ```
480
+
481
+ ## Tools for LLM function calling
482
+
483
+ `hyperbrowser.tools` has ready-made tools: `WebsiteScrapeTool`,
484
+ `WebsiteScreenshotTool`, `WebsiteCrawlTool`, `WebsiteExtractTool`,
485
+ `BrowserUseTool` and `CredentialFillTool`. Each has `openai_tool_definition`
486
+ (a strict Chat Completions function tool), `anthropic_tool_definition`, and
487
+ `runnable()` / `async_runnable()` to run the model's input and return text for
488
+ the model. `CredentialFillTool` also provides `gemini_tool_definition`, a
489
+ function declaration dict for google-genai.
490
+
141
491
  ## Usage
142
492
 
143
493
  Hyperbrowser 1.0 accepts plain dictionaries for request parameters. Method
@@ -356,27 +706,76 @@ client.secrets.add_version("provider-key", "rotated-value") # becomes version 2
356
706
  detail = client.secrets.get("provider-key") # metadata and versions, never the value
357
707
  client.secrets.list({"search": "provider"})
358
708
 
709
+ # A vault groups variables; sandboxes and processes take it by name.
710
+ client.vaults.create(
711
+ {"name": "app-env", "entries": {"API_KEY": {"name": "provider-key"}}}
712
+ )
713
+
359
714
  sandbox = client.sandboxes.create(
360
715
  {
361
716
  "image_name": "node",
362
717
  # Delivered to the image's startup command (image launches only).
363
- "secrets": {"API_KEY": "provider-key"},
718
+ "vault": "app-env",
364
719
  }
365
720
  )
721
+ print(sandbox.startup_secrets) # what each variable received, never values
366
722
 
367
723
  # A plain name uses the latest version; {"name": ..., "version": N} pins one.
724
+ # `secrets` are merged over the vault's entries and win on a clash.
368
725
  result = sandbox.exec(
369
726
  "node app.js",
727
+ vault="app-env",
370
728
  secrets={"API_KEY": {"name": "provider-key", "version": 2}},
371
729
  )
372
730
  process = sandbox.processes.start("node worker.js", secrets={"API_KEY": "provider-key"})
373
731
  print(process.secrets) # the version each variable received
374
732
 
733
+ client.vaults.delete("app-env")
375
734
  client.secrets.delete_version("provider-key", 1)
376
735
  client.secrets.delete("provider-key")
377
736
  client.close()
378
737
  ```
379
738
 
739
+ Values can also come straight from a 1Password vault. Store a 1Password
740
+ service account token (it starts with `ops_`) as an `op_token` secret: it is
741
+ only used to sign in to 1Password, never set as a variable or typed into a
742
+ browser itself, and can't have allowed origins. Browser sessions can fill
743
+ 1Password fields too; see [Fill from 1Password](#fill-from-1password). Then name the fields each variable
744
+ takes with `one_password` when creating a sandbox or starting a process.
745
+ Nothing about them is stored: the sandbox reads the fields when the process
746
+ starts.
747
+
748
+ ```python
749
+ client.secrets.create(
750
+ {"name": "op-token", "type": "op_token", "value": Path("op-token.txt").read_text().strip()}
751
+ )
752
+ one_password = {
753
+ "token": "op-token", # the op_token secret's name; its latest version signs in
754
+ "vault": "Engineering", # the 1Password vault's name or ID
755
+ # Items, sections and fields by name or ID: op://Engineering/Postgres/prod/password
756
+ "fields": {
757
+ "DB_PASS": {"item": "Postgres", "section": "prod", "field": "password"},
758
+ "DB_OTP": {"item": "Postgres", "field": "one-time password?attribute=totp"},
759
+ },
760
+ }
761
+
762
+ # Delivered to the image's startup command (image launches only).
763
+ sandbox = client.sandboxes.create({"image_name": "node", "one_password": one_password})
764
+ print(sandbox.startup_secrets) # each with its op:// reference and token_secret
765
+
766
+ result = sandbox.exec("psql -c 'select 1'", one_password=one_password)
767
+ process = sandbox.processes.start("node worker.js", one_password=one_password)
768
+ print(process.secrets) # source.type is "onePassword" for these variables
769
+ ```
770
+
771
+ `one_password` takes one token and vault per request and 1-32 fields, named
772
+ like environment variables. A field may end with a query such as
773
+ `?attribute=otp`, `?attribute=totp` or `?ssh-format=openssh`. Vaults, items,
774
+ sections and fields are 1-256 characters (UTF-16 code units) without `/`, `?` or control characters, and
775
+ not `.` or `..`; the whole `op://` reference is at most 512 bytes. These are
776
+ checked by the API, as is a variable that `secrets`, `env` or the request's
777
+ `vault` also sets: a variable from 1Password must come from it alone.
778
+
380
779
  Things to know:
381
780
 
382
781
  - A secret is set only in the environment of the process it was passed to.
@@ -412,11 +811,17 @@ Things to know:
412
811
  `sandbox.processes.list()` before retrying. When the API reported the
413
812
  unknown outcome itself, its response body, including its own code
414
813
  (`SECRET_PROCESS_OUTCOME_UNKNOWN`), is in the error's `details`.
415
- - `client.sandboxes.create()` with `secrets` raises the same
814
+ - `client.sandboxes.create()` with `secrets`, `vault` or `one_password`
815
+ raises the same
416
816
  `secret_process_outcome_unknown` error when the create may have reached the
417
817
  API but its result is unknown (a timeout, a dropped connection, or an
418
818
  unreadable response): a sandbox may be running its startup command with the
419
819
  secrets, so list sandboxes before retrying.
820
+ - Vaults hold up to 32 stored secrets, named like environment variables; a
821
+ team has up to 100. Methods that take a vault accept its ID or exact name,
822
+ and vaults need the same scopes as secrets. A vault, `secrets` and
823
+ `one_password` together deliver at most 32 variables, and a process's `env`
824
+ may not set one of them.
420
825
  - Error codes from the API:
421
826
  - `SECRETS_ADMIN_REQUIRED` (403): the request used an `hx auth login`
422
827
  session of a team member who isn't an owner or admin. API keys are
@@ -426,8 +831,31 @@ Things to know:
426
831
  - `SECRETS_NOT_CONFIGURED` (503): the platform has no secrets key
427
832
  configured. It isn't retried.
428
833
  - `SECRET_STARTUP_UNSUPPORTED` (400): the image has no startup command to
429
- deliver `secrets` to. Create the sandbox without them, then pass them to
834
+ deliver `secrets`, a `vault` or `one_password` to. Create the sandbox without them, then pass them to
430
835
  `sandbox.exec()` or `sandbox.processes.start()`.
836
+ - `VAULT_NOT_FOUND` (404), `VAULT_NAME_TAKEN`, `VAULT_LIMIT_REACHED`,
837
+ `VAULT_ENTRY_LIMIT_REACHED` (409).
838
+ - `SECRET_IN_USE` (409): a vault uses the secret as an entry;
839
+ `SECRET_VERSION_IN_USE` (409): a vault pins the version. Only vaults
840
+ hold secrets, so an `op_token` can always be deleted.
841
+ - `SECRET_TYPE_MISMATCH` (409): an `op_token` used as a variable or vault
842
+ entry, or another type named as `one_password`'s `token`.
843
+ - `SECRET_SOURCES_INVALID` (400): for example `env` sets a variable that
844
+ `vault` or `secrets` also sets, a `one_password` variable is also set by
845
+ `vault`, `secrets` or `env`, or the sources deliver more than 32
846
+ variables.
847
+ - `SECRET_VALUE_INVALID` (422): a stored value is unusable, such as a bad
848
+ TOTP seed; fix it by adding a new version.
849
+ - `SECRET_TOTP_UNSUPPORTED` and `SECRET_ONEPASSWORD_UNSUPPORTED` (501): the
850
+ sandbox's host predates TOTP or 1Password, so nothing started. Retryable
851
+ on sandbox create, which may land on a newer host; not on a process start.
852
+ - `SECRET_ONEPASSWORD_REFERENCE_FAILED` (422, `details` names the
853
+ variables) and `SECRET_ONEPASSWORD_AUTH_FAILED` (422, also a malformed
854
+ token, or one that doesn't sign in to `1password.com`, `.ca` or `.eu`,
855
+ which is never sent to 1Password): fix the fields or the token.
856
+ `SECRET_ONEPASSWORD_RATE_LIMITED` (429) and
857
+ `SECRET_ONEPASSWORD_FAILED` (502/503) are retryable.
858
+ - `retryable` is the API's flag when it sends one.
431
859
 
432
860
  ### List sandboxes with filters
433
861