kscale 0.0.13__cp313-cp313-macosx_11_0_arm64.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.
- kscale/__init__.py +9 -0
- kscale/api.py +14 -0
- kscale/artifacts/__init__.py +8 -0
- kscale/artifacts/plane.obj +18 -0
- kscale/artifacts/plane.urdf +28 -0
- kscale/cli.py +25 -0
- kscale/conf.py +45 -0
- kscale/py.typed +0 -0
- kscale/requirements-dev.txt +11 -0
- kscale/requirements.txt +17 -0
- kscale/rust.cpython-313-darwin.so +0 -0
- kscale/utils/__init__.py +0 -0
- kscale/utils/api_base.py +6 -0
- kscale/utils/checksum.py +41 -0
- kscale/utils/cli.py +28 -0
- kscale/web/__init__.py +0 -0
- kscale/web/api.py +98 -0
- kscale/web/gen/__init__.py +0 -0
- kscale/web/gen/api.py +612 -0
- kscale/web/kernels.py +207 -0
- kscale/web/krec.py +175 -0
- kscale/web/pybullet.py +188 -0
- kscale/web/urdf.py +185 -0
- kscale/web/utils.py +48 -0
- kscale/web/www_client.py +134 -0
- kscale-0.0.13.dist-info/LICENSE +21 -0
- kscale-0.0.13.dist-info/METADATA +55 -0
- kscale-0.0.13.dist-info/RECORD +31 -0
- kscale-0.0.13.dist-info/WHEEL +5 -0
- kscale-0.0.13.dist-info/entry_points.txt +2 -0
- kscale-0.0.13.dist-info/top_level.txt +1 -0
kscale/web/urdf.py
ADDED
@@ -0,0 +1,185 @@
|
|
1
|
+
"""Utility functions for managing artifacts in K-Scale WWW."""
|
2
|
+
|
3
|
+
import logging
|
4
|
+
import shutil
|
5
|
+
import tarfile
|
6
|
+
from pathlib import Path
|
7
|
+
|
8
|
+
import click
|
9
|
+
import httpx
|
10
|
+
import requests
|
11
|
+
|
12
|
+
from kscale.utils.cli import coro
|
13
|
+
from kscale.web.gen.api import SingleArtifactResponse, UploadArtifactResponse
|
14
|
+
from kscale.web.utils import get_api_key, get_artifact_dir, get_cache_dir
|
15
|
+
from kscale.web.www_client import KScaleWWWClient
|
16
|
+
|
17
|
+
# Set up logging
|
18
|
+
logging.basicConfig(level=logging.INFO)
|
19
|
+
logger = logging.getLogger(__name__)
|
20
|
+
|
21
|
+
ALLOWED_SUFFIXES = {
|
22
|
+
".urdf",
|
23
|
+
".mjcf",
|
24
|
+
".stl",
|
25
|
+
".obj",
|
26
|
+
".dae",
|
27
|
+
".png",
|
28
|
+
".jpg",
|
29
|
+
".jpeg",
|
30
|
+
}
|
31
|
+
|
32
|
+
|
33
|
+
async def fetch_urdf_info(artifact_id: str, cache_dir: Path) -> SingleArtifactResponse:
|
34
|
+
response_path = cache_dir / "response.json"
|
35
|
+
if response_path.exists():
|
36
|
+
return SingleArtifactResponse.model_validate_json(response_path.read_text())
|
37
|
+
async with KScaleWWWClient() as client:
|
38
|
+
response = await client.get_artifact_info(artifact_id)
|
39
|
+
response_path.write_text(response.model_dump_json())
|
40
|
+
return response
|
41
|
+
|
42
|
+
|
43
|
+
async def download_artifact(artifact_url: str, cache_dir: Path) -> Path:
|
44
|
+
filename = cache_dir / Path(artifact_url).name
|
45
|
+
headers = {
|
46
|
+
"Authorization": f"Bearer {get_api_key()}",
|
47
|
+
}
|
48
|
+
|
49
|
+
if not filename.exists():
|
50
|
+
logger.info("Downloading artifact from %s", artifact_url)
|
51
|
+
|
52
|
+
async with httpx.AsyncClient() as client:
|
53
|
+
response = await client.get(artifact_url, headers=headers)
|
54
|
+
response.raise_for_status()
|
55
|
+
filename.write_bytes(response.content)
|
56
|
+
logger.info("Artifact downloaded to %s", filename)
|
57
|
+
else:
|
58
|
+
logger.info("Artifact already cached at %s", filename)
|
59
|
+
|
60
|
+
# Extract the .tgz file
|
61
|
+
extract_dir = cache_dir / filename.stem
|
62
|
+
if not extract_dir.exists():
|
63
|
+
logger.info("Extracting %s to %s", filename, extract_dir)
|
64
|
+
with tarfile.open(filename, "r:gz") as tar:
|
65
|
+
tar.extractall(path=extract_dir)
|
66
|
+
else:
|
67
|
+
logger.info("Artifact already extracted at %s", extract_dir)
|
68
|
+
|
69
|
+
return extract_dir
|
70
|
+
|
71
|
+
|
72
|
+
def create_tarball(folder_path: Path, output_filename: str, cache_dir: Path) -> Path:
|
73
|
+
tarball_path = cache_dir / output_filename
|
74
|
+
with tarfile.open(tarball_path, "w:gz") as tar:
|
75
|
+
for file_path in folder_path.rglob("*"):
|
76
|
+
if file_path.is_file() and file_path.suffix.lower() in ALLOWED_SUFFIXES:
|
77
|
+
tar.add(file_path, arcname=file_path.relative_to(folder_path))
|
78
|
+
logger.info("Added %s to tarball", file_path)
|
79
|
+
else:
|
80
|
+
logger.warning("Skipping %s", file_path)
|
81
|
+
logger.info("Created tarball %s", tarball_path)
|
82
|
+
return tarball_path
|
83
|
+
|
84
|
+
|
85
|
+
async def download_urdf(artifact_id: str) -> Path:
|
86
|
+
cache_dir = get_artifact_dir(artifact_id)
|
87
|
+
try:
|
88
|
+
urdf_info = await fetch_urdf_info(artifact_id, cache_dir)
|
89
|
+
artifact_url = urdf_info.urls.large
|
90
|
+
return await download_artifact(artifact_url, cache_dir)
|
91
|
+
|
92
|
+
except requests.RequestException:
|
93
|
+
logger.exception("Failed to fetch URDF info")
|
94
|
+
raise
|
95
|
+
|
96
|
+
|
97
|
+
async def show_urdf_info(artifact_id: str) -> None:
|
98
|
+
try:
|
99
|
+
urdf_info = await fetch_urdf_info(artifact_id, get_artifact_dir(artifact_id))
|
100
|
+
logger.info("URDF Artifact ID: %s", urdf_info.artifact_id)
|
101
|
+
logger.info("URDF URL: %s", urdf_info.urls.large)
|
102
|
+
except requests.RequestException:
|
103
|
+
logger.exception("Failed to fetch URDF info")
|
104
|
+
raise
|
105
|
+
|
106
|
+
|
107
|
+
async def remove_local_urdf(artifact_id: str) -> None:
|
108
|
+
try:
|
109
|
+
if artifact_id.lower() == "all":
|
110
|
+
cache_dir = get_cache_dir()
|
111
|
+
if cache_dir.exists():
|
112
|
+
logger.info("Removing all local caches at %s", cache_dir)
|
113
|
+
shutil.rmtree(cache_dir)
|
114
|
+
else:
|
115
|
+
logger.error("No local caches found")
|
116
|
+
else:
|
117
|
+
artifact_dir = get_artifact_dir(artifact_id)
|
118
|
+
if artifact_dir.exists():
|
119
|
+
logger.info("Removing local cache at %s", artifact_dir)
|
120
|
+
shutil.rmtree(artifact_dir)
|
121
|
+
else:
|
122
|
+
logger.error("No local cache found for artifact %s", artifact_id)
|
123
|
+
|
124
|
+
except Exception:
|
125
|
+
logger.error("Failed to remove local cache")
|
126
|
+
raise
|
127
|
+
|
128
|
+
|
129
|
+
async def upload_urdf(listing_id: str, root_dir: Path) -> UploadArtifactResponse:
|
130
|
+
tarball_path = create_tarball(root_dir, "robot.tgz", get_artifact_dir(listing_id))
|
131
|
+
|
132
|
+
async with KScaleWWWClient() as client:
|
133
|
+
response = await client.upload_artifact(listing_id, str(tarball_path))
|
134
|
+
|
135
|
+
logger.info("Uploaded artifacts: %s", [artifact.artifact_id for artifact in response.artifacts])
|
136
|
+
return response
|
137
|
+
|
138
|
+
|
139
|
+
async def upload_urdf_cli(listing_id: str, root_dir: Path) -> UploadArtifactResponse:
|
140
|
+
response = await upload_urdf(listing_id, root_dir)
|
141
|
+
return response
|
142
|
+
|
143
|
+
|
144
|
+
@click.group()
|
145
|
+
def cli() -> None:
|
146
|
+
"""K-Scale URDF Store CLI tool."""
|
147
|
+
pass
|
148
|
+
|
149
|
+
|
150
|
+
@cli.command()
|
151
|
+
@click.argument("artifact_id")
|
152
|
+
@coro
|
153
|
+
async def download(artifact_id: str) -> None:
|
154
|
+
"""Download a URDF artifact."""
|
155
|
+
await download_urdf(artifact_id)
|
156
|
+
|
157
|
+
|
158
|
+
@cli.command()
|
159
|
+
@click.argument("artifact_id")
|
160
|
+
@coro
|
161
|
+
async def info(artifact_id: str) -> None:
|
162
|
+
"""Show information about a URDF artifact."""
|
163
|
+
await show_urdf_info(artifact_id)
|
164
|
+
|
165
|
+
|
166
|
+
@cli.command("remove-local")
|
167
|
+
@click.argument("artifact_id")
|
168
|
+
@coro
|
169
|
+
async def remove_local(artifact_id: str) -> None:
|
170
|
+
"""Remove local cache of a URDF artifact."""
|
171
|
+
await remove_local_urdf(artifact_id)
|
172
|
+
|
173
|
+
|
174
|
+
@cli.command()
|
175
|
+
@click.argument("listing_id")
|
176
|
+
@click.argument("root_dir", type=click.Path(exists=True, path_type=Path))
|
177
|
+
@coro
|
178
|
+
async def upload(listing_id: str, root_dir: Path) -> None:
|
179
|
+
"""Upload a URDF artifact."""
|
180
|
+
await upload_urdf_cli(listing_id, root_dir)
|
181
|
+
|
182
|
+
|
183
|
+
if __name__ == "__main__":
|
184
|
+
# python -m kscale.web.urdf
|
185
|
+
cli()
|
kscale/web/utils.py
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
"""Utility functions for interacting with the K-Scale WWW API."""
|
2
|
+
|
3
|
+
import os
|
4
|
+
from pathlib import Path
|
5
|
+
|
6
|
+
from kscale.conf import Settings
|
7
|
+
|
8
|
+
DEFAULT_UPLOAD_TIMEOUT = 300.0 # 5 minutes
|
9
|
+
|
10
|
+
|
11
|
+
def get_api_root() -> str:
|
12
|
+
"""Returns the base URL for the K-Scale WWW API.
|
13
|
+
|
14
|
+
This can be overridden when targetting a different server.
|
15
|
+
|
16
|
+
Returns:
|
17
|
+
The base URL for the K-Scale WWW API.
|
18
|
+
"""
|
19
|
+
return os.getenv("KSCALE_API_ROOT", "https://api.kscale.dev")
|
20
|
+
|
21
|
+
|
22
|
+
def get_api_key() -> str:
|
23
|
+
"""Returns the API key for the K-Scale WWW API.
|
24
|
+
|
25
|
+
Returns:
|
26
|
+
The API key for the K-Scale WWW API.
|
27
|
+
"""
|
28
|
+
api_key = Settings.load().www.api_key
|
29
|
+
if api_key is None:
|
30
|
+
api_key = os.getenv("KSCALE_API_KEY")
|
31
|
+
if not api_key:
|
32
|
+
raise ValueError(
|
33
|
+
"API key not found! Get one here and set it as the `KSCALE_API_KEY` environment variable or in your "
|
34
|
+
"config file: https://kscale.dev/keys"
|
35
|
+
)
|
36
|
+
return api_key
|
37
|
+
|
38
|
+
|
39
|
+
def get_cache_dir() -> Path:
|
40
|
+
"""Returns the cache directory for artifacts."""
|
41
|
+
return Path(Settings.load().www.cache_dir).expanduser().resolve()
|
42
|
+
|
43
|
+
|
44
|
+
def get_artifact_dir(artifact_id: str) -> Path:
|
45
|
+
"""Returns the directory for a specific artifact."""
|
46
|
+
cache_dir = get_cache_dir() / artifact_id
|
47
|
+
cache_dir.mkdir(parents=True, exist_ok=True)
|
48
|
+
return cache_dir
|
kscale/web/www_client.py
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
"""Defines a typed client for the K-Scale WWW API."""
|
2
|
+
|
3
|
+
import logging
|
4
|
+
from pathlib import Path
|
5
|
+
from types import TracebackType
|
6
|
+
from typing import Any, Dict, Type
|
7
|
+
from urllib.parse import urljoin
|
8
|
+
|
9
|
+
import httpx
|
10
|
+
from pydantic import BaseModel
|
11
|
+
|
12
|
+
from kscale.web.gen.api import (
|
13
|
+
BodyAddListingListingsAddPost,
|
14
|
+
NewListingResponse,
|
15
|
+
SingleArtifactResponse,
|
16
|
+
UploadArtifactResponse,
|
17
|
+
UploadKRecRequest,
|
18
|
+
)
|
19
|
+
from kscale.web.utils import DEFAULT_UPLOAD_TIMEOUT, get_api_key, get_api_root
|
20
|
+
|
21
|
+
logger = logging.getLogger(__name__)
|
22
|
+
|
23
|
+
|
24
|
+
class KScaleWWWClient:
|
25
|
+
def __init__(self, base_url: str = get_api_root(), upload_timeout: float = DEFAULT_UPLOAD_TIMEOUT) -> None:
|
26
|
+
self.base_url = base_url
|
27
|
+
self.upload_timeout = upload_timeout
|
28
|
+
self._client: httpx.AsyncClient | None = None
|
29
|
+
|
30
|
+
@property
|
31
|
+
def client(self) -> httpx.AsyncClient:
|
32
|
+
if self._client is None:
|
33
|
+
self._client = httpx.AsyncClient(
|
34
|
+
base_url=self.base_url,
|
35
|
+
headers={"Authorization": f"Bearer {get_api_key()}"},
|
36
|
+
timeout=httpx.Timeout(30.0),
|
37
|
+
)
|
38
|
+
return self._client
|
39
|
+
|
40
|
+
async def _request(
|
41
|
+
self,
|
42
|
+
method: str,
|
43
|
+
endpoint: str,
|
44
|
+
*,
|
45
|
+
params: Dict[str, Any] | None = None,
|
46
|
+
data: BaseModel | None = None,
|
47
|
+
files: Dict[str, Any] | None = None,
|
48
|
+
) -> Dict[str, Any]:
|
49
|
+
url = urljoin(self.base_url, endpoint)
|
50
|
+
kwargs: Dict[str, Any] = {"params": params}
|
51
|
+
|
52
|
+
if data:
|
53
|
+
kwargs["json"] = data.dict(exclude_unset=True)
|
54
|
+
if files:
|
55
|
+
kwargs["files"] = files
|
56
|
+
|
57
|
+
response = await self.client.request(method, url, **kwargs)
|
58
|
+
|
59
|
+
if response.is_error:
|
60
|
+
logger.error("Error response from K-Scale: %s", response.text)
|
61
|
+
response.raise_for_status()
|
62
|
+
return response.json()
|
63
|
+
|
64
|
+
async def get_artifact_info(self, artifact_id: str) -> SingleArtifactResponse:
|
65
|
+
data = await self._request("GET", f"/artifacts/info/{artifact_id}")
|
66
|
+
return SingleArtifactResponse(**data)
|
67
|
+
|
68
|
+
async def upload_artifact(self, listing_id: str, file_path: str) -> UploadArtifactResponse:
|
69
|
+
file_name = Path(file_path).name
|
70
|
+
with open(file_path, "rb") as f:
|
71
|
+
files = {"files": (file_name, f, "application/gzip")}
|
72
|
+
data = await self._request("POST", f"/artifacts/upload/{listing_id}", files=files)
|
73
|
+
return UploadArtifactResponse(**data)
|
74
|
+
|
75
|
+
async def create_listing(self, request: BodyAddListingListingsAddPost) -> NewListingResponse:
|
76
|
+
data = await self._request("POST", "/listings", data=request)
|
77
|
+
return NewListingResponse(**data)
|
78
|
+
|
79
|
+
async def create_krec(self, request: UploadKRecRequest) -> dict:
|
80
|
+
"""Create a new K-Rec upload and get the presigned URL."""
|
81
|
+
return await self._request(
|
82
|
+
"POST",
|
83
|
+
"/krecs/upload",
|
84
|
+
data=request,
|
85
|
+
)
|
86
|
+
|
87
|
+
async def close(self) -> None:
|
88
|
+
if self._client is not None:
|
89
|
+
await self._client.aclose()
|
90
|
+
self._client = None
|
91
|
+
|
92
|
+
async def __aenter__(self) -> "KScaleWWWClient":
|
93
|
+
return self
|
94
|
+
|
95
|
+
async def __aexit__(
|
96
|
+
self,
|
97
|
+
exc_type: Type[BaseException] | None,
|
98
|
+
exc_val: BaseException | None,
|
99
|
+
exc_tb: TracebackType | None,
|
100
|
+
) -> None:
|
101
|
+
await self.close()
|
102
|
+
|
103
|
+
async def upload_to_presigned_url(self, url: str, file_path: str) -> None:
|
104
|
+
"""Upload a file using a presigned URL."""
|
105
|
+
with open(file_path, "rb") as f:
|
106
|
+
async with httpx.AsyncClient(timeout=httpx.Timeout(timeout=self.upload_timeout)) as client:
|
107
|
+
response = await client.put(url, content=f.read(), headers={"Content-Type": "application/octet-stream"})
|
108
|
+
response.raise_for_status()
|
109
|
+
|
110
|
+
async def get_presigned_url(self, listing_id: str, file_name: str, checksum: str | None = None) -> dict:
|
111
|
+
"""Get a presigned URL for uploading an artifact."""
|
112
|
+
params = {"filename": file_name}
|
113
|
+
if checksum:
|
114
|
+
params["checksum"] = checksum
|
115
|
+
return await self._request("POST", f"/artifacts/presigned/{listing_id}", params=params)
|
116
|
+
|
117
|
+
async def get_krec_info(self, krec_id: str) -> dict:
|
118
|
+
"""Get information about a K-Rec."""
|
119
|
+
logger.info("Getting K-Rec info for ID: %s", krec_id)
|
120
|
+
try:
|
121
|
+
data = await self._request("GET", f"/krecs/download/{krec_id}")
|
122
|
+
if not isinstance(data, dict):
|
123
|
+
logger.error("Server returned unexpected type: %s", type(data))
|
124
|
+
logger.error("Response data: %s", data)
|
125
|
+
raise ValueError(f"Server returned {type(data)} instead of dictionary")
|
126
|
+
|
127
|
+
return {
|
128
|
+
"url": data.get("url"),
|
129
|
+
"filename": data.get("filename"),
|
130
|
+
"checksum": data.get("checksum"),
|
131
|
+
}
|
132
|
+
except Exception as e:
|
133
|
+
logger.error("Failed to get K-Rec info: %s", str(e))
|
134
|
+
raise
|
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2023 Benjamin Bolte
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
@@ -0,0 +1,55 @@
|
|
1
|
+
Metadata-Version: 2.1
|
2
|
+
Name: kscale
|
3
|
+
Version: 0.0.13
|
4
|
+
Summary: The kscale project
|
5
|
+
Home-page: https://github.com/kscalelabs/kscale
|
6
|
+
Author: Benjamin Bolte
|
7
|
+
Requires-Python: >=3.11
|
8
|
+
Description-Content-Type: text/markdown
|
9
|
+
License-File: LICENSE
|
10
|
+
Requires-Dist: omegaconf
|
11
|
+
Requires-Dist: email_validator
|
12
|
+
Requires-Dist: httpx
|
13
|
+
Requires-Dist: requests
|
14
|
+
Requires-Dist: pydantic
|
15
|
+
Requires-Dist: click
|
16
|
+
Requires-Dist: aiofiles
|
17
|
+
Requires-Dist: krec
|
18
|
+
Provides-Extra: dev
|
19
|
+
Requires-Dist: black; extra == "dev"
|
20
|
+
Requires-Dist: darglint; extra == "dev"
|
21
|
+
Requires-Dist: mypy; extra == "dev"
|
22
|
+
Requires-Dist: pytest; extra == "dev"
|
23
|
+
Requires-Dist: ruff; extra == "dev"
|
24
|
+
Requires-Dist: datamodel-code-generator; extra == "dev"
|
25
|
+
|
26
|
+
<p align="center">
|
27
|
+
<picture>
|
28
|
+
<img alt="K-Scale Open Source Robotics" src="https://media.kscale.dev/kscale-open-source-header.png" style="max-width: 100%;">
|
29
|
+
</picture>
|
30
|
+
</p>
|
31
|
+
|
32
|
+
<div align="center">
|
33
|
+
|
34
|
+
[](https://github.com/kscalelabs/ksim/blob/main/LICENSE)
|
35
|
+
[](https://discord.gg/k5mSvCkYQh)
|
36
|
+
[](https://humanoids.wiki)
|
37
|
+
<br />
|
38
|
+
[](https://github.com/pre-commit/pre-commit)
|
39
|
+
[](https://black.readthedocs.io/en/stable/)
|
40
|
+
[](https://github.com/charliermarsh/ruff)
|
41
|
+
<br />
|
42
|
+
[](https://github.com/kscalelabs/kscale/actions/workflows/test.yml)
|
43
|
+
[](https://github.com/kscalelabs/kscale/actions/workflows/publish.yml)
|
44
|
+
|
45
|
+
</div>
|
46
|
+
|
47
|
+
# K-Scale Command Line Interface
|
48
|
+
|
49
|
+
This is a command line tool for interacting with various services provided by K-Scale Labs. For more information, see the [documentation](https://docs.kscale.dev/pkg/intro).
|
50
|
+
|
51
|
+
## Installation
|
52
|
+
|
53
|
+
```bash
|
54
|
+
pip install kscale
|
55
|
+
```
|
@@ -0,0 +1,31 @@
|
|
1
|
+
kscale/rust.cpython-313-darwin.so,sha256=_sDcc2ZoeEzTkwKVYW4A-RwsHCYtdESsvy-ANkQGODo,431696
|
2
|
+
kscale/requirements.txt,sha256=zj7Rg5vE2Fl3ao66K9jmOlhv5_8SybHvHK5-4xOGZBs,141
|
3
|
+
kscale/conf.py,sha256=iI6eWgzIhzmqqy9EhVh6MbsmNpqCbTlSVdLEYfxyjvI,1416
|
4
|
+
kscale/__init__.py,sha256=FOc1X2Sjq58lZ0IMP9e0P4HQ2MM_7yBHk-KbuvApm-0,173
|
5
|
+
kscale/api.py,sha256=314tgy4e9fbQWsKPYLyofZyiKfKr9RmvstldXs2jDvY,322
|
6
|
+
kscale/cli.py,sha256=kfu-5dCr0DIEDEnF80tg5md56bEI2GsyA7mEuad1cDw,655
|
7
|
+
kscale/requirements-dev.txt,sha256=WI7-ea4IRJakmqVMN8QKhOsDGrghwtvk03aIsFaNSIw,130
|
8
|
+
kscale/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
9
|
+
kscale/artifacts/plane.obj,sha256=x59-IIrWpLjhotChiqT2Ul6U8s0RcHkaEeUZb4KXL1c,348
|
10
|
+
kscale/artifacts/__init__.py,sha256=RK8wdybtCJPgdLLJ8R8-YMi1Ph5ojqAKVJZowHONtgo,232
|
11
|
+
kscale/artifacts/plane.urdf,sha256=LCiTk14AyTHjkZ1jvsb0hNaEaJUxDb8Z1JjsgpXu3YM,819
|
12
|
+
kscale/web/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
13
|
+
kscale/web/pybullet.py,sha256=_at_1yf63Fzy7LmXbR-4fvdM9LqPmzYiMniqhKU-BtE,7395
|
14
|
+
kscale/web/api.py,sha256=f7_BPwMnByXSBDE1Rzuhr7IIqoIBY2wqDGoqFLQ_0ng,3809
|
15
|
+
kscale/web/www_client.py,sha256=Vu_rf2NuJ7fSiqTEb1B71OoTurdNrCtSVKc7K8AIk24,4963
|
16
|
+
kscale/web/utils.py,sha256=mXfrtH2TuxXkSF01QpcUyC7OHAszQPsiFYGZcHHgTDk,1337
|
17
|
+
kscale/web/urdf.py,sha256=rJjQBG3q5HBRxebNAccOqD-rXMS-2fpNdh8k5-6p4p0,5821
|
18
|
+
kscale/web/kernels.py,sha256=ddp8IEFSLS9raETQZIphDjxbrj9Jd0gfeQ6Fad0ee-Q,7242
|
19
|
+
kscale/web/krec.py,sha256=k9AhBHwLoxRDRUSllgQ88C_xMzwHCY4mHRrBsQFP1EA,5910
|
20
|
+
kscale/web/gen/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
21
|
+
kscale/web/gen/api.py,sha256=JqNit9Q6zUuWz-SrWjcKx3BQIzSmL50r0BT1CfT2hbo,23092
|
22
|
+
kscale/utils/checksum.py,sha256=jt6QmmQND9zrOEnUtOfZpLYROhgto4Gh3OpdUWk4tZA,1093
|
23
|
+
kscale/utils/api_base.py,sha256=Kk_WtRDdJHmOg6NtHmVxVrcfARSUkhfr29ypLch_pO0,112
|
24
|
+
kscale/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
25
|
+
kscale/utils/cli.py,sha256=JoaY5x5SdUx97KmMM9j5AjRRUqqrTlJ9qVckZptEsYA,827
|
26
|
+
kscale-0.0.13.dist-info/RECORD,,
|
27
|
+
kscale-0.0.13.dist-info/LICENSE,sha256=HCN2bImAzUOXldAZZI7JZ9PYq6OwMlDAP_PpX1HnuN0,1071
|
28
|
+
kscale-0.0.13.dist-info/WHEEL,sha256=fAn_VQwWuH_rMRyKwhTwvKm9fx9GheRVEYtY-6ugsD8,109
|
29
|
+
kscale-0.0.13.dist-info/entry_points.txt,sha256=c2pxylFuUJe3aQ_GLEgZrrBW98PPNkiH8Lg0NL_vnrA,42
|
30
|
+
kscale-0.0.13.dist-info/top_level.txt,sha256=C2ynjYwopg6YjgttnI2dJjasyq3EKNmYp-IfQg9Xms4,7
|
31
|
+
kscale-0.0.13.dist-info/METADATA,sha256=-Nmkr2zrj4ydnMtEW2miV_dqgoyY5PJuiirwUrIRsF8,2174
|
@@ -0,0 +1 @@
|
|
1
|
+
kscale
|