kscale 0.0.1__py3-none-any.whl → 0.0.3__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
kscale/__init__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.0.1"
1
+ __version__ = "0.0.3"
kscale/conf.py CHANGED
@@ -17,21 +17,13 @@ def get_path() -> Path:
17
17
 
18
18
  @dataclass
19
19
  class StoreSettings:
20
- api_key: str = field(default=II("oc.env:KSCALE_API_KEY"))
21
-
22
- def get_api_key(self) -> str:
23
- try:
24
- return self.api_key
25
- except AttributeError:
26
- raise ValueError(
27
- "API key not found! Get one here and set it as the `KSCALE_API_KEY` "
28
- "environment variable: https://kscale.store/keys"
29
- )
20
+ api_key: str = field(default=II("oc.env:KSCALE_API_KEY,"))
21
+ cache_dir: str = field(default=II("oc.env:KSCALE_CACHE_DIR,'~/.kscale/cache/'"))
30
22
 
31
23
 
32
24
  @dataclass
33
25
  class Settings:
34
- store: StoreSettings = StoreSettings()
26
+ store: StoreSettings = field(default_factory=StoreSettings)
35
27
 
36
28
  def save(self) -> None:
37
29
  (dir_path := get_path()).mkdir(parents=True, exist_ok=True)
kscale/store/__init__.py CHANGED
@@ -0,0 +1 @@
1
+ __version__ = "0.0.1"
kscale/store/cli.py ADDED
@@ -0,0 +1,35 @@
1
+ """Defines the top-level KOL CLI."""
2
+
3
+ import argparse
4
+ import asyncio
5
+ from typing import Sequence
6
+
7
+ from kscale.store import pybullet, urdf
8
+
9
+
10
+ async def main(args: Sequence[str] | None = None) -> None:
11
+ parser = argparse.ArgumentParser(description="K-Scale OnShape Library", add_help=False)
12
+ parser.add_argument(
13
+ "subcommand",
14
+ choices=[
15
+ "urdf",
16
+ "pybullet",
17
+ ],
18
+ help="The subcommand to run",
19
+ )
20
+ parsed_args, remaining_args = parser.parse_known_args(args)
21
+
22
+ match parsed_args.subcommand:
23
+ case "urdf":
24
+ await urdf.main(remaining_args)
25
+ case "pybullet":
26
+ await pybullet.main(remaining_args)
27
+
28
+
29
+ def sync_main(args: Sequence[str] | None = None) -> None:
30
+ asyncio.run(main(args))
31
+
32
+
33
+ if __name__ == "__main__":
34
+ # python3 -m kscale.store.cli
35
+ sync_main()
@@ -0,0 +1,179 @@
1
+ """Simple script to interact with a URDF in PyBullet."""
2
+
3
+ import argparse
4
+ import asyncio
5
+ import itertools
6
+ import logging
7
+ import math
8
+ import time
9
+ from pathlib import Path
10
+ from typing import Sequence
11
+
12
+ from kscale.store.urdf import download_urdf
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ async def main(args: Sequence[str] | None = None) -> None:
18
+ parser = argparse.ArgumentParser(description="Show a URDF in PyBullet")
19
+ parser.add_argument("listing_id", help="Listing ID for the URDF")
20
+ parser.add_argument("--dt", type=float, default=0.01, help="Time step")
21
+ parser.add_argument("-n", "--hide-gui", action="store_true", help="Hide the GUI")
22
+ parser.add_argument("--no-merge", action="store_true", help="Do not merge fixed links")
23
+ parser.add_argument("--hide-origin", action="store_true", help="Do not show the origin")
24
+ parser.add_argument("--show-inertia", action="store_true", help="Visualizes the inertia frames")
25
+ parser.add_argument("--see-thru", action="store_true", help="Use see-through mode")
26
+ parser.add_argument("--show-collision", action="store_true", help="Show collision meshes")
27
+ parsed_args = parser.parse_args(args)
28
+
29
+ # Gets the URDF path.
30
+ urdf_path = await download_urdf(parsed_args.listing_id)
31
+
32
+ breakpoint()
33
+
34
+ try:
35
+ import pybullet as p # type: ignore[import-not-found]
36
+ except ImportError:
37
+ raise ImportError("pybullet is required to run this script")
38
+
39
+ # Connect to PyBullet.
40
+ p.connect(p.GUI)
41
+ p.setGravity(0, 0, -9.81)
42
+ p.setRealTimeSimulation(0)
43
+
44
+ # Turn off panels.
45
+ if parsed_args.hide_gui:
46
+ p.configureDebugVisualizer(p.COV_ENABLE_GUI, 0)
47
+ p.configureDebugVisualizer(p.COV_ENABLE_SEGMENTATION_MARK_PREVIEW, 0)
48
+ p.configureDebugVisualizer(p.COV_ENABLE_DEPTH_BUFFER_PREVIEW, 0)
49
+ p.configureDebugVisualizer(p.COV_ENABLE_RGB_BUFFER_PREVIEW, 0)
50
+
51
+ # Enable mouse picking.
52
+ p.configureDebugVisualizer(p.COV_ENABLE_MOUSE_PICKING, 1)
53
+
54
+ # Loads the floor plane.
55
+ floor = p.loadURDF(str((Path(__file__).parent / "bullet" / "plane.urdf").resolve()))
56
+
57
+ # Load the robot URDF.
58
+ start_position = [0.0, 0.0, 1.0]
59
+ start_orientation = p.getQuaternionFromEuler([0.0, 0.0, 0.0])
60
+ flags = p.URDF_USE_INERTIA_FROM_FILE
61
+ if not parsed_args.no_merge:
62
+ flags |= p.URDF_MERGE_FIXED_LINKS
63
+ robot = p.loadURDF(str(urdf_path), start_position, start_orientation, flags=flags, useFixedBase=0)
64
+
65
+ # Display collision meshes as separate object.
66
+ if parsed_args.show_collision:
67
+ collision_flags = p.URDF_USE_INERTIA_FROM_FILE | p.URDF_USE_SELF_COLLISION_EXCLUDE_ALL_PARENTS
68
+ collision = p.loadURDF(str(urdf_path), start_position, start_orientation, flags=collision_flags, useFixedBase=0)
69
+
70
+ # Make collision shapes semi-transparent.
71
+ joint_ids = [i for i in range(p.getNumJoints(collision))] + [-1]
72
+ for i in joint_ids:
73
+ p.changeVisualShape(collision, i, rgbaColor=[1, 0, 0, 0.5])
74
+
75
+ # Initializes physics parameters.
76
+ p.changeDynamics(floor, -1, lateralFriction=1, spinningFriction=-1, rollingFriction=-1)
77
+ p.setPhysicsEngineParameter(fixedTimeStep=parsed_args.dt, maxNumCmdPer1ms=1000)
78
+
79
+ # Shows the origin of the robot.
80
+ if not parsed_args.hide_origin:
81
+ p.addUserDebugLine([0, 0, 0], [0.1, 0, 0], [1, 0, 0], parentObjectUniqueId=robot, parentLinkIndex=-1)
82
+ p.addUserDebugLine([0, 0, 0], [0, 0.1, 0], [0, 1, 0], parentObjectUniqueId=robot, parentLinkIndex=-1)
83
+ p.addUserDebugLine([0, 0, 0], [0, 0, 0.1], [0, 0, 1], parentObjectUniqueId=robot, parentLinkIndex=-1)
84
+
85
+ # Make the robot see-through.
86
+ joint_ids = [i for i in range(p.getNumJoints(robot))] + [-1]
87
+ if parsed_args.see_thru:
88
+ for i in joint_ids:
89
+ p.changeVisualShape(robot, i, rgbaColor=[1, 1, 1, 0.5])
90
+
91
+ def draw_box(pt: list[list[float]], color: tuple[float, float, float], obj_id: int, link_id: int) -> None:
92
+ assert len(pt) == 8
93
+ assert all(len(p) == 3 for p in pt)
94
+
95
+ mapping = [1, 3, 0, 2]
96
+ for i in range(4):
97
+ p.addUserDebugLine(pt[i], pt[i + 4], color, 1, parentObjectUniqueId=obj_id, parentLinkIndex=link_id)
98
+ p.addUserDebugLine(pt[i], pt[mapping[i]], color, 1, parentObjectUniqueId=obj_id, parentLinkIndex=link_id)
99
+ p.addUserDebugLine(
100
+ pt[i + 4], pt[mapping[i] + 4], color, 1, parentObjectUniqueId=obj_id, parentLinkIndex=link_id
101
+ )
102
+
103
+ # Shows bounding boxes around each part of the robot representing the inertia frame.
104
+ if parsed_args.show_inertia:
105
+ for i in joint_ids:
106
+ dynamics_info = p.getDynamicsInfo(robot, i)
107
+ mass = dynamics_info[0]
108
+ if mass <= 0:
109
+ continue
110
+ inertia = dynamics_info[2]
111
+ ixx = inertia[0]
112
+ iyy = inertia[1]
113
+ izz = inertia[2]
114
+ box_scale_x = 0.5 * math.sqrt(6 * (izz + iyy - ixx) / mass)
115
+ box_scale_y = 0.5 * math.sqrt(6 * (izz + ixx - iyy) / mass)
116
+ box_scale_z = 0.5 * math.sqrt(6 * (ixx + iyy - izz) / mass)
117
+
118
+ half_extents = [box_scale_x, box_scale_y, box_scale_z]
119
+ pt = [
120
+ [x, y, z]
121
+ for x, y, z in itertools.product(
122
+ [-half_extents[0], half_extents[0]],
123
+ [-half_extents[1], half_extents[1]],
124
+ [-half_extents[2], half_extents[2]],
125
+ )
126
+ ]
127
+ draw_box(pt, (1, 0, 0), robot, i)
128
+
129
+ # Show joint controller.
130
+ joints: dict[str, int] = {}
131
+ controls: dict[str, float] = {}
132
+ for i in range(p.getNumJoints(robot)):
133
+ joint_info = p.getJointInfo(robot, i)
134
+ name = joint_info[1].decode("utf-8")
135
+ joint_type = joint_info[2]
136
+ joints[name] = i
137
+ if joint_type == p.JOINT_PRISMATIC:
138
+ joint_min, joint_max = joint_info[8:10]
139
+ controls[name] = p.addUserDebugParameter(name, joint_min, joint_max, 0.0)
140
+ elif joint_type == p.JOINT_REVOLUTE:
141
+ joint_min, joint_max = joint_info[8:10]
142
+ controls[name] = p.addUserDebugParameter(name, joint_min, joint_max, 0.0)
143
+
144
+ # Run the simulation until the user closes the window.
145
+ last_time = time.time()
146
+ prev_control_values = {k: 0.0 for k in controls}
147
+ while p.isConnected():
148
+ # Reset the simulation if "r" was pressed.
149
+ keys = p.getKeyboardEvents()
150
+ if ord("r") in keys and keys[ord("r")] & p.KEY_WAS_TRIGGERED:
151
+ p.resetBasePositionAndOrientation(robot, start_position, start_orientation)
152
+ p.setJointMotorControlArray(
153
+ robot,
154
+ range(p.getNumJoints(robot)),
155
+ p.POSITION_CONTROL,
156
+ targetPositions=[0] * p.getNumJoints(robot),
157
+ )
158
+
159
+ # Set joint positions.
160
+ for k, v in controls.items():
161
+ try:
162
+ target_position = p.readUserDebugParameter(v)
163
+ if target_position != prev_control_values[k]:
164
+ prev_control_values[k] = target_position
165
+ p.setJointMotorControl2(robot, joints[k], p.POSITION_CONTROL, target_position)
166
+ except p.error:
167
+ logger.debug("Failed to set joint %s", k)
168
+ pass
169
+
170
+ # Step simulation.
171
+ p.stepSimulation()
172
+ cur_time = time.time()
173
+ time.sleep(max(0, parsed_args.dt - (cur_time - last_time)))
174
+ last_time = cur_time
175
+
176
+
177
+ if __name__ == "__main__":
178
+ # python -m kscale.store.pybullet
179
+ asyncio.run(main())
kscale/store/urdf.py CHANGED
@@ -1 +1,213 @@
1
1
  """Utility functions for managing artifacts in the K-Scale store."""
2
+
3
+ import argparse
4
+ import asyncio
5
+ import logging
6
+ import os
7
+ import shutil
8
+ import sys
9
+ import tarfile
10
+ from pathlib import Path
11
+ from typing import Literal, Sequence, get_args
12
+
13
+ import httpx
14
+ import requests
15
+
16
+ from kscale.conf import Settings
17
+ from kscale.store.gen.api import UrdfResponse
18
+
19
+ # Set up logging
20
+ logging.basicConfig(level=logging.INFO)
21
+ logger = logging.getLogger(__name__)
22
+
23
+
24
+ def get_api_key() -> str:
25
+ api_key = Settings.load().store.api_key
26
+ if not api_key:
27
+ raise ValueError(
28
+ "API key not found! Get one here and set it as the `KSCALE_API_KEY` environment variable or in your "
29
+ "config file: https://kscale.store/keys"
30
+ )
31
+ return api_key
32
+
33
+
34
+ def get_cache_dir() -> Path:
35
+ return Path(Settings.load().store.cache_dir).expanduser().resolve()
36
+
37
+
38
+ def get_listing_dir(listing_id: str) -> Path:
39
+ (cache_dir := get_cache_dir() / listing_id).mkdir(parents=True, exist_ok=True)
40
+ return cache_dir
41
+
42
+
43
+ def fetch_urdf_info(listing_id: str) -> UrdfResponse:
44
+ url = f"https://api.kscale.store/urdf/info/{listing_id}"
45
+ headers = {
46
+ "Authorization": f"Bearer {get_api_key()}",
47
+ }
48
+ response = requests.get(url, headers=headers)
49
+ response.raise_for_status()
50
+ return UrdfResponse(**response.json())
51
+
52
+
53
+ async def download_artifact(artifact_url: str, cache_dir: Path) -> Path:
54
+ filename = os.path.join(cache_dir, artifact_url.split("/")[-1])
55
+ headers = {
56
+ "Authorization": f"Bearer {get_api_key()}",
57
+ }
58
+
59
+ if not os.path.exists(filename):
60
+ logger.info("Downloading artifact from %s", artifact_url)
61
+
62
+ async with httpx.AsyncClient() as client:
63
+ response = await client.get(artifact_url, headers=headers)
64
+ response.raise_for_status()
65
+ with open(filename, "wb") as f:
66
+ for chunk in response.iter_bytes(chunk_size=8192):
67
+ f.write(chunk)
68
+ logger.info("Artifact downloaded to %s", filename)
69
+ else:
70
+ logger.info("Artifact already cached at %s", filename)
71
+
72
+ # Extract the .tgz file
73
+ extract_dir = cache_dir / os.path.splitext(os.path.basename(filename))[0]
74
+ if not extract_dir.exists():
75
+ logger.info("Extracting %s to %s", filename, extract_dir)
76
+ with tarfile.open(filename, "r:gz") as tar:
77
+ tar.extractall(path=extract_dir)
78
+ else:
79
+ logger.info("Artifact already extracted at %s", extract_dir)
80
+
81
+ return extract_dir
82
+
83
+
84
+ def create_tarball(folder_path: str | Path, output_filename: str, cache_dir: Path) -> str:
85
+ tarball_path = os.path.join(cache_dir, output_filename)
86
+ with tarfile.open(tarball_path, "w:gz") as tar:
87
+ for root, _, files in os.walk(folder_path):
88
+ for file in files:
89
+ file_path = os.path.join(root, file)
90
+ arcname = os.path.relpath(file_path, start=folder_path)
91
+ tar.add(file_path, arcname=arcname)
92
+ logger.info("Added %s as %s", file_path, arcname)
93
+ logger.info("Created tarball %s", tarball_path)
94
+ return tarball_path
95
+
96
+
97
+ async def upload_artifact(tarball_path: str, listing_id: str) -> None:
98
+ url = f"https://api.kscale.store/urdf/upload/{listing_id}"
99
+ headers = {
100
+ "Authorization": f"Bearer {get_api_key()}",
101
+ }
102
+
103
+ async with httpx.AsyncClient() as client:
104
+ with open(tarball_path, "rb") as f:
105
+ files = {"file": (f.name, f, "application/gzip")}
106
+ response = await client.post(url, headers=headers, files=files)
107
+
108
+ response.raise_for_status()
109
+
110
+ logger.info("Uploaded artifact to %s", url)
111
+
112
+
113
+ async def download_urdf(listing_id: str) -> Path:
114
+ try:
115
+ urdf_info = fetch_urdf_info(listing_id)
116
+
117
+ if urdf_info.urdf is None:
118
+ breakpoint()
119
+ raise ValueError(f"No URDF found for listing {listing_id}")
120
+
121
+ artifact_url = urdf_info.urdf.url
122
+ return await download_artifact(artifact_url, get_listing_dir(listing_id))
123
+
124
+ except requests.RequestException:
125
+ logger.exception("Failed to fetch URDF info")
126
+ raise
127
+
128
+
129
+ async def show_urdf_info(listing_id: str) -> None:
130
+ try:
131
+ urdf_info = fetch_urdf_info(listing_id)
132
+
133
+ if urdf_info.urdf:
134
+ logger.info("URDF Artifact ID: %s", urdf_info.urdf.artifact_id)
135
+ logger.info("URDF URL: %s", urdf_info.urdf.url)
136
+ else:
137
+ logger.info("No URDF found for listing %s", listing_id)
138
+ except requests.RequestException:
139
+ logger.exception("Failed to fetch URDF info")
140
+ raise
141
+
142
+
143
+ async def upload_urdf(listing_id: str, args: Sequence[str] | None = None) -> None:
144
+ parser = argparse.ArgumentParser(description="Upload a URDF artifact to the K-Scale store")
145
+ parser.add_argument("folder_path", help="The path to the folder containing the URDF files")
146
+ parsed_args = parser.parse_args(args)
147
+ folder_path = Path(parsed_args.folder_path).expanduser().resolve()
148
+
149
+ output_filename = f"{listing_id}.tgz"
150
+ tarball_path = create_tarball(folder_path, output_filename, get_listing_dir(listing_id))
151
+
152
+ try:
153
+ fetch_urdf_info(listing_id)
154
+ await upload_artifact(tarball_path, listing_id)
155
+ except requests.RequestException:
156
+ logger.exception("Failed to upload artifact")
157
+ raise
158
+
159
+
160
+ async def remove_local_urdf(listing_id: str) -> None:
161
+ try:
162
+ if listing_id.lower() == "all":
163
+ cache_dir = get_cache_dir()
164
+ if cache_dir.exists():
165
+ logger.info("Removing all local caches at %s", cache_dir)
166
+ shutil.rmtree(cache_dir)
167
+ else:
168
+ logger.error("No local caches found")
169
+ else:
170
+ listing_dir = get_listing_dir(listing_id)
171
+ if listing_dir.exists():
172
+ logger.info("Removing local cache at %s", listing_dir)
173
+ shutil.rmtree(listing_dir)
174
+ else:
175
+ logger.error("No local cache found for listing %s", listing_id)
176
+
177
+ except Exception:
178
+ logger.error("Failed to remove local cache")
179
+ raise
180
+
181
+
182
+ Command = Literal["download", "info", "upload", "remove-local"]
183
+
184
+
185
+ async def main(args: Sequence[str] | None = None) -> None:
186
+ parser = argparse.ArgumentParser(description="K-Scale URDF Store", add_help=False)
187
+ parser.add_argument("command", choices=get_args(Command), help="The command to run")
188
+ parser.add_argument("listing_id", help="The listing ID to operate on")
189
+ parsed_args, remaining_args = parser.parse_known_args(args)
190
+
191
+ command: Command = parsed_args.command
192
+ listing_id: str = parsed_args.listing_id
193
+
194
+ match command:
195
+ case "download":
196
+ await download_urdf(listing_id)
197
+
198
+ case "info":
199
+ await show_urdf_info(listing_id)
200
+
201
+ case "upload":
202
+ await upload_urdf(listing_id, remaining_args)
203
+
204
+ case "remove-local":
205
+ await remove_local_urdf(listing_id)
206
+
207
+ case _:
208
+ logger.error("Invalid command")
209
+ sys.exit(1)
210
+
211
+
212
+ if __name__ == "__main__":
213
+ asyncio.run(main())
@@ -0,0 +1,52 @@
1
+ Metadata-Version: 2.1
2
+ Name: kscale
3
+ Version: 0.0.3
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: httpx
12
+ Requires-Dist: requests
13
+ Provides-Extra: dev
14
+ Requires-Dist: black ; extra == 'dev'
15
+ Requires-Dist: darglint ; extra == 'dev'
16
+ Requires-Dist: mypy ; extra == 'dev'
17
+ Requires-Dist: pytest ; extra == 'dev'
18
+ Requires-Dist: ruff ; extra == 'dev'
19
+ Requires-Dist: datamodel-code-generator ; extra == 'dev'
20
+
21
+ <p align="center">
22
+ <picture>
23
+ <img alt="K-Scale Open Source Robotics" src="https://media.kscale.dev/kscale-open-source-header.png" style="max-width: 100%;">
24
+ </picture>
25
+ </p>
26
+
27
+ <div align="center">
28
+
29
+ [![License](https://img.shields.io/badge/license-MIT-green)](https://github.com/kscalelabs/ksim/blob/main/LICENSE)
30
+ [![Discord](https://img.shields.io/discord/1224056091017478166)](https://discord.gg/k5mSvCkYQh)
31
+ [![Wiki](https://img.shields.io/badge/wiki-humanoids-black)](https://humanoids.wiki)
32
+ <br />
33
+ [![python](https://img.shields.io/badge/-Python_3.11-blue?logo=python&logoColor=white)](https://github.com/pre-commit/pre-commit)
34
+ [![black](https://img.shields.io/badge/Code%20Style-Black-black.svg?labelColor=gray)](https://black.readthedocs.io/en/stable/)
35
+ [![ruff](https://img.shields.io/badge/Linter-Ruff-red.svg?labelColor=gray)](https://github.com/charliermarsh/ruff)
36
+ <br />
37
+ [![Python Checks](https://github.com/kscalelabs/kscale/actions/workflows/test.yml/badge.svg)](https://github.com/kscalelabs/kscale/actions/workflows/test.yml)
38
+ [![Publish Python Package](https://github.com/kscalelabs/kscale/actions/workflows/publish.yml/badge.svg)](https://github.com/kscalelabs/kscale/actions/workflows/publish.yml)
39
+
40
+ </div>
41
+
42
+ # K-Scale Command Line Interface
43
+
44
+ This is a command line tool for interacting with various services provided by K-Scale Labs, such as:
45
+
46
+ - [K-Scale Store](https://kscale.store/)
47
+
48
+ ## Installation
49
+
50
+ ```bash
51
+ pip install kscale
52
+ ```
@@ -0,0 +1,15 @@
1
+ kscale/__init__.py,sha256=4GZKi13lDTD25YBkGakhZyEQZWTER_OWQMNPoH_UM2c,22
2
+ kscale/conf.py,sha256=dnO8qii7JeMPMdjfuxnFXURRM4krwzL404RnwDkyL2M,1441
3
+ kscale/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ kscale/store/__init__.py,sha256=sXLh7g3KC4QCFxcZGBTpG2scR7hmmBsMjq6LqRptkRg,22
5
+ kscale/store/cli.py,sha256=8ygg_1tZzOOHJotEIgSN9pfumcriPmA31sI_FCFQiTo,859
6
+ kscale/store/pybullet.py,sha256=y51Oc6ZjOGe38O-L-Lm6HDbgNWc-5mpeXCeXRtCUl18,7522
7
+ kscale/store/urdf.py,sha256=0u6c2UxYlKwgUl0n3VmMSmfTqdixNP5b-hjox5mIgqM,7064
8
+ kscale/store/gen/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ kscale/store/gen/api.py,sha256=7r6KvlZyB-kWKSCixc-YOtaWPy2mn0v11d2tuLlRMSc,7700
10
+ kscale-0.0.3.dist-info/LICENSE,sha256=HCN2bImAzUOXldAZZI7JZ9PYq6OwMlDAP_PpX1HnuN0,1071
11
+ kscale-0.0.3.dist-info/METADATA,sha256=KN2mHTYV9mkJFxYqe4u1PFkCFQkc5o-E3460k66gN_M,2028
12
+ kscale-0.0.3.dist-info/WHEEL,sha256=Mdi9PDNwEZptOjTlUcAth7XJDFtKrHYaQMPulZeBCiQ,91
13
+ kscale-0.0.3.dist-info/entry_points.txt,sha256=PaVs1ivqB0BBdGUsiFkxGUYjGLz05VqagxwRVwi4yV4,54
14
+ kscale-0.0.3.dist-info/top_level.txt,sha256=C2ynjYwopg6YjgttnI2dJjasyq3EKNmYp-IfQg9Xms4,7
15
+ kscale-0.0.3.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (72.2.0)
2
+ Generator: setuptools (73.0.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ kscale = kscale.store.cli:sync_main
kscale/store/auth.py DELETED
@@ -1,13 +0,0 @@
1
- """Defines utility functions for authenticating the K-Scale Store API."""
2
-
3
- from kscale.conf import Settings
4
-
5
-
6
- def get_api_key() -> str:
7
- try:
8
- return Settings.load().store.api_key
9
- except AttributeError:
10
- raise ValueError(
11
- "API key not found! Get one here and set it as the `KSCALE_API_KEY` "
12
- "environment variable: https://kscale.store/keys"
13
- )
@@ -1,29 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: kscale
3
- Version: 0.0.1
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
- Provides-Extra: dev
12
- Requires-Dist: black ; extra == 'dev'
13
- Requires-Dist: darglint ; extra == 'dev'
14
- Requires-Dist: mypy ; extra == 'dev'
15
- Requires-Dist: pytest ; extra == 'dev'
16
- Requires-Dist: ruff ; extra == 'dev'
17
- Requires-Dist: datamodel-code-generator ; extra == 'dev'
18
-
19
- # K-Scale Command Line Interface
20
-
21
- This is a command line tool for interacting with various services provided by K-Scale Labs, such as:
22
-
23
- - [K-Scale Store](https://kscale.store/)
24
-
25
- ## Installation
26
-
27
- ```bash
28
- pip install kscale
29
- ```
@@ -1,13 +0,0 @@
1
- kscale/__init__.py,sha256=sXLh7g3KC4QCFxcZGBTpG2scR7hmmBsMjq6LqRptkRg,22
2
- kscale/conf.py,sha256=jLGM_bn-QdbZlZyqkhEYo07iIJBsIbuA1sg93YBOqhw,1641
3
- kscale/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- kscale/store/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
- kscale/store/auth.py,sha256=f84jTwrPXb5OFaY2UGfb9lNqoL1Kqac4N1Q61P2qthk,397
6
- kscale/store/urdf.py,sha256=QNwXe677pZm3brUStqu6LLbZrGHCQqpGYQtDOxid4WM,69
7
- kscale/store/gen/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- kscale/store/gen/api.py,sha256=7r6KvlZyB-kWKSCixc-YOtaWPy2mn0v11d2tuLlRMSc,7700
9
- kscale-0.0.1.dist-info/LICENSE,sha256=HCN2bImAzUOXldAZZI7JZ9PYq6OwMlDAP_PpX1HnuN0,1071
10
- kscale-0.0.1.dist-info/METADATA,sha256=xXZUlVlvE5Z0RGl5I-MWamdB2-Pwc3m8WeJfJEBNHkk,756
11
- kscale-0.0.1.dist-info/WHEEL,sha256=HiCZjzuy6Dw0hdX5R3LCFPDmFS4BWl8H-8W39XfmgX4,91
12
- kscale-0.0.1.dist-info/top_level.txt,sha256=C2ynjYwopg6YjgttnI2dJjasyq3EKNmYp-IfQg9Xms4,7
13
- kscale-0.0.1.dist-info/RECORD,,