xpipe_api 0.1.26__py3-none-any.whl → 0.1.27__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.
- xpipe_api/clients.py +24 -7
- xpipe_api-0.1.27.dist-info/METADATA +246 -0
- xpipe_api-0.1.27.dist-info/RECORD +7 -0
- xpipe_api-0.1.26.dist-info/METADATA +0 -103
- xpipe_api-0.1.26.dist-info/RECORD +0 -7
- {xpipe_api-0.1.26.dist-info → xpipe_api-0.1.27.dist-info}/LICENSE +0 -0
- {xpipe_api-0.1.26.dist-info → xpipe_api-0.1.27.dist-info}/WHEEL +0 -0
xpipe_api/clients.py
CHANGED
@@ -19,7 +19,7 @@ class Client:
|
|
19
19
|
base_url: str
|
20
20
|
raise_errors: bool
|
21
21
|
session: Optional[str] = None
|
22
|
-
min_version: Version = Version("
|
22
|
+
min_version: Version = Version("14.0")
|
23
23
|
|
24
24
|
def __init__(
|
25
25
|
self, token: Optional[str] = None, base_url: Optional[str] = None, ptb: bool = False, raise_errors: bool = True
|
@@ -36,7 +36,7 @@ class Client:
|
|
36
36
|
except PermissionError:
|
37
37
|
raise NoTokenFoundException("Bad permissions on xpipe_auth: is the daemon running as another user?")
|
38
38
|
except Exception as e:
|
39
|
-
raise NoTokenFoundException(f"No auth provided and couldn't load xpipe_auth: {e!r}")
|
39
|
+
raise NoTokenFoundException(f"No auth provided and couldn't load xpipe_auth: {e!r}. Is the XPipe daemon running?")
|
40
40
|
|
41
41
|
if not base_url:
|
42
42
|
base_url = "http://127.0.0.1:21722" if ptb else "http://127.0.0.1:21721"
|
@@ -57,11 +57,12 @@ class Client:
|
|
57
57
|
session = response.get("sessionToken", None)
|
58
58
|
if session:
|
59
59
|
self.session = session
|
60
|
+
daemon_version = self.daemon_version()["version"]
|
61
|
+
assert (
|
62
|
+
daemon_version == "dev" or Version(daemon_version) >= self.min_version
|
63
|
+
), f"xpipe_api requires XPipe of at least {self.min_version}"
|
60
64
|
else:
|
61
65
|
raise AuthFailedException(json.dumps(response))
|
62
|
-
assert (
|
63
|
-
Version(self.daemon_version()["version"]) >= self.min_version
|
64
|
-
), f"xpipe_api requires XPipe of at least {self.min_version}"
|
65
66
|
|
66
67
|
def _post(self, *args, **kwargs) -> requests.Response:
|
67
68
|
if not self.session:
|
@@ -124,12 +125,20 @@ class Client:
|
|
124
125
|
response = self.post(endpoint, json=data)
|
125
126
|
return json.loads(response).get("infos", [])
|
126
127
|
|
127
|
-
def connection_add(self, name: str, conn_data: dict, validate: bool = False) -> str:
|
128
|
+
def connection_add(self, name: str, conn_data: dict, validate: bool = False, category: str = None) -> str:
|
128
129
|
endpoint = f"{self.base_url}/connection/add"
|
129
130
|
data = {"name": name, "data": conn_data, "validate": validate}
|
131
|
+
if category:
|
132
|
+
data["category"] = category
|
130
133
|
response = self.post(endpoint, json=data)
|
131
134
|
return json.loads(response)["connection"]
|
132
135
|
|
136
|
+
def category_add(self, name: str, parent: str) -> str:
|
137
|
+
endpoint = f"{self.base_url}/category/add"
|
138
|
+
data = {"name": name, "parent": parent}
|
139
|
+
response = self.post(endpoint, json=data)
|
140
|
+
return json.loads(response)["category"]
|
141
|
+
|
133
142
|
def connection_remove(self, uuids: Union[str, List[str]]):
|
134
143
|
endpoint = f"{self.base_url}/connection/remove"
|
135
144
|
if not isinstance(uuids, list):
|
@@ -316,12 +325,20 @@ class AsyncClient(Client):
|
|
316
325
|
response = await self.post(endpoint, json=data)
|
317
326
|
return json.loads(response).get("infos", [])
|
318
327
|
|
319
|
-
async def connection_add(self, name: str, conn_data: dict, validate: bool = False) -> str:
|
328
|
+
async def connection_add(self, name: str, conn_data: dict, validate: bool = False, category: str = None) -> str:
|
320
329
|
endpoint = f"{self.base_url}/connection/add"
|
321
330
|
data = {"name": name, "data": conn_data, "validate": validate}
|
331
|
+
if category:
|
332
|
+
data["category"] = category
|
322
333
|
response = await self.post(endpoint, json=data)
|
323
334
|
return json.loads(response)["connection"]
|
324
335
|
|
336
|
+
async def category_add(self, name: str, parent: str) -> str:
|
337
|
+
endpoint = f"{self.base_url}/category/add"
|
338
|
+
data = {"name": name, "parent": parent}
|
339
|
+
response = await self.post(endpoint, json=data)
|
340
|
+
return json.loads(response)["category"]
|
341
|
+
|
325
342
|
async def connection_remove(self, uuids: Union[str, List[str]]):
|
326
343
|
endpoint = f"{self.base_url}/connection/remove"
|
327
344
|
if not isinstance(uuids, list):
|
@@ -0,0 +1,246 @@
|
|
1
|
+
Metadata-Version: 2.1
|
2
|
+
Name: xpipe_api
|
3
|
+
Version: 0.1.27
|
4
|
+
Summary: Client for the XPipe API
|
5
|
+
Home-page: https://github.com/xpipe-io/xpipe-python-api
|
6
|
+
License: MIT
|
7
|
+
Author: Clint Olson
|
8
|
+
Author-email: coandco@gmail.com
|
9
|
+
Requires-Python: >=3.10,<4.0
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
15
|
+
Requires-Dist: aiohttp-requests (>=0.2.4,<0.3.0)
|
16
|
+
Requires-Dist: packaging (>=24.1,<25.0)
|
17
|
+
Requires-Dist: requests (>=2.32.3,<3.0.0)
|
18
|
+
Project-URL: Repository, https://github.com/xpipe-io/xpipe-python-api
|
19
|
+
Description-Content-Type: text/markdown
|
20
|
+
|
21
|
+
# XPipe Python API
|
22
|
+
|
23
|
+
[](https://github.com/xpipe-io/xpipe-python-api/blob/master/LICENSE)
|
24
|
+
[](https://pypi.org/project/xpipe_api/)
|
25
|
+
|
26
|
+
Python client for the XPipe API. This library is a wrapper for the raw [HTTP API](https://github.com/xpipe-io/xpipe/blob/master/openapi.yaml) and intended to make working with it more convenient. You can find the raw API reference at http://localhost:21721 if your XPipe desktop application is running. XPipe contains a local webserver that includes the API docs as well.
|
27
|
+
|
28
|
+
## Installation
|
29
|
+
|
30
|
+
```
|
31
|
+
python3 -m pip install xpipe_api
|
32
|
+
```
|
33
|
+
|
34
|
+
## Connecting to the XPipe application
|
35
|
+
|
36
|
+
To start out, you need to enable the API access in the XPipe settings. You can find that under **Settings** -> **API**. For only a local API access, you don't need to worry about the API key in the settings menu.
|
37
|
+
|
38
|
+
```python
|
39
|
+
from xpipe_api import Client
|
40
|
+
|
41
|
+
# By default, Client() will read an access key from the file xpipe_auth on the local filesystem
|
42
|
+
# and talk to the XPipe HTTP server on localhost.
|
43
|
+
client = Client()
|
44
|
+
|
45
|
+
# If you want to connect to a PTB build of XPipe, you have to configure the client with that information
|
46
|
+
# as the PTB releases use a different port
|
47
|
+
client = Client(ptb=True)
|
48
|
+
|
49
|
+
# To connect to a remote instance with an API key, use
|
50
|
+
client = Client(token="foo", base_url = "http://servername:21721")
|
51
|
+
```
|
52
|
+
|
53
|
+
There's also an async version of the client that can be accessed as AsyncClient. All calls are run asynchronously with that client. This intro will only cover the sync client though.
|
54
|
+
|
55
|
+
```python
|
56
|
+
import asyncio
|
57
|
+
from xpipe_api import AsyncClient
|
58
|
+
|
59
|
+
async def main():
|
60
|
+
client = AsyncClient()
|
61
|
+
|
62
|
+
# Do your stuff async
|
63
|
+
|
64
|
+
if __name__ == "__main__":
|
65
|
+
asyncio.run(main())
|
66
|
+
```
|
67
|
+
|
68
|
+
## Querying connections
|
69
|
+
|
70
|
+
A connection reference is just a UUID string. You can query one or multiple connections stored in XPipe based on various filters.
|
71
|
+
|
72
|
+
**API reference**: http://localhost:21721/#query-connections
|
73
|
+
|
74
|
+
```python
|
75
|
+
from xpipe_api import Client
|
76
|
+
|
77
|
+
client = Client()
|
78
|
+
|
79
|
+
# connection_query accepts glob-based filters on the category, connection name, and connection type
|
80
|
+
connections = client.connection_query(categories="Default/**")
|
81
|
+
|
82
|
+
# Find only ssh config host connections in the "MyCategory" category that has the word "MyCompany" in its name
|
83
|
+
# - Searching for strings is case-insensitive here
|
84
|
+
# - This will not include subcategories of "MyCategory"
|
85
|
+
# - You can find all the available connection type names by just querying all connections and taking a look add the individual type names of the connections.
|
86
|
+
connections = client.connection_query(categories="MyCategory", types="sshConfigHost", connections="*MyCompany*")
|
87
|
+
|
88
|
+
# Query a specific connection by name in a category
|
89
|
+
# This will only return one element, so we can just access that
|
90
|
+
connection = client.connection_query(categories="MyCategory", connections="MyConnectionName")[0]
|
91
|
+
|
92
|
+
# Query the local machine connection
|
93
|
+
connection = client.connection_query(connections="Local Machine")[0]
|
94
|
+
|
95
|
+
# A connection reference is just a UUID string, so you can also specify it fixed
|
96
|
+
# If you have the HTTP API setting enabled in XPipe, you can copy the API UUID of a connection in the context menu
|
97
|
+
connection = "f0ec68aa-63f5-405c-b178-9a4454556d6b"
|
98
|
+
```
|
99
|
+
|
100
|
+
## Getting connection information
|
101
|
+
|
102
|
+
Since each connection reference is just a UUID string, you have to retrieve information for it using another API call.
|
103
|
+
|
104
|
+
**API Reference**: http://localhost:21721/#connection-information
|
105
|
+
|
106
|
+
```python
|
107
|
+
from xpipe_api import Client
|
108
|
+
|
109
|
+
client = Client()
|
110
|
+
|
111
|
+
connections = client.connection_query(categories="Default/**")
|
112
|
+
|
113
|
+
# Works in bulk, so it expects an array of connections
|
114
|
+
infos = client.connection_info(connections)
|
115
|
+
# Get the first info
|
116
|
+
info = infos[0]
|
117
|
+
|
118
|
+
# The connection UUID
|
119
|
+
uuid = info["connection"]
|
120
|
+
# This is an array containing all category hierarchy names
|
121
|
+
category = info["category"]
|
122
|
+
# This is an array containing all connection hierarchy names
|
123
|
+
name = info["name"]
|
124
|
+
# This is the connection type name that you can use in the query
|
125
|
+
type = info["type"]
|
126
|
+
# There is also some other data for internal state management, e.g. if a tunnel is running for example
|
127
|
+
```
|
128
|
+
|
129
|
+
## GUI actions
|
130
|
+
|
131
|
+
You can perform some actions for the desktop application from the CLI as well.
|
132
|
+
|
133
|
+
**API Reference**: http://localhost:21721/#open-connection-in-file-browser
|
134
|
+
|
135
|
+
```python
|
136
|
+
from xpipe_api import Client
|
137
|
+
|
138
|
+
client = Client()
|
139
|
+
|
140
|
+
connection = "f0ec68aa-63f5-405c-b178-9a4454556d6b"
|
141
|
+
|
142
|
+
# Opens the file browser in a specified starting directory for a connection
|
143
|
+
client.connection_browse(connection, "/etc")
|
144
|
+
|
145
|
+
# Opens a terminal session in a specified starting directory for a connection
|
146
|
+
client.connection_terminal(connection, "/etc")
|
147
|
+
|
148
|
+
# Toggles the session state for a connection. If this connection is a tunnel, then this operation will start or stop the tunnel
|
149
|
+
client.connection_toggle(connection, True)
|
150
|
+
|
151
|
+
# Refreshes a connection. This operation is the same as for validating a connection when creating it by attempting whether the connection is functioning.
|
152
|
+
client.connection_refresh(connection)
|
153
|
+
```
|
154
|
+
|
155
|
+
## Shell operations
|
156
|
+
|
157
|
+
You can open remote shell sessions to systems and run arbitrary commands in them.
|
158
|
+
|
159
|
+
**API Reference**: http://localhost:21721/#start-shell-connection
|
160
|
+
|
161
|
+
```python
|
162
|
+
from xpipe_api import Client
|
163
|
+
import re
|
164
|
+
|
165
|
+
client = Client()
|
166
|
+
|
167
|
+
connection = "f0ec68aa-63f5-405c-b178-9a4454556d6b"
|
168
|
+
|
169
|
+
# This will start a shell session for the connection
|
170
|
+
# Note that this session is non-interactive, meaning that password prompts are not supported
|
171
|
+
shell_info = client.shell_start(connection)
|
172
|
+
|
173
|
+
# The shell dialect of the shell. For example cmd, powershell, bash, etc.
|
174
|
+
dialect = shell_info["shellDialect"]
|
175
|
+
# The operating system type of the system. For example, windows, linux, macos, bsd, solaris
|
176
|
+
osType = shell_info["osType"]
|
177
|
+
# The display name of the operating system. For example Windows 10 Home 22H2
|
178
|
+
osName = shell_info["osName"]
|
179
|
+
|
180
|
+
# Prints {'exitCode': 0, 'stdout': 'hello world', 'stderr': ''}
|
181
|
+
print(client.shell_exec(connection, "echo hello world"))
|
182
|
+
|
183
|
+
# Prints {'exitCode': 0, 'stdout': '<user>', 'stderr': ''}
|
184
|
+
print(client.shell_exec(connection, "echo $USER"))
|
185
|
+
|
186
|
+
# Prints {'exitCode': 127, 'stdout': 'invalid: command not found', 'stderr': ''}
|
187
|
+
print(client.shell_exec(connection, "invalid"))
|
188
|
+
|
189
|
+
# Extract ssh version from system
|
190
|
+
version_format = re.compile(r'[0-9.]+')
|
191
|
+
ret = client.shell_exec(connection, "ssh -V")
|
192
|
+
ssh_version = version_format.findall(ret["stderr"])[0]
|
193
|
+
|
194
|
+
# Clean up after ourselves by stopping the shell session
|
195
|
+
client.shell_stop(connection)
|
196
|
+
```
|
197
|
+
|
198
|
+
## File system operations
|
199
|
+
|
200
|
+
You can interact with the file system of any remote shell as well.
|
201
|
+
|
202
|
+
**API reference**: http://localhost:21721/#store-a-raw-blob-to-be-used-later
|
203
|
+
|
204
|
+
```python
|
205
|
+
from xpipe_api import Client
|
206
|
+
|
207
|
+
client = Client()
|
208
|
+
|
209
|
+
connection = "f0ec68aa-63f5-405c-b178-9a4454556d6b"
|
210
|
+
|
211
|
+
# We need a shell for the file system
|
212
|
+
client.shell_start(connection)
|
213
|
+
|
214
|
+
# You are responsible for decoding files in the correct file encoding
|
215
|
+
file_bytes = client.fs_read(connection, "~/.ssh/config")
|
216
|
+
file_string = file_bytes.decode('utf-8')
|
217
|
+
|
218
|
+
# Writing files can be done via blobs
|
219
|
+
file_write_content = "test\nfile"
|
220
|
+
blob_id = client.fs_blob(file_write_content)
|
221
|
+
client.fs_write(connection, blob_id, "~/test_file.txt")
|
222
|
+
|
223
|
+
# You can do the same with script files as well
|
224
|
+
# This will create an executable script in the system's temp directory
|
225
|
+
script_write_content = "echo hello\necho world"
|
226
|
+
blob_script_id = client.fs_blob(file_write_content)
|
227
|
+
script_path = client.fs_script(connection, blob_script_id)
|
228
|
+
|
229
|
+
# You can then use this script for your commands
|
230
|
+
# Prints {'exitCode': 0, 'stdout': 'hello\nworld', 'stderr': ''}
|
231
|
+
print(client.shell_exec(connection, "\"" + script_path + "\""))
|
232
|
+
|
233
|
+
# Clean up after ourselves by stopping the shell session
|
234
|
+
client.shell_stop(connection)
|
235
|
+
```
|
236
|
+
|
237
|
+
## Development
|
238
|
+
|
239
|
+
To run the test suite, you'll need to define the XPIPE_APIKEY env var. This will allow the two "log in with the ApiKey
|
240
|
+
rather than Local method" tests to work. Here's the recommended method for running the tests with poetry:
|
241
|
+
|
242
|
+
```commandline
|
243
|
+
cd /path/to/xpipe-python-api
|
244
|
+
poetry install
|
245
|
+
XPIPE_APIKEY=<api_key> poetry run pytest
|
246
|
+
```
|
@@ -0,0 +1,7 @@
|
|
1
|
+
xpipe_api/__init__.py,sha256=XLv-a-mt7OcGS9OQbDBHKBsI4mO-4lgWDA13nZ0njFY,108
|
2
|
+
xpipe_api/clients.py,sha256=N5CCrQJTIHAwTedPTpol21IQcVmECwVMSE_8XRSMW80,19563
|
3
|
+
xpipe_api/exceptions.py,sha256=FdkNKLV1gOH8mOuNM9wH_q3hYNFEdMbQwAEZUX4yZQU,410
|
4
|
+
xpipe_api-0.1.27.dist-info/LICENSE,sha256=hWd_i4lSck0lBXcxe-2P4VCUyPAecKW-X1oOiXv21wE,1089
|
5
|
+
xpipe_api-0.1.27.dist-info/METADATA,sha256=FW1cTa8fBTcqjXBAbM_cxvXkNe98FERz6tRqx7bTmnI,8925
|
6
|
+
xpipe_api-0.1.27.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
7
|
+
xpipe_api-0.1.27.dist-info/RECORD,,
|
@@ -1,103 +0,0 @@
|
|
1
|
-
Metadata-Version: 2.1
|
2
|
-
Name: xpipe_api
|
3
|
-
Version: 0.1.26
|
4
|
-
Summary: Client for the XPipe API
|
5
|
-
Home-page: https://github.com/xpipe-io/xpipe-python-api
|
6
|
-
License: MIT
|
7
|
-
Author: Clint Olson
|
8
|
-
Author-email: coandco@gmail.com
|
9
|
-
Requires-Python: >=3.10,<4.0
|
10
|
-
Classifier: License :: OSI Approved :: MIT License
|
11
|
-
Classifier: Programming Language :: Python :: 3
|
12
|
-
Classifier: Programming Language :: Python :: 3.10
|
13
|
-
Classifier: Programming Language :: Python :: 3.11
|
14
|
-
Classifier: Programming Language :: Python :: 3.12
|
15
|
-
Requires-Dist: aiohttp-requests (>=0.2.4,<0.3.0)
|
16
|
-
Requires-Dist: packaging (>=24.1,<25.0)
|
17
|
-
Requires-Dist: requests (>=2.32.3,<3.0.0)
|
18
|
-
Project-URL: Repository, https://github.com/xpipe-io/xpipe-python-api
|
19
|
-
Description-Content-Type: text/markdown
|
20
|
-
|
21
|
-
# XPipe Python API
|
22
|
-
|
23
|
-
[](https://github.com/coandco/python_xpipe_api/blob/master/LICENSE)
|
24
|
-
[](https://pypi.org/project/xpipe_api/)
|
25
|
-
|
26
|
-
Python client for the XPipe API. This library is a wrapper for the raw [HTTP API](https://github.com/xpipe-io/xpipe/blob/master/openapi.yaml) and intended to make working with it more convenient.
|
27
|
-
|
28
|
-
## Installation
|
29
|
-
```
|
30
|
-
python3 -m pip install xpipe_api
|
31
|
-
```
|
32
|
-
|
33
|
-
## Usage
|
34
|
-
|
35
|
-
```python
|
36
|
-
from xpipe_api import Client
|
37
|
-
|
38
|
-
# By default, Client() will read an access key from the file xpipe_auth on the local filesystem
|
39
|
-
# and talk to the XPipe HTTP server on localhost. To connect to a remote instance with an API
|
40
|
-
# key, use Client(token="foo", base_url = "http://servername:21721")
|
41
|
-
client = Client()
|
42
|
-
|
43
|
-
# connection_query accepts glob-based filters on the category, connection name, and connection type
|
44
|
-
all_connections = client.connection_query()
|
45
|
-
|
46
|
-
# Each connection includes uuid, category, connection, and type information
|
47
|
-
first_connection_uuid = all_connections[0]["uuid"]
|
48
|
-
|
49
|
-
# Before any shell commands can be run, a shell session must be started on a connection
|
50
|
-
client.shell_start(first_connection_uuid)
|
51
|
-
|
52
|
-
# Prints {'exitCode': 0, 'stdout': 'hello world', 'stderr': ''}
|
53
|
-
print(client.shell_exec(first_connection_uuid, "echo hello world"))
|
54
|
-
|
55
|
-
# Clean up after ourselves by stopping the shell session
|
56
|
-
client.shell_stop(first_connection_uuid)
|
57
|
-
```
|
58
|
-
|
59
|
-
There's also an async version of the client that can be accessed as AsyncClient:
|
60
|
-
|
61
|
-
```python
|
62
|
-
import asyncio
|
63
|
-
from xpipe_api import AsyncClient
|
64
|
-
|
65
|
-
|
66
|
-
async def main():
|
67
|
-
# By default, Client() will read an access key from the file xpipe_auth on the local filesystem
|
68
|
-
# and talk to the XPipe HTTP server on localhost. To connect to a remote instance with an API
|
69
|
-
# key, use Client(token="foo", base_url = "http://servername:21721")
|
70
|
-
client = AsyncClient()
|
71
|
-
|
72
|
-
# connection_query accepts glob-based filters on the category, connection name, and connection type
|
73
|
-
all_connections = await client.connection_query()
|
74
|
-
|
75
|
-
# Each connection includes uuid, category, connection, and type information
|
76
|
-
first_connection_uuid = all_connections[0]["uuid"]
|
77
|
-
|
78
|
-
# Before any shell commands can be run, a shell session must be started on a connection
|
79
|
-
await client.shell_start(first_connection_uuid)
|
80
|
-
|
81
|
-
# Prints {'exitCode': 0, 'stdout': 'hello world', 'stderr': ''}
|
82
|
-
print(await client.shell_exec(first_connection_uuid, "echo hello world"))
|
83
|
-
|
84
|
-
# Clean up after ourselves by stopping the shell session
|
85
|
-
await client.shell_stop(first_connection_uuid)
|
86
|
-
|
87
|
-
|
88
|
-
if __name__ == "__main__":
|
89
|
-
asyncio.run(main())
|
90
|
-
```
|
91
|
-
|
92
|
-
This is only a short summary of the library. You can find more supported functionalities in the source itself.
|
93
|
-
|
94
|
-
## Tests
|
95
|
-
|
96
|
-
To run the test suite, you'll need to define the XPIPE_APIKEY env var. This will allow the two "log in with the ApiKey
|
97
|
-
rather than Local method" tests to work. Here's the recommended method for running the tests with poetry:
|
98
|
-
|
99
|
-
```commandline
|
100
|
-
cd /path/to/python_xpipe_api
|
101
|
-
poetry install
|
102
|
-
XPIPE_APIKEY=<api_key> poetry run pytest
|
103
|
-
```
|
@@ -1,7 +0,0 @@
|
|
1
|
-
xpipe_api/__init__.py,sha256=XLv-a-mt7OcGS9OQbDBHKBsI4mO-4lgWDA13nZ0njFY,108
|
2
|
-
xpipe_api/clients.py,sha256=Pg0jyrJpjv8bB0Xo4vdQUiqK3RsLdlAmJXorJLNBwhw,18740
|
3
|
-
xpipe_api/exceptions.py,sha256=FdkNKLV1gOH8mOuNM9wH_q3hYNFEdMbQwAEZUX4yZQU,410
|
4
|
-
xpipe_api-0.1.26.dist-info/LICENSE,sha256=hWd_i4lSck0lBXcxe-2P4VCUyPAecKW-X1oOiXv21wE,1089
|
5
|
-
xpipe_api-0.1.26.dist-info/METADATA,sha256=EMi4uQvxYDAw6jGDTK_ViCmUzgb9BNOZVeqyJbwIGvY,3900
|
6
|
-
xpipe_api-0.1.26.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
7
|
-
xpipe_api-0.1.26.dist-info/RECORD,,
|
File without changes
|
File without changes
|