kscale 0.0.1__py3-none-any.whl → 0.0.3__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.
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,,