diff --git a/cuenca/__init__.py b/cuenca/__init__.py index 3e9a5e43..5ec2c2b9 100644 --- a/cuenca/__init__.py +++ b/cuenca/__init__.py @@ -1,5 +1,7 @@ __all__ = [ '__version__', + 'Agent', + 'AgentVerification', 'ApiKey', 'Account', 'Arpc', @@ -53,6 +55,8 @@ from . import http from .resources import ( Account, + Agent, + AgentVerification, ApiKey, Arpc, BalanceEntry, diff --git a/cuenca/resources/__init__.py b/cuenca/resources/__init__.py index a69347f4..1a7a0d05 100644 --- a/cuenca/resources/__init__.py +++ b/cuenca/resources/__init__.py @@ -1,4 +1,6 @@ __all__ = [ + 'Agent', + 'AgentVerification', 'ApiKey', 'Account', 'Arpc', @@ -47,6 +49,8 @@ ] from .accounts import Account +from .agent_verifications import AgentVerification +from .agents import Agent from .api_keys import ApiKey from .arpc import Arpc from .balance_entries import BalanceEntry @@ -96,6 +100,8 @@ # avoid circular imports resource_classes = [ + Agent, + AgentVerification, ApiKey, Account, Arpc, diff --git a/cuenca/resources/agent_verifications.py b/cuenca/resources/agent_verifications.py new file mode 100644 index 00000000..3ead6c6e --- /dev/null +++ b/cuenca/resources/agent_verifications.py @@ -0,0 +1,37 @@ +import datetime as dt +from typing import ClassVar + +from cuenca_validations.types.identities import PhoneNumber +from pydantic import ConfigDict + +from ..http import Session, session as global_session +from .base import Creatable + + +class AgentVerification(Creatable): + _resource: ClassVar = 'agent_verifications' + + created_at: dt.datetime + user_id: str + platform_id: str + phone_number: PhoneNumber + pairing_code: str + deactivated_at: dt.datetime + + model_config = ConfigDict( + json_schema_extra={ + 'example': { + 'id': 'AVjTtPH1mhT65GEgOeomJ4DQ', + 'created_at': '2026-06-25T22:50:08.495768', + 'user_id': 'USMP3EPgI4T4CPFyVmXUPi8A', + 'platform_id': 'PTZbBlk__kQt-wfwzP5nwA9A', + 'phone_number': '+525512345678', + 'pairing_code': 'OJC37W', + 'deactivated_at': '2026-06-25T22:55:08.495779', + } + } + ) + + @classmethod + def create(cls, session: Session = global_session) -> 'AgentVerification': + return cls._create(session=session) diff --git a/cuenca/resources/agents.py b/cuenca/resources/agents.py new file mode 100644 index 00000000..fb062c72 --- /dev/null +++ b/cuenca/resources/agents.py @@ -0,0 +1,52 @@ +import datetime as dt +from typing import ClassVar, Optional + +from cuenca_validations.types import AgentQuery, AgentRequest, PhoneNumber +from cuenca_validations.typing import DictStrAny +from pydantic import ConfigDict, Field + +from ..http import Session, session as global_session +from .base import Creatable, Queryable + + +class Agent(Creatable, Queryable): + _resource: ClassVar = 'agents' + _query_params: ClassVar = AgentQuery + + created_at: dt.datetime + user_id: str + platform_id: str + agent_verification_id: str + session_id: str + device_info: DictStrAny = Field(default_factory=dict) + deactivated_at: Optional[dt.datetime] = None + + model_config = ConfigDict( + json_schema_extra={ + 'example': { + 'id': 'AG1G6Bm0oGQOCRjTDaeFSsyA', + 'created_at': '2026-06-27T01:56:42.613781', + 'user_id': 'USMP3EPgI4T4CPFyVmXUPi8A', + 'platform_id': 'PTZbBlk__kQt-wfwzP5nwA9A', + 'agent_verification_id': 'AV7A1FLC7MSnqKcb7oAkiQxA', + 'session_id': 'SSH0M5yteyRHas-PmfT9pu9w', + 'device_info': {'client': 'cursor', 'os': 'macOS'}, + } + } + ) + + @classmethod + def create( + cls, + pairing_code: str, + phone_number: PhoneNumber, + device_info: Optional[DictStrAny] = None, + *, + session: Session = global_session, + ) -> 'Agent': + req = AgentRequest( + pairing_code=pairing_code, + phone_number=phone_number, + device_info=device_info or {}, + ) + return cls._create(session=session, **req.model_dump()) diff --git a/cuenca/version.py b/cuenca/version.py index a1029370..20502c51 100644 --- a/cuenca/version.py +++ b/cuenca/version.py @@ -1,3 +1,3 @@ -__version__ = '2.2.1' +__version__ = '2.2.2' CLIENT_VERSION = __version__ API_VERSION = '2020-03-19' diff --git a/requirements.txt b/requirements.txt index 3da97f14..0d18fa50 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ requests==2.32.3 -cuenca-validations==2.1.34 +cuenca-validations==2.1.39 pydantic-extra-types==2.10.2 diff --git a/tests/resources/cassettes/test_agent_create.yaml b/tests/resources/cassettes/test_agent_create.yaml new file mode 100644 index 00000000..062ad12b --- /dev/null +++ b/tests/resources/cassettes/test_agent_create.yaml @@ -0,0 +1,93 @@ +interactions: +- request: + body: '{}' + headers: + Authorization: + - DUMMY + Content-Length: + - '2' + Content-Type: + - application/json + User-Agent: + - cuenca-python/2.2.2.dev0 + X-Cuenca-Api-Version: + - '2020-03-19' + X-Cuenca-LoginId: + - DUMMY + method: POST + uri: https://sandbox.cuenca.com/agent_verifications + response: + body: + string: '{"id":"AVGrMmja4vRpCs1L7q5oZaGg","created_at":"2026-06-29T21:12:29.455175","user_id":"USMP3EPgI4T4CPFyVmXUPi8A","platform_id":"PTZbBlk__kQt-wfwzP5nwA9A","phone_number":"+525520902020","pairing_code":"E89U5U","deactivated_at":"2026-06-29T21:17:29.455186"}' + headers: + Connection: + - keep-alive + Content-Length: + - '254' + Content-Type: + - application/json + Date: + - Mon, 29 Jun 2026 21:12:29 GMT + X-Request-Time: + - 'value: 0.433' + x-amz-apigw-id: + - fvjWTGVvCYcEcIQ= + x-amzn-Remapped-Connection: + - keep-alive + x-amzn-Remapped-Content-Length: + - '254' + x-amzn-Remapped-Date: + - Mon, 29 Jun 2026 21:12:29 GMT + x-amzn-Remapped-Server: + - nginx/1.30.3 + x-amzn-RequestId: + - e418d89f-5e4f-4540-8334-d81914362ad9 + status: + code: 201 + message: Created +- request: + body: '{"pairing_code": "E89U5U", "phone_number": "+525520902020", "device_info": + {"client": "cursor", "os": "macOS"}}' + headers: + Authorization: + - DUMMY + Content-Length: + - '111' + Content-Type: + - application/json + User-Agent: + - cuenca-python/2.2.2.dev0 + X-Cuenca-Api-Version: + - '2020-03-19' + method: POST + uri: https://sandbox.cuenca.com/agents + response: + body: + string: '{"id":"AG7eGV9zSIR92V7fNKnM57UA","created_at":"2026-06-29T21:12:34.918228","user_id":"USMP3EPgI4T4CPFyVmXUPi8A","platform_id":"PTZbBlk__kQt-wfwzP5nwA9A","agent_verification_id":"AVGrMmja4vRpCs1L7q5oZaGg","session_id":"SSHdQzltPgQByBqJYGXcBJUg","device_info":{}}' + headers: + Connection: + - keep-alive + Content-Length: + - '261' + Content-Type: + - application/json + Date: + - Mon, 29 Jun 2026 21:12:34 GMT + X-Request-Time: + - 'value: 0.283' + x-amz-apigw-id: + - fvjWeF6iiYcEGYw= + x-amzn-Remapped-Connection: + - keep-alive + x-amzn-Remapped-Content-Length: + - '261' + x-amzn-Remapped-Date: + - Mon, 29 Jun 2026 21:12:34 GMT + x-amzn-Remapped-Server: + - nginx/1.30.3 + x-amzn-RequestId: + - babb4d7a-b745-46f8-894d-fb2a070f8484 + status: + code: 201 + message: Created +version: 1 diff --git a/tests/resources/test_agents.py b/tests/resources/test_agents.py new file mode 100644 index 00000000..f11b455c --- /dev/null +++ b/tests/resources/test_agents.py @@ -0,0 +1,22 @@ +import pytest + +from cuenca.http.client import Session +from cuenca.resources import Agent, AgentVerification + + +@pytest.mark.vcr +def test_agent_create(): + verification = AgentVerification.create() # Created by user in APP + agent_session = Session() + agent_session.configure(sandbox=True) + agent = Agent.create( + pairing_code=verification.pairing_code, + phone_number=verification.phone_number, + device_info={'client': 'cursor', 'os': 'macOS'}, + session=agent_session, + ) + assert agent.id + assert agent.agent_verification_id == verification.id + assert agent.session_id + assert agent.user_id + assert agent.platform_id