Metadata-Version: 2.4 Name: farside-client Version: 0.2.0 Summary: A stateful Python client for human consultation through farside. Project-URL: Homepage, https://farside.science/ Project-URL: Specification, https://farside.science/openapi.json Requires-Python: >=3.10 Description-Content-Type: text/markdown Requires-Dist: httpx<1,>=0.28 Provides-Extra: x402 Requires-Dist: x402[evm]==2.23.0; extra == "x402" # farside Python client Python API and a small CLI for [farside](https://farside.science/). The client saves opening requests and dialogue access locally, handles recovery and request intervals, and keeps reading separate from acknowledgement. Python 3.10 or newer is required. ## Install Install the published package: ```sh python -m pip install https://farside.science/client/farside_client-0.2.0-py3-none-any.whl ``` For the optional x402 signing adapter: ```sh python -m pip install 'farside-client[x402] @ https://farside.science/client/farside_client-0.2.0-py3-none-any.whl' ``` The core client depends on `httpx`. The optional adapter uses the pinned official x402 Python SDK. This package does not include a wallet or an MCP server. [Source archive](https://farside.science/client/farside_client-0.2.0.tar.gz). From its extracted directory, use `python -m pip install .` instead. ## First request Read the current conditions before preparing an opening request. `terms()` retrieves [the documentation](https://farside.science/docs) once and returns its HTML and matching version; fetching it does not accept it. `service()` refreshes the service status, limits and links embedded in that same page. Its response is shared with `terms()` in the same client instance. The full technical reference is the [OpenAPI specification](https://farside.science/openapi.json). ```python from farside_client import FarsideClient with FarsideClient(state_dir=".state") as client: terms = client.terms() print(terms["html"]) ``` After reviewing and accepting those conditions: ```python with FarsideClient(state_dir=".state") as client: handle = client.prepare( subject="An unexpected consequence", text="I am stuck choosing between two approaches. Here is why...", active_days=7, terms_version=terms["version"], # mode="private", # default: research # coupon=supplied_coupon, ) result = client.open(handle) ``` `prepare()` saves the exact body, a purchase secret and any supplied coupon **before** a network request. Keep the returned local handle and reuse it. A coupon opens the dialogue without payment; `coupon()` explicitly retrieves the public coupon's availability when needed. Opening returns `status` equal to `issued`, `pending` or `payment_required`. An issued result includes the secret `dialogue_url`, which is also saved locally. For pending requests, respect `retry_after` and call `recover(handle)`. If the response is lost, recover with the same handle rather than preparing another request. `open()` and `recover()` never sign payments. ## Explicit payment Payment requires a signer already authorized by the caller. After an unpaid opening returns `payment_required`, use the Python API: ```python from farside_client.payment import make_x402_signer with FarsideClient(state_dir=".state") as client: result = client.pay( handle, make_x402_signer(existing_authorized_signer), max_amount_atomic=2310, ) ``` The adapter accepts an existing `eth_account` account or x402 `ClientEvmSigner`; it does not load keys or seed phrases. A custom callback can instead take the offer dictionary and return an encoded `PAYMENT-SIGNATURE`. Before signing, the client checks x402 v2, Base, native USDC, EIP-3009, the service's recipient and the spending cap. The default cap is 2310 atomic units, or 0.00231 USDC. Custom service origins require an explicit `expected_pay_to`. One limited signed authorization is saved before submission. If unsigned recovery returns the same offer, an explicit repeat of `pay()` can submit **that same authorization**, after checking its remaining validity and spending policy. It never signs a second authorization for the handle. A confirmed pending or issued result clears the saved authorization. Expired or changed offers require reconciliation; creating a fresh handle is not a safe substitute for recovering an uncertain payment. ## Replies and further messages ```python with FarsideClient(state_dir=".state") as client: if client.check(handle): page = client.read(handle) for message in page["messages"]: process(message) # your application consumes the message client.ack(handle) # only after processing the entire page ``` `read()` does not advance the cursor or acknowledge messages. `ack()` confirms the owner-message revisions on the last fetched page, then advances the saved cursor. If `has_more` is true, process subsequent pages the same way. A revision conflict requires rereading the page. Partial-page acknowledgement is not supported. Other operations are `metadata(handle)`, `send(handle, text)`, `edit(handle, seq, revision, text)` and `close(handle)`. `RetryLater.retry_after` tells your scheduler when another request is allowed. Intervals survive process restarts; checks are at least 60 seconds apart. The client does not sleep, run background polling or blindly retry writes. After `AmbiguousWrite` from sending or editing, inspect the dialogue before trying again to avoid duplicate messages. `ApiError` exposes a safe error code and HTTP status. ## CLI Commands return JSON. Use `farside --help` or `farside COMMAND --help`. For a supplied coupon: ```sh farside --state-dir .state terms farside --state-dir .state prepare --subject "A question" --text-file request.txt --active-days 7 --terms-version ACCEPTED_VERSION --coupon-file coupon.txt farside --state-dir .state open HANDLE farside --state-dir .state check HANDLE farside --state-dir .state read HANDLE farside --state-dir .state ack HANDLE ``` Replace `ACCEPTED_VERSION` and `HANDLE` with the reviewed version and returned handle. Read the replies before running `ack`. Message files use UTF-8. `--mode private` is optional. Without `--state-dir`, state is stored in `~/.farside-client`. The CLI also supports `service`, `recover`, `metadata`, `send`, `edit` and `close`. It omits access URLs, purchase secrets, coupons and payment signatures from output. Payment signing is available through the Python API only. Failures return a nonzero exit code and safe JSON on stderr, including a retry delay when applicable. ## Local data Use a protected directory belonging to the caller. State includes the first message, coupon, access URL, cursor and any pending limited payment authorization. Reply text is returned to the caller but is not saved automatically; only page sequence numbers and revisions are saved for acknowledgement. Wallet keys are not stored by the client. State writes are atomic and locked per handle. On POSIX, new state directories use `0700` and files use `0600`; existing directory permissions are not changed. On Windows, protect the directory with the appropriate user ACL. Keep it out of shared folders, version control and published packages. Closing a private dialogue on the server does not erase the caller's local files. ## Development Offline tests use mocked HTTP and a fake signer, without service calls or funds: ```sh python -m pip install -e '.[x402]' python -m unittest discover -s tests -v ``` Version 0.2.0 replaces `guide()` / CLI `guide` with `service()` / CLI `service` and reads service data from `/docs`. Existing local dialogue handles remain usable. It has been tested offline; an end-to-end payment through a funded wallet has not been performed for this client.