kscale 0.1.2__tar.gz → 0.1.3__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. {kscale-0.1.2/kscale.egg-info → kscale-0.1.3}/PKG-INFO +1 -1
  2. {kscale-0.1.2 → kscale-0.1.3}/kscale/__init__.py +1 -1
  3. {kscale-0.1.2 → kscale-0.1.3}/kscale/web/clients/base.py +94 -36
  4. {kscale-0.1.2 → kscale-0.1.3}/kscale/web/clients/robot_class.py +5 -1
  5. {kscale-0.1.2 → kscale-0.1.3/kscale.egg-info}/PKG-INFO +1 -1
  6. {kscale-0.1.2 → kscale-0.1.3}/LICENSE +0 -0
  7. {kscale-0.1.2 → kscale-0.1.3}/MANIFEST.in +0 -0
  8. {kscale-0.1.2 → kscale-0.1.3}/README.md +0 -0
  9. {kscale-0.1.2 → kscale-0.1.3}/kscale/api.py +0 -0
  10. {kscale-0.1.2 → kscale-0.1.3}/kscale/artifacts/__init__.py +0 -0
  11. {kscale-0.1.2 → kscale-0.1.3}/kscale/artifacts/plane.obj +0 -0
  12. {kscale-0.1.2 → kscale-0.1.3}/kscale/artifacts/plane.urdf +0 -0
  13. {kscale-0.1.2 → kscale-0.1.3}/kscale/cli.py +0 -0
  14. {kscale-0.1.2 → kscale-0.1.3}/kscale/conf.py +0 -0
  15. {kscale-0.1.2 → kscale-0.1.3}/kscale/py.typed +0 -0
  16. {kscale-0.1.2 → kscale-0.1.3}/kscale/requirements-dev.txt +0 -0
  17. {kscale-0.1.2 → kscale-0.1.3}/kscale/requirements.txt +0 -0
  18. {kscale-0.1.2 → kscale-0.1.3}/kscale/utils/__init__.py +0 -0
  19. {kscale-0.1.2 → kscale-0.1.3}/kscale/utils/api_base.py +0 -0
  20. {kscale-0.1.2 → kscale-0.1.3}/kscale/utils/checksum.py +0 -0
  21. {kscale-0.1.2 → kscale-0.1.3}/kscale/utils/cli.py +0 -0
  22. {kscale-0.1.2 → kscale-0.1.3}/kscale/web/__init__.py +0 -0
  23. {kscale-0.1.2 → kscale-0.1.3}/kscale/web/api.py +0 -0
  24. {kscale-0.1.2 → kscale-0.1.3}/kscale/web/cli/__init__.py +0 -0
  25. {kscale-0.1.2 → kscale-0.1.3}/kscale/web/cli/robot.py +0 -0
  26. {kscale-0.1.2 → kscale-0.1.3}/kscale/web/cli/robot_class.py +0 -0
  27. {kscale-0.1.2 → kscale-0.1.3}/kscale/web/cli/token.py +0 -0
  28. {kscale-0.1.2 → kscale-0.1.3}/kscale/web/cli/user.py +0 -0
  29. {kscale-0.1.2 → kscale-0.1.3}/kscale/web/clients/__init__.py +0 -0
  30. {kscale-0.1.2 → kscale-0.1.3}/kscale/web/clients/client.py +0 -0
  31. {kscale-0.1.2 → kscale-0.1.3}/kscale/web/clients/robot.py +0 -0
  32. {kscale-0.1.2 → kscale-0.1.3}/kscale/web/clients/user.py +0 -0
  33. {kscale-0.1.2 → kscale-0.1.3}/kscale/web/gen/__init__.py +0 -0
  34. {kscale-0.1.2 → kscale-0.1.3}/kscale/web/gen/api.py +0 -0
  35. {kscale-0.1.2 → kscale-0.1.3}/kscale/web/utils.py +0 -0
  36. {kscale-0.1.2 → kscale-0.1.3}/kscale.egg-info/SOURCES.txt +0 -0
  37. {kscale-0.1.2 → kscale-0.1.3}/kscale.egg-info/dependency_links.txt +0 -0
  38. {kscale-0.1.2 → kscale-0.1.3}/kscale.egg-info/entry_points.txt +0 -0
  39. {kscale-0.1.2 → kscale-0.1.3}/kscale.egg-info/not-zip-safe +0 -0
  40. {kscale-0.1.2 → kscale-0.1.3}/kscale.egg-info/requires.txt +0 -0
  41. {kscale-0.1.2 → kscale-0.1.3}/kscale.egg-info/top_level.txt +0 -0
  42. {kscale-0.1.2 → kscale-0.1.3}/pyproject.toml +0 -0
  43. {kscale-0.1.2 → kscale-0.1.3}/setup.cfg +0 -0
  44. {kscale-0.1.2 → kscale-0.1.3}/setup.py +0 -0
  45. {kscale-0.1.2 → kscale-0.1.3}/tests/test_dummy.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: kscale
3
- Version: 0.1.2
3
+ Version: 0.1.3
4
4
  Summary: The kscale project
5
5
  Home-page: https://github.com/kscalelabs/kscale
6
6
  Author: Benjamin Bolte
@@ -1,6 +1,6 @@
1
1
  """Defines the common interface for the K-Scale Python API."""
2
2
 
3
- __version__ = "0.1.2"
3
+ __version__ = "0.1.3"
4
4
 
5
5
  from pathlib import Path
6
6
 
@@ -29,14 +29,22 @@ OAUTH_PORT = 16821
29
29
 
30
30
  class OAuthCallback:
31
31
  def __init__(self) -> None:
32
+ self.token_type: str | None = None
32
33
  self.access_token: str | None = None
34
+ self.id_token: str | None = None
35
+ self.state: str | None = None
36
+ self.expires_in: str | None = None
33
37
  self.app = web.Application()
34
38
  self.app.router.add_get("/token", self.handle_token)
35
39
  self.app.router.add_get("/callback", self.handle_callback)
36
40
 
37
41
  async def handle_token(self, request: web.Request) -> web.Response:
38
42
  """Handle the token extraction."""
43
+ self.token_type = request.query.get("token_type")
39
44
  self.access_token = request.query.get("access_token")
45
+ self.id_token = request.query.get("id_token")
46
+ self.state = request.query.get("state")
47
+ self.expires_in = request.query.get("expires_in")
40
48
  return web.Response(text="OK")
41
49
 
42
50
  async def handle_callback(self, request: web.Request) -> web.Response:
@@ -45,62 +53,92 @@ class OAuthCallback:
45
53
  text="""
46
54
  <!DOCTYPE html>
47
55
  <html lang="en">
48
-
49
56
  <head>
50
57
  <meta charset="UTF-8">
51
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
52
58
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
53
59
  <title>Authentication successful</title>
54
60
  <style>
55
61
  body {
62
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
56
63
  display: flex;
57
64
  justify-content: center;
58
65
  align-items: center;
59
66
  min-height: 100vh;
60
67
  margin: 0;
61
- text-align: center;
68
+ background: #f5f5f5;
69
+ color: #333;
70
+ }
71
+ .container {
72
+ background: white;
73
+ padding: 2rem;
74
+ border-radius: 8px;
75
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
76
+ max-width: 600px;
77
+ width: 90%;
78
+ }
79
+ h1 {
80
+ color: #2c3e50;
81
+ margin-bottom: 1rem;
62
82
  }
63
- #content {
64
- padding: 20px;
83
+ .token-info {
84
+ background: #f8f9fa;
85
+ border: 1px solid #dee2e6;
86
+ border-radius: 4px;
87
+ padding: 1rem;
88
+ margin: 1rem 0;
89
+ word-break: break-all;
65
90
  }
66
- #closeNotification {
67
- display: none;
68
- padding: 10px 20px;
69
- margin-top: 20px;
70
- cursor: pointer;
71
- margin-left: auto;
72
- margin-right: auto;
91
+ .token-label {
92
+ font-weight: bold;
93
+ color: #6c757d;
94
+ margin-bottom: 0.5rem;
95
+ }
96
+ .success-icon {
97
+ color: #28a745;
98
+ font-size: 48px;
99
+ margin-bottom: 1rem;
73
100
  }
74
101
  </style>
75
102
  </head>
76
-
77
103
  <body>
78
- <div id="content">
104
+ <div class="container">
105
+ <div class="success-icon">✓</div>
79
106
  <h1>Authentication successful!</h1>
80
- <p>This window will close in <span id="countdown">3</span> seconds.</p>
81
- <p id="closeNotification" onclick="window.close()">Please close this window manually.</p>
107
+ <p>Your authentication tokens are shown below. You can now close this window.</p>
108
+
109
+ <div class="token-info">
110
+ <div class="token-label">Access Token:</div>
111
+ <div id="accessTokenDisplay"></div>
112
+ </div>
113
+
114
+ <div class="token-info">
115
+ <div class="token-label">ID Token:</div>
116
+ <div id="idTokenDisplay"></div>
117
+ </div>
82
118
  </div>
119
+
83
120
  <script>
84
121
  const params = new URLSearchParams(window.location.hash.substring(1));
85
- const token = params.get('access_token');
86
- if (token) {
87
- fetch('/token?access_token=' + token);
122
+ const tokenType = params.get('token_type');
123
+ const accessToken = params.get('access_token');
124
+ const idToken = params.get('id_token');
125
+ const state = params.get('state');
126
+ const expiresIn = params.get('expires_in');
127
+
128
+ // Display tokens
129
+ document.getElementById('accessTokenDisplay').textContent = accessToken || 'Not provided';
130
+ document.getElementById('idTokenDisplay').textContent = idToken || 'Not provided';
131
+
132
+ if (accessToken) {
133
+ const tokenUrl = new URL(window.location.href);
134
+ tokenUrl.pathname = '/token';
135
+ tokenUrl.searchParams.set('access_token', accessToken);
136
+ tokenUrl.searchParams.set('token_type', tokenType);
137
+ tokenUrl.searchParams.set('id_token', idToken);
138
+ tokenUrl.searchParams.set('state', state);
139
+ tokenUrl.searchParams.set('expires_in', expiresIn);
140
+ fetch(tokenUrl.toString());
88
141
  }
89
-
90
- let timeLeft = 3;
91
- const countdownElement = document.getElementById('countdown');
92
- const closeNotification = document.getElementById('closeNotification');
93
- const timer = setInterval(() => {
94
- timeLeft--;
95
- countdownElement.textContent = timeLeft;
96
- if (timeLeft <= 0) {
97
- clearInterval(timer);
98
- window.close();
99
- setTimeout(() => {
100
- closeNotification.style.display = 'block';
101
- }, 500);
102
- }
103
- }, 1000);
104
142
  </script>
105
143
  </body>
106
144
  </html>
@@ -167,8 +205,23 @@ class BaseClient:
167
205
  oicd_info = await self._get_oicd_info()
168
206
  metadata = await self._get_oicd_metadata()
169
207
  auth_endpoint = metadata["authorization_endpoint"]
170
- state = secrets.token_urlsafe(32)
171
- nonce = secrets.token_urlsafe(32)
208
+
209
+ # Use the cached state and nonce if available, otherwise generate.
210
+ state_file = get_cache_dir() / "oauth_state.json"
211
+ state: str | None = None
212
+ nonce: str | None = None
213
+ if state_file.exists():
214
+ with open(state_file, "r") as f:
215
+ state_data = json.load(f)
216
+ state = state_data.get("state")
217
+ nonce = state_data.get("nonce")
218
+ if state is None:
219
+ state = secrets.token_urlsafe(32)
220
+ if nonce is None:
221
+ nonce = secrets.token_urlsafe(32)
222
+
223
+ # Change /oauth2/authorize to /login to use the login endpoint.
224
+ auth_endpoint = auth_endpoint.replace("/oauth2/authorize", "/login")
172
225
 
173
226
  auth_url = str(
174
227
  URL(auth_endpoint).with_query(
@@ -208,6 +261,11 @@ class BaseClient:
208
261
  raise TimeoutError("Authentication timed out after 30 seconds")
209
262
  await asyncio.sleep(0.1)
210
263
 
264
+ # Save the state and nonce to the cache.
265
+ state = callback_handler.state
266
+ state_file.parent.mkdir(parents=True, exist_ok=True)
267
+ state_file.write_text(json.dumps({"state": state, "nonce": nonce}))
268
+
211
269
  return callback_handler.access_token
212
270
  finally:
213
271
  await runner.cleanup()
@@ -8,7 +8,11 @@ from pathlib import Path
8
8
  import httpx
9
9
 
10
10
  from kscale.web.clients.base import BaseClient
11
- from kscale.web.gen.api import RobotClass, RobotDownloadURDFResponse, RobotUploadURDFResponse
11
+ from kscale.web.gen.api import (
12
+ RobotClass,
13
+ RobotDownloadURDFResponse,
14
+ RobotUploadURDFResponse,
15
+ )
12
16
  from kscale.web.utils import get_cache_dir, should_refresh_file
13
17
 
14
18
  logger = logging.getLogger(__name__)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: kscale
3
- Version: 0.1.2
3
+ Version: 0.1.3
4
4
  Summary: The kscale project
5
5
  Home-page: https://github.com/kscalelabs/kscale
6
6
  Author: Benjamin Bolte
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes