karrio-eshipper 2025.5.6__py3-none-any.whl → 2026.1__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.
@@ -1,14 +1,74 @@
1
1
  """Karrio eShipper client proxy."""
2
2
 
3
+ import datetime
3
4
  import karrio.lib as lib
4
5
  import karrio.api.proxy as proxy
6
+ import karrio.core.errors as errors
7
+ import karrio.core.models as models
8
+ import karrio.providers.eshipper.error as provider_error
5
9
  import karrio.mappers.eshipper.settings as provider_settings
6
10
 
7
11
 
8
12
  class Proxy(proxy.Proxy):
9
13
  settings: provider_settings.Settings
10
14
 
15
+ def authenticate(self, _=None) -> lib.Deserializable[str]:
16
+ """Retrieve the token using the principal|credential pair
17
+ or collect it from the cache if an unexpired token exist.
18
+ """
19
+ cache_key = f"{self.settings.carrier_name}|{self.settings.principal}|{self.settings.credential}"
20
+
21
+ def get_token():
22
+ result = lib.request(
23
+ url=f"{self.settings.server_url}/api/v2/authenticate",
24
+ trace=self.settings.trace_as("json"),
25
+ method="POST",
26
+ headers={"content-Type": "application/json"},
27
+ data=lib.to_json(
28
+ {
29
+ "principal": self.settings.principal,
30
+ "credential": self.settings.credential,
31
+ }
32
+ ),
33
+ max_retries=2,
34
+ )
35
+
36
+ response = lib.to_dict(result)
37
+ messages = provider_error.parse_error_response(response, self.settings)
38
+
39
+ if any(messages):
40
+ raise errors.ParsedMessagesError(messages=messages)
41
+
42
+ # Validate that token is present in the response
43
+ if "token" not in response:
44
+ raise errors.ParsedMessagesError(
45
+ messages=[
46
+ models.Message(
47
+ carrier_name=self.settings.carrier_name,
48
+ carrier_id=self.settings.carrier_id,
49
+ message="Authentication failed: No token received",
50
+ code="AUTH_ERROR",
51
+ )
52
+ ]
53
+ )
54
+
55
+ expiry = datetime.datetime.now() + datetime.timedelta(
56
+ seconds=float(response.get("expires_in", 0))
57
+ )
58
+
59
+ return {**response, "expiry": lib.fdatetime(expiry)}
60
+
61
+ token = self.settings.connection_cache.thread_safe(
62
+ refresh_func=get_token,
63
+ cache_key=cache_key,
64
+ buffer_minutes=30,
65
+ token_field="token",
66
+ )
67
+
68
+ return lib.Deserializable(token.get_state())
69
+
11
70
  def get_rates(self, request: lib.Serializable) -> lib.Deserializable[str]:
71
+ access_token = self.authenticate().deserialize()
12
72
  response = lib.request(
13
73
  url=f"{self.settings.server_url}/api/v2/quote",
14
74
  data=lib.to_json(request.serialize()),
@@ -16,13 +76,14 @@ class Proxy(proxy.Proxy):
16
76
  method="POST",
17
77
  headers={
18
78
  "content-Type": "application/json",
19
- "Authorization": f"Bearer {self.settings.access_token}",
79
+ "Authorization": f"Bearer {access_token}",
20
80
  },
21
81
  )
22
82
 
23
83
  return lib.Deserializable(response, lib.to_dict)
24
84
 
25
85
  def create_shipment(self, request: lib.Serializable) -> lib.Deserializable[str]:
86
+ access_token = self.authenticate().deserialize()
26
87
  response = lib.request(
27
88
  url=f"{self.settings.server_url}/api/v2/ship",
28
89
  data=lib.to_json(request.serialize()),
@@ -30,13 +91,14 @@ class Proxy(proxy.Proxy):
30
91
  method="PUT",
31
92
  headers={
32
93
  "content-Type": "application/json",
33
- "Authorization": f"Bearer {self.settings.access_token}",
94
+ "Authorization": f"Bearer {access_token}",
34
95
  },
35
96
  )
36
97
 
37
98
  return lib.Deserializable(response, lib.to_dict)
38
99
 
39
100
  def cancel_shipment(self, request: lib.Serializable) -> lib.Deserializable[str]:
101
+ access_token = self.authenticate().deserialize()
40
102
  response = lib.request(
41
103
  url=f"{self.settings.server_url}/api/v2/ship/cancel",
42
104
  data=lib.to_json(request.serialize()),
@@ -44,13 +106,14 @@ class Proxy(proxy.Proxy):
44
106
  method="DELETE",
45
107
  headers={
46
108
  "content-Type": "application/json",
47
- "Authorization": f"Bearer {self.settings.access_token}",
109
+ "Authorization": f"Bearer {access_token}",
48
110
  },
49
111
  )
50
112
 
51
113
  return lib.Deserializable(response, lib.to_dict)
52
114
 
53
115
  def get_tracking(self, request: lib.Serializable) -> lib.Deserializable[str]:
116
+ access_token = self.authenticate().deserialize()
54
117
  query = lib.to_query_string(request.serialize())
55
118
  response = lib.request(
56
119
  url=f"{self.settings.server_url}/api/v2/track/events?{query}",
@@ -58,7 +121,7 @@ class Proxy(proxy.Proxy):
58
121
  method="GET",
59
122
  headers={
60
123
  "content-Type": "application/json",
61
- "Authorization": f"Bearer {self.settings.access_token}",
124
+ "Authorization": f"Bearer {access_token}",
62
125
  },
63
126
  )
64
127
 
@@ -30,18 +30,21 @@ def _extract_details(
30
30
  rate.serviceName,
31
31
  service_id=rate.serviceId,
32
32
  test_mode=settings.test_mode,
33
+ carrier_name=rate.carrierName,
33
34
  )
34
35
  carrier_id = provider_units.ShippingService.carrier_id(
35
36
  rate.carrierName,
36
37
  test_mode=settings.test_mode,
37
38
  service_search=service.name_or_key,
38
39
  service_id=rate.serviceId,
40
+ carrier_name=rate.carrierName,
39
41
  )
40
42
  rate_provider = provider_units.RateProvider.find(
41
43
  rate.carrierName,
42
44
  test_mode=settings.test_mode,
43
45
  service_search=service.name_or_key,
44
46
  service_id=rate.serviceId,
47
+ carrier_name=rate.carrierName,
45
48
  )
46
49
 
47
50
  charges = [
@@ -34,10 +34,12 @@ def _extract_details(
34
34
  service = provider_units.ShippingService.find(
35
35
  shipment.carrier.serviceName,
36
36
  test_mode=settings.test_mode,
37
+ carrier_name=shipment.carrier.carrierName,
37
38
  )
38
39
  rate_provider = provider_units.RateProvider.find(
39
40
  shipment.carrier.carrierName,
40
41
  test_mode=settings.test_mode,
42
+ carrier_name=shipment.carrier.carrierName,
41
43
  )
42
44
 
43
45
  return models.ShipmentDetails(
@@ -89,11 +91,24 @@ def shipment_request(
89
91
  initializer=provider_units.shipping_options_initializer,
90
92
  )
91
93
  service = provider_units.ShippingService.map(payload.service)
94
+
95
+ # Extract carrier from service code if available
96
+ carrier_name_from_service = None
97
+ if "_" in service.name_or_key:
98
+ parts = service.name_or_key.split("_")
99
+ # Carrier is always at index 1 after "eshipper" prefix
100
+ if len(parts) >= 2 and parts[0] == "eshipper":
101
+ potential_carrier = parts[1] # e.g., "canadapost" from "eshipper_canadapost_expedited"
102
+ # Validate it's a known carrier
103
+ if provider_units.RateProvider.map(potential_carrier).name:
104
+ carrier_name_from_service = potential_carrier
105
+
92
106
  service_id = lib.identity(
93
107
  options.eshipper_service_id.state
94
108
  or provider_units.ShippingService.service_id(
95
109
  service.name_or_key,
96
110
  test_mode=settings.test_mode,
111
+ carrier_name=carrier_name_from_service,
97
112
  )
98
113
  )
99
114
  carrier_id = lib.identity(
@@ -103,6 +118,7 @@ def shipment_request(
103
118
  service_id=service_id,
104
119
  test_mode=settings.test_mode,
105
120
  service_search=service.name_or_key,
121
+ carrier_name=carrier_name_from_service,
106
122
  )
107
123
  )
108
124
 
@@ -51,6 +51,23 @@ def _extract_details(
51
51
  description=event.description,
52
52
  date=lib.fdate(event.originalEvent.eventDate, "%Y-%m-%d %H:%M:%S"),
53
53
  time=lib.flocaltime(event.originalEvent.eventDate, "%Y-%m-%d %H:%M:%S"),
54
+ timestamp=lib.fiso_timestamp(event.originalEvent.eventDate, current_format="%Y-%m-%d %H:%M:%S"),
55
+ status=next(
56
+ (
57
+ s.name
58
+ for s in list(provider_units.TrackingStatus)
59
+ if event.originalEvent.name in s.value
60
+ ),
61
+ None,
62
+ ),
63
+ reason=next(
64
+ (
65
+ r.name
66
+ for r in list(provider_units.TrackingIncidentReason)
67
+ if event.originalEvent.name in r.value
68
+ ),
69
+ None,
70
+ ),
54
71
  )
55
72
  for event in details.event
56
73
  ],
@@ -102,6 +102,14 @@ class TrackingStatus(lib.Enum):
102
102
  ready_for_pickup = ["ready_for_pickup"]
103
103
 
104
104
 
105
+ class TrackingIncidentReason(lib.Enum):
106
+ """Maps eShipper exception codes to normalized TrackingIncidentReason."""
107
+ carrier_damaged_parcel = []
108
+ consignee_refused = []
109
+ consignee_not_home = []
110
+ unknown = []
111
+
112
+
105
113
  def to_carrier_code(carrierDTO: typing.Dict[str, str]) -> str:
106
114
  _code = lib.to_snake_case((carrierDTO or {}).get("name") or "eshipper")
107
115
 
@@ -132,7 +140,12 @@ def to_service_code(service: typing.Dict[str, str]) -> str:
132
140
  return output
133
141
 
134
142
 
135
- def get_service(search: str, test_mode: bool = False, service_id: str = None):
143
+ def get_service(
144
+ search: str,
145
+ test_mode: bool = False,
146
+ service_id: str = None,
147
+ carrier_name: str = None,
148
+ ):
136
149
  prod_metadata = METADATA_JSON["PROD_SERVICES"]
137
150
  test_metadata = METADATA_JSON["DEV_SERVICES"]
138
151
  metadata = lib.identity(
@@ -144,27 +157,53 @@ def get_service(search: str, test_mode: bool = False, service_id: str = None):
144
157
  service
145
158
  for service in metadata
146
159
  if to_service_code(service) == search
147
- or service.get("name") == search
148
160
  or str(service.get("id")) == search
149
161
  or (service_id and service_id == str(service.get("id")))
162
+ or (
163
+ service.get("name") == search
164
+ and (
165
+ not carrier_name
166
+ or service.get("carrierDTO", {}).get("name") == carrier_name
167
+ )
168
+ )
150
169
  ),
151
170
  {},
152
171
  )
153
172
 
154
173
 
155
- def get_service_id(search: str, test_mode: bool = False, service_id: str = None):
174
+ def get_service_id(
175
+ search: str,
176
+ test_mode: bool = False,
177
+ service_id: str = None,
178
+ carrier_name: str = None,
179
+ ):
156
180
  return (
157
- get_service(search, test_mode=test_mode, service_id=service_id).get("id")
181
+ get_service(
182
+ search,
183
+ test_mode=test_mode,
184
+ service_id=service_id,
185
+ carrier_name=carrier_name,
186
+ ).get("id")
158
187
  or service_id
159
188
  )
160
189
 
161
190
 
162
- def find_service(search: str, test_mode: bool = False, service_id: str = None):
191
+ def find_service(
192
+ search: str,
193
+ test_mode: bool = False,
194
+ service_id: str = None,
195
+ carrier_name: str = None,
196
+ ):
163
197
 
164
198
  if ShippingService.map(search).name:
165
199
  return ShippingService.map(search)
166
200
 
167
- service = get_service(search, test_mode=test_mode, service_id=service_id)
201
+ service = get_service(
202
+ search,
203
+ test_mode=test_mode,
204
+ service_id=service_id,
205
+ carrier_name=carrier_name,
206
+ )
168
207
 
169
208
  if service:
170
209
  return ShippingService.map(to_service_code(service))
@@ -177,10 +216,16 @@ def get_carrier(
177
216
  test_mode: bool = False,
178
217
  service_search: str = None,
179
218
  service_id: str = None,
219
+ carrier_name: str = None,
180
220
  ):
181
221
  id_key = "test_id" if test_mode else "prod_id"
182
222
  alternate_key = "prod_id" if not test_mode else "test_id"
183
- service = get_service(service_search, test_mode=test_mode, service_id=service_id)
223
+ service = get_service(
224
+ service_search,
225
+ test_mode=test_mode,
226
+ service_id=service_id,
227
+ carrier_name=carrier_name or search,
228
+ )
184
229
 
185
230
  return service.get("carrierDTO") or next(
186
231
  (
@@ -204,12 +249,14 @@ def get_carrier_id(
204
249
  test_mode: bool = False,
205
250
  service_search: str = None,
206
251
  service_id: str = None,
252
+ carrier_name: str = None,
207
253
  ):
208
254
  return get_carrier(
209
255
  search,
210
256
  test_mode=test_mode,
211
257
  service_search=service_search,
212
258
  service_id=service_id,
259
+ carrier_name=carrier_name,
213
260
  ).get("id")
214
261
 
215
262
 
@@ -218,6 +265,7 @@ def find_rate_provider(
218
265
  test_mode: bool = False,
219
266
  service_search: str = None,
220
267
  service_id: str = None,
268
+ carrier_name: str = None,
221
269
  ):
222
270
 
223
271
  if RateProvider.map(lib.to_snake_case(search)).name:
@@ -228,6 +276,7 @@ def find_rate_provider(
228
276
  test_mode=test_mode,
229
277
  service_search=service_search,
230
278
  service_id=service_id,
279
+ carrier_name=carrier_name,
231
280
  )
232
281
 
233
282
  if carrier and RateProvider.map(to_carrier_code(carrier)).name:
@@ -273,7 +322,7 @@ ESHIPPER_CARRIER_METADATA = {
273
322
  }
274
323
 
275
324
  ESHIPPER_SERVICE_METADATA = {
276
- lib.to_snake_case(service.get("esServicename") or service.get("name")): {
325
+ to_service_code(service): {
277
326
  **service,
278
327
  "ids": list(
279
328
  set(
@@ -281,8 +330,7 @@ ESHIPPER_SERVICE_METADATA = {
281
330
  s["id"]
282
331
  for s in METADATA_JSON["PROD_SERVICES"]
283
332
  + METADATA_JSON["DEV_SERVICES"]
284
- if lib.to_snake_case(s["name"])
285
- == lib.to_snake_case(service["name"])
333
+ if to_service_code(s) == to_service_code(service)
286
334
  ]
287
335
  )
288
336
  ),
@@ -290,7 +338,7 @@ ESHIPPER_SERVICE_METADATA = {
290
338
  (
291
339
  s["id"]
292
340
  for s in METADATA_JSON["PROD_SERVICES"]
293
- if s["name"] == service["name"]
341
+ if to_service_code(s) == to_service_code(service)
294
342
  ),
295
343
  None,
296
344
  ),
@@ -298,15 +346,15 @@ ESHIPPER_SERVICE_METADATA = {
298
346
  (
299
347
  s["id"]
300
348
  for s in METADATA_JSON["DEV_SERVICES"]
301
- if s["name"] == service["name"]
349
+ if to_service_code(s) == to_service_code(service)
302
350
  ),
303
351
  None,
304
352
  ),
305
353
  "carrier": lib.to_snake_case(service["carrierDTO"]["name"]),
306
354
  }
307
355
  for service in {
308
- s["name"]: s
309
- for s in METADATA_JSON["PROD_SERVICES"] + METADATA_JSON["DEV_SERVICES"]
356
+ to_service_code(s): s
357
+ for s in METADATA_JSON["DEV_SERVICES"] + METADATA_JSON["PROD_SERVICES"]
310
358
  }.values()
311
359
  }
312
360
 
@@ -314,7 +362,7 @@ ESHIPPER_SERVICE_METADATA = {
314
362
  ShippingService = lib.StrEnum(
315
363
  "ShippingService",
316
364
  {
317
- to_service_code(service): service["name"]
365
+ to_service_code(service): to_service_code(service)
318
366
  for service in ESHIPPER_SERVICE_METADATA.values()
319
367
  },
320
368
  )
@@ -1,8 +1,6 @@
1
- import datetime
2
- import karrio.lib as lib
1
+ """eShipper connection settings."""
2
+
3
3
  import karrio.core as core
4
- import karrio.core.errors as errors
5
- import karrio.core.models as models
6
4
 
7
5
 
8
6
  class Settings(core.Settings):
@@ -20,65 +18,3 @@ class Settings(core.Settings):
20
18
  return (
21
19
  "https://uu2.eshipper.com" if self.test_mode else "https://ww2.eshipper.com"
22
20
  )
23
-
24
- @property
25
- def access_token(self):
26
- """Retrieve the "token" using the principal|credential pair
27
- or collect it from the cache if an unexpired "token" exist.
28
- """
29
- cache_key = f"{self.carrier_name}|{self.principal}|{self.credential}"
30
-
31
- return self.connection_cache.thread_safe(
32
- refresh_func=lambda: login(self),
33
- cache_key=cache_key,
34
- buffer_minutes=30,
35
- token_field="token",
36
- ).get_state()
37
-
38
-
39
- def login(settings: Settings):
40
- """Sign in response
41
- {
42
- "token": "string",
43
- "expires_in": "string",
44
- "token_type": "string",
45
- "refresh_token": "string",
46
- "refresh_expires_in": "string"
47
- }
48
- """
49
- import karrio.providers.eshipper.error as error
50
-
51
- result = lib.request(
52
- url=f"{settings.server_url}/api/v2/authenticate",
53
- data=lib.to_json(
54
- {
55
- "principal": settings.principal,
56
- "credential": settings.credential,
57
- }
58
- ),
59
- method="POST",
60
- headers={"content-Type": "application/json"},
61
- )
62
- response = lib.to_dict(result)
63
- messages = error.parse_error_response(response, settings)
64
-
65
- if any(messages):
66
- raise errors.ParsedMessagesError(messages=messages)
67
-
68
- # Validate that token is present in the response
69
- if "token" not in response:
70
- raise errors.ParsedMessagesError(
71
- messages=[
72
- models.Message(
73
- carrier_name=settings.carrier_name,
74
- carrier_id=settings.carrier_id,
75
- message="Authentication failed: No token received",
76
- code="AUTH_ERROR",
77
- )
78
- ]
79
- )
80
-
81
- expiry = datetime.datetime.now() + datetime.timedelta(
82
- seconds=float(response.get("expires_in", 0))
83
- )
84
- return {**response, "expiry": lib.fdatetime(expiry)}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: karrio_eshipper
3
- Version: 2025.5.6
3
+ Version: 2026.1
4
4
  Summary: Karrio - eShipper Shipping Extension
5
5
  Author-email: karrio <hello@karrio.io>
6
6
  License-Expression: LGPL-3.0
@@ -1,18 +1,18 @@
1
1
  karrio/mappers/eshipper/__init__.py,sha256=klSnHWetpSpOBsj7OgncePwLbSK1etgRQN_PZSK2pUo,152
2
2
  karrio/mappers/eshipper/mapper.py,sha256=oFs6tqFI9vbm13sh8hHoZVxA2VrnqdaixOxeB9nnJeY,1969
3
- karrio/mappers/eshipper/proxy.py,sha256=ZLOXHxgGPSVkitCqHJRFeRWpqjcFY01xVEuL2yz4dd8,2350
3
+ karrio/mappers/eshipper/proxy.py,sha256=2U683Jl_Prr1Rov8fbtuUyhJHWGA-cxkJPHLVMvYIk8,4770
4
4
  karrio/mappers/eshipper/settings.py,sha256=ZN7DTMBMTC_JtOgUFQ2UNCyU5j-VUfv2us_F90fL1jw,491
5
5
  karrio/plugins/eshipper/__init__.py,sha256=v5cNriCwL5R2QR5x6Iki23CcBGPmGmH8zEL5j02u9Ko,448
6
6
  karrio/providers/eshipper/__init__.py,sha256=Y9vKPYScbmFvM17L-x52m_yLLiG6WsX9j6o4NIERBMU,400
7
7
  karrio/providers/eshipper/error.py,sha256=uroZ2dW7OHEIrhi3HKnsd8EntmXaLpZisSDyJ2HLKOY,2038
8
8
  karrio/providers/eshipper/metadata.json,sha256=KBae69ntmCIkZ0dKrAX0vjkm1CyE9UwvIstt7mLrq4M,369526
9
- karrio/providers/eshipper/rate.py,sha256=r0OTKeXFXmaWdi7XcNDNQ0wL5R1idQAnNhMtXTRfpPM,7184
10
- karrio/providers/eshipper/tracking.py,sha256=46DVFj8iMLWSsi0f5irTzFpH0uArZtya4eyYh5LkdFQ,2452
11
- karrio/providers/eshipper/units.py,sha256=CTi0NbWEpVc-GwPEKrtGVR7VEEV9Ij0odaQTUacwgyc,9658
12
- karrio/providers/eshipper/utils.py,sha256=0Ok4BNFe4KoNNtCNjp6FLUfR8iwfuaMSh_jZRsPK4KE,2381
9
+ karrio/providers/eshipper/rate.py,sha256=sShKjW25Av07gPE3dIHEe4X2-Vw6bwtbmpIQzlLZJsE,7301
10
+ karrio/providers/eshipper/tracking.py,sha256=wIYItHY_aQ1Cnth6c9Wqdjfr4fn9fALTBiWSB-FhjLs,3137
11
+ karrio/providers/eshipper/units.py,sha256=rY8zN9DewnwcyFGcI_zsxdtq40MyIc9vBGM-mRSIzac,10563
12
+ karrio/providers/eshipper/utils.py,sha256=LOHTEHMgoLpYIPMuCRxcyGwVp3UsMC7DZ3h36YtXwvQ,403
13
13
  karrio/providers/eshipper/shipment/__init__.py,sha256=oPCX3bykNDVeczan1tbmSpuOtFYTtd6h8J9otYVDQqQ,233
14
14
  karrio/providers/eshipper/shipment/cancel.py,sha256=vdxMmK7AmI-xOn62NlaMzCCKQ8CBql8QBVPft0VQWn0,1317
15
- karrio/providers/eshipper/shipment/create.py,sha256=jL4RWdtYOmpofcGVYn-7l2rstMvXK-DKoIAe_TjCBkY,11632
15
+ karrio/providers/eshipper/shipment/create.py,sha256=4Y_t011XNIaFXqEsuEahAp2kQviCVMYUgfI2ccn9BmM,12408
16
16
  karrio/schemas/eshipper/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
17
  karrio/schemas/eshipper/cancel_request.py,sha256=mGMmVxCEwUhWyw-XkK4wsHZ4OQ34e6Dnc0JxBU1CixQ,334
18
18
  karrio/schemas/eshipper/cancel_response.py,sha256=yLJe8qk71gy6GVGZ07ylBIomYFLUChZx9GDyJ3XzEsA,346
@@ -26,8 +26,8 @@ karrio/schemas/eshipper/shipping_request.py,sha256=OrqSutwyzTj-iau3LDPJUEGBRzzV9
26
26
  karrio/schemas/eshipper/shipping_response.py,sha256=RdMXh9goyTrH2qdU_5KMDIAWAEDzMchy_e5f45k-zxs,3892
27
27
  karrio/schemas/eshipper/tracking_request.py,sha256=EAE7PJFp2rTYCXMQJ6Kg-hMs3vL-GSTFjhHvzSmMzPg,252
28
28
  karrio/schemas/eshipper/tracking_response.py,sha256=2m9doy3y5f6zdA-gbNfsw8rp0oqs3eYrgw8Por_1fjE,2219
29
- karrio_eshipper-2025.5.6.dist-info/METADATA,sha256=rIE9q93gzGfM1WEZ9bNdXN8nr0KA8WuzC45wSqwJjYM,997
30
- karrio_eshipper-2025.5.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
31
- karrio_eshipper-2025.5.6.dist-info/entry_points.txt,sha256=u9RQVmPE2uC3-aL85DnlIHdsHAWYkOnwTAfJvi2--DY,61
32
- karrio_eshipper-2025.5.6.dist-info/top_level.txt,sha256=FZCY8Nwft8oEGHdl--xku8P3TrnOxu5dETEU_fWpRSM,20
33
- karrio_eshipper-2025.5.6.dist-info/RECORD,,
29
+ karrio_eshipper-2026.1.dist-info/METADATA,sha256=gehsHjeLm7Fc7qE_7GZfYleIeaeMPydoIM8_eYeLllU,995
30
+ karrio_eshipper-2026.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
31
+ karrio_eshipper-2026.1.dist-info/entry_points.txt,sha256=u9RQVmPE2uC3-aL85DnlIHdsHAWYkOnwTAfJvi2--DY,61
32
+ karrio_eshipper-2026.1.dist-info/top_level.txt,sha256=FZCY8Nwft8oEGHdl--xku8P3TrnOxu5dETEU_fWpRSM,20
33
+ karrio_eshipper-2026.1.dist-info/RECORD,,