e2a 2.4.0__tar.gz → 3.0.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.
- {e2a-2.4.0 → e2a-3.0.0}/.gitignore +3 -0
- {e2a-2.4.0 → e2a-3.0.0}/CHANGELOG.md +55 -0
- e2a-3.0.0/PKG-INFO +178 -0
- e2a-3.0.0/README.md +141 -0
- {e2a-2.4.0 → e2a-3.0.0}/pyproject.toml +19 -4
- e2a-3.0.0/scripts/generate-oag.sh +39 -0
- e2a-3.0.0/scripts/strip-enum-validators.py +91 -0
- e2a-3.0.0/src/e2a/__init__.py +52 -0
- e2a-3.0.0/src/e2a/v1/__init__.py +71 -0
- e2a-3.0.0/src/e2a/v1/_retry.py +152 -0
- e2a-3.0.0/src/e2a/v1/client.py +644 -0
- e2a-3.0.0/src/e2a/v1/errors.py +354 -0
- e2a-3.0.0/src/e2a/v1/generated/__init__.py +228 -0
- e2a-3.0.0/src/e2a/v1/generated/api/__init__.py +13 -0
- e2a-3.0.0/src/e2a/v1/generated/api/account_api.py +2122 -0
- e2a-3.0.0/src/e2a/v1/generated/api/agents_api.py +2206 -0
- e2a-3.0.0/src/e2a/v1/generated/api/conversations_api.py +645 -0
- e2a-3.0.0/src/e2a/v1/generated/api/domains_api.py +1356 -0
- e2a-3.0.0/src/e2a/v1/generated/api/events_api.py +971 -0
- e2a-3.0.0/src/e2a/v1/generated/api/messages_api.py +2971 -0
- e2a-3.0.0/src/e2a/v1/generated/api/meta_api.py +281 -0
- e2a-3.0.0/src/e2a/v1/generated/api/reviews_api.py +1144 -0
- e2a-3.0.0/src/e2a/v1/generated/api/webhooks_api.py +2226 -0
- e2a-3.0.0/src/e2a/v1/generated/api_client.py +807 -0
- e2a-3.0.0/src/e2a/v1/generated/api_response.py +21 -0
- e2a-3.0.0/src/e2a/v1/generated/configuration.py +577 -0
- e2a-3.0.0/src/e2a/v1/generated/exceptions.py +216 -0
- e2a-3.0.0/src/e2a/v1/generated/models/__init__.py +98 -0
- e2a-3.0.0/src/e2a/v1/generated/models/account_user_view.py +89 -0
- e2a-3.0.0/src/e2a/v1/generated/models/account_view.py +111 -0
- e2a-3.0.0/src/e2a/v1/generated/models/agent_identity.py +154 -0
- e2a-3.0.0/src/e2a/v1/generated/models/agent_view.py +98 -0
- e2a-3.0.0/src/e2a/v1/generated/models/api_key_export_entry.py +98 -0
- e2a-3.0.0/src/e2a/v1/generated/models/api_key_view.py +102 -0
- e2a-3.0.0/src/e2a/v1/generated/models/approve_request.py +107 -0
- e2a-3.0.0/src/e2a/v1/generated/models/attachment.py +91 -0
- e2a-3.0.0/src/e2a/v1/generated/models/attachment_meta_view.py +93 -0
- e2a-3.0.0/src/e2a/v1/generated/models/attachment_view.py +100 -0
- e2a-3.0.0/src/e2a/v1/generated/models/auth_verdict.py +101 -0
- e2a-3.0.0/src/e2a/v1/generated/models/check_result.py +89 -0
- e2a-3.0.0/src/e2a/v1/generated/models/conversation_detail_view.py +118 -0
- e2a-3.0.0/src/e2a/v1/generated/models/conversation_summary_view.py +104 -0
- e2a-3.0.0/src/e2a/v1/generated/models/create_agent_request.py +89 -0
- e2a-3.0.0/src/e2a/v1/generated/models/create_api_key_request.py +94 -0
- e2a-3.0.0/src/e2a/v1/generated/models/create_api_key_response.py +104 -0
- e2a-3.0.0/src/e2a/v1/generated/models/create_webhook_request.py +97 -0
- e2a-3.0.0/src/e2a/v1/generated/models/create_webhook_response.py +110 -0
- e2a-3.0.0/src/e2a/v1/generated/models/delete_user_data_result.py +107 -0
- e2a-3.0.0/src/e2a/v1/generated/models/delivery_status_json.py +93 -0
- e2a-3.0.0/src/e2a/v1/generated/models/deployment_info_view.py +93 -0
- e2a-3.0.0/src/e2a/v1/generated/models/dns_record_view.py +91 -0
- e2a-3.0.0/src/e2a/v1/generated/models/dns_records_view.py +101 -0
- e2a-3.0.0/src/e2a/v1/generated/models/domain.py +114 -0
- e2a-3.0.0/src/e2a/v1/generated/models/domain_view.py +122 -0
- e2a-3.0.0/src/e2a/v1/generated/models/error_body.py +98 -0
- e2a-3.0.0/src/e2a/v1/generated/models/error_envelope.py +91 -0
- e2a-3.0.0/src/e2a/v1/generated/models/event_json.py +123 -0
- e2a-3.0.0/src/e2a/v1/generated/models/forward_request.py +107 -0
- e2a-3.0.0/src/e2a/v1/generated/models/limits_caps_view.py +93 -0
- e2a-3.0.0/src/e2a/v1/generated/models/limits_usage_view.py +93 -0
- e2a-3.0.0/src/e2a/v1/generated/models/message.py +208 -0
- e2a-3.0.0/src/e2a/v1/generated/models/message_body_view.py +89 -0
- e2a-3.0.0/src/e2a/v1/generated/models/message_parsed_view.py +89 -0
- e2a-3.0.0/src/e2a/v1/generated/models/message_summary_view.py +134 -0
- e2a-3.0.0/src/e2a/v1/generated/models/message_view.py +160 -0
- e2a-3.0.0/src/e2a/v1/generated/models/o_auth_connection_entry.py +100 -0
- e2a-3.0.0/src/e2a/v1/generated/models/page_agent_view.py +102 -0
- e2a-3.0.0/src/e2a/v1/generated/models/page_api_key_view.py +102 -0
- e2a-3.0.0/src/e2a/v1/generated/models/page_conversation_summary_view.py +102 -0
- e2a-3.0.0/src/e2a/v1/generated/models/page_domain_view.py +102 -0
- e2a-3.0.0/src/e2a/v1/generated/models/page_event_json.py +102 -0
- e2a-3.0.0/src/e2a/v1/generated/models/page_message_summary_view.py +102 -0
- e2a-3.0.0/src/e2a/v1/generated/models/page_review_view.py +102 -0
- e2a-3.0.0/src/e2a/v1/generated/models/page_suppression.py +102 -0
- e2a-3.0.0/src/e2a/v1/generated/models/page_webhook_delivery_view.py +102 -0
- e2a-3.0.0/src/e2a/v1/generated/models/page_webhook_view.py +102 -0
- e2a-3.0.0/src/e2a/v1/generated/models/protection_config_view.py +102 -0
- e2a-3.0.0/src/e2a/v1/generated/models/protection_direction_view.py +97 -0
- e2a-3.0.0/src/e2a/v1/generated/models/protection_event_export_entry.py +108 -0
- e2a-3.0.0/src/e2a/v1/generated/models/protection_gate_view.py +92 -0
- e2a-3.0.0/src/e2a/v1/generated/models/protection_holds_view.py +90 -0
- e2a-3.0.0/src/e2a/v1/generated/models/protection_scan_view.py +87 -0
- e2a-3.0.0/src/e2a/v1/generated/models/redeliver_delivery.py +93 -0
- e2a-3.0.0/src/e2a/v1/generated/models/redeliver_event_request.py +87 -0
- e2a-3.0.0/src/e2a/v1/generated/models/redeliver_view.py +103 -0
- e2a-3.0.0/src/e2a/v1/generated/models/register_domain_request.py +87 -0
- e2a-3.0.0/src/e2a/v1/generated/models/reject_request.py +87 -0
- e2a-3.0.0/src/e2a/v1/generated/models/reject_result_view.py +91 -0
- e2a-3.0.0/src/e2a/v1/generated/models/reply_request.py +107 -0
- e2a-3.0.0/src/e2a/v1/generated/models/result.py +101 -0
- e2a-3.0.0/src/e2a/v1/generated/models/review_view.py +108 -0
- e2a-3.0.0/src/e2a/v1/generated/models/rotate_secret_response.py +90 -0
- e2a-3.0.0/src/e2a/v1/generated/models/send_email_request.py +109 -0
- e2a-3.0.0/src/e2a/v1/generated/models/send_result_view.py +100 -0
- e2a-3.0.0/src/e2a/v1/generated/models/sending_dns_record_view.py +91 -0
- e2a-3.0.0/src/e2a/v1/generated/models/suppression.py +96 -0
- e2a-3.0.0/src/e2a/v1/generated/models/suppression_export_entry.py +94 -0
- e2a-3.0.0/src/e2a/v1/generated/models/test_webhook_request.py +102 -0
- e2a-3.0.0/src/e2a/v1/generated/models/test_webhook_response.py +87 -0
- e2a-3.0.0/src/e2a/v1/generated/models/update_agent_request.py +88 -0
- e2a-3.0.0/src/e2a/v1/generated/models/update_message_request.py +89 -0
- e2a-3.0.0/src/e2a/v1/generated/models/update_message_result_view.py +89 -0
- e2a-3.0.0/src/e2a/v1/generated/models/update_webhook_request.py +99 -0
- e2a-3.0.0/src/e2a/v1/generated/models/usage_event_entry.py +98 -0
- e2a-3.0.0/src/e2a/v1/generated/models/user_export.py +176 -0
- e2a-3.0.0/src/e2a/v1/generated/models/user_export_user.py +94 -0
- e2a-3.0.0/src/e2a/v1/generated/models/verify_domain_view.py +98 -0
- e2a-3.0.0/src/e2a/v1/generated/models/webhook_delivery_view.py +104 -0
- e2a-3.0.0/src/e2a/v1/generated/models/webhook_filters_view.py +91 -0
- e2a-3.0.0/src/e2a/v1/generated/models/webhook_view.py +108 -0
- e2a-3.0.0/src/e2a/v1/generated/py.typed +0 -0
- e2a-3.0.0/src/e2a/v1/generated/rest.py +194 -0
- e2a-3.0.0/src/e2a/v1/pagination.py +98 -0
- e2a-3.0.0/src/e2a/v1/py.typed +0 -0
- e2a-3.0.0/src/e2a/v1/webhook_signature.py +141 -0
- e2a-3.0.0/src/e2a/v1/websocket.py +215 -0
- e2a-3.0.0/tests/__init__.py +0 -0
- {e2a-2.4.0 → e2a-3.0.0}/tests/test_contract.py +25 -129
- e2a-3.0.0/tests/test_enum_forward_compat.py +64 -0
- e2a-3.0.0/tests/test_exports.py +84 -0
- e2a-3.0.0/tests/test_v1_client.py +422 -0
- e2a-3.0.0/tests/test_v1_errors.py +189 -0
- e2a-3.0.0/tests/test_v1_pagination.py +113 -0
- e2a-3.0.0/tests/test_v1_retry.py +183 -0
- {e2a-2.4.0 → e2a-3.0.0}/tests/test_v1_websocket.py +161 -48
- e2a-3.0.0/tests/test_webhook_signature.py +161 -0
- {e2a-2.4.0 → e2a-3.0.0}/uv.lock +540 -2
- e2a-2.4.0/PKG-INFO +0 -464
- e2a-2.4.0/README.md +0 -430
- e2a-2.4.0/codegen-requirements.txt +0 -22
- e2a-2.4.0/src/e2a/__init__.py +0 -44
- e2a-2.4.0/src/e2a/v1/__init__.py +0 -44
- e2a-2.4.0/src/e2a/v1/api.py +0 -383
- e2a-2.4.0/src/e2a/v1/async_client.py +0 -704
- e2a-2.4.0/src/e2a/v1/client.py +0 -458
- e2a-2.4.0/src/e2a/v1/generated/__init__.py +0 -526
- e2a-2.4.0/src/e2a/v1/generated/_internal.py +0 -401
- e2a-2.4.0/src/e2a/v1/generated/github_com_Mnexa_AI_e2a_internal_identity.py +0 -127
- e2a-2.4.0/src/e2a/v1/generated/internal_agent.py +0 -27
- e2a-2.4.0/src/e2a/v1/handler.py +0 -931
- e2a-2.4.0/src/e2a/v1/websocket.py +0 -151
- e2a-2.4.0/tests/test_e2e.py +0 -192
- e2a-2.4.0/tests/test_exports.py +0 -84
- e2a-2.4.0/tests/test_generated_models.py +0 -32
- e2a-2.4.0/tests/test_idempotency.py +0 -183
- e2a-2.4.0/tests/test_v1_api.py +0 -676
- e2a-2.4.0/tests/test_v1_async_client.py +0 -413
- e2a-2.4.0/tests/test_v1_client.py +0 -605
- e2a-2.4.0/tests/test_v1_handler.py +0 -646
- /e2a-2.4.0/tests/__init__.py → /e2a-3.0.0/src/e2a/py.typed +0 -0
|
@@ -1,5 +1,60 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.0.0
|
|
4
|
+
|
|
5
|
+
Breaking redesign. The SDK is now a namespaced, **async-only** `E2AClient`
|
|
6
|
+
wrapping a generated client over the agent-scoped `/v1` API surface, with a
|
|
7
|
+
typed error hierarchy, automatic retries + idempotency, and async
|
|
8
|
+
auto-pagination.
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- **Namespaced, async-only surface.** Resources are grouped under the client:
|
|
12
|
+
`client.agents`, `client.messages`, `client.conversations`, `client.domains`,
|
|
13
|
+
`client.events`, `client.webhooks`, `client.account`. Per-agent methods take
|
|
14
|
+
the agent `address` as the first argument
|
|
15
|
+
(`await client.messages.send(address, {...})`,
|
|
16
|
+
`await client.messages.list(address).to_list(limit=...)`,
|
|
17
|
+
`await client.messages.get(address, id)`,
|
|
18
|
+
`await client.messages.reply(address, id, {...})`). Use the client as an async
|
|
19
|
+
context manager (`async with E2AClient() as client:`).
|
|
20
|
+
- **Webhook verification.** Verify and decode a delivery with the standalone
|
|
21
|
+
`construct_event(raw_body, signature_header, secret)`, which checks the
|
|
22
|
+
`X-E2A-Signature` header and returns a typed event (raising
|
|
23
|
+
`E2AWebhookSignatureError` on a bad signature). Per-webhook `whsec_…` secrets,
|
|
24
|
+
Stripe-style.
|
|
25
|
+
- **Typed errors.** Failures raise `E2AError` subclasses (`E2ANotFoundError`,
|
|
26
|
+
`E2AConflictError`, `E2AValidationError`, `E2ARateLimitError`,
|
|
27
|
+
`E2AWebhookSignatureError`, …) carrying `.code`, `.status`, `.request_id`, and
|
|
28
|
+
`.retryable`.
|
|
29
|
+
|
|
30
|
+
### Removed
|
|
31
|
+
- The flat methods `send` / `reply` / `get_messages` / `get_message` and the
|
|
32
|
+
per-call `agent_email` inference. Pass the agent `address` explicitly.
|
|
33
|
+
- The lower-level `E2AApi` class.
|
|
34
|
+
- The synchronous client — the SDK is async-only.
|
|
35
|
+
- `InboundEmail` / `AsyncInboundEmail` and the `parse_webhook` / `parse` +
|
|
36
|
+
`verify_signature()` flow. Replaced by `construct_event`. There is no
|
|
37
|
+
unverified-email type and no field-access gating.
|
|
38
|
+
|
|
39
|
+
## 2.5.0
|
|
40
|
+
|
|
41
|
+
### Added
|
|
42
|
+
- Generated types for the per-user resource-limits primitive that
|
|
43
|
+
shipped with #158: `LimitsInfo`, `LimitsCaps`, `LimitsUsage`. These
|
|
44
|
+
describe the response shape of `GET /api/v1/users/me/limits`, which
|
|
45
|
+
the hosted dashboard uses to render the upgrade affordance and the
|
|
46
|
+
"you've used X of Y" surface. The high-level `E2AClient` doesn't
|
|
47
|
+
yet expose a typed helper for this endpoint — it's surfaced as a
|
|
48
|
+
dashboard-only concern today, and SDK consumers querying their own
|
|
49
|
+
usage should call `/agents` / `/messages` directly. The types are
|
|
50
|
+
emitted so anyone consuming the raw OpenAPI generation has the
|
|
51
|
+
shapes available.
|
|
52
|
+
|
|
53
|
+
### Notes
|
|
54
|
+
- No runtime client behavior changed in this release. If you're not
|
|
55
|
+
using the limits primitive (self-host deployments without a paid
|
|
56
|
+
tier), 2.5.0 is functionally identical to 2.4.0.
|
|
57
|
+
|
|
3
58
|
## 2.4.0
|
|
4
59
|
|
|
5
60
|
### Added
|
e2a-3.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: e2a
|
|
3
|
+
Version: 3.0.0
|
|
4
|
+
Summary: Python SDK for the e2a protocol — email-to-agent authentication
|
|
5
|
+
Project-URL: Homepage, https://e2a.dev
|
|
6
|
+
Project-URL: Repository, https://github.com/Mnexa-AI/e2a
|
|
7
|
+
Project-URL: Documentation, https://e2a.dev
|
|
8
|
+
Author-email: Mnexa AI <josh@mnexa.ai>
|
|
9
|
+
License-Expression: Apache-2.0
|
|
10
|
+
Keywords: agent,authentication,e2a,email,webhook
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Communications :: Email
|
|
21
|
+
Requires-Python: >=3.9
|
|
22
|
+
Requires-Dist: httpx>=0.24
|
|
23
|
+
Requires-Dist: pydantic<3,>=2.12
|
|
24
|
+
Requires-Dist: python-dateutil>=2.8
|
|
25
|
+
Requires-Dist: typing-extensions>=4.7
|
|
26
|
+
Requires-Dist: urllib3>=1.25
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: anyio[trio]; extra == 'dev'
|
|
29
|
+
Requires-Dist: build; extra == 'dev'
|
|
30
|
+
Requires-Dist: pytest-httpx; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest>=7; extra == 'dev'
|
|
32
|
+
Requires-Dist: pyyaml>=6; extra == 'dev'
|
|
33
|
+
Requires-Dist: twine; extra == 'dev'
|
|
34
|
+
Provides-Extra: ws
|
|
35
|
+
Requires-Dist: websockets>=14; extra == 'ws'
|
|
36
|
+
Description-Content-Type: text/markdown
|
|
37
|
+
|
|
38
|
+
# e2a Python SDK
|
|
39
|
+
|
|
40
|
+
Async Python SDK for [e2a](https://e2a.dev) — email for AI agents.
|
|
41
|
+
|
|
42
|
+
## Install
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pip install e2a # add the [ws] extra for client.listen(): pip install "e2a[ws]"
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The SDK major version tracks the SDK package's own breaking changes and is
|
|
49
|
+
independent of the API version path (`/v1`): SDK 3.x targets the e2a v1 API.
|
|
50
|
+
|
|
51
|
+
## Upgrading from 2.x to 3.0
|
|
52
|
+
|
|
53
|
+
3.0 is a breaking redesign. The SDK now wraps a generated `/v1` client behind a
|
|
54
|
+
namespaced, **async-only** surface, with a typed error hierarchy, automatic
|
|
55
|
+
retries + idempotency, and async auto-pagination.
|
|
56
|
+
|
|
57
|
+
- **Async-only, namespaced.** The sync client and the flat methods are gone.
|
|
58
|
+
`client.get_messages()` → `client.messages.list(address)`,
|
|
59
|
+
`client.get_message(id)` → `client.messages.get(address, id)`,
|
|
60
|
+
`client.send(...)` → `client.messages.send(address, body)`. Per-agent calls
|
|
61
|
+
take an explicit `address`.
|
|
62
|
+
- **Webhook verification.** `client.parse` / `client.parse_webhook` /
|
|
63
|
+
`InboundEmail` are removed. Verify and parse a delivery with the standalone
|
|
64
|
+
`construct_event(raw_body, header, secret)`, which returns a typed
|
|
65
|
+
`WebhookEvent`. Signatures are per-webhook (`whsec_…`), Stripe-style.
|
|
66
|
+
- **Typed errors.** Failures raise `E2AError` subclasses (`E2ANotFoundError`,
|
|
67
|
+
`E2AConflictError`, `E2AValidationError`, `E2ARateLimitError`, …) carrying
|
|
68
|
+
`.code`, `.status`, `.request_id`, and `.retryable`.
|
|
69
|
+
|
|
70
|
+
## Quick Start
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
import asyncio
|
|
74
|
+
from e2a.v1 import E2AClient
|
|
75
|
+
|
|
76
|
+
async def main():
|
|
77
|
+
# reads E2A_API_KEY; base_url defaults to https://api.e2a.dev
|
|
78
|
+
async with E2AClient() as client:
|
|
79
|
+
address = "my-agent@agents.e2a.dev"
|
|
80
|
+
|
|
81
|
+
# List endpoints return an AutoPager: async-iterate, or collect with a limit.
|
|
82
|
+
async for m in client.messages.list(address, status="unread"):
|
|
83
|
+
email = await client.messages.get(address, m.message_id)
|
|
84
|
+
print(email.subject)
|
|
85
|
+
await client.messages.reply(address, m.message_id, {"body": "Got it!"})
|
|
86
|
+
|
|
87
|
+
asyncio.run(main())
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Send mail
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
await client.messages.send(address, {
|
|
94
|
+
"to": ["alice@example.com"],
|
|
95
|
+
"subject": "Hello",
|
|
96
|
+
"body": "Hi from my agent!",
|
|
97
|
+
"html_body": "<p>Hi!</p>",
|
|
98
|
+
})
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
The mail-sending writes (`send` / `reply` / `forward` / `approve`) auto-mint an
|
|
102
|
+
`Idempotency-Key` and reuse it across retries, so a network blip can't
|
|
103
|
+
double-send. Pass a stable key to also survive a process restart:
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
await client.messages.send(address, body, idempotency_key=derive_from(event))
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Request bodies accept a plain `dict` (shown above) or the generated model
|
|
110
|
+
(`from e2a.v1 import SendEmailRequest`).
|
|
111
|
+
|
|
112
|
+
### Verify a webhook
|
|
113
|
+
|
|
114
|
+
Each subscription is signed with its own `whsec_…` secret. `construct_event`
|
|
115
|
+
verifies the `X-E2A-Signature` header (replay-protected) and returns a typed
|
|
116
|
+
event. **Pass the raw request body** — re-serialized JSON won't match.
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
from e2a.v1 import construct_event, E2AWebhookSignatureError
|
|
120
|
+
|
|
121
|
+
@app.post("/webhook")
|
|
122
|
+
async def webhook(request):
|
|
123
|
+
try:
|
|
124
|
+
event = construct_event(await request.body(), request.headers["X-E2A-Signature"], SECRET)
|
|
125
|
+
except E2AWebhookSignatureError:
|
|
126
|
+
return Response(status_code=400)
|
|
127
|
+
if event.type == "email.received":
|
|
128
|
+
... # event.data carries the message payload
|
|
129
|
+
return {"ok": True}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
During a rotation you can pass a list of secrets — accepted if any matches:
|
|
133
|
+
`construct_event(body, header, [old_secret, new_secret])`.
|
|
134
|
+
|
|
135
|
+
## Resources
|
|
136
|
+
|
|
137
|
+
`client.agents`, `client.messages`, `client.conversations`, `client.domains`,
|
|
138
|
+
`client.events`, `client.webhooks`, `client.account` (with
|
|
139
|
+
`client.account.suppressions`), plus `await client.info()`. Each method maps to
|
|
140
|
+
a `/v1` operation; per-agent methods take the agent `address` first.
|
|
141
|
+
|
|
142
|
+
### `E2AClient(api_key=None, *, base_url=None, max_retries=2, max_elapsed_ms=None)`
|
|
143
|
+
|
|
144
|
+
`api_key` falls back to `E2A_API_KEY`; `base_url` to `E2A_BASE_URL` then
|
|
145
|
+
`https://api.e2a.dev`. Use it as an async context manager (or call
|
|
146
|
+
`await client.aclose()`) to close the underlying HTTP connections.
|
|
147
|
+
|
|
148
|
+
### Errors
|
|
149
|
+
|
|
150
|
+
Every failure raises an `E2AError` (or subclass) with `.code`, `.status`,
|
|
151
|
+
`.request_id`, `.retryable`: `E2AAuthError` (401), `E2APermissionError` (403),
|
|
152
|
+
`E2ANotFoundError` (404), `E2AConflictError` (409), `E2AValidationError` (422),
|
|
153
|
+
`E2AIdempotencyError`, `E2ARateLimitError` (429), `E2AServerError` (5xx),
|
|
154
|
+
`E2AConnectionError` (no response), `E2AWebhookSignatureError`.
|
|
155
|
+
|
|
156
|
+
> e2a hides the existence of agents you don't own — `agents.get` of an unknown
|
|
157
|
+
> address raises `E2APermissionError` (403), not `E2ANotFoundError`.
|
|
158
|
+
|
|
159
|
+
### Pagination
|
|
160
|
+
|
|
161
|
+
List methods return an `AutoPager` — async-iterate it, or use
|
|
162
|
+
`await pager.to_list(limit=N)` (the limit is required, to bound memory) or
|
|
163
|
+
`await pager.for_each(fn)` (return `False` to stop early).
|
|
164
|
+
|
|
165
|
+
## WebSocket (real-time delivery for local agents)
|
|
166
|
+
|
|
167
|
+
```python
|
|
168
|
+
async for notif in client.listen("bot@agents.e2a.dev"): # falls back to E2A_AGENT_EMAIL
|
|
169
|
+
email = await client.messages.get(notif.recipient, notif.message_id)
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
`client.listen(address)` returns a `WSStream` (async-iterable of
|
|
173
|
+
`WSNotification`) that reconnects with exponential backoff. Requires the `[ws]`
|
|
174
|
+
extra (`pip install "e2a[ws]"`).
|
|
175
|
+
|
|
176
|
+
## License
|
|
177
|
+
|
|
178
|
+
Apache-2.0 — see [LICENSE](https://github.com/Mnexa-AI/e2a/blob/main/LICENSE) and [NOTICE](https://github.com/Mnexa-AI/e2a/blob/main/NOTICE) in the upstream repo.
|
e2a-3.0.0/README.md
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# e2a Python SDK
|
|
2
|
+
|
|
3
|
+
Async Python SDK for [e2a](https://e2a.dev) — email for AI agents.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install e2a # add the [ws] extra for client.listen(): pip install "e2a[ws]"
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
The SDK major version tracks the SDK package's own breaking changes and is
|
|
12
|
+
independent of the API version path (`/v1`): SDK 3.x targets the e2a v1 API.
|
|
13
|
+
|
|
14
|
+
## Upgrading from 2.x to 3.0
|
|
15
|
+
|
|
16
|
+
3.0 is a breaking redesign. The SDK now wraps a generated `/v1` client behind a
|
|
17
|
+
namespaced, **async-only** surface, with a typed error hierarchy, automatic
|
|
18
|
+
retries + idempotency, and async auto-pagination.
|
|
19
|
+
|
|
20
|
+
- **Async-only, namespaced.** The sync client and the flat methods are gone.
|
|
21
|
+
`client.get_messages()` → `client.messages.list(address)`,
|
|
22
|
+
`client.get_message(id)` → `client.messages.get(address, id)`,
|
|
23
|
+
`client.send(...)` → `client.messages.send(address, body)`. Per-agent calls
|
|
24
|
+
take an explicit `address`.
|
|
25
|
+
- **Webhook verification.** `client.parse` / `client.parse_webhook` /
|
|
26
|
+
`InboundEmail` are removed. Verify and parse a delivery with the standalone
|
|
27
|
+
`construct_event(raw_body, header, secret)`, which returns a typed
|
|
28
|
+
`WebhookEvent`. Signatures are per-webhook (`whsec_…`), Stripe-style.
|
|
29
|
+
- **Typed errors.** Failures raise `E2AError` subclasses (`E2ANotFoundError`,
|
|
30
|
+
`E2AConflictError`, `E2AValidationError`, `E2ARateLimitError`, …) carrying
|
|
31
|
+
`.code`, `.status`, `.request_id`, and `.retryable`.
|
|
32
|
+
|
|
33
|
+
## Quick Start
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
import asyncio
|
|
37
|
+
from e2a.v1 import E2AClient
|
|
38
|
+
|
|
39
|
+
async def main():
|
|
40
|
+
# reads E2A_API_KEY; base_url defaults to https://api.e2a.dev
|
|
41
|
+
async with E2AClient() as client:
|
|
42
|
+
address = "my-agent@agents.e2a.dev"
|
|
43
|
+
|
|
44
|
+
# List endpoints return an AutoPager: async-iterate, or collect with a limit.
|
|
45
|
+
async for m in client.messages.list(address, status="unread"):
|
|
46
|
+
email = await client.messages.get(address, m.message_id)
|
|
47
|
+
print(email.subject)
|
|
48
|
+
await client.messages.reply(address, m.message_id, {"body": "Got it!"})
|
|
49
|
+
|
|
50
|
+
asyncio.run(main())
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Send mail
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
await client.messages.send(address, {
|
|
57
|
+
"to": ["alice@example.com"],
|
|
58
|
+
"subject": "Hello",
|
|
59
|
+
"body": "Hi from my agent!",
|
|
60
|
+
"html_body": "<p>Hi!</p>",
|
|
61
|
+
})
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The mail-sending writes (`send` / `reply` / `forward` / `approve`) auto-mint an
|
|
65
|
+
`Idempotency-Key` and reuse it across retries, so a network blip can't
|
|
66
|
+
double-send. Pass a stable key to also survive a process restart:
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
await client.messages.send(address, body, idempotency_key=derive_from(event))
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Request bodies accept a plain `dict` (shown above) or the generated model
|
|
73
|
+
(`from e2a.v1 import SendEmailRequest`).
|
|
74
|
+
|
|
75
|
+
### Verify a webhook
|
|
76
|
+
|
|
77
|
+
Each subscription is signed with its own `whsec_…` secret. `construct_event`
|
|
78
|
+
verifies the `X-E2A-Signature` header (replay-protected) and returns a typed
|
|
79
|
+
event. **Pass the raw request body** — re-serialized JSON won't match.
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
from e2a.v1 import construct_event, E2AWebhookSignatureError
|
|
83
|
+
|
|
84
|
+
@app.post("/webhook")
|
|
85
|
+
async def webhook(request):
|
|
86
|
+
try:
|
|
87
|
+
event = construct_event(await request.body(), request.headers["X-E2A-Signature"], SECRET)
|
|
88
|
+
except E2AWebhookSignatureError:
|
|
89
|
+
return Response(status_code=400)
|
|
90
|
+
if event.type == "email.received":
|
|
91
|
+
... # event.data carries the message payload
|
|
92
|
+
return {"ok": True}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
During a rotation you can pass a list of secrets — accepted if any matches:
|
|
96
|
+
`construct_event(body, header, [old_secret, new_secret])`.
|
|
97
|
+
|
|
98
|
+
## Resources
|
|
99
|
+
|
|
100
|
+
`client.agents`, `client.messages`, `client.conversations`, `client.domains`,
|
|
101
|
+
`client.events`, `client.webhooks`, `client.account` (with
|
|
102
|
+
`client.account.suppressions`), plus `await client.info()`. Each method maps to
|
|
103
|
+
a `/v1` operation; per-agent methods take the agent `address` first.
|
|
104
|
+
|
|
105
|
+
### `E2AClient(api_key=None, *, base_url=None, max_retries=2, max_elapsed_ms=None)`
|
|
106
|
+
|
|
107
|
+
`api_key` falls back to `E2A_API_KEY`; `base_url` to `E2A_BASE_URL` then
|
|
108
|
+
`https://api.e2a.dev`. Use it as an async context manager (or call
|
|
109
|
+
`await client.aclose()`) to close the underlying HTTP connections.
|
|
110
|
+
|
|
111
|
+
### Errors
|
|
112
|
+
|
|
113
|
+
Every failure raises an `E2AError` (or subclass) with `.code`, `.status`,
|
|
114
|
+
`.request_id`, `.retryable`: `E2AAuthError` (401), `E2APermissionError` (403),
|
|
115
|
+
`E2ANotFoundError` (404), `E2AConflictError` (409), `E2AValidationError` (422),
|
|
116
|
+
`E2AIdempotencyError`, `E2ARateLimitError` (429), `E2AServerError` (5xx),
|
|
117
|
+
`E2AConnectionError` (no response), `E2AWebhookSignatureError`.
|
|
118
|
+
|
|
119
|
+
> e2a hides the existence of agents you don't own — `agents.get` of an unknown
|
|
120
|
+
> address raises `E2APermissionError` (403), not `E2ANotFoundError`.
|
|
121
|
+
|
|
122
|
+
### Pagination
|
|
123
|
+
|
|
124
|
+
List methods return an `AutoPager` — async-iterate it, or use
|
|
125
|
+
`await pager.to_list(limit=N)` (the limit is required, to bound memory) or
|
|
126
|
+
`await pager.for_each(fn)` (return `False` to stop early).
|
|
127
|
+
|
|
128
|
+
## WebSocket (real-time delivery for local agents)
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
async for notif in client.listen("bot@agents.e2a.dev"): # falls back to E2A_AGENT_EMAIL
|
|
132
|
+
email = await client.messages.get(notif.recipient, notif.message_id)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
`client.listen(address)` returns a `WSStream` (async-iterable of
|
|
136
|
+
`WSNotification`) that reconnects with exponential backoff. Requires the `[ws]`
|
|
137
|
+
extra (`pip install "e2a[ws]"`).
|
|
138
|
+
|
|
139
|
+
## License
|
|
140
|
+
|
|
141
|
+
Apache-2.0 — see [LICENSE](https://github.com/Mnexa-AI/e2a/blob/main/LICENSE) and [NOTICE](https://github.com/Mnexa-AI/e2a/blob/main/NOTICE) in the upstream repo.
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "e2a"
|
|
7
|
-
version = "
|
|
7
|
+
version = "3.0.0"
|
|
8
8
|
description = "Python SDK for the e2a protocol — email-to-agent authentication"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "Apache-2.0"
|
|
@@ -12,7 +12,7 @@ requires-python = ">=3.9"
|
|
|
12
12
|
authors = [{ name = "Mnexa AI", email = "josh@mnexa.ai" }]
|
|
13
13
|
keywords = ["email", "agent", "authentication", "e2a", "webhook"]
|
|
14
14
|
classifiers = [
|
|
15
|
-
"Development Status ::
|
|
15
|
+
"Development Status :: 5 - Production/Stable",
|
|
16
16
|
"Intended Audience :: Developers",
|
|
17
17
|
"License :: OSI Approved :: Apache Software License",
|
|
18
18
|
"Programming Language :: Python :: 3",
|
|
@@ -23,7 +23,22 @@ classifiers = [
|
|
|
23
23
|
"Programming Language :: Python :: 3.13",
|
|
24
24
|
"Topic :: Communications :: Email",
|
|
25
25
|
]
|
|
26
|
-
dependencies = [
|
|
26
|
+
dependencies = [
|
|
27
|
+
"httpx>=0.24",
|
|
28
|
+
"pydantic>=2.12,<3",
|
|
29
|
+
# Runtime deps of the OpenAPI-Generator /v1 client base (e2a.v1.generated, httpx
|
|
30
|
+
# library): date parsing, the Retry type in configuration, and Self on the
|
|
31
|
+
# generated pydantic v2 models.
|
|
32
|
+
"python-dateutil>=2.8",
|
|
33
|
+
"urllib3>=1.25",
|
|
34
|
+
"typing-extensions>=4.7",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
[tool.hatch.build.targets.wheel]
|
|
38
|
+
# src layout: ship the e2a package (incl. PEP 561 py.typed markers at every
|
|
39
|
+
# hand-written level, not just the generated sub-package).
|
|
40
|
+
packages = ["src/e2a"]
|
|
41
|
+
artifacts = ["src/e2a/py.typed", "src/e2a/v1/py.typed", "src/e2a/v1/generated/py.typed"]
|
|
27
42
|
|
|
28
43
|
[project.urls]
|
|
29
44
|
Homepage = "https://e2a.dev"
|
|
@@ -32,4 +47,4 @@ Documentation = "https://e2a.dev"
|
|
|
32
47
|
|
|
33
48
|
[project.optional-dependencies]
|
|
34
49
|
dev = ["pytest>=7", "pytest-httpx", "anyio[trio]", "pyyaml>=6", "build", "twine"]
|
|
35
|
-
ws = ["websockets>=
|
|
50
|
+
ws = ["websockets>=14"]
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Regenerate the Python /v1 client base from the canonical api/openapi.yaml
|
|
3
|
+
# using OpenAPI Generator's `python` generator with the **httpx** library:
|
|
4
|
+
# async-native (matches the async-only Python decision) and httpx-based (the
|
|
5
|
+
# same HTTP client the hand-written layer uses — no second HTTP dependency).
|
|
6
|
+
# Output lands as the package e2a.v1.generated; the hand-written ergonomic layer wraps it.
|
|
7
|
+
# Pinned image tag → reproducible for the drift gate. Run via Docker (no Java).
|
|
8
|
+
#
|
|
9
|
+
# packageName=e2a.v1.generated so the generator's absolute imports (`from
|
|
10
|
+
# e2a.v1.generated ...`) match the package's final location. We generate to a
|
|
11
|
+
# temp dir and copy only the leaf package, so nothing pollutes the source root
|
|
12
|
+
# and the existing e2a/__init__.py and e2a/v1/__init__.py are never touched.
|
|
13
|
+
set -euo pipefail
|
|
14
|
+
ROOT="$(cd "$(dirname "$0")/../../.." && pwd)"
|
|
15
|
+
TMP="$ROOT/sdks/python/.oag-tmp"
|
|
16
|
+
DEST="$ROOT/sdks/python/src/e2a/v1/generated"
|
|
17
|
+
IMG="openapitools/openapi-generator-cli:v7.16.0"
|
|
18
|
+
|
|
19
|
+
rm -rf "$TMP"
|
|
20
|
+
# Run as the invoking host user (not the container's default root) so the
|
|
21
|
+
# generated files + the .oag-tmp scratch dir are host-user-owned and removable
|
|
22
|
+
# on CI's non-root runner. HOME is a writable path for tools that expect it.
|
|
23
|
+
# (Docker Desktop/macOS maps ownership already, so this is a no-op there but
|
|
24
|
+
# required on Linux CI.)
|
|
25
|
+
docker run --rm --user "$(id -u):$(id -g)" -e HOME=/tmp -v "$ROOT:/work" "$IMG" generate \
|
|
26
|
+
-i /work/api/openapi.yaml -g python \
|
|
27
|
+
-o /work/sdks/python/.oag-tmp \
|
|
28
|
+
--additional-properties=packageName=e2a.v1.generated,library=httpx >/dev/null
|
|
29
|
+
|
|
30
|
+
rm -rf "$DEST"
|
|
31
|
+
cp -r "$TMP/e2a/v1/generated" "$DEST"
|
|
32
|
+
rm -rf "$TMP"
|
|
33
|
+
|
|
34
|
+
# Strip the generator's `*_validate_enum` validators so the client tolerates
|
|
35
|
+
# unknown enum values (forward-compat: a new server enum value must not crash a
|
|
36
|
+
# deployed client). Matches the TypeScript SDK's passthrough behavior.
|
|
37
|
+
python3 "$ROOT/sdks/python/scripts/strip-enum-validators.py" "$DEST"
|
|
38
|
+
|
|
39
|
+
echo "Python /v1 client base regenerated at sdks/python/src/e2a/v1/generated"
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Strip OpenAPI-Generator's `*_validate_enum` pydantic validators from the
|
|
3
|
+
generated models so the client tolerates unknown enum values.
|
|
4
|
+
|
|
5
|
+
Why: the generator emits, for every enum-typed field, a `@field_validator` that
|
|
6
|
+
raises `ValueError` when the value isn't in a hard-coded set. On a RESPONSE
|
|
7
|
+
field that means the day the server adds a new enum value (a new event `type`,
|
|
8
|
+
`delivery_status`, `inbound_policy`, …) every deployed client crashes while
|
|
9
|
+
deserializing — turning an additive, non-breaking server change into a
|
|
10
|
+
client-breaking one. The TypeScript SDK passes unknown enum values through
|
|
11
|
+
untouched; this makes Python match, keeping enum fields typed as plain strings.
|
|
12
|
+
|
|
13
|
+
Run as part of generate-oag.sh (so the drift gate's regenerated output matches)
|
|
14
|
+
and idempotently re-runnable on the committed tree. Removes each block of the
|
|
15
|
+
form:
|
|
16
|
+
|
|
17
|
+
@field_validator('field')
|
|
18
|
+
def field_validate_enum(cls, value):
|
|
19
|
+
\"\"\"Validates the enum\"\"\"
|
|
20
|
+
if value not in set([...]):
|
|
21
|
+
raise ValueError(...)
|
|
22
|
+
return value
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
import glob
|
|
28
|
+
import os
|
|
29
|
+
import re
|
|
30
|
+
import sys
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def strip_file(path: str) -> bool:
|
|
34
|
+
with open(path, "r", encoding="utf-8") as f:
|
|
35
|
+
lines = f.readlines()
|
|
36
|
+
|
|
37
|
+
out: list[str] = []
|
|
38
|
+
i = 0
|
|
39
|
+
n = len(lines)
|
|
40
|
+
changed = False
|
|
41
|
+
decorator = re.compile(r"^\s*@field_validator\(")
|
|
42
|
+
enum_def = re.compile(r"^\s*def\s+\w+_validate_enum\(")
|
|
43
|
+
while i < n:
|
|
44
|
+
line = lines[i]
|
|
45
|
+
# A `@field_validator(...)` decorator whose method is an enum validator:
|
|
46
|
+
# drop the decorator(s) + the entire def body. The body runs until the
|
|
47
|
+
# next non-blank line indented no deeper than the def itself (the next
|
|
48
|
+
# class member). Consuming by indentation — not by a `return value`
|
|
49
|
+
# sentinel — correctly handles optional-field validators that early-
|
|
50
|
+
# return on None before the enum check.
|
|
51
|
+
if decorator.match(line):
|
|
52
|
+
j = i + 1
|
|
53
|
+
while j < n and lines[j].strip().startswith("@"):
|
|
54
|
+
j += 1
|
|
55
|
+
if j < n and enum_def.match(lines[j]):
|
|
56
|
+
def_indent = len(lines[j]) - len(lines[j].lstrip())
|
|
57
|
+
k = j + 1
|
|
58
|
+
while k < n:
|
|
59
|
+
stripped = lines[k].strip()
|
|
60
|
+
if stripped == "":
|
|
61
|
+
k += 1
|
|
62
|
+
continue
|
|
63
|
+
indent = len(lines[k]) - len(lines[k].lstrip())
|
|
64
|
+
if indent > def_indent:
|
|
65
|
+
k += 1
|
|
66
|
+
continue
|
|
67
|
+
break
|
|
68
|
+
i = k
|
|
69
|
+
changed = True
|
|
70
|
+
continue
|
|
71
|
+
out.append(line)
|
|
72
|
+
i += 1
|
|
73
|
+
|
|
74
|
+
if changed:
|
|
75
|
+
with open(path, "w", encoding="utf-8") as f:
|
|
76
|
+
f.writelines(out)
|
|
77
|
+
return changed
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def main() -> int:
|
|
81
|
+
root = sys.argv[1] if len(sys.argv) > 1 else os.path.join(
|
|
82
|
+
os.path.dirname(__file__), "..", "src", "e2a", "v1", "generated"
|
|
83
|
+
)
|
|
84
|
+
models = glob.glob(os.path.join(root, "models", "*.py"))
|
|
85
|
+
touched = sum(1 for p in sorted(models) if strip_file(p))
|
|
86
|
+
print(f"strip-enum-validators: removed enum validators from {touched} model file(s)")
|
|
87
|
+
return 0
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
if __name__ == "__main__":
|
|
91
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Top-level convenience alias — points to the current stable API version (v1).
|
|
2
|
+
#
|
|
3
|
+
# The pinned contract path is `e2a.v1`:
|
|
4
|
+
# from e2a.v1 import E2AClient
|
|
5
|
+
#
|
|
6
|
+
# The top-level `e2a` package re-exports that surface for convenience.
|
|
7
|
+
|
|
8
|
+
from e2a.v1 import ( # noqa: F401
|
|
9
|
+
AutoPager,
|
|
10
|
+
E2AAuthError,
|
|
11
|
+
E2AClient,
|
|
12
|
+
E2AConflictError,
|
|
13
|
+
E2AConnectionError,
|
|
14
|
+
E2AError,
|
|
15
|
+
E2AIdempotencyError,
|
|
16
|
+
E2ANotFoundError,
|
|
17
|
+
E2APermissionError,
|
|
18
|
+
E2ARateLimitError,
|
|
19
|
+
E2AServerError,
|
|
20
|
+
E2AValidationError,
|
|
21
|
+
E2AWebhookSignatureError,
|
|
22
|
+
Page,
|
|
23
|
+
WebhookEvent,
|
|
24
|
+
WSNotification,
|
|
25
|
+
WSStream,
|
|
26
|
+
construct_event,
|
|
27
|
+
models,
|
|
28
|
+
verify_webhook_signature,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
__all__ = [
|
|
32
|
+
"E2AClient",
|
|
33
|
+
"E2AError",
|
|
34
|
+
"E2AAuthError",
|
|
35
|
+
"E2APermissionError",
|
|
36
|
+
"E2ANotFoundError",
|
|
37
|
+
"E2AConflictError",
|
|
38
|
+
"E2AValidationError",
|
|
39
|
+
"E2AIdempotencyError",
|
|
40
|
+
"E2ARateLimitError",
|
|
41
|
+
"E2AServerError",
|
|
42
|
+
"E2AConnectionError",
|
|
43
|
+
"E2AWebhookSignatureError",
|
|
44
|
+
"AutoPager",
|
|
45
|
+
"Page",
|
|
46
|
+
"verify_webhook_signature",
|
|
47
|
+
"construct_event",
|
|
48
|
+
"WebhookEvent",
|
|
49
|
+
"WSNotification",
|
|
50
|
+
"WSStream",
|
|
51
|
+
"models",
|
|
52
|
+
]
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""Public surface of the e2a v1 SDK (async-only).
|
|
2
|
+
|
|
3
|
+
The canonical request/response types are the OpenAPI-Generator ``generated``
|
|
4
|
+
models; the hand-written ergonomic layer (:class:`E2AClient` + resources, the
|
|
5
|
+
typed error hierarchy, retry/pagination, webhook verification, WS) wraps them.
|
|
6
|
+
The legacy flat/sync ``api`` / ``client`` / ``handler`` surface and the old
|
|
7
|
+
swag-generated Pydantic types have been retired in favour of this.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
# Generated request/response models (the canonical types).
|
|
11
|
+
from e2a.v1.generated import models # noqa: F401
|
|
12
|
+
from e2a.v1.generated.models import * # noqa: F401,F403
|
|
13
|
+
|
|
14
|
+
# High-level async client.
|
|
15
|
+
from e2a.v1.client import E2AClient # noqa: F401
|
|
16
|
+
|
|
17
|
+
# Typed error hierarchy.
|
|
18
|
+
from e2a.v1.errors import ( # noqa: F401
|
|
19
|
+
E2AAuthError,
|
|
20
|
+
E2AConflictError,
|
|
21
|
+
E2AConnectionError,
|
|
22
|
+
E2AError,
|
|
23
|
+
E2AIdempotencyError,
|
|
24
|
+
E2ANotFoundError,
|
|
25
|
+
E2APermissionError,
|
|
26
|
+
E2ARateLimitError,
|
|
27
|
+
E2AServerError,
|
|
28
|
+
E2AValidationError,
|
|
29
|
+
E2AWebhookSignatureError,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
# Auto-pagination.
|
|
33
|
+
from e2a.v1.pagination import AutoPager, Page # noqa: F401
|
|
34
|
+
|
|
35
|
+
# Webhook signature verification.
|
|
36
|
+
from e2a.v1.webhook_signature import ( # noqa: F401
|
|
37
|
+
WebhookEvent,
|
|
38
|
+
construct_event,
|
|
39
|
+
verify_webhook_signature,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
# Real-time WebSocket stream.
|
|
43
|
+
from e2a.v1.websocket import WSNotification, WSStream # noqa: F401
|
|
44
|
+
|
|
45
|
+
__all__ = [
|
|
46
|
+
"E2AClient",
|
|
47
|
+
# Errors
|
|
48
|
+
"E2AError",
|
|
49
|
+
"E2AAuthError",
|
|
50
|
+
"E2APermissionError",
|
|
51
|
+
"E2ANotFoundError",
|
|
52
|
+
"E2AConflictError",
|
|
53
|
+
"E2AValidationError",
|
|
54
|
+
"E2AIdempotencyError",
|
|
55
|
+
"E2ARateLimitError",
|
|
56
|
+
"E2AServerError",
|
|
57
|
+
"E2AConnectionError",
|
|
58
|
+
"E2AWebhookSignatureError",
|
|
59
|
+
# Pagination
|
|
60
|
+
"AutoPager",
|
|
61
|
+
"Page",
|
|
62
|
+
# Webhooks
|
|
63
|
+
"verify_webhook_signature",
|
|
64
|
+
"construct_event",
|
|
65
|
+
"WebhookEvent",
|
|
66
|
+
# WebSocket
|
|
67
|
+
"WSNotification",
|
|
68
|
+
"WSStream",
|
|
69
|
+
# Generated models namespace
|
|
70
|
+
"models",
|
|
71
|
+
]
|