xpipe_api 0.1.27__py3-none-any.whl → 0.1.28__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 +10 -8
- xpipe_api-0.1.28.dist-info/METADATA +44 -0
- xpipe_api-0.1.28.dist-info/RECORD +7 -0
- {xpipe_api-0.1.27.dist-info → xpipe_api-0.1.28.dist-info}/WHEEL +1 -1
- xpipe_api-0.1.27.dist-info/METADATA +0 -246
- xpipe_api-0.1.27.dist-info/RECORD +0 -7
- {xpipe_api-0.1.27.dist-info → xpipe_api-0.1.28.dist-info}/LICENSE +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("15.2")
|
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
|
@@ -110,7 +110,7 @@ class Client:
|
|
110
110
|
def get(self, *args, **kwargs) -> bytes:
|
111
111
|
return self._get(*args, **kwargs).content
|
112
112
|
|
113
|
-
def connection_query(self, categories: str = "
|
113
|
+
def connection_query(self, categories: str = "**", connections: str = "**", types: str = "*") -> List[str]:
|
114
114
|
endpoint = f"{self.base_url}/connection/query"
|
115
115
|
data = {"categoryFilter": categories, "connectionFilter": connections, "typeFilter": types}
|
116
116
|
response = self.post(endpoint, json=data)
|
@@ -170,7 +170,7 @@ class Client:
|
|
170
170
|
data = {"connection": connection}
|
171
171
|
self.post(endpoint, json=data)
|
172
172
|
|
173
|
-
def get_connections(self, categories: str = "
|
173
|
+
def get_connections(self, categories: str = "**", connections: str = "**", types: str = "*") -> List[dict]:
|
174
174
|
"""Convenience method to chain connection/query with connection/info"""
|
175
175
|
uuids = self.connection_query(categories, connections, types)
|
176
176
|
return self.connection_info(uuids) if uuids else []
|
@@ -246,11 +246,13 @@ class AsyncClient(Client):
|
|
246
246
|
session_token = parsed.get("sessionToken", None)
|
247
247
|
if session_token:
|
248
248
|
self.session = session_token
|
249
|
+
daemon_version = (await self.daemon_version())["version"]
|
250
|
+
assert (
|
251
|
+
daemon_version == "dev" or Version(daemon_version) >= self.min_version
|
252
|
+
), f"xpipe_api requires XPipe of at least {self.min_version}"
|
249
253
|
else:
|
250
254
|
raise AuthFailedException(json.dumps(parsed))
|
251
|
-
|
252
|
-
Version((await self.daemon_version())["version"]) >= self.min_version
|
253
|
-
), f"xpipe_api requires XPipe of at least {self.min_version}"
|
255
|
+
|
254
256
|
|
255
257
|
async def _post(self, *args, **kwargs) -> aiohttp.ClientResponse:
|
256
258
|
if not self.session:
|
@@ -310,7 +312,7 @@ class AsyncClient(Client):
|
|
310
312
|
resp = await self._get(*args, **kwargs)
|
311
313
|
return await resp.read()
|
312
314
|
|
313
|
-
async def connection_query(self, categories: str = "
|
315
|
+
async def connection_query(self, categories: str = "**", connections: str = "**", types: str = "*") -> List[str]:
|
314
316
|
endpoint = f"{self.base_url}/connection/query"
|
315
317
|
data = {"categoryFilter": categories, "connectionFilter": connections, "typeFilter": types}
|
316
318
|
response = await self.post(endpoint, json=data)
|
@@ -370,7 +372,7 @@ class AsyncClient(Client):
|
|
370
372
|
data = {"connection": connection}
|
371
373
|
await self.post(endpoint, json=data)
|
372
374
|
|
373
|
-
async def get_connections(self, categories: str = "
|
375
|
+
async def get_connections(self, categories: str = "**", connections: str = "**", types: str = "*") -> List[dict]:
|
374
376
|
uuids = await self.connection_query(categories, connections, types)
|
375
377
|
return (await self.connection_info(uuids)) if uuids else []
|
376
378
|
|
@@ -0,0 +1,44 @@
|
|
1
|
+
Metadata-Version: 2.3
|
2
|
+
Name: xpipe_api
|
3
|
+
Version: 0.1.28
|
4
|
+
Summary: Client for the XPipe API
|
5
|
+
License: MIT
|
6
|
+
Author: Clint Olson
|
7
|
+
Author-email: coandco@gmail.com
|
8
|
+
Requires-Python: >=3.10,<4.0
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
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.
|
27
|
+
|
28
|
+
```bash
|
29
|
+
python3 -m pip install xpipe_api
|
30
|
+
```
|
31
|
+
|
32
|
+
You can find the documentation at https://docs.xpipe.io/guide/python-api.
|
33
|
+
|
34
|
+
## Development
|
35
|
+
|
36
|
+
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
|
37
|
+
rather than Local method" tests to work. Here's the recommended method for running the tests with poetry:
|
38
|
+
|
39
|
+
```bash
|
40
|
+
cd /path/to/xpipe-python-api
|
41
|
+
poetry install
|
42
|
+
XPIPE_APIKEY=<api_key> poetry run pytest
|
43
|
+
```
|
44
|
+
|
@@ -0,0 +1,7 @@
|
|
1
|
+
xpipe_api/__init__.py,sha256=XLv-a-mt7OcGS9OQbDBHKBsI4mO-4lgWDA13nZ0njFY,108
|
2
|
+
xpipe_api/clients.py,sha256=ZbNjpuOSsNtt_ge9XQYxNdYB_zQppBbJOUvg1IFg9Qo,19661
|
3
|
+
xpipe_api/exceptions.py,sha256=FdkNKLV1gOH8mOuNM9wH_q3hYNFEdMbQwAEZUX4yZQU,410
|
4
|
+
xpipe_api-0.1.28.dist-info/LICENSE,sha256=hWd_i4lSck0lBXcxe-2P4VCUyPAecKW-X1oOiXv21wE,1089
|
5
|
+
xpipe_api-0.1.28.dist-info/METADATA,sha256=sOkrtJfXrwEpfYmCW3nsbDI24hUqR8X5tSKH1ZWcKKU,1660
|
6
|
+
xpipe_api-0.1.28.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
|
7
|
+
xpipe_api-0.1.28.dist-info/RECORD,,
|
@@ -1,246 +0,0 @@
|
|
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
|
-
```
|
@@ -1,7 +0,0 @@
|
|
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,,
|
File without changes
|