Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/refresh-llms.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,12 @@ jobs:
# The generator reports a failed spec fetch on stderr and still exits 0,
# so stderr is captured to a file and inspected after the run.
status=0
make docs-llms >/tmp/generate-llms.out 2>/tmp/generate-llms.err || status=$?
make docs-llms-refresh >/tmp/generate-llms.out 2>/tmp/generate-llms.err || status=$?
cat /tmp/generate-llms.out
cat /tmp/generate-llms.err >&2

if [ "$status" -ne 0 ]; then
echo "::error::make docs-llms failed with exit code $status."
echo "::error::make docs-llms-refresh failed with exit code $status."
exit "$status"
fi

Expand Down Expand Up @@ -106,7 +106,7 @@ jobs:
DATE=$(date -u +%Y-%m-%d)
TITLE="docs: refresh generated llms.txt"
BODY=$(printf '%s\n\n%s\n\n%s\n\n%s\n' \
"Automated daily regeneration of \`$TARGET\` via \`make docs-llms\`, which builds the API section from the live OpenAPI spec." \
"Automated daily regeneration of \`$TARGET\` via \`make docs-llms-refresh\`, which builds the API section from the live OpenAPI spec." \
"Updated $DATE (UTC): $STAT" \
"No hand edits. The run fails instead of committing if the spec cannot be fetched or comes back incomplete." \
"Opened by .github/workflows/refresh-llms.yml. Merges automatically once the required checks pass.")
Expand Down
1 change: 1 addition & 0 deletions docs/src/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@
"sdk-reference/remotesession/observe",
"sdk-reference/remotesession/execute",
"sdk-reference/remotesession/scrape",
"sdk-reference/remotesession/fetch",
"sdk-reference/remotesession/replay",
"sdk-reference/remotesession/cdp_url",
"sdk-reference/remotesession/set_cookies",
Expand Down
20 changes: 20 additions & 0 deletions docs/src/features/sessions/browser-controls.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import Click from "/snippets/browser-controls/click.mdx";
import Fill from "/snippets/browser-controls/fill.mdx";
import Check from "/snippets/browser-controls/check.mdx";
import EvaluateJs from "/snippets/browser-controls/eval_js.mdx";
import Fetch from "/snippets/browser-controls/fetch.mdx";
import SelectDropdownOption from "/snippets/browser-controls/select_dropdown_option.mdx";
import PressKey from "/snippets/browser-controls/press_key.mdx";
import ScrollUp from "/snippets/browser-controls/scroll_up.mdx";
Expand Down Expand Up @@ -205,6 +206,25 @@ Evaluate JavaScript code on the current page and return the result. `session.eva

---

### Fetch

Issue an HTTP request from the page the session is on. `session.fetch(url)` runs the browser's own `fetch()` inside the current page, so the request carries the page's cookies, the session's proxy and the browser's network fingerprint. A relative URL resolves against the current page, which also keeps it same-origin; a cross-origin URL is subject to CORS as in any browser tab, so `goto` the target origin first. The result is a standard `requests.Response`: `status_code`, `ok`, `headers`, `text`, `url`, `json()` and `raise_for_status()` work as usual.

<Fetch />

**Parameters:**
- `url` (str): The URL to request, absolute or relative to the current page
- `method` (str): HTTP method, `GET` by default
- `headers` (dict): Extra request headers
- `params` (dict): Query parameters appended to the URL
- `json` (Any): Body serialised as JSON with an `application/json` content type
- `data` (str | dict): Body sent verbatim, or form-encoded when a dict
- `timeout` (float): Seconds before the request is aborted

**Use for:** Calling a site's own JSON endpoints with the session's cookies and IP, without leaving the browser

---

## Scrolling Actions

### ScrollUp
Expand Down
18 changes: 9 additions & 9 deletions docs/src/llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,8 @@ The SDK docs below are for generated-code editing and reference. They are not th

## APIs

<!-- openapi:begin https://api.notte.cc/openapi.json -->

## Agents

- [POST Agent Start](https://docs.notte.cc/api-reference/agents/agent-start.md)
Expand Down Expand Up @@ -301,7 +303,10 @@ The SDK docs below are for generated-code editing and reference. They are not th

## Sessions

- [DELETE Delete Session File](https://docs.notte.cc/api-reference/sessions/delete-session-file.md)
- [GET Download Session File](https://docs.notte.cc/api-reference/sessions/download-session-file.md)
- [GET Get Session Script](https://docs.notte.cc/api-reference/sessions/get-session-script.md)
- [GET List Session Files](https://docs.notte.cc/api-reference/sessions/list-session-files.md)
- [GET List Sessions](https://docs.notte.cc/api-reference/sessions/list-sessions.md)
- [POST Page Execute](https://docs.notte.cc/api-reference/sessions/page-execute.md)
- [POST Page Observe](https://docs.notte.cc/api-reference/sessions/page-observe.md)
Expand All @@ -316,15 +321,7 @@ The SDK docs below are for generated-code editing and reference. They are not th
- [POST Session Start](https://docs.notte.cc/api-reference/sessions/session-start.md)
- [GET Session Status](https://docs.notte.cc/api-reference/sessions/session-status.md)
- [DELETE Session Stop](https://docs.notte.cc/api-reference/sessions/session-stop.md)

## Storage

- [GET File Download](https://docs.notte.cc/api-reference/storage/file-download.md)
- [GET File Download Uploaded File](https://docs.notte.cc/api-reference/storage/file-download-uploaded-file.md)
- [GET File List Downloads](https://docs.notte.cc/api-reference/storage/file-list-downloads.md)
- [GET File List Uploads](https://docs.notte.cc/api-reference/storage/file-list-uploads.md)
- [POST File Upload](https://docs.notte.cc/api-reference/storage/file-upload.md)
- [POST File Upload Downloaded File](https://docs.notte.cc/api-reference/storage/file-upload-downloaded-file.md)
- [POST Upload Session File](https://docs.notte.cc/api-reference/sessions/upload-session-file.md)

## Usage

Expand All @@ -345,6 +342,8 @@ The SDK docs below are for generated-code editing and reference. They are not th
- [DELETE Vault Delete](https://docs.notte.cc/api-reference/vaults/vault-delete.md)
- [PATCH Vault Update](https://docs.notte.cc/api-reference/vaults/vault-update.md)

<!-- openapi:end -->


## SDK

Expand All @@ -368,6 +367,7 @@ The SDK docs below are for generated-code editing and reference. They are not th
- [observe](https://docs.notte.cc/sdk-reference/remotesession/observe.md): Observes the current session page
- [execute](https://docs.notte.cc/sdk-reference/remotesession/execute.md): Executes an action on the current session page
- [scrape](https://docs.notte.cc/sdk-reference/remotesession/scrape.md): Scrape the current page data
- [fetch](https://docs.notte.cc/sdk-reference/remotesession/fetch.md): Issue an HTTP request from the page the session is on and return the response
- [replay](https://docs.notte.cc/sdk-reference/remotesession/replay.md): Get presigned URLs for the session replay
- [cdp_url](https://docs.notte.cc/sdk-reference/remotesession/cdp_url.md): Get the Chrome DevTools Protocol WebSocket URL for the session
- [set_cookies](https://docs.notte.cc/sdk-reference/remotesession/set_cookies.md): Uploads cookies to the session
Expand Down
69 changes: 55 additions & 14 deletions docs/src/scripts/generate_llms.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,19 @@
Each page is emitted as a bullet with title + description pulled from
the page's YAML frontmatter.

The API section is rendered from the OpenAPI spec and kept between marker
comments. By default it is reused verbatim from the existing llms.txt so the
generator is deterministic and offline, which is what the pre-commit hook
needs; pass --refresh-openapi to fetch the live spec and rebuild it, which is
what the daily refresh workflow does.

Run from anywhere:
python3 src/scripts/generate_llms.py
python3 src/scripts/generate_llms.py [--refresh-openapi]
"""

from __future__ import annotations

import argparse
import json
import re
import sys
Expand Down Expand Up @@ -168,6 +175,41 @@ def render_openapi(spec: dict) -> list[str]:
return lines


OPENAPI_END_MARKER = "<!-- openapi:end -->"


def openapi_begin_marker(url: str) -> str:
return f"<!-- openapi:begin {url} -->"


def cached_openapi_section(existing: str, url: str) -> list[str] | None:
"""Return the marker-delimited API block for `url` from a previous llms.txt, or None."""
begin = openapi_begin_marker(url)
start = existing.find(begin)
if start == -1:
return None
end = existing.find(OPENAPI_END_MARKER, start)
if end == -1:
return None
block = existing[start : end + len(OPENAPI_END_MARKER)]
return block.split("\n")


def openapi_section(url: str, *, refresh: bool, existing: str) -> list[str]:
"""The API block for `url`: reused from `existing` unless refreshing or absent."""
if not refresh:
cached = cached_openapi_section(existing, url)
if cached is not None:
return cached + [""]
print(f" warning: no cached openapi section for {url}, fetching live", file=sys.stderr)
try:
spec = fetch_openapi(url)
except Exception as e:
print(f" warning: failed to fetch openapi {url}: {e}", file=sys.stderr)
return [f"- OpenAPI spec: {url}", ""]
return [openapi_begin_marker(url), ""] + render_openapi(spec) + [OPENAPI_END_MARKER, ""]


def read_frontmatter(page_path: str) -> dict:
"""Return parsed frontmatter dict for a nav page path."""
for ext in (".mdx", ".md"):
Expand Down Expand Up @@ -226,7 +268,16 @@ def render_pages(pages: list, depth: int) -> list[str]:
return lines


def main() -> int:
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument(
"--refresh-openapi",
action="store_true",
help="fetch the live OpenAPI spec and rebuild the API section instead of reusing the committed one",
)
args = parser.parse_args(argv)
existing = OUTPUT.read_text(encoding="utf-8") if OUTPUT.exists() else ""

config = json.loads(DOCS_JSON.read_text())
site_name = config.get("name", "Docs")

Expand All @@ -253,21 +304,11 @@ def main() -> int:
for group in tab.get("groups", []):
out += [f"## {group['group']}", ""]
if "openapi" in group:
try:
spec = fetch_openapi(group["openapi"])
out += render_openapi(spec)
except Exception as e:
print(f" warning: failed to fetch openapi {group['openapi']}: {e}", file=sys.stderr)
out += [f"- OpenAPI spec: {group['openapi']}", ""]
out += openapi_section(group["openapi"], refresh=args.refresh_openapi, existing=existing)
out += render_pages(group.get("pages", []), depth=3)
out += [""]
if "openapi" in tab:
try:
spec = fetch_openapi(tab["openapi"])
out += render_openapi(spec)
except Exception as e:
print(f" warning: failed to fetch openapi {tab['openapi']}: {e}", file=sys.stderr)
out += [f"- OpenAPI spec: {tab['openapi']}", ""]
out += openapi_section(tab["openapi"], refresh=args.refresh_openapi, existing=existing)

OUTPUT.write_text("\n".join(out).rstrip() + "\n")
print(f"wrote {OUTPUT.relative_to(SRC_DIR.parent)} ({OUTPUT.stat().st_size} bytes)")
Expand Down
14 changes: 14 additions & 0 deletions docs/src/sdk-reference/misc/remotesession.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,20 @@ Result containing execution details, any errors, and the updated session state.

---

### fetch

```python
fetch(url: <class 'str'>, method: <class 'str'> = GET, headers: collections.abc.Mapping[str, str] | None = None, params: collections.abc.Mapping[str, typing.Any] | None = None, json: typing.Any = None, data: str | collections.abc.Mapping[str, typing.Any] | None = None, timeout: float | None = None) -> <class 'requests.models.Response'>
```

Issue an HTTP request from the page the session is on and return the response

**Returns:**

<Visibility for="humans">[`Response`](/sdk-reference/misc/response)</Visibility><Visibility for="agents">[`Response`](/sdk-reference/misc/response.md)</Visibility>

---

### get_cookies

```python
Expand Down
65 changes: 65 additions & 0 deletions docs/src/sdk-reference/misc/response.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
---
title: "Response"
description: "The :class:`Response <Response>` object, which contains a"
Comment thread
coderabbitai[bot] marked this conversation as resolved.
---


server's response to an HTTP request

## Methods

### close

```python
close()
```

Releases the connection back to the pool

---

### iter_content

```python
iter_content(chunk_size = 1, decode_unicode = False)
```

Iterates over the response data

---

### iter_lines

```python
iter_lines(chunk_size = 512, decode_unicode = False, delimiter = None)
```

Iterates over the response data, one line at a time

---

### json

```python
json(kwargs)
```
Comment thread
coderabbitai[bot] marked this conversation as resolved.

Decodes the JSON response body (if any) as a Python object

---

### raise_for_status

```python
raise_for_status()
```

Raises :class:`HTTPError`, if one occurred

---



## Module

`requests.models`
53 changes: 53 additions & 0 deletions docs/src/sdk-reference/remotesession/fetch.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
---
title: "fetch"
description: "Issue an HTTP request from the page the session is on and return the response"
---
import AgentMdNotice from '/partials/agent-md-notice.mdx';

<AgentMdNotice />

The request runs inside the browser through `fetch()`, so it carries the
page's cookies, the session's proxy and the browser's own network
fingerprint. A relative `url` resolves against the current page, which
also makes it same-origin; a cross-origin URL is subject to CORS exactly
as in a browser tab, so `goto` the target origin first. Redirects are
followed and the final URL is on `response.url`. The result is a standard
`requests.Response`: a non-2xx status is returned, not raised, and
`response.raise_for_status()` raises `requests.HTTPError`. A network
failure surfaces as the JavaScript error.

`json` is serialised as the body with an `application/json` content type,
`data` as a form body when it is a mapping or verbatim when it is a string.

```python
session.execute(type="goto", url="https://en.wikipedia.org/wiki/Main_Page")
summary = session.fetch("/api/rest_v1/page/summary/Main_Page").json()
```


## Parameters

<ParamField path="url" type="str" required>
</ParamField>

<ParamField path="method" type="str" default="GET">
</ParamField>

<ParamField path="headers" type="UnionType[Mapping[str, str], None]" default="None">
</ParamField>

<ParamField path="params" type="UnionType[Mapping[str, Any], None]" default="None">
</ParamField>

<ParamField path="json" type="Any" default="None">
</ParamField>

<ParamField path="data" type="UnionType[str, Mapping[str, Any], None]" default="None">
</ParamField>

<ParamField path="timeout" type="UnionType[float, None]" default="None">
</ParamField>

## Returns

<Visibility for="humans">[`Response`](/sdk-reference/misc/response)</Visibility><Visibility for="agents">[`Response`](/sdk-reference/misc/response.md)</Visibility>
18 changes: 18 additions & 0 deletions docs/src/sdk-reference/remotesession/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,24 @@ Attributes:
Executes an action on the current session page
</Card>
</Visibility>
<Visibility for="humans">
<Card
title="fetch"
icon="function"
href="/sdk-reference/remotesession/fetch"
>
Issue an HTTP request from the page the session is on and return the response
</Card>
</Visibility>
<Visibility for="agents">
<Card
title="fetch"
icon="function"
href="/sdk-reference/remotesession/fetch.md"
>
Issue an HTTP request from the page the session is on and return the response
</Card>
</Visibility>
<Visibility for="humans">
<Card
title="get_cookies"
Expand Down
Loading
Loading