boxline-sdk 1.1.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.
- boxline_sdk-1.1.0/.gitignore +8 -0
- boxline_sdk-1.1.0/CHANGELOG.md +187 -0
- boxline_sdk-1.1.0/LICENSE +21 -0
- boxline_sdk-1.1.0/PKG-INFO +568 -0
- boxline_sdk-1.1.0/README.md +530 -0
- boxline_sdk-1.1.0/pyproject.toml +53 -0
- boxline_sdk-1.1.0/src/boxline/__init__.py +180 -0
- boxline_sdk-1.1.0/src/boxline/_async_client.py +2089 -0
- boxline_sdk-1.1.0/src/boxline/_base.py +267 -0
- boxline_sdk-1.1.0/src/boxline/_client.py +2083 -0
- boxline_sdk-1.1.0/src/boxline/_errors.py +404 -0
- boxline_sdk-1.1.0/src/boxline/_pagination.py +126 -0
- boxline_sdk-1.1.0/src/boxline/_version.py +3 -0
- boxline_sdk-1.1.0/src/boxline/_webhooks.py +137 -0
- boxline_sdk-1.1.0/src/boxline/py.typed +0 -0
- boxline_sdk-1.1.0/src/boxline/types.py +1502 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `boxline-sdk`, the Python SDK (imported as `boxline`). It follows
|
|
4
|
+
[semantic versioning](https://semver.org).
|
|
5
|
+
|
|
6
|
+
## 1.1.0 (2026-10-02)
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- **Partial results from a several-page extract**: a page that did not load (`page_unreachable`, `page_timeout`) or is
|
|
11
|
+
not a web page is listed in `pages` with `status: None`, `finalUrl: None` and `error: {code, message}`, and the
|
|
12
|
+
call fails only when none load.
|
|
13
|
+
- **`NotAWebPageError`** (422 `not_a_web_page`, not retried): `fetch` or `extract` of an address that answers with a
|
|
14
|
+
PDF or another document Chrome only displays.
|
|
15
|
+
- **Streamed exec**: `exec_stream` skips the API's `waiting` and `ping` lines and raises an `error` line as the error
|
|
16
|
+
it names; `exec` allows up to 10 minutes of session setup (`SETUP_WAIT_S`) before the command's own time limit.
|
|
17
|
+
- **Own model keys and a default model** (sync and async): `bx.project.model_keys()`, `set_model_key(provider, key=, use=)` and
|
|
18
|
+
`delete_model_key(provider)` (a key is write-only: `preview` is its last 4 characters); `default_model=` on
|
|
19
|
+
`project.set_settings()`; `keySource` on agent runs, extract results and `agent.models()` providers; `Provider` now also
|
|
20
|
+
`"xai"` (Grok) and `"google"` (Gemini); the plan feature `platformModels` (off on Free) and `ownKeyModelCostUsd` in stats.
|
|
21
|
+
- **Save a sign-in from a working session** (sync and async): `bx.contexts.create(name=, from_session=, attach=)` makes a
|
|
22
|
+
saved login from the session's current cookies and site storage; `attach=True` also makes the session save to it from
|
|
23
|
+
now on. `ErrorCode.CONTEXT_TOO_LARGE` (413 over 16 MB); `PlanLimitError` past the plan's `maxContexts` or
|
|
24
|
+
`maxContextBytes` (new `Plan` fields).
|
|
25
|
+
- **Runs whose server stopped**: the run errorCode `server_restarted` (continuable: `agent.continue_run` goes on in the
|
|
26
|
+
same browser; `ErrorCode.SERVER_RESTARTED`, in `AgentRunErrorCode`), and `RunNotLiveError` (409 `run_not_live`,
|
|
27
|
+
`ErrorCode.RUN_NOT_LIVE`) from `agent.takeover`, `hand_back` and `send_message` on such a run.
|
|
28
|
+
- **Webhook events for everything a receiver may need** (sync and async): `types.WebhookEventType` has
|
|
29
|
+
`session.started`, `session.expiring`, `agent_run.started`, `agent_run.waiting`, `agent_run.resumed`, `captcha.solved`,
|
|
30
|
+
`captcha.failed`, `task_run.started`, `task.schedule_paused`, `usage.limit_reached`, `api_key.created`,
|
|
31
|
+
`api_key.revoked`, `secret.changed`, `webhook.changed`, `extension.uploaded` and `extension.deleted`. `events=["*"]`
|
|
32
|
+
subscribes to every type (`types.WebhookSubscription`). Typed payloads: `types.WebhookEventPayload` (a union of
|
|
33
|
+
TypedDicts keyed by `type`) and one TypedDict per `data` shape; `WebhookEvent` has `test`. `bx.webhooks.event_types()`
|
|
34
|
+
(`GET /v1/webhooks/events`); `bx.webhooks.test(id, type=)` sends a sample of any type. `verify_webhook` is unchanged.
|
|
35
|
+
- Agent runs carry `variableNames` (the names of the run's own variables, never values): what `continue_run` needs
|
|
36
|
+
again.
|
|
37
|
+
|
|
38
|
+
- **Continue a run that stopped at a limit** (sync and async): `bx.agent.continue_run(run_id, max_steps=,
|
|
39
|
+
max_cost_usd=, instruction=, variables=)` returns the new run (same session; `continuedFrom`), which works with `wait`
|
|
40
|
+
and `stream` like any run. Runs carry `continuable` (`{"until"}` or None), `continuedFrom`, `continuedBy`,
|
|
41
|
+
`sessionExpiresAt`, `maxSteps`, `maxCostUsd` and `maxConsecutiveErrors`; the stream's `done` event carries
|
|
42
|
+
`continuable`. `NotContinuableError` (409 `not_continuable`), `SessionNotRunningError`; types `Continuable`,
|
|
43
|
+
`AgentRunErrorCode`, `SessionEndReason`.
|
|
44
|
+
- **Messages to a working run**: `bx.agent.send_message(run_id, text)` (`AgentMessageSent`); delivered messages are
|
|
45
|
+
`message` steps in the run and its stream. `TooManyMessagesError` after 50.
|
|
46
|
+
- **Run limits** on `agent.run`: `max_cost_usd`, `max_consecutive_errors`, and `timeout` / `idle_timeout` for the run's
|
|
47
|
+
own session. `max_steps` takes 1-1000; `max_steps=None` now means no step limit (not passing it keeps the default of
|
|
48
|
+
30). New `ErrorCode` values `MAX_STEPS`, `MAX_COST`, `TOO_MANY_ERRORS`, `NO_PROGRESS`, `SESSION_TIMEOUT`,
|
|
49
|
+
`SESSION_ENDED`, `NOT_CONTINUABLE`, `TOO_MANY_MESSAGES`, `SESSION_NOT_RUNNING`.
|
|
50
|
+
- **Idle timeout**: `idle_timeout` on `sessions.create`, `sessions.update` and `Session.update` (`None` switches it off
|
|
51
|
+
on update); sessions carry `idleTimeout`, and `endReason` can be `"idle"`.
|
|
52
|
+
- Tasks: `max_steps=None` (no step limit) on `tasks.create` and `tasks.update`, `max_cost_usd`, and `timeout` /
|
|
53
|
+
`idleTimeout` in `browser`.
|
|
54
|
+
- `IDEMPOTENT_POSTS` has `/v1/agent/runs/:id/continue` and `/v1/agent/runs/:id/messages`: both send an Idempotency-Key
|
|
55
|
+
and are retried like the other creates.
|
|
56
|
+
- **Tasks** (saved agent runs), in `Boxline` and `AsyncBoxline`: `bx.tasks.create/list/get/update/delete/run/runs`,
|
|
57
|
+
with `%name%` variables (plain, or `"secret": True` with `origins` and `shell`), `secrets` (project secret names,
|
|
58
|
+
usable by scheduled tasks too), an `output` schema, `browser` settings, `saved_login`, `model`, `max_steps` and a `schedule` (`cron`, `timezone`, `variables`, `enabled`;
|
|
59
|
+
`nextRunAt` and `nextRuns`, the next 3 times in UTC, on the schedule; `lastRun`, the newest run, on the task; `None` removes a field on update, schedule fields are merged). `tasks.runs(task_id,
|
|
60
|
+
status=…)` pages a task's run history by cursor; a run's status can be `queued` (a scheduled run waiting for its
|
|
61
|
+
turn), `skipped` and `missed` too. `tasks.wait_for_run(run | task_id, task_run_id, poll=1.0, timeout=1800.0)`
|
|
62
|
+
waits for a task run's result. Types `Task`, `TaskRun`, `TaskRunStatus`, `TaskVariable`, `TaskSchedule`,
|
|
63
|
+
`TaskSavedLogin` in `boxline.types`.
|
|
64
|
+
- **Structured output**: `output=` (a JSON Schema, `boxline.types.OutputSchema`) on `agent.run`. `AgentRun.result` is
|
|
65
|
+
typed `Any` (the JSON answer with a schema, else the text); runs carry `resultText`, `output`, `errorCode`
|
|
66
|
+
(`ErrorCode.OUTPUT_INVALID`), `taskId` and `taskRunId`; the stream's `done` event carries `resultText` and
|
|
67
|
+
`errorCode`.
|
|
68
|
+
- `MissingVariablesError` (400 `missing_variables`) and `PlanLimitError` (402 `plan_limit`).
|
|
69
|
+
- Saving a task and starting a task run send an Idempotency-Key and are retried like the other creates:
|
|
70
|
+
`IDEMPOTENT_POSTS` has `/v1/tasks` and `/v1/tasks/:id/runs` (`:id` is one path segment; `is_idempotent_post(route)`).
|
|
71
|
+
- `examples/tasks.py`.
|
|
72
|
+
- **Project secrets** (write-only), in `Boxline` and `AsyncBoxline`: `bx.secrets.list/create/get/update/delete/audit`,
|
|
73
|
+
with `scope` (`"agent"`, `"shell"`, `"all"`), `origins`, `shell` and `description` (`None` clears it on update,
|
|
74
|
+
`origins=None` allows any site); `preview` shows the last 4 characters of long values, never more.
|
|
75
|
+
`secrets.audit(name=…)` pages the changes and uses. Types `Secret`, `SecretScope`, `SecretAuditEntry` in
|
|
76
|
+
`boxline.types`. Creating a secret is not retried (the API takes no Idempotency-Key there).
|
|
77
|
+
- **Saved login details with 2FA**: `bx.contexts.set_login(context_id, origin, username, password, totp_secret=None)`
|
|
78
|
+
(a full replace), `contexts.update_login(context_id, origin=, username=, password=, totp_secret=)` (keeps what is not
|
|
79
|
+
passed; `totp_secret=None` removes 2FA; a new `origin` needs `password`, and `totp_secret` when the login has 2FA) and
|
|
80
|
+
`contexts.delete_login(context_id)`; contexts carry `login` (never the password or the 2FA secret);
|
|
81
|
+
`loginDetails` in plan features.
|
|
82
|
+
- **Shell environment and secrets**: `env=` and `secrets=` on `sessions.create` (sessions show the names in `env` and
|
|
83
|
+
`secrets`), `secrets=` on `exec` and `exec_stream` for one command; `sessions.move` also returns `shell` (the
|
|
84
|
+
directory, the exported variables that came along, the processes that were stopped).
|
|
85
|
+
- **Secrets for the AI**: `secrets=` on `agent.run` (and `context=`, a saved login for the run's own session), on
|
|
86
|
+
`session.step()`, and on `run_script` with `login=` (the saved login's details for `step()`);
|
|
87
|
+
`allow_with_extensions=` on `step` and `run_script`, as on `agent.run`.
|
|
88
|
+
- `TooManySecretValuesError` (409 `too_many_secret_values`), `SecretExistsError` (409 `secret_exists`),
|
|
89
|
+
`SecretNotAllowedError` (400 `secret_not_for_ai` / `secret_not_for_shell`), `MachineTooOldError` (409
|
|
90
|
+
`machine_too_old`); `PlanLimitError` also for secrets beyond `maxSecrets`. `plan_limit` is always 402 (a session longer than the plan allows was 403).
|
|
91
|
+
- Plans carry `tasks`, `schedules` and `maxSecrets`.
|
|
92
|
+
- `examples/secrets.py`.
|
|
93
|
+
- `emailVerified` and `termsCurrentVersion` on `me()["user"]`; `"account_recovered"` as a webhook endpoint's
|
|
94
|
+
`disabledReason` and a session's `endReason` (a password reset recovered an account whose email was not confirmed).
|
|
95
|
+
|
|
96
|
+
## 1.0.0 (not published yet)
|
|
97
|
+
|
|
98
|
+
The first stable release: every public API operation has a method, calls are retried safely, and there is an async
|
|
99
|
+
client.
|
|
100
|
+
|
|
101
|
+
### Added
|
|
102
|
+
|
|
103
|
+
- **Web search**: `bx.search(query, limit, country, language, recency, safe_search, fetch, proxy)`, with the top pages
|
|
104
|
+
as Markdown when `fetch` is set; `SearchUnavailableError`; `webSearch` in plan features, `searchesPerMonth` and
|
|
105
|
+
`extraSearchesPer1000Usd` on plans, `searches` in `usage()`, `webSearch` in `pricing()`.
|
|
106
|
+
- **Mouse, keyboard and computer use**: `session.mouse.move/move_by/click/down/up/drag` (points as `(x, y)`, dicts or
|
|
107
|
+
selectors), `session.hover`, `session.keyboard.key/type/press`, `session.cursor()`; `click` takes `button`, `count`
|
|
108
|
+
and `modifiers`, `scroll` takes `delta_x` and `modifiers`, `screenshot` takes `max_width` and `cursor`; bare strings in
|
|
109
|
+
action lists are plain-English steps; results carry `text`. `session.computer(action, max_width, screenshot, format,
|
|
110
|
+
quality, cursor)` / `bx.sessions.computer(session_id, …)` runs one Anthropic- or OpenAI-shaped computer-use action
|
|
111
|
+
and returns the screen; `OutOfViewportError`.
|
|
112
|
+
- **Agent**: `agent.run(task, mode="computer")`, `mode` on runs, `thought` on steps and the `thought` stream event,
|
|
113
|
+
`supportsComputerUse` and `computerTool` in `agent.models()`.
|
|
114
|
+
- `ModelRefusedError` (422 `model_refused`, extract).
|
|
115
|
+
- **Webhooks**: `bx.webhooks.create/list/get/update/delete/rotate_secret/test/deliveries/retry_delivery` (deliveries
|
|
116
|
+
page by cursor, filter by `status`, and carry `errorCode` and their attempt `history`; endpoints carry
|
|
117
|
+
`pausedUntil`), and `verify_webhook(body, header, secret | secrets, tolerance_seconds=300)`, which accepts exactly
|
|
118
|
+
what the API's signer makes (checked against its vectors, JavaScript's trimming and length rules included) and raises
|
|
119
|
+
`WebhookSignatureError` (`reason`); `webhook_signature_header()` for tests. `WebhookUrlNotAllowedError`,
|
|
120
|
+
`WebhooksUnavailableError`, `WebhookDisabledError`, `PayloadExpiredError`, and the `queue_full` code;
|
|
121
|
+
`webhookEndpoints` on plans.
|
|
122
|
+
- **Project settings**: `bx.project.settings()` and `set_settings(captcha_default=…)`.
|
|
123
|
+
- **Accounts and retention**: `auth.signup(…, name=…)`; `name` on users, `termsVersion` and `termsUpdate` on
|
|
124
|
+
`me()["user"]`; `retentionDays` on plans (how long an ended session's recording, logs and run steps are kept) and
|
|
125
|
+
`dataDeletedAt` on sessions (when they were deleted).
|
|
126
|
+
- **Browser settings**: `block_ads` and `cookie_banners` (`"reject"` | `"off"`) on `sessions.create`, `update`
|
|
127
|
+
(and `Session.update`) and `agent.run`; `block_ads` on `fetch`, `screenshot`, `pdf`, `extract` and `crawl.start`;
|
|
128
|
+
sessions carry `blockAds`, `blockedRequests`, `cookieBanners` and `extensions` (`s.blocked_requests`…).
|
|
129
|
+
- **Chrome extensions**: `bx.extensions.upload(zip)` (bytes or a path; raw `application/zip`; with an automatic
|
|
130
|
+
Idempotency-Key), `list`, `get`, `delete`, sync and async; `extensions=[id]` on `sessions.create` and
|
|
131
|
+
`agent.run`; `allow_with_extensions` on `agent.run`; the `Extension` type; `extensions` in plan features.
|
|
132
|
+
`VariablesWithExtensionsError`, `ExtensionDeniedError`, `CrossSiteRequestError`, `InvalidExtensionError`,
|
|
133
|
+
`PayloadTooLargeError` and `LimitReachedError`.
|
|
134
|
+
- `Session.actions()` is typed to take bare-string steps too (`ActionItem = dict | str`), like `sessions.actions()`.
|
|
135
|
+
|
|
136
|
+
- **`AsyncBoxline`** (httpx.AsyncClient) with the same methods as `Boxline`: `await` them, `async for` over lists and
|
|
137
|
+
streams, `async with` for the client and sessions. Both clients come from one source (`_async_client.py`).
|
|
138
|
+
- **Every public endpoint.** New: `auth.signup/login/logout`, `api_keys.list/create/revoke`, `sessions.live` (also
|
|
139
|
+
`session.live()`), `sessions.stream_events`, `sessions.recording_frame`, `crawl.pages`, `pricing`, `openapi`,
|
|
140
|
+
`health`. Session methods are also on `bx.sessions` with the id first (`bx.sessions.pause(session_id)`).
|
|
141
|
+
- **Browser helpers** on a session: `scroll`, `wait`, `select`, `elements`, `upload`, `tabs`, `new_tab`,
|
|
142
|
+
`switch_tab`, `close_tab`, `back`, `forward`, `reload`; `click` also takes `x`, `y`; `type` takes `delay_ms`.
|
|
143
|
+
- **Retries** with exponential backoff (0.5 s to 8 s) and jitter after network errors, time-outs, 429 and 5xx, for
|
|
144
|
+
GETs and for the calls that create or start something; `Retry-After` and `RateLimit-Reset` are honoured.
|
|
145
|
+
`max_retries` (default 2) per client, or `options={"max_retries": …}` per call.
|
|
146
|
+
- **Idempotency keys**: a new `Idempotency-Key` per create call, reused by its retries; `options={"idempotency_key":
|
|
147
|
+
…}` for your own.
|
|
148
|
+
- **Time limits**: `timeout` per client (seconds, default 120) or `options={"timeout": …}` per call.
|
|
149
|
+
- **Typed errors**: `BoxlineError` now has `request_id` (the API's), `client_request_id` (yours), `headers`, `body` and
|
|
150
|
+
`retryable`, with subclasses `RateLimitError`, `FeatureNotInPlanError`, `ProjectSuspendedError`,
|
|
151
|
+
`IdempotencyMismatchError`, `IdempotencyInProgressError`, `InvalidCursorError`, `CaptchaTimeoutError`,
|
|
152
|
+
`PageUnreachableError`, `PageTimeoutError`, `AuthenticationError`, `NotFoundError`, `BoxlineConnectionError`,
|
|
153
|
+
`BoxlineTimeoutError`, and the `ErrorCode` constants.
|
|
154
|
+
- **Cursor pages**: every list takes `limit` and `after` and returns a `Page` (`data`, `next`, `has_next_page()`,
|
|
155
|
+
`get_next_page()`, `iter_pages()`); iterating it goes through every page.
|
|
156
|
+
- **Types**: TypedDicts for the API's objects in `boxline.types` (`SessionData`, `AgentRun`, `CrawlJob`, `Usage`, …),
|
|
157
|
+
matched to the API description.
|
|
158
|
+
- `options={"client_request_id": …}` (sent as `X-Client-Request-Id`), `headers=`, `bx.with_options()`, the
|
|
159
|
+
`Boxline-SDK: python/1.0.0` header; `fetch` takes `links`, `delay_ms` and `viewport`; `pdf` takes `viewport`.
|
|
160
|
+
- Runnable examples in `examples/`, including browser + shell + files in one session.
|
|
161
|
+
- Dependency: `typing-extensions`.
|
|
162
|
+
|
|
163
|
+
### Changed (breaking)
|
|
164
|
+
|
|
165
|
+
- **The package is `boxline-sdk` on PyPI** (`pip install boxline-sdk`; the name `boxline` there is an unrelated
|
|
166
|
+
project). The import name stays `boxline`.
|
|
167
|
+
- The default API is `https://api.boxline.dev` (it was `http://localhost:8080`); `BOXLINE_API_URL` and `base_url` still
|
|
168
|
+
choose another one.
|
|
169
|
+
- `auth.signup(email, password, accept_terms=True)`: `accept_terms` is required (the user accepts the terms of service
|
|
170
|
+
and acceptable use policy); the API refuses a signup without it (400 `terms_not_accepted`).
|
|
171
|
+
|
|
172
|
+
- List methods return a `Page` instead of a list: iterate it (every page) or use `.data` (this page). `page["data"]`,
|
|
173
|
+
`page["total"]` and `len(page)` still work as before.
|
|
174
|
+
- `contexts.list(limit, after)` and `crawl.get(crawl_id, limit, after)`: the `offset` arguments are gone (use `after`),
|
|
175
|
+
and a crawl's `next` is a cursor string.
|
|
176
|
+
- `sessions.list()` takes named filters (`status`, `kind`, `q`, `from_`, `to`, `sort`, `limit`, `after`).
|
|
177
|
+
- `session.pages()` returns a `Page` instead of a list.
|
|
178
|
+
- A client given its own `http_client` sends the API key per request (before, it was only set on the SDK's own
|
|
179
|
+
client), and `close()` leaves an `http_client` you passed in open.
|
|
180
|
+
|
|
181
|
+
### Deprecated
|
|
182
|
+
|
|
183
|
+
- `sessions.page()`: use `sessions.list()`.
|
|
184
|
+
|
|
185
|
+
## 0.1.0
|
|
186
|
+
|
|
187
|
+
- The first version: sessions, actions, exec, files, contexts, the agent, crawl and the quick APIs.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Boxline
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|