shipinfo-sdk 0.1.1__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.
- shipinfo_sdk-0.1.1/PKG-INFO +68 -0
- shipinfo_sdk-0.1.1/README.md +57 -0
- shipinfo_sdk-0.1.1/pyproject.toml +32 -0
- shipinfo_sdk-0.1.1/setup.cfg +4 -0
- shipinfo_sdk-0.1.1/shipinfo_sdk/__init__.py +4 -0
- shipinfo_sdk-0.1.1/shipinfo_sdk/client.py +180 -0
- shipinfo_sdk-0.1.1/shipinfo_sdk/errors.py +26 -0
- shipinfo_sdk-0.1.1/shipinfo_sdk.egg-info/PKG-INFO +68 -0
- shipinfo_sdk-0.1.1/shipinfo_sdk.egg-info/SOURCES.txt +10 -0
- shipinfo_sdk-0.1.1/shipinfo_sdk.egg-info/dependency_links.txt +1 -0
- shipinfo_sdk-0.1.1/shipinfo_sdk.egg-info/requires.txt +1 -0
- shipinfo_sdk-0.1.1/shipinfo_sdk.egg-info/top_level.txt +2 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: shipinfo_sdk
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: ShipInfo maritime analytics SDK for AI agents
|
|
5
|
+
Project-URL: Homepage, https://shipinfo.net/topos/for-agents
|
|
6
|
+
Project-URL: Repository, https://github.com/bifurcafe/shipinfo-topos
|
|
7
|
+
Project-URL: Issues, https://github.com/bifurcafe/shipinfo-topos/issues
|
|
8
|
+
Keywords: maritime,shipping,ais,vessel-tracking,port-congestion,sts,route-stress,agent-native,ai-agent-api,x402,pay-what-you-want
|
|
9
|
+
Requires-Python: >=3.9
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
|
|
12
|
+
# shipinfo-sdk
|
|
13
|
+
|
|
14
|
+
Python SDK for ShipInfo agent-native API.
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pip install shipinfo-sdk
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Quickstart
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
from shipinfo_sdk import ShipInfoClient
|
|
26
|
+
|
|
27
|
+
client = ShipInfoClient(
|
|
28
|
+
base_url="https://shipinfo.net/topos/api",
|
|
29
|
+
api_key=None,
|
|
30
|
+
agent_headers={
|
|
31
|
+
"name": "demo-agent",
|
|
32
|
+
"vendor": "example-inc",
|
|
33
|
+
"contact": "https://example.com/agent",
|
|
34
|
+
"session": "sess-001",
|
|
35
|
+
},
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
caps = client.capabilities()
|
|
39
|
+
print(caps.get("status"))
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## API
|
|
43
|
+
|
|
44
|
+
- `capabilities()`
|
|
45
|
+
- `policy()`
|
|
46
|
+
- `quality()`
|
|
47
|
+
- `billing_pricing()`
|
|
48
|
+
- `billing_x402_requirements(resource="/topos/api/v1/vessels/lookup")`
|
|
49
|
+
- `billing_x402_verify(resource, payment, payment_signature=None)`
|
|
50
|
+
- `vessel_lookup(vessel_id)`
|
|
51
|
+
- `port_congestion(port_id, range=None, vessel_type=None)`
|
|
52
|
+
- `sts_events(**kwargs)`
|
|
53
|
+
- `route_stress_index(zone_key=None, range=None)`
|
|
54
|
+
- `get_paginated(path, params=None, limit_pages=10, cursor_field="next_cursor", items_field=None)`
|
|
55
|
+
|
|
56
|
+
## x402 Notes
|
|
57
|
+
|
|
58
|
+
- On HTTP `402`, `ShipInfoHttpError` includes:
|
|
59
|
+
- `status_code`
|
|
60
|
+
- `retryable`
|
|
61
|
+
- `response_headers`
|
|
62
|
+
- `payment_required` (parsed from `PAYMENT-REQUIRED`/`payment-required` header if available)
|
|
63
|
+
- For proof-based verify flow use `payment_signature`; SDK sends `PAYMENT-SIGNATURE` header.
|
|
64
|
+
|
|
65
|
+
## Fair Value Rule
|
|
66
|
+
|
|
67
|
+
Paid calls follow payer autonomy semantics:
|
|
68
|
+
`agent_self_assessed_fair_value`, `paid_interactions_only`.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# shipinfo-sdk
|
|
2
|
+
|
|
3
|
+
Python SDK for ShipInfo agent-native API.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install shipinfo-sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quickstart
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from shipinfo_sdk import ShipInfoClient
|
|
15
|
+
|
|
16
|
+
client = ShipInfoClient(
|
|
17
|
+
base_url="https://shipinfo.net/topos/api",
|
|
18
|
+
api_key=None,
|
|
19
|
+
agent_headers={
|
|
20
|
+
"name": "demo-agent",
|
|
21
|
+
"vendor": "example-inc",
|
|
22
|
+
"contact": "https://example.com/agent",
|
|
23
|
+
"session": "sess-001",
|
|
24
|
+
},
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
caps = client.capabilities()
|
|
28
|
+
print(caps.get("status"))
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## API
|
|
32
|
+
|
|
33
|
+
- `capabilities()`
|
|
34
|
+
- `policy()`
|
|
35
|
+
- `quality()`
|
|
36
|
+
- `billing_pricing()`
|
|
37
|
+
- `billing_x402_requirements(resource="/topos/api/v1/vessels/lookup")`
|
|
38
|
+
- `billing_x402_verify(resource, payment, payment_signature=None)`
|
|
39
|
+
- `vessel_lookup(vessel_id)`
|
|
40
|
+
- `port_congestion(port_id, range=None, vessel_type=None)`
|
|
41
|
+
- `sts_events(**kwargs)`
|
|
42
|
+
- `route_stress_index(zone_key=None, range=None)`
|
|
43
|
+
- `get_paginated(path, params=None, limit_pages=10, cursor_field="next_cursor", items_field=None)`
|
|
44
|
+
|
|
45
|
+
## x402 Notes
|
|
46
|
+
|
|
47
|
+
- On HTTP `402`, `ShipInfoHttpError` includes:
|
|
48
|
+
- `status_code`
|
|
49
|
+
- `retryable`
|
|
50
|
+
- `response_headers`
|
|
51
|
+
- `payment_required` (parsed from `PAYMENT-REQUIRED`/`payment-required` header if available)
|
|
52
|
+
- For proof-based verify flow use `payment_signature`; SDK sends `PAYMENT-SIGNATURE` header.
|
|
53
|
+
|
|
54
|
+
## Fair Value Rule
|
|
55
|
+
|
|
56
|
+
Paid calls follow payer autonomy semantics:
|
|
57
|
+
`agent_self_assessed_fair_value`, `paid_interactions_only`.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "shipinfo_sdk"
|
|
7
|
+
version = "0.1.1"
|
|
8
|
+
description = "ShipInfo maritime analytics SDK for AI agents"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
dependencies = ["httpx>=0.27.0"]
|
|
12
|
+
keywords = [
|
|
13
|
+
"maritime",
|
|
14
|
+
"shipping",
|
|
15
|
+
"ais",
|
|
16
|
+
"vessel-tracking",
|
|
17
|
+
"port-congestion",
|
|
18
|
+
"sts",
|
|
19
|
+
"route-stress",
|
|
20
|
+
"agent-native",
|
|
21
|
+
"ai-agent-api",
|
|
22
|
+
"x402",
|
|
23
|
+
"pay-what-you-want"
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[project.urls]
|
|
27
|
+
Homepage = "https://shipinfo.net/topos/for-agents"
|
|
28
|
+
Repository = "https://github.com/bifurcafe/shipinfo-topos"
|
|
29
|
+
Issues = "https://github.com/bifurcafe/shipinfo-topos/issues"
|
|
30
|
+
|
|
31
|
+
[tool.setuptools.packages.find]
|
|
32
|
+
where = ["."]
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import time
|
|
5
|
+
from typing import Any, Dict, List, Optional
|
|
6
|
+
|
|
7
|
+
import httpx
|
|
8
|
+
|
|
9
|
+
from .errors import ShipInfoDecodeError, ShipInfoHttpError
|
|
10
|
+
|
|
11
|
+
RETRYABLE_STATUS = {429, 500, 502, 503, 504}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ShipInfoClient:
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
base_url: str = "https://shipinfo.net/topos/api",
|
|
18
|
+
api_key: Optional[str] = None,
|
|
19
|
+
agent_headers: Optional[Dict[str, str]] = None,
|
|
20
|
+
timeout: float = 20.0,
|
|
21
|
+
max_retries: int = 2,
|
|
22
|
+
retry_base_seconds: float = 0.5,
|
|
23
|
+
) -> None:
|
|
24
|
+
self.base_url = base_url.rstrip("/")
|
|
25
|
+
self.api_key = api_key
|
|
26
|
+
self.agent_headers = agent_headers or {}
|
|
27
|
+
self.timeout = timeout
|
|
28
|
+
self.max_retries = max_retries
|
|
29
|
+
self.retry_base_seconds = retry_base_seconds
|
|
30
|
+
|
|
31
|
+
def _headers(self) -> Dict[str, str]:
|
|
32
|
+
h: Dict[str, str] = {"Accept": "application/json"}
|
|
33
|
+
if self.api_key:
|
|
34
|
+
h["Authorization"] = f"Bearer {self.api_key}"
|
|
35
|
+
if self.agent_headers.get("name"):
|
|
36
|
+
h["X-Agent-Name"] = self.agent_headers["name"]
|
|
37
|
+
if self.agent_headers.get("vendor"):
|
|
38
|
+
h["X-Agent-Vendor"] = self.agent_headers["vendor"]
|
|
39
|
+
if self.agent_headers.get("contact"):
|
|
40
|
+
h["X-Agent-Contact"] = self.agent_headers["contact"]
|
|
41
|
+
if self.agent_headers.get("session"):
|
|
42
|
+
h["X-Agent-Session"] = self.agent_headers["session"]
|
|
43
|
+
return h
|
|
44
|
+
|
|
45
|
+
def _parse_payment_required(self, header_value: Optional[str]) -> Optional[Any]:
|
|
46
|
+
if not header_value:
|
|
47
|
+
return None
|
|
48
|
+
try:
|
|
49
|
+
return json.loads(header_value)
|
|
50
|
+
except ValueError:
|
|
51
|
+
return header_value
|
|
52
|
+
|
|
53
|
+
def _request_json(
|
|
54
|
+
self,
|
|
55
|
+
method: str,
|
|
56
|
+
path: str,
|
|
57
|
+
params: Optional[Dict[str, Any]] = None,
|
|
58
|
+
json_body: Optional[Dict[str, Any]] = None,
|
|
59
|
+
extra_headers: Optional[Dict[str, str]] = None,
|
|
60
|
+
) -> Dict[str, Any]:
|
|
61
|
+
params = params or {}
|
|
62
|
+
headers = self._headers()
|
|
63
|
+
if extra_headers:
|
|
64
|
+
headers.update(extra_headers)
|
|
65
|
+
attempt = 0
|
|
66
|
+
while True:
|
|
67
|
+
with httpx.Client(timeout=self.timeout) as client:
|
|
68
|
+
response = client.request(
|
|
69
|
+
method.upper(),
|
|
70
|
+
f"{self.base_url}{path}",
|
|
71
|
+
params=params,
|
|
72
|
+
json=json_body,
|
|
73
|
+
headers=headers,
|
|
74
|
+
)
|
|
75
|
+
if response.status_code < 400:
|
|
76
|
+
try:
|
|
77
|
+
payload = response.json()
|
|
78
|
+
except ValueError as exc:
|
|
79
|
+
raise ShipInfoDecodeError("invalid json response") from exc
|
|
80
|
+
if not isinstance(payload, dict):
|
|
81
|
+
raise ShipInfoDecodeError("json payload is not an object")
|
|
82
|
+
return payload
|
|
83
|
+
|
|
84
|
+
retryable = response.status_code in RETRYABLE_STATUS
|
|
85
|
+
if retryable and attempt < self.max_retries:
|
|
86
|
+
attempt += 1
|
|
87
|
+
time.sleep(self.retry_base_seconds * attempt)
|
|
88
|
+
continue
|
|
89
|
+
|
|
90
|
+
response_headers = {k.lower(): v for k, v in response.headers.items()}
|
|
91
|
+
payment_required = self._parse_payment_required(response_headers.get("payment-required"))
|
|
92
|
+
raise ShipInfoHttpError(
|
|
93
|
+
response.status_code,
|
|
94
|
+
response.text,
|
|
95
|
+
retryable,
|
|
96
|
+
response_headers=response_headers,
|
|
97
|
+
payment_required=payment_required,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
def get_paginated(
|
|
101
|
+
self,
|
|
102
|
+
path: str,
|
|
103
|
+
params: Optional[Dict[str, Any]] = None,
|
|
104
|
+
limit_pages: int = 10,
|
|
105
|
+
cursor_field: str = "next_cursor",
|
|
106
|
+
items_field: Optional[str] = None,
|
|
107
|
+
) -> Dict[str, Any]:
|
|
108
|
+
params = dict(params or {})
|
|
109
|
+
pages: List[Dict[str, Any]] = []
|
|
110
|
+
all_items: List[Any] = []
|
|
111
|
+
|
|
112
|
+
for _ in range(max(1, limit_pages)):
|
|
113
|
+
payload = self._request_json("GET", path, params=params)
|
|
114
|
+
pages.append(payload)
|
|
115
|
+
|
|
116
|
+
data = payload.get("data") if isinstance(payload, dict) else None
|
|
117
|
+
if isinstance(data, dict):
|
|
118
|
+
if items_field and isinstance(data.get(items_field), list):
|
|
119
|
+
all_items.extend(data[items_field])
|
|
120
|
+
cursor = data.get(cursor_field)
|
|
121
|
+
if not cursor:
|
|
122
|
+
break
|
|
123
|
+
params["cursor"] = cursor
|
|
124
|
+
else:
|
|
125
|
+
break
|
|
126
|
+
|
|
127
|
+
return {"pages": pages, "all_items": all_items}
|
|
128
|
+
|
|
129
|
+
def capabilities(self) -> Dict[str, Any]:
|
|
130
|
+
return self._request_json("GET", "/v1/capabilities")
|
|
131
|
+
|
|
132
|
+
def policy(self) -> Dict[str, Any]:
|
|
133
|
+
return self._request_json("GET", "/v1/policy")
|
|
134
|
+
|
|
135
|
+
def quality(self) -> Dict[str, Any]:
|
|
136
|
+
return self._request_json("GET", "/v1/quality")
|
|
137
|
+
|
|
138
|
+
def billing_pricing(self) -> Dict[str, Any]:
|
|
139
|
+
return self._request_json("GET", "/v1/billing/pricing")
|
|
140
|
+
|
|
141
|
+
def billing_x402_requirements(self, resource: str = "/topos/api/v1/vessels/lookup") -> Dict[str, Any]:
|
|
142
|
+
return self._request_json("GET", "/v1/billing/x402/requirements", params={"resource": resource})
|
|
143
|
+
|
|
144
|
+
def billing_x402_verify(
|
|
145
|
+
self,
|
|
146
|
+
resource: str,
|
|
147
|
+
payment: Dict[str, Any],
|
|
148
|
+
payment_signature: Optional[str] = None,
|
|
149
|
+
) -> Dict[str, Any]:
|
|
150
|
+
headers: Dict[str, str] = {}
|
|
151
|
+
if payment_signature:
|
|
152
|
+
headers["PAYMENT-SIGNATURE"] = payment_signature
|
|
153
|
+
return self._request_json(
|
|
154
|
+
"POST",
|
|
155
|
+
"/v1/billing/x402/verify",
|
|
156
|
+
json_body={"resource": resource, "payment": payment},
|
|
157
|
+
extra_headers=headers,
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
def vessel_lookup(self, vessel_id: str) -> Dict[str, Any]:
|
|
161
|
+
return self._request_json("GET", "/v1/vessels/lookup", params={"id": vessel_id})
|
|
162
|
+
|
|
163
|
+
def port_congestion(self, port_id: int, range: Optional[str] = None, vessel_type: Optional[str] = None) -> Dict[str, Any]:
|
|
164
|
+
params: Dict[str, Any] = {}
|
|
165
|
+
if range:
|
|
166
|
+
params["range"] = range
|
|
167
|
+
if vessel_type:
|
|
168
|
+
params["vessel_type"] = vessel_type
|
|
169
|
+
return self._request_json("GET", f"/v1/ports/{port_id}/congestion", params=params)
|
|
170
|
+
|
|
171
|
+
def sts_events(self, **kwargs: Any) -> Dict[str, Any]:
|
|
172
|
+
return self._request_json("GET", "/v1/sts/events", params=kwargs)
|
|
173
|
+
|
|
174
|
+
def route_stress_index(self, zone_key: Optional[str] = None, range: Optional[str] = None) -> Dict[str, Any]:
|
|
175
|
+
params: Dict[str, Any] = {}
|
|
176
|
+
if zone_key:
|
|
177
|
+
params["zone_key"] = zone_key
|
|
178
|
+
if range:
|
|
179
|
+
params["range"] = range
|
|
180
|
+
return self._request_json("GET", "/v1/metrics/route_stress_index", params=params)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from typing import Any, Dict, Optional
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ShipInfoError(Exception):
|
|
5
|
+
pass
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ShipInfoHttpError(ShipInfoError):
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
status_code: int,
|
|
12
|
+
body_text: str,
|
|
13
|
+
retryable: bool,
|
|
14
|
+
response_headers: Optional[Dict[str, str]] = None,
|
|
15
|
+
payment_required: Optional[Any] = None,
|
|
16
|
+
) -> None:
|
|
17
|
+
self.status_code = status_code
|
|
18
|
+
self.body_text = body_text
|
|
19
|
+
self.retryable = retryable
|
|
20
|
+
self.response_headers = response_headers or {}
|
|
21
|
+
self.payment_required = payment_required
|
|
22
|
+
super().__init__(f"HTTP {status_code}: {body_text}")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ShipInfoDecodeError(ShipInfoError):
|
|
26
|
+
pass
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: shipinfo-sdk
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: ShipInfo maritime analytics SDK for AI agents
|
|
5
|
+
Project-URL: Homepage, https://shipinfo.net/topos/for-agents
|
|
6
|
+
Project-URL: Repository, https://github.com/bifurcafe/shipinfo-topos
|
|
7
|
+
Project-URL: Issues, https://github.com/bifurcafe/shipinfo-topos/issues
|
|
8
|
+
Keywords: maritime,shipping,ais,vessel-tracking,port-congestion,sts,route-stress,agent-native,ai-agent-api,x402,pay-what-you-want
|
|
9
|
+
Requires-Python: >=3.9
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
|
|
12
|
+
# shipinfo-sdk
|
|
13
|
+
|
|
14
|
+
Python SDK for ShipInfo agent-native API.
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pip install shipinfo-sdk
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Quickstart
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
from shipinfo_sdk import ShipInfoClient
|
|
26
|
+
|
|
27
|
+
client = ShipInfoClient(
|
|
28
|
+
base_url="https://shipinfo.net/topos/api",
|
|
29
|
+
api_key=None,
|
|
30
|
+
agent_headers={
|
|
31
|
+
"name": "demo-agent",
|
|
32
|
+
"vendor": "example-inc",
|
|
33
|
+
"contact": "https://example.com/agent",
|
|
34
|
+
"session": "sess-001",
|
|
35
|
+
},
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
caps = client.capabilities()
|
|
39
|
+
print(caps.get("status"))
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## API
|
|
43
|
+
|
|
44
|
+
- `capabilities()`
|
|
45
|
+
- `policy()`
|
|
46
|
+
- `quality()`
|
|
47
|
+
- `billing_pricing()`
|
|
48
|
+
- `billing_x402_requirements(resource="/topos/api/v1/vessels/lookup")`
|
|
49
|
+
- `billing_x402_verify(resource, payment, payment_signature=None)`
|
|
50
|
+
- `vessel_lookup(vessel_id)`
|
|
51
|
+
- `port_congestion(port_id, range=None, vessel_type=None)`
|
|
52
|
+
- `sts_events(**kwargs)`
|
|
53
|
+
- `route_stress_index(zone_key=None, range=None)`
|
|
54
|
+
- `get_paginated(path, params=None, limit_pages=10, cursor_field="next_cursor", items_field=None)`
|
|
55
|
+
|
|
56
|
+
## x402 Notes
|
|
57
|
+
|
|
58
|
+
- On HTTP `402`, `ShipInfoHttpError` includes:
|
|
59
|
+
- `status_code`
|
|
60
|
+
- `retryable`
|
|
61
|
+
- `response_headers`
|
|
62
|
+
- `payment_required` (parsed from `PAYMENT-REQUIRED`/`payment-required` header if available)
|
|
63
|
+
- For proof-based verify flow use `payment_signature`; SDK sends `PAYMENT-SIGNATURE` header.
|
|
64
|
+
|
|
65
|
+
## Fair Value Rule
|
|
66
|
+
|
|
67
|
+
Paid calls follow payer autonomy semantics:
|
|
68
|
+
`agent_self_assessed_fair_value`, `paid_interactions_only`.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
shipinfo_sdk/__init__.py
|
|
4
|
+
shipinfo_sdk/client.py
|
|
5
|
+
shipinfo_sdk/errors.py
|
|
6
|
+
shipinfo_sdk.egg-info/PKG-INFO
|
|
7
|
+
shipinfo_sdk.egg-info/SOURCES.txt
|
|
8
|
+
shipinfo_sdk.egg-info/dependency_links.txt
|
|
9
|
+
shipinfo_sdk.egg-info/requires.txt
|
|
10
|
+
shipinfo_sdk.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
httpx>=0.27.0
|