booking-agent-cli 0.1.0__py3-none-any.whl

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.
Files changed (74) hide show
  1. booking_agent/__init__.py +6 -0
  2. booking_agent/authorization.py +450 -0
  3. booking_agent/backend/__init__.py +217 -0
  4. booking_agent/backend/live.py +494 -0
  5. booking_agent/calendar/__init__.py +55 -0
  6. booking_agent/calendar/filter.py +41 -0
  7. booking_agent/calendar/google.py +582 -0
  8. booking_agent/calendar/oauth_listener.py +287 -0
  9. booking_agent/calendar/scope.py +97 -0
  10. booking_agent/circuit_breaker.py +116 -0
  11. booking_agent/config.py +51 -0
  12. booking_agent/defaults.env +50 -0
  13. booking_agent/discovery.py +188 -0
  14. booking_agent/headless.py +463 -0
  15. booking_agent/http_deadlines.py +43 -0
  16. booking_agent/http_retry.py +178 -0
  17. booking_agent/http_trace.py +428 -0
  18. booking_agent/merchant_pacing.py +73 -0
  19. booking_agent/models/__init__.py +14 -0
  20. booking_agent/models/platform.py +200 -0
  21. booking_agent/models/wire.py +868 -0
  22. booking_agent/namespace.py +53 -0
  23. booking_agent/observability.py +143 -0
  24. booking_agent/platform_profile.py +120 -0
  25. booking_agent/pop.py +347 -0
  26. booking_agent/profile_cache.py +130 -0
  27. booking_agent/registry_url.py +35 -0
  28. booking_agent/rest_client.py +1493 -0
  29. booking_agent/secure_file.py +52 -0
  30. booking_agent/settings.py +110 -0
  31. booking_agent/signals.py +130 -0
  32. booking_agent/step_timing.py +168 -0
  33. booking_agent/ucp_capabilities.py +148 -0
  34. booking_agent/ucp_checkout.py +1363 -0
  35. booking_agent/ucp_negotiation.py +184 -0
  36. booking_agent/ucp_profile.py +410 -0
  37. booking_agent/usp_methods.py +110 -0
  38. booking_agent_cli/__init__.py +3 -0
  39. booking_agent_cli/activity_log.py +106 -0
  40. booking_agent_cli/booking_poll.py +141 -0
  41. booking_agent_cli/booking_summary.py +151 -0
  42. booking_agent_cli/calendar_connection.py +196 -0
  43. booking_agent_cli/calendar_event.py +611 -0
  44. booking_agent_cli/calendar_preference.py +176 -0
  45. booking_agent_cli/catalog.py +864 -0
  46. booking_agent_cli/checkout_prereq.py +67 -0
  47. booking_agent_cli/commands/__init__.py +0 -0
  48. booking_agent_cli/commands/config.py +106 -0
  49. booking_agent_cli/commands/flow.py +6988 -0
  50. booking_agent_cli/commands/lowlevel.py +163 -0
  51. booking_agent_cli/commands/runtime.py +46 -0
  52. booking_agent_cli/config_loader.py +108 -0
  53. booking_agent_cli/engine.py +168 -0
  54. booking_agent_cli/flow_timings.py +34 -0
  55. booking_agent_cli/geo_eligibility.py +183 -0
  56. booking_agent_cli/geocode.py +233 -0
  57. booking_agent_cli/identity.py +104 -0
  58. booking_agent_cli/link_cli.py +620 -0
  59. booking_agent_cli/main.py +75 -0
  60. booking_agent_cli/output.py +199 -0
  61. booking_agent_cli/payment_helpers.py +884 -0
  62. booking_agent_cli/payment_token.py +23 -0
  63. booking_agent_cli/reschedule.py +137 -0
  64. booking_agent_cli/runtime.py +55 -0
  65. booking_agent_cli/runtime_prepare.py +129 -0
  66. booking_agent_cli/search_area.py +111 -0
  67. booking_agent_cli/session.py +837 -0
  68. booking_agent_cli/support_bundle_schema.py +557 -0
  69. booking_agent_cli/support_report.py +1210 -0
  70. booking_agent_cli/timezone_context.py +314 -0
  71. booking_agent_cli-0.1.0.dist-info/METADATA +75 -0
  72. booking_agent_cli-0.1.0.dist-info/RECORD +74 -0
  73. booking_agent_cli-0.1.0.dist-info/WHEEL +4 -0
  74. booking_agent_cli-0.1.0.dist-info/entry_points.txt +2 -0
@@ -0,0 +1,200 @@
1
+ """Platform-side USP orchestration models for booking-agent-cli.
2
+
3
+ These models live on the platform (Booking Agent CLI) side and are NOT part of the
4
+ USP wire protocol. Business-side implementations do not need them.
5
+ """
6
+ from enum import Enum
7
+ from typing import Any, List, Optional, Union
8
+
9
+ from pydantic import (
10
+ AliasChoices,
11
+ BaseModel,
12
+ ConfigDict,
13
+ Field,
14
+ ValidationError,
15
+ field_validator,
16
+ )
17
+ from pydantic.alias_generators import to_camel
18
+
19
+ from .wire import AvailabilityHint, Coordinates, RegistryBusinessInfo
20
+
21
+
22
+ class BookingPaymentMode(str, Enum):
23
+ GENERIC = "generic"
24
+ UCP = "ucp"
25
+ REDIRECT = "redirect"
26
+
27
+
28
+ class ServiceSearchLocation(BaseModel):
29
+ model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)
30
+
31
+ coordinates: Coordinates
32
+ radius_km: Optional[float] = None
33
+
34
+
35
+ class RangeMatchMode(str, Enum):
36
+ """How a service interval S is compared to a registry filter interval F.
37
+
38
+ Mirrors ``schemas/registry.json#/$defs/RangeMatchMode`` (USP §6.3.1).
39
+ Omitted ``match`` on the wire MUST be treated as ``overlap`` by registries.
40
+ """
41
+
42
+ OVERLAP = "overlap"
43
+ CONTAINED = "contained"
44
+ CONTAINS = "contains"
45
+ EQUALS = "equals"
46
+
47
+
48
+ class PriceRange(BaseModel):
49
+ """Registry ``price_range`` filter (``schemas/registry.json#/$defs/PriceRangeFilter``)."""
50
+
51
+ model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)
52
+
53
+ min: Optional[int] = None
54
+ max: Optional[int] = None
55
+ currency: str = "USD"
56
+ match: Optional[RangeMatchMode] = None
57
+
58
+
59
+ class DurationRange(BaseModel):
60
+ """Registry ``duration_range`` filter (``schemas/registry.json#/$defs/DurationRangeFilter``)."""
61
+
62
+ model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)
63
+
64
+ min_minutes: Optional[int] = None
65
+ max_minutes: Optional[int] = None
66
+ match: Optional[RangeMatchMode] = None
67
+
68
+
69
+ class DesiredServiceTimeMoment(BaseModel):
70
+ model_config = ConfigDict(
71
+ alias_generator=to_camel, populate_by_name=True, extra="forbid"
72
+ )
73
+
74
+ at: str
75
+
76
+
77
+ class DesiredServiceTimeRange(BaseModel):
78
+ model_config = ConfigDict(
79
+ alias_generator=to_camel, populate_by_name=True, extra="forbid"
80
+ )
81
+
82
+ start: str
83
+ end: str
84
+
85
+
86
+ class DesiredServiceTimeFrom(BaseModel):
87
+ model_config = ConfigDict(
88
+ alias_generator=to_camel, populate_by_name=True, extra="forbid"
89
+ )
90
+
91
+ start: str
92
+
93
+
94
+ DesiredServiceTimePreference = Union[
95
+ DesiredServiceTimeMoment,
96
+ DesiredServiceTimeRange,
97
+ DesiredServiceTimeFrom,
98
+ ]
99
+
100
+
101
+ class ServiceSearchIntent(BaseModel):
102
+ """Platform-side intent object for service discovery (analogous to IntentMandate)."""
103
+ model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)
104
+
105
+ natural_language_description: str
106
+ location: Optional[ServiceSearchLocation] = None
107
+ verticals: List[str] = []
108
+ categories: List[str] = []
109
+ query: str
110
+ price_range: Optional[PriceRange] = None
111
+ duration_range: Optional[DurationRange] = None
112
+ date_preference: Optional[str] = None
113
+ desired_service_time_ranges: Optional[List[DesiredServiceTimePreference]] = None
114
+ prefer_sooner_availability_slots: Optional[bool] = None
115
+ intent_expiry: str
116
+
117
+
118
+ # ---------------------------------------------------------------------------
119
+ # Registry search response models (mirrors /registry/search_services response)
120
+ # ---------------------------------------------------------------------------
121
+
122
+ class ServiceSearchResultPrice(BaseModel):
123
+ model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)
124
+
125
+ model: Optional[str] = None
126
+ amount: Optional[int] = None
127
+ currency: Optional[str] = None
128
+
129
+ @field_validator("amount", mode="before")
130
+ @classmethod
131
+ def _coerce_amount(cls, value: object) -> Optional[int]:
132
+ if value is None or value == "":
133
+ return None
134
+ return int(value)
135
+
136
+
137
+ class ServiceSearchResultLocation(BaseModel):
138
+ model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)
139
+
140
+ address: Optional[str] = None
141
+ coordinates: Optional[Coordinates] = None
142
+
143
+
144
+ class RankSignals(BaseModel):
145
+ model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)
146
+
147
+ relevance: float = Field(ge=0, le=1)
148
+ coverage: Optional[float] = Field(ge=0, le=1)
149
+ density: Optional[float] = Field(ge=0, le=1)
150
+ soonness: Optional[float] = Field(ge=0, le=1)
151
+ hint_usable: bool
152
+
153
+
154
+ class ServiceSearchResult(BaseModel):
155
+ """schemas/registry.json#/$defs/ServiceSearchResult.
156
+
157
+ Spec REQUIRES (service_id, service_name, business, category, pricing, timezone).
158
+ ``pricing`` (named ``price`` here, the field name the client reads) and
159
+ ``timezone`` are Optional: the client accepts results without them.
160
+ """
161
+ model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)
162
+
163
+ service_id: str
164
+ service_name: str
165
+ business: RegistryBusinessInfo
166
+ category: str
167
+ duration_minutes: Optional[int] = None
168
+ price: Optional[ServiceSearchResultPrice] = Field(
169
+ default=None,
170
+ validation_alias=AliasChoices("price", "pricing"),
171
+ serialization_alias="pricing",
172
+ )
173
+ location: Optional[ServiceSearchResultLocation] = None
174
+ timezone: Optional[str] = None
175
+ category_ids: Optional[List[str]] = None
176
+ availability_hint: Optional[AvailabilityHint] = None
177
+ rank_signals: Optional[RankSignals] = None
178
+
179
+ @field_validator("availability_hint", mode="before")
180
+ @classmethod
181
+ def _drop_malformed_availability_hint(
182
+ cls, value: Any
183
+ ) -> Optional[AvailabilityHint]:
184
+ if value is None or isinstance(value, AvailabilityHint):
185
+ return value
186
+ try:
187
+ return AvailabilityHint.model_validate(value)
188
+ except (ValidationError, TypeError, ValueError):
189
+ return None
190
+
191
+
192
+ class ServiceSearchPagination(BaseModel):
193
+ """Pagination metadata; supports cursor- and offset-based styles."""
194
+ model_config = ConfigDict(alias_generator=to_camel, populate_by_name=True)
195
+
196
+ cursor: Optional[str] = None
197
+ has_more: Optional[bool] = None
198
+ count: Optional[int] = None
199
+ offset: Optional[int] = None
200
+ has_next: Optional[bool] = None