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.
@@ -0,0 +1,8 @@
1
+ node_modules/
2
+ dist/
3
+ output/
4
+ .env
5
+ .venv/
6
+ __pycache__/
7
+ *.egg-info/
8
+ .DS_Store
@@ -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.