unikraft-cloud-plugin-sandbox-api 1.1.0.dev17__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.
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2026, Unikraft GmbH. All rights reserved.
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,96 @@
1
+ Metadata-Version: 2.4
2
+ Name: unikraft-cloud-plugin-sandbox-api
3
+ Version: 1.1.0.dev17
4
+ Summary: Generated Python client for the Unikraft Cloud `sandbox` plugin API
5
+ Keywords: unikraft,unikraft-cloud,plugin,sandbox,sdk
6
+ Author: Unikraft GmbH
7
+ License-Expression: BSD-3-Clause
8
+ License-File: LICENSE.md
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Programming Language :: Python :: 3 :: Only
12
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
13
+ Classifier: Typing :: Typed
14
+ Requires-Dist: pydantic>=2.7
15
+ Requires-Dist: typing-extensions>=4.4
16
+ Requires-Python: >=3.10
17
+ Project-URL: Homepage, https://unikraft.com
18
+ Project-URL: Repository, https://github.com/unikraft-cloud/plugins
19
+ Description-Content-Type: text/markdown
20
+
21
+ # unikraft-cloud-plugin-sandbox-api
22
+
23
+ The generated client for the Unikraft Cloud `sandbox` plugin API.
24
+ openapi-gen generates it from the plugin's OpenAPI specification, so **do not
25
+ edit it by hand**.
26
+
27
+ This package is the raw ("plumbing") layer. Each method carries the name of its
28
+ `operationId` and returns the response envelope exactly as the specification
29
+ describes it. If you want the idiomatic layer, use
30
+ [`unikraft-cloud`](https://pypi.org/project/unikraft-cloud/), which wraps this
31
+ package and resolves the instance for you.
32
+
33
+ ## Install
34
+
35
+ ```sh
36
+ pip install unikraft-cloud-plugin-sandbox-api
37
+ ```
38
+
39
+ The package depends on pydantic and typing_extensions only. Each generated
40
+ class takes a `Transport`,
41
+ an object with `request()`, `request_no_content()`, `request_bytes()` and
42
+ `stream()`. The `ApiClient` of `unikraft-cloud`
43
+ 0.2.0 or later is one. If you want to use it as the transport, install
44
+ the SDK.
45
+
46
+ ## Usage
47
+
48
+ The plugin runs inside a single instance and answers on that instance's plugin
49
+ route, so `base_url` is the full plugin endpoint rather than a metro root.
50
+
51
+ ```python
52
+ import os
53
+
54
+ from unikraft_cloud.core.http import ApiClient, ApiClientConfig
55
+ from unikraft_cloud_plugin_sandbox_api import SandboxPluginApi
56
+
57
+ uuid = "..." # The instance the plugin is attached to.
58
+ api = SandboxPluginApi(
59
+ ApiClient(
60
+ ApiClientConfig(
61
+ base_url=f"https://api.fra.unikraft.cloud/v1/instances/{uuid}/plugins/sandbox",
62
+ token=os.environ["UKC_TOKEN"],
63
+ )
64
+ )
65
+ )
66
+ ```
67
+
68
+ The last path segment is whatever name the plugin was attached under in the
69
+ instance's `plugins` array, so it is not necessarily `sandbox`. To let the
70
+ SDK build that URL for you, use `unikraft-cloud` instead.
71
+
72
+ ## Exports
73
+
74
+ Everything below is importable from `unikraft_cloud_plugin_sandbox_api`.
75
+
76
+ | Name | Carries |
77
+ | --- | --- |
78
+ | `SandboxPluginApi` | Every resource of the plugin behind one transport |
79
+ | `CommandsApi` | The `commands` resource |
80
+ | `FsApi` | The `fs` resource |
81
+ | `models` | The wire models, an alias of `unikraft_cloud_plugin_sandbox_api.models_gen` |
82
+ | `PLUGIN_NAME` | `"sandbox"`, the name the plugin is attached under unless the instance's creator chose another |
83
+ | `Transport`, `RawResponse` | The protocols a transport and a raw-bytes response satisfy |
84
+ | `CallOptions` | The options every method takes as `**options`: `headers`, `base_url` and `timeout` |
85
+ | `QueryValue` | What a query parameter may hold |
86
+ | `VERSION`, `SDK_MIN` | This package's version, and the oldest `unikraft-cloud` whose `ApiClient` is a transport for it |
87
+ | `SPEC_HASH`, `TEMPLATES_HASH`, `CONFIG_HASH` | SHA-256 fingerprints of the specification, the templates and the configuration this package was built from |
88
+
89
+ A `CallOptions` key that is absent leaves the transport's default in place, so
90
+ `timeout=None` lifts the timeout for one call and no `timeout` at all keeps the
91
+ client's.
92
+
93
+ `SandboxPluginApi` groups one client per resource:
94
+
95
+ - `api.commands`: the `commands` resource
96
+ - `api.fs`: the `fs` resource
@@ -0,0 +1,76 @@
1
+ # unikraft-cloud-plugin-sandbox-api
2
+
3
+ The generated client for the Unikraft Cloud `sandbox` plugin API.
4
+ openapi-gen generates it from the plugin's OpenAPI specification, so **do not
5
+ edit it by hand**.
6
+
7
+ This package is the raw ("plumbing") layer. Each method carries the name of its
8
+ `operationId` and returns the response envelope exactly as the specification
9
+ describes it. If you want the idiomatic layer, use
10
+ [`unikraft-cloud`](https://pypi.org/project/unikraft-cloud/), which wraps this
11
+ package and resolves the instance for you.
12
+
13
+ ## Install
14
+
15
+ ```sh
16
+ pip install unikraft-cloud-plugin-sandbox-api
17
+ ```
18
+
19
+ The package depends on pydantic and typing_extensions only. Each generated
20
+ class takes a `Transport`,
21
+ an object with `request()`, `request_no_content()`, `request_bytes()` and
22
+ `stream()`. The `ApiClient` of `unikraft-cloud`
23
+ 0.2.0 or later is one. If you want to use it as the transport, install
24
+ the SDK.
25
+
26
+ ## Usage
27
+
28
+ The plugin runs inside a single instance and answers on that instance's plugin
29
+ route, so `base_url` is the full plugin endpoint rather than a metro root.
30
+
31
+ ```python
32
+ import os
33
+
34
+ from unikraft_cloud.core.http import ApiClient, ApiClientConfig
35
+ from unikraft_cloud_plugin_sandbox_api import SandboxPluginApi
36
+
37
+ uuid = "..." # The instance the plugin is attached to.
38
+ api = SandboxPluginApi(
39
+ ApiClient(
40
+ ApiClientConfig(
41
+ base_url=f"https://api.fra.unikraft.cloud/v1/instances/{uuid}/plugins/sandbox",
42
+ token=os.environ["UKC_TOKEN"],
43
+ )
44
+ )
45
+ )
46
+ ```
47
+
48
+ The last path segment is whatever name the plugin was attached under in the
49
+ instance's `plugins` array, so it is not necessarily `sandbox`. To let the
50
+ SDK build that URL for you, use `unikraft-cloud` instead.
51
+
52
+ ## Exports
53
+
54
+ Everything below is importable from `unikraft_cloud_plugin_sandbox_api`.
55
+
56
+ | Name | Carries |
57
+ | --- | --- |
58
+ | `SandboxPluginApi` | Every resource of the plugin behind one transport |
59
+ | `CommandsApi` | The `commands` resource |
60
+ | `FsApi` | The `fs` resource |
61
+ | `models` | The wire models, an alias of `unikraft_cloud_plugin_sandbox_api.models_gen` |
62
+ | `PLUGIN_NAME` | `"sandbox"`, the name the plugin is attached under unless the instance's creator chose another |
63
+ | `Transport`, `RawResponse` | The protocols a transport and a raw-bytes response satisfy |
64
+ | `CallOptions` | The options every method takes as `**options`: `headers`, `base_url` and `timeout` |
65
+ | `QueryValue` | What a query parameter may hold |
66
+ | `VERSION`, `SDK_MIN` | This package's version, and the oldest `unikraft-cloud` whose `ApiClient` is a transport for it |
67
+ | `SPEC_HASH`, `TEMPLATES_HASH`, `CONFIG_HASH` | SHA-256 fingerprints of the specification, the templates and the configuration this package was built from |
68
+
69
+ A `CallOptions` key that is absent leaves the transport's default in place, so
70
+ `timeout=None` lifts the timeout for one call and no `timeout` at all keeps the
71
+ client's.
72
+
73
+ `SandboxPluginApi` groups one client per resource:
74
+
75
+ - `api.commands`: the `commands` resource
76
+ - `api.fs`: the `fs` resource
@@ -0,0 +1,31 @@
1
+ [project]
2
+ name = "unikraft-cloud-plugin-sandbox-api"
3
+ version = "1.1.0.dev17"
4
+ description = "Generated Python client for the Unikraft Cloud `sandbox` plugin API"
5
+ readme = "README.md"
6
+ license = "BSD-3-Clause"
7
+ license-files = ["LICENSE.md"]
8
+ authors = [{ name = "Unikraft GmbH" }]
9
+ requires-python = ">=3.10"
10
+ keywords = ["unikraft", "unikraft-cloud", "plugin", "sandbox", "sdk"]
11
+ classifiers = [
12
+ "Development Status :: 3 - Alpha",
13
+ "Intended Audience :: Developers",
14
+ "Programming Language :: Python :: 3 :: Only",
15
+ "Topic :: Software Development :: Libraries :: Python Modules",
16
+ "Typing :: Typed",
17
+ ]
18
+ # The wire models are pydantic; the transport is whatever the caller passes.
19
+ # Unpack on **options needs typing_extensions below Python 3.12.
20
+ dependencies = [
21
+ "pydantic>=2.7",
22
+ "typing_extensions>=4.4",
23
+ ]
24
+
25
+ [project.urls]
26
+ Homepage = "https://unikraft.com"
27
+ Repository = "https://github.com/unikraft-cloud/plugins"
28
+
29
+ [build-system]
30
+ requires = ["uv_build>=0.8.0,<0.9.0"]
31
+ build-backend = "uv_build"
@@ -0,0 +1,85 @@
1
+ # Code generated by openapi-gen; DO NOT EDIT.
2
+ #
3
+ # The `sandbox` plugin API's "plumbing" layer: the generated clients, plus
4
+ # a container that groups them behind one transport. Everything here returns
5
+ # the response envelope exactly as the OpenAPI specification describes it, or,
6
+ # for an operation that answers with raw bytes, the payload untouched.
7
+
8
+ from __future__ import annotations
9
+
10
+ from . import models_gen as models
11
+ from ._meta import CONFIG_HASH, SDK_MIN, SPEC_HASH, TEMPLATES_HASH, VERSION
12
+ from .commands_gen import CommandsApi
13
+ from .fs_gen import FsApi
14
+ from .transport import CallOptions, QueryValue, RawResponse, Transport
15
+
16
+ __all__ = [
17
+ "CONFIG_HASH",
18
+ "PLUGIN_NAME",
19
+ "SDK_MIN",
20
+ "SPEC_HASH",
21
+ "TEMPLATES_HASH",
22
+ "VERSION",
23
+ "CommandsApi",
24
+ "FsApi",
25
+ "CallOptions",
26
+ "QueryValue",
27
+ "RawResponse",
28
+ "SandboxPluginApi",
29
+ "Transport",
30
+ "models",
31
+ ]
32
+
33
+ __version__ = VERSION
34
+
35
+ #: The name the plugin is attached to an instance under, unless the instance's
36
+ #: creator chose another, and hence the ``<name>`` in its route.
37
+ PLUGIN_NAME = "sandbox"
38
+
39
+ #: What a transport must offer, for the error that names a missing one.
40
+ _TRANSPORT_METHODS = ("request", "request_no_content", "request_bytes", "stream")
41
+
42
+
43
+ class SandboxPluginApi:
44
+ """Every `sandbox` plugin API resource, raw, behind one transport.
45
+
46
+ The idiomatic layer in ``unikraft_cloud`` derives the plugin's route from an
47
+ instance. Use this class only when you want the raw envelopes against a
48
+ route you already have.
49
+
50
+ .. code-block:: python
51
+
52
+ import os
53
+
54
+ from unikraft_cloud.core.http import ApiClient, ApiClientConfig
55
+ from unikraft_cloud_plugin_sandbox_api import SandboxPluginApi
56
+
57
+ uuid = "..." # The instance the plugin is attached to.
58
+ api = SandboxPluginApi(
59
+ ApiClient(
60
+ ApiClientConfig(
61
+ base_url=f"https://api.fra.unikraft.cloud/v1/instances/{uuid}/plugins/sandbox",
62
+ token=os.environ["UKC_TOKEN"],
63
+ )
64
+ )
65
+ )
66
+ """
67
+
68
+ def __init__(self, transport: Transport) -> None:
69
+ missing = [
70
+ name
71
+ for name in _TRANSPORT_METHODS
72
+ if not callable(getattr(transport, name, None))
73
+ ]
74
+ if missing:
75
+ raise TypeError(
76
+ f"The transport has no {missing[0]}() method. Pass an ApiClient from "
77
+ "unikraft_cloud.core.http, or an object with "
78
+ f"{', '.join(_TRANSPORT_METHODS)}."
79
+ )
80
+ #: The transport every request goes through.
81
+ self.transport = transport
82
+ #: The `commands` resource of this plugin.
83
+ self.commands = CommandsApi(transport)
84
+ #: The `fs` resource of this plugin.
85
+ self.fs = FsApi(transport)
@@ -0,0 +1,20 @@
1
+ # Code generated by openapi-gen; DO NOT EDIT.
2
+ #
3
+ # What this package was built from. The version is what it was published as;
4
+ # the hashes fingerprint the sources that decided its contents, so a publish
5
+ # can tell a rebuild of the same sources from a change under the same version.
6
+
7
+ #: The version this package was published as.
8
+ VERSION = "1.1.0.dev17"
9
+
10
+ #: The oldest unikraft-cloud SDK whose ApiClient is a transport for this package.
11
+ SDK_MIN = "0.2.0"
12
+
13
+ #: SHA-256 of the OpenAPI specification.
14
+ SPEC_HASH = "fe2a902ae04c2934eb14f7e7db0f5e75f9598471532986f01a939229d38b06d4"
15
+
16
+ #: SHA-256 over the generator's templates and static files.
17
+ TEMPLATES_HASH = "5efe373fc719c482bc42cc6ff89c27852a6bfef78c9e6d841a1f21eab2f97859"
18
+
19
+ #: SHA-256 over the generation configuration: the SDK floor and the generator.
20
+ CONFIG_HASH = "4332d5b927c34d12c6ba2d2ba5ef3350298d5ee9f95fb06fd6dbcca1d5877ae2"
@@ -0,0 +1,183 @@
1
+ # Code generated by openapi-gen; DO NOT EDIT.
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing_extensions import Unpack
6
+ from urllib.parse import quote
7
+
8
+ from . import models_gen as models
9
+ from .transport import CallOptions, RawResponse, Transport
10
+
11
+
12
+ class CommandsApi:
13
+ """Low-level "plumbing" client for the `commands` resource.
14
+
15
+ Methods mirror the OpenAPI specification and return the raw response
16
+ envelope. The idiomatic clients are built on top of this.
17
+ """
18
+
19
+ def __init__(self, transport: Transport) -> None:
20
+ #: The transport every request of this resource goes through.
21
+ self.transport = transport
22
+
23
+ async def delete_command_by_uuid(
24
+ self,
25
+ uuid: str,
26
+ **options: Unpack[CallOptions],
27
+ ) -> models.EmptyResponse:
28
+ """Delete Command by UUID"""
29
+ return await self.transport.request(
30
+ models.EmptyResponse,
31
+ method="DELETE",
32
+ path=f"/commands/{quote(uuid, safe='')}",
33
+ **options,
34
+ )
35
+
36
+ async def delete_command_logs_by_uuid(
37
+ self,
38
+ uuid: str,
39
+ **options: Unpack[CallOptions],
40
+ ) -> models.EmptyResponse:
41
+ """Delete Command Logs"""
42
+ return await self.transport.request(
43
+ models.EmptyResponse,
44
+ method="DELETE",
45
+ path=f"/commands/{quote(uuid, safe='')}/logs",
46
+ **options,
47
+ )
48
+
49
+ async def get_command_by_uuid(
50
+ self,
51
+ uuid: str,
52
+ **options: Unpack[CallOptions],
53
+ ) -> models.GetCommandResponse:
54
+ """Get Command by UUID"""
55
+ return await self.transport.request(
56
+ models.GetCommandResponse,
57
+ method="GET",
58
+ path=f"/commands/{quote(uuid, safe='')}",
59
+ **options,
60
+ )
61
+
62
+ async def get_command_logs(
63
+ self,
64
+ uuid: str,
65
+ *,
66
+ body: models.CommandLogsRequest | None = None,
67
+ **options: Unpack[CallOptions],
68
+ ) -> models.CommandLogsResponse:
69
+ """Get Command Logs"""
70
+ return await self.transport.request(
71
+ models.CommandLogsResponse,
72
+ method="GET",
73
+ path=f"/commands/{quote(uuid, safe='')}/logs",
74
+ body=body,
75
+ **options,
76
+ )
77
+
78
+ async def get_raw_command_log(
79
+ self,
80
+ uuid: str,
81
+ stream: models.CommandLogStream,
82
+ *,
83
+ range: str | None = None,
84
+ **options: Unpack[CallOptions],
85
+ ) -> RawResponse:
86
+ """Get Raw Command Log"""
87
+ request_headers: dict[str, str] = dict(options.get("headers") or {})
88
+ if range is not None:
89
+ request_headers["Range"] = range
90
+ options["headers"] = request_headers
91
+ return await self.transport.request_bytes(
92
+ method="GET",
93
+ path=f"/commands/{quote(uuid, safe='')}/logs/raw/{quote(stream, safe='')}",
94
+ **options,
95
+ )
96
+
97
+ async def list_commands(
98
+ self,
99
+ **options: Unpack[CallOptions],
100
+ ) -> models.ListCommandsResponse:
101
+ """List Commands"""
102
+ return await self.transport.request(
103
+ models.ListCommandsResponse,
104
+ method="GET",
105
+ path="/commands",
106
+ **options,
107
+ )
108
+
109
+ async def run_command(
110
+ self,
111
+ *,
112
+ body: models.RunCommandRequest,
113
+ **options: Unpack[CallOptions],
114
+ ) -> models.RunCommandResponse:
115
+ """Start Command"""
116
+ return await self.transport.request(
117
+ models.RunCommandResponse,
118
+ method="POST",
119
+ path="/commands",
120
+ body=body,
121
+ **options,
122
+ )
123
+
124
+ async def signal_command(
125
+ self,
126
+ uuid: str,
127
+ *,
128
+ body: models.CommandSignalRequest,
129
+ **options: Unpack[CallOptions],
130
+ ) -> models.EmptyResponse:
131
+ """Signal Command"""
132
+ return await self.transport.request(
133
+ models.EmptyResponse,
134
+ method="POST",
135
+ path=f"/commands/{quote(uuid, safe='')}/signal",
136
+ body=body,
137
+ **options,
138
+ )
139
+
140
+ async def wait_for_command(
141
+ self,
142
+ uuid: str,
143
+ **options: Unpack[CallOptions],
144
+ ) -> models.EmptyResponse:
145
+ """Wait for Command"""
146
+ return await self.transport.request(
147
+ models.EmptyResponse,
148
+ method="POST",
149
+ path=f"/commands/{quote(uuid, safe='')}/wait",
150
+ **options,
151
+ )
152
+
153
+ async def wait_for_command_with_timeout(
154
+ self,
155
+ uuid: str,
156
+ *,
157
+ body: models.CommandWaitTimeoutRequest,
158
+ **options: Unpack[CallOptions],
159
+ ) -> models.EmptyResponse:
160
+ """Wait for Command with Timeout"""
161
+ return await self.transport.request(
162
+ models.EmptyResponse,
163
+ method="POST",
164
+ path=f"/commands/{quote(uuid, safe='')}/wait_timeout",
165
+ body=body,
166
+ **options,
167
+ )
168
+
169
+ async def write_command_stdin(
170
+ self,
171
+ uuid: str,
172
+ *,
173
+ body: models.CommandStdinRequest,
174
+ **options: Unpack[CallOptions],
175
+ ) -> models.EmptyResponse:
176
+ """Write Command Stdin"""
177
+ return await self.transport.request(
178
+ models.EmptyResponse,
179
+ method="POST",
180
+ path=f"/commands/{quote(uuid, safe='')}/stdin",
181
+ body=body,
182
+ **options,
183
+ )
@@ -0,0 +1,94 @@
1
+ # Code generated by openapi-gen; DO NOT EDIT.
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing_extensions import Unpack
6
+
7
+ from . import models_gen as models
8
+ from .transport import CallOptions, RawResponse, Transport
9
+
10
+
11
+ class FsApi:
12
+ """Low-level "plumbing" client for the `fs` resource.
13
+
14
+ Methods mirror the OpenAPI specification and return the raw response
15
+ envelope. The idiomatic clients are built on top of this.
16
+ """
17
+
18
+ def __init__(self, transport: Transport) -> None:
19
+ #: The transport every request of this resource goes through.
20
+ self.transport = transport
21
+
22
+ async def create_directory(
23
+ self,
24
+ *,
25
+ body: models.MkdirRequest,
26
+ **options: Unpack[CallOptions],
27
+ ) -> models.EmptyResponse:
28
+ """Create Directory"""
29
+ return await self.transport.request(
30
+ models.EmptyResponse,
31
+ method="POST",
32
+ path="/fs/mkdir",
33
+ body=body,
34
+ **options,
35
+ )
36
+
37
+ async def read_file(
38
+ self,
39
+ *,
40
+ body: models.ReadFileRequest,
41
+ **options: Unpack[CallOptions],
42
+ ) -> models.ReadFileResponse:
43
+ """Read File"""
44
+ return await self.transport.request(
45
+ models.ReadFileResponse,
46
+ method="POST",
47
+ path="/fs/read",
48
+ body=body,
49
+ **options,
50
+ )
51
+
52
+ async def read_raw_file(
53
+ self,
54
+ *,
55
+ body: models.ReadFileRequest,
56
+ **options: Unpack[CallOptions],
57
+ ) -> RawResponse:
58
+ """Read Raw File"""
59
+ return await self.transport.request_bytes(
60
+ method="POST",
61
+ path="/fs/read_raw",
62
+ body=body,
63
+ **options,
64
+ )
65
+
66
+ async def upload_file(
67
+ self,
68
+ *,
69
+ body: models.UploadFileRequest,
70
+ **options: Unpack[CallOptions],
71
+ ) -> models.EmptyResponse:
72
+ """Upload File"""
73
+ return await self.transport.request(
74
+ models.EmptyResponse,
75
+ method="POST",
76
+ path="/fs/upload",
77
+ body=body,
78
+ **options,
79
+ )
80
+
81
+ async def write_file(
82
+ self,
83
+ *,
84
+ body: models.WriteFileRequest,
85
+ **options: Unpack[CallOptions],
86
+ ) -> models.EmptyResponse:
87
+ """Write File"""
88
+ return await self.transport.request(
89
+ models.EmptyResponse,
90
+ method="POST",
91
+ path="/fs/write",
92
+ body=body,
93
+ **options,
94
+ )
@@ -0,0 +1,473 @@
1
+ # Code generated by openapi-gen; DO NOT EDIT.
2
+ #
3
+ # Type definitions for a Unikraft Cloud plugin API, generated from its OpenAPI
4
+ # specification. These mirror the wire format exactly.
5
+
6
+ from __future__ import annotations
7
+
8
+ from typing import Any, Literal
9
+
10
+ from pydantic import BaseModel, ConfigDict
11
+
12
+ # Enumerations, as Literal aliases rather than enum classes: the values stay
13
+ # plain strings, so `state == "running"` needs no import, and a value the server
14
+ # adds is one line to widen.
15
+
16
+
17
+ CommandLogStream = Literal["stdout", "stderr"]
18
+ """The output stream to read raw bytes from."""
19
+
20
+ FileEncoding = Literal["utf-8", "base64"]
21
+ """The encoding of the data written to a file."""
22
+
23
+ ResponseStatus = Literal["success", "error", "partial_success"]
24
+ """The response status of an API request."""
25
+
26
+
27
+ class CommandLogsData(BaseModel):
28
+ """The collected output of a command."""
29
+
30
+ # Fields the specification does not describe are kept rather than dropped,
31
+ # so a newly added one survives a round trip and does not break this client.
32
+ # Every field is optional: one response shape serves several requests, and a
33
+ # summary listing or a failed bulk item carries only some of them.
34
+ model_config = ConfigDict(extra="allow", populate_by_name=True)
35
+
36
+ stdout: str | None = None
37
+ """The requested part of the standard output stream, base64-encoded."""
38
+ stderr: str | None = None
39
+ """The requested part of the standard error stream, base64-encoded."""
40
+ stdout_available: int | None = None
41
+ """The total number of bytes available in the standard output stream."""
42
+ stderr_available: int | None = None
43
+ """The total number of bytes available in the standard error stream."""
44
+
45
+
46
+ class CommandLogsRange(BaseModel):
47
+ """The byte range of a single output stream to return."""
48
+
49
+ # Fields the specification does not describe are kept rather than dropped,
50
+ # so a newly added one survives a round trip and does not break this client.
51
+ # A field the specification requires is required here too, so a request
52
+ # missing one fails at construction rather than at the server.
53
+ model_config = ConfigDict(extra="allow", populate_by_name=True)
54
+
55
+ offset: int
56
+ """The first byte of the stream to return."""
57
+ limit: int | None = None
58
+ """
59
+ The maximum number of bytes to return. When omitted, everything from
60
+ `offset` to the end of the stream is returned.
61
+ """
62
+
63
+
64
+ class CommandLogsRequest(BaseModel):
65
+ """
66
+ The (optional) request body limiting which parts of the output streams are
67
+ returned. When omitted, both streams are returned in their entirety.
68
+ """
69
+
70
+ # Fields the specification does not describe are kept rather than dropped,
71
+ # so a newly added one survives a round trip and does not break this client.
72
+ # A field the specification requires is required here too, so a request
73
+ # missing one fails at construction rather than at the server.
74
+ model_config = ConfigDict(extra="allow", populate_by_name=True)
75
+
76
+ stdout: CommandLogsRange
77
+ """The part of the standard output stream to return."""
78
+ stderr: CommandLogsRange
79
+ """The part of the standard error stream to return."""
80
+
81
+
82
+ class CommandLogsResponse(BaseModel):
83
+ """
84
+ The response to fetching the output of a command. `404` is returned, with an
85
+ empty body, when no command with the given UUID exists. `410` is returned,
86
+ with an
87
+ empty body, if the command's logs have been deleted.
88
+ """
89
+
90
+ # Fields the specification does not describe are kept rather than dropped,
91
+ # so a newly added one survives a round trip and does not break this client.
92
+ # Every field is optional: one response shape serves several requests, and a
93
+ # summary listing or a failed bulk item carries only some of them.
94
+ model_config = ConfigDict(extra="allow", populate_by_name=True)
95
+
96
+ status: ResponseStatus | None = None
97
+ """The status of the response."""
98
+ message: str | None = None
99
+ """An optional message providing additional information about the status."""
100
+ data: CommandLogsData | None = None
101
+ """The response data for this request."""
102
+ errors: list[ResponseError] | None = None
103
+ """A list of errors which may have occurred during the request."""
104
+ op_time_us: int | None = None
105
+ """The operation time in microseconds."""
106
+
107
+
108
+ class CommandSignalRequest(BaseModel):
109
+ """The request to send a signal to a command."""
110
+
111
+ # Fields the specification does not describe are kept rather than dropped,
112
+ # so a newly added one survives a round trip and does not break this client.
113
+ # A field the specification requires is required here too, so a request
114
+ # missing one fails at construction rather than at the server.
115
+ model_config = ConfigDict(extra="allow", populate_by_name=True)
116
+
117
+ signal: int | str
118
+ """
119
+ The signal to send, either as a number or as a name, with or without the
120
+ `SIG` prefix, e.g. `15`, `TERM` or `SIGTERM`.
121
+ """
122
+
123
+
124
+ class CommandStdinRequest(BaseModel):
125
+ """The request to feed data into a command's standard input."""
126
+
127
+ # Fields the specification does not describe are kept rather than dropped,
128
+ # so a newly added one survives a round trip and does not break this client.
129
+ # A field the specification requires is required here too, so a request
130
+ # missing one fails at construction rather than at the server.
131
+ model_config = ConfigDict(extra="allow", populate_by_name=True)
132
+
133
+ data: str
134
+ """The bytes to feed into standard input, base64-encoded."""
135
+ eof: bool | None = None
136
+ """Whether to close standard input after injecting `data`."""
137
+
138
+
139
+ class CommandWaitTimeoutRequest(BaseModel):
140
+ """The request to wait for a command to finish, up to a timeout."""
141
+
142
+ # Fields the specification does not describe are kept rather than dropped,
143
+ # so a newly added one survives a round trip and does not break this client.
144
+ # A field the specification requires is required here too, so a request
145
+ # missing one fails at construction rather than at the server.
146
+ model_config = ConfigDict(extra="allow", populate_by_name=True)
147
+
148
+ timeout_s: float
149
+ """The maximum time to wait, in seconds. Decimals are accepted."""
150
+
151
+
152
+ class EmptyResponse(BaseModel):
153
+ """
154
+ A response which carries no data of its own. `data` is `null`; the outcome
155
+ of
156
+ the request is the HTTP status code.
157
+ """
158
+
159
+ # Fields the specification does not describe are kept rather than dropped,
160
+ # so a newly added one survives a round trip and does not break this client.
161
+ # Every field is optional: one response shape serves several requests, and a
162
+ # summary listing or a failed bulk item carries only some of them.
163
+ model_config = ConfigDict(extra="allow", populate_by_name=True)
164
+
165
+ status: ResponseStatus | None = None
166
+ """The status of the response."""
167
+ message: str | None = None
168
+ """An optional message providing additional information about the status."""
169
+ data: Any | None = None
170
+ """The response data for this request."""
171
+ errors: list[ResponseError] | None = None
172
+ """A list of errors which may have occurred during the request."""
173
+ op_time_us: int | None = None
174
+ """The operation time in microseconds."""
175
+
176
+
177
+ class GetCommandData(BaseModel):
178
+ """The state of a single command."""
179
+
180
+ # Fields the specification does not describe are kept rather than dropped,
181
+ # so a newly added one survives a round trip and does not break this client.
182
+ # Every field is optional: one response shape serves several requests, and a
183
+ # summary listing or a failed bulk item carries only some of them.
184
+ model_config = ConfigDict(extra="allow", populate_by_name=True)
185
+
186
+ uuid: str | None = None
187
+ """The UUID of the command."""
188
+ cmdline: CommandLineShell | CommandLineArgs | None = None
189
+ """
190
+ The command the command was started with, in the form it was supplied in:
191
+ a shell command line, or an argument vector.
192
+ """
193
+ cwd: str | None = None
194
+ """The current working directory, if supplied at startup, else null"""
195
+ env: dict[str, str] | None = None
196
+ """Environment variables, if supplied at startup, else null"""
197
+ exitcode: int | None = None
198
+ """
199
+ The exit code of the command. It is `null` while the command is still
200
+ running.
201
+ """
202
+
203
+
204
+ class GetCommandResponse(BaseModel):
205
+ """
206
+ The response to inspecting a command. `404` is returned, with an empty body,
207
+ when no command with the given UUID exists.
208
+ """
209
+
210
+ # Fields the specification does not describe are kept rather than dropped,
211
+ # so a newly added one survives a round trip and does not break this client.
212
+ # Every field is optional: one response shape serves several requests, and a
213
+ # summary listing or a failed bulk item carries only some of them.
214
+ model_config = ConfigDict(extra="allow", populate_by_name=True)
215
+
216
+ status: ResponseStatus | None = None
217
+ """The status of the response."""
218
+ message: str | None = None
219
+ """An optional message providing additional information about the status."""
220
+ data: GetCommandData | None = None
221
+ """The response data for this request."""
222
+ errors: list[ResponseError] | None = None
223
+ """A list of errors which may have occurred during the request."""
224
+ op_time_us: int | None = None
225
+ """The operation time in microseconds."""
226
+
227
+
228
+ class ListCommandsData(BaseModel):
229
+ """The known commands."""
230
+
231
+ # Fields the specification does not describe are kept rather than dropped,
232
+ # so a newly added one survives a round trip and does not break this client.
233
+ # Every field is optional: one response shape serves several requests, and a
234
+ # summary listing or a failed bulk item carries only some of them.
235
+ model_config = ConfigDict(extra="allow", populate_by_name=True)
236
+
237
+ commands: list[str] | None = None
238
+ """The UUIDs of all known commands, in the order they were started."""
239
+
240
+
241
+ class ListCommandsResponse(BaseModel):
242
+ """The response to listing the commands known to the sandbox."""
243
+
244
+ # Fields the specification does not describe are kept rather than dropped,
245
+ # so a newly added one survives a round trip and does not break this client.
246
+ # Every field is optional: one response shape serves several requests, and a
247
+ # summary listing or a failed bulk item carries only some of them.
248
+ model_config = ConfigDict(extra="allow", populate_by_name=True)
249
+
250
+ status: ResponseStatus | None = None
251
+ """The status of the response."""
252
+ message: str | None = None
253
+ """An optional message providing additional information about the status."""
254
+ data: ListCommandsData | None = None
255
+ """The response data for this request."""
256
+ errors: list[ResponseError] | None = None
257
+ """A list of errors which may have occurred during the request."""
258
+ op_time_us: int | None = None
259
+ """The operation time in microseconds."""
260
+
261
+
262
+ class MkdirRequest(BaseModel):
263
+ """The request to create a directory in the sandbox filesystem."""
264
+
265
+ # Fields the specification does not describe are kept rather than dropped,
266
+ # so a newly added one survives a round trip and does not break this client.
267
+ # A field the specification requires is required here too, so a request
268
+ # missing one fails at construction rather than at the server.
269
+ model_config = ConfigDict(extra="allow", populate_by_name=True)
270
+
271
+ path: str
272
+ """The absolute path of the directory to create."""
273
+ parents: bool
274
+ """Whether to create missing parent directories."""
275
+
276
+
277
+ class ReadFileData(BaseModel):
278
+ """The contents of a file."""
279
+
280
+ # Fields the specification does not describe are kept rather than dropped,
281
+ # so a newly added one survives a round trip and does not break this client.
282
+ # Every field is optional: one response shape serves several requests, and a
283
+ # summary listing or a failed bulk item carries only some of them.
284
+ model_config = ConfigDict(extra="allow", populate_by_name=True)
285
+
286
+ contents: str | None = None
287
+ """The contents of the file, base64-encoded."""
288
+
289
+
290
+ class ReadFileRequest(BaseModel):
291
+ """The request to read a file from the sandbox filesystem."""
292
+
293
+ # Fields the specification does not describe are kept rather than dropped,
294
+ # so a newly added one survives a round trip and does not break this client.
295
+ # A field the specification requires is required here too, so a request
296
+ # missing one fails at construction rather than at the server.
297
+ model_config = ConfigDict(extra="allow", populate_by_name=True)
298
+
299
+ path: str
300
+ """The absolute path of the file to read."""
301
+
302
+
303
+ class ReadFileResponse(BaseModel):
304
+ """
305
+ The response to reading a file. `500` is returned when the file could not be
306
+ read.
307
+ """
308
+
309
+ # Fields the specification does not describe are kept rather than dropped,
310
+ # so a newly added one survives a round trip and does not break this client.
311
+ # Every field is optional: one response shape serves several requests, and a
312
+ # summary listing or a failed bulk item carries only some of them.
313
+ model_config = ConfigDict(extra="allow", populate_by_name=True)
314
+
315
+ status: ResponseStatus | None = None
316
+ """The status of the response."""
317
+ message: str | None = None
318
+ """An optional message providing additional information about the status."""
319
+ data: ReadFileData | None = None
320
+ """The response data for this request."""
321
+ errors: list[ResponseError] | None = None
322
+ """A list of errors which may have occurred during the request."""
323
+ op_time_us: int | None = None
324
+ """The operation time in microseconds."""
325
+
326
+
327
+ class ResponseError(BaseModel):
328
+ """The error response message for an API request."""
329
+
330
+ # Fields the specification does not describe are kept rather than dropped,
331
+ # so a newly added one survives a round trip and does not break this client.
332
+ # Every field is optional: one response shape serves several requests, and a
333
+ # summary listing or a failed bulk item carries only some of them.
334
+ model_config = ConfigDict(extra="allow", populate_by_name=True)
335
+
336
+ status: int | None = None
337
+ """The HTTP status code of the error."""
338
+
339
+
340
+ class RunCommandData(BaseModel):
341
+ """The identity of a newly started command."""
342
+
343
+ # Fields the specification does not describe are kept rather than dropped,
344
+ # so a newly added one survives a round trip and does not break this client.
345
+ # Every field is optional: one response shape serves several requests, and a
346
+ # summary listing or a failed bulk item carries only some of them.
347
+ model_config = ConfigDict(extra="allow", populate_by_name=True)
348
+
349
+ uuid: str | None = None
350
+ """The UUID assigned to the newly started command."""
351
+
352
+
353
+ class RunCommandRequest(BaseModel):
354
+ """The request to start a command in the sandbox."""
355
+
356
+ # Fields the specification does not describe are kept rather than dropped,
357
+ # so a newly added one survives a round trip and does not break this client.
358
+ # A field the specification requires is required here too, so a request
359
+ # missing one fails at construction rather than at the server.
360
+ model_config = ConfigDict(extra="allow", populate_by_name=True)
361
+
362
+ cmd: CommandLineShell | CommandLineArgs
363
+ """
364
+ The command to run: a command line interpreted by the shell, or an argument
365
+ vector executed directly. A bare program name in a vector is looked up in
366
+ the `PATH` of `env`, falling back to the sandbox's own `PATH`. `400` is
367
+ returned if the vector is empty or the program cannot be resolved.
368
+ """
369
+ cwd: str | None = None
370
+ """(OPTIONAL) Current working directory"""
371
+ env: dict[str, str] | None = None
372
+ """(OPTIONAL) Environment variables"""
373
+
374
+
375
+ class RunCommandResponse(BaseModel):
376
+ """
377
+ The response to starting a command. `500` is returned when the command could
378
+ not be spawned.
379
+ """
380
+
381
+ # Fields the specification does not describe are kept rather than dropped,
382
+ # so a newly added one survives a round trip and does not break this client.
383
+ # Every field is optional: one response shape serves several requests, and a
384
+ # summary listing or a failed bulk item carries only some of them.
385
+ model_config = ConfigDict(extra="allow", populate_by_name=True)
386
+
387
+ status: ResponseStatus | None = None
388
+ """The status of the response."""
389
+ message: str | None = None
390
+ """An optional message providing additional information about the status."""
391
+ data: RunCommandData | None = None
392
+ """The response data for this request."""
393
+ errors: list[ResponseError] | None = None
394
+ """A list of errors which may have occurred during the request."""
395
+ op_time_us: int | None = None
396
+ """The operation time in microseconds."""
397
+
398
+
399
+ class UploadFileRequest(BaseModel):
400
+ """The request to upload a file to the sandbox filesystem."""
401
+
402
+ # Fields the specification does not describe are kept rather than dropped,
403
+ # so a newly added one survives a round trip and does not break this client.
404
+ # A field the specification requires is required here too, so a request
405
+ # missing one fails at construction rather than at the server.
406
+ model_config = ConfigDict(extra="allow", populate_by_name=True)
407
+
408
+ path: str
409
+ """Path where to upload the file."""
410
+ filename: str
411
+ """Name of uploaded file; will be used if `path` is a directory."""
412
+ parents: bool
413
+ """Whether to create parent directories of `path`."""
414
+ encoding: FileEncoding
415
+ """The encoding of `data`."""
416
+ data: str
417
+ """The data to write, in the given encoding"""
418
+
419
+
420
+ class WriteFileRequest(BaseModel):
421
+ """The request to write a file in the sandbox filesystem."""
422
+
423
+ # Fields the specification does not describe are kept rather than dropped,
424
+ # so a newly added one survives a round trip and does not break this client.
425
+ # A field the specification requires is required here too, so a request
426
+ # missing one fails at construction rather than at the server.
427
+ model_config = ConfigDict(extra="allow", populate_by_name=True)
428
+
429
+ path: str
430
+ """The absolute path of the file to write. It is created if missing."""
431
+ append: bool
432
+ """Whether to append to the file instead of truncating it."""
433
+ encoding: FileEncoding
434
+ """The encoding of `data`."""
435
+ data: str
436
+ """The data to write, in the given encoding."""
437
+
438
+
439
+ CommandLineArgs = list[str]
440
+ """The command as an argument vector, executed directly without a shell."""
441
+
442
+ CommandLineShell = str
443
+ """The command line, interpreted by the shell."""
444
+
445
+
446
+ # Annotations are deferred, so a model may name one defined further down. This
447
+ # resolves them all now: a reference that cannot be resolved becomes an error on
448
+ # import rather than a surprise at the first request.
449
+ for _model in (
450
+ CommandLogsData,
451
+ CommandLogsRange,
452
+ CommandLogsRequest,
453
+ CommandLogsResponse,
454
+ CommandSignalRequest,
455
+ CommandStdinRequest,
456
+ CommandWaitTimeoutRequest,
457
+ EmptyResponse,
458
+ GetCommandData,
459
+ GetCommandResponse,
460
+ ListCommandsData,
461
+ ListCommandsResponse,
462
+ MkdirRequest,
463
+ ReadFileData,
464
+ ReadFileRequest,
465
+ ReadFileResponse,
466
+ ResponseError,
467
+ RunCommandData,
468
+ RunCommandRequest,
469
+ RunCommandResponse,
470
+ UploadFileRequest,
471
+ WriteFileRequest,
472
+ ):
473
+ _model.model_rebuild()
@@ -0,0 +1,125 @@
1
+ # Code generated by openapi-gen; DO NOT EDIT.
2
+ #
3
+ # The contract between this package and the transport that carries its
4
+ # requests. `ApiClient` in `unikraft_cloud.core.http` satisfies it, and so does
5
+ # any object with these four methods, because the types are protocols. This
6
+ # module imports no SDK, so the package depends on none.
7
+ #
8
+ # This is the only copy of the contract. The SDK's `ApiClient` satisfies it by
9
+ # shape, which the SDK's own type check confirms wherever it hands one over.
10
+ # Widening is safe: a new optional parameter, a wider input type. A rename, a
11
+ # new required parameter, or a changed return type must reach `ApiClient` in
12
+ # the SDK as well, and every plugin package must be republished.
13
+
14
+ from __future__ import annotations
15
+
16
+ from collections.abc import AsyncIterator, Mapping, Sequence
17
+ from typing import Any, Protocol, TypedDict, TypeVar
18
+
19
+ from typing_extensions import Unpack
20
+
21
+ __all__ = ["CallOptions", "QueryValue", "RawResponse", "Transport"]
22
+
23
+ T = TypeVar("T")
24
+
25
+ #: A value that can be serialised into a query string.
26
+ QueryValue = str | int | float | bool | Sequence[str | int | float | bool] | None
27
+
28
+
29
+ class CallOptions(TypedDict, total=False):
30
+ """What a caller may set on one call, over the transport's own defaults.
31
+
32
+ A key that is absent leaves the transport's default in place, so
33
+ ``timeout=None`` lifts the timeout for one call and no ``timeout`` at all
34
+ keeps the client's. A generated method passes these through untouched.
35
+ """
36
+
37
+ #: Extra headers, merged over the transport's defaults for this call.
38
+ headers: Mapping[str, str] | None
39
+ #: The base URL to send this call to, e.g. another metro.
40
+ base_url: str | None
41
+ #: Seconds to wait for this call, or ``None`` to wait without limit.
42
+ timeout: float | None
43
+
44
+
45
+ class RawResponse(Protocol):
46
+ """The payload of an operation that answers with raw bytes, unenveloped.
47
+
48
+ The status and headers travel with the bytes because they carry meaning
49
+ of their own there: a ``206`` answers a ``Range`` request, and its
50
+ ``Content-Range`` header says which bytes these are.
51
+ """
52
+
53
+ @property
54
+ def content(self) -> bytes:
55
+ """The response body, untouched."""
56
+ ...
57
+
58
+ @property
59
+ def status(self) -> int:
60
+ """The HTTP status code."""
61
+ ...
62
+
63
+ @property
64
+ def headers(self) -> Mapping[str, str]:
65
+ """The response headers, case-insensitive."""
66
+ ...
67
+
68
+
69
+ class Transport(Protocol):
70
+ """What a generated class needs from the SDK: one method per response shape.
71
+
72
+ Every method takes the request as keyword arguments, applies the
73
+ :class:`CallOptions` it is given over its own defaults, and raises the
74
+ SDK's error on a failure.
75
+ """
76
+
77
+ async def request(
78
+ self,
79
+ model: type[T],
80
+ *,
81
+ method: str,
82
+ path: str,
83
+ query: Mapping[str, QueryValue] | None = None,
84
+ body: Any = None,
85
+ **options: Unpack[CallOptions],
86
+ ) -> T:
87
+ """Perform a request and return its response envelope parsed as ``model``."""
88
+ ...
89
+
90
+ async def request_no_content(
91
+ self,
92
+ *,
93
+ method: str,
94
+ path: str,
95
+ query: Mapping[str, QueryValue] | None = None,
96
+ body: Any = None,
97
+ **options: Unpack[CallOptions],
98
+ ) -> None:
99
+ """Perform a request whose response carries no payload worth parsing."""
100
+ ...
101
+
102
+ async def request_bytes(
103
+ self,
104
+ *,
105
+ method: str,
106
+ path: str,
107
+ query: Mapping[str, QueryValue] | None = None,
108
+ body: Any = None,
109
+ **options: Unpack[CallOptions],
110
+ ) -> RawResponse:
111
+ """Perform a request whose success response is a raw byte payload."""
112
+ ...
113
+
114
+ def stream(
115
+ self,
116
+ model: type[T],
117
+ *,
118
+ method: str,
119
+ path: str,
120
+ query: Mapping[str, QueryValue] | None = None,
121
+ body: Any = None,
122
+ **options: Unpack[CallOptions],
123
+ ) -> AsyncIterator[T]:
124
+ """Yield each event of a ``text/event-stream`` response, parsed as ``model``."""
125
+ ...