hyperpocket 0.4.4__py3-none-any.whl → 0.5.0__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.
- hyperpocket/auth/kraken/README.md +8 -0
- hyperpocket/auth/kraken/context.py +14 -0
- hyperpocket/auth/kraken/keypair_context.py +17 -0
- hyperpocket/auth/kraken/keypair_handler.py +97 -0
- hyperpocket/auth/kraken/keypair_schema.py +10 -0
- hyperpocket/auth/provider.py +1 -0
- hyperpocket/auth/valyu/token_handler.py +1 -2
- hyperpocket/builtin.py +3 -3
- hyperpocket/cli/__main__.py +0 -2
- hyperpocket/config/logger.py +2 -3
- hyperpocket/futures/futurestore.py +7 -2
- hyperpocket/pocket_auth.py +10 -8
- hyperpocket/pocket_main.py +251 -100
- hyperpocket/server/auth/kraken.py +58 -0
- hyperpocket/server/server.py +70 -239
- hyperpocket/session/in_memory.py +20 -26
- hyperpocket/tool/__init__.py +1 -2
- hyperpocket/tool/dock/dock.py +6 -25
- hyperpocket/tool/function/tool.py +1 -1
- hyperpocket/tool/tool.py +6 -35
- hyperpocket/tool_like.py +2 -3
- hyperpocket/util/git_parser.py +63 -0
- hyperpocket/util/json_schema_to_model.py +2 -2
- hyperpocket/util/short_hashing_str.py +5 -0
- {hyperpocket-0.4.4.dist-info → hyperpocket-0.5.0.dist-info}/METADATA +5 -5
- {hyperpocket-0.4.4.dist-info → hyperpocket-0.5.0.dist-info}/RECORD +29 -26
- hyperpocket/pocket_core.py +0 -283
- hyperpocket/repository/__init__.py +0 -4
- hyperpocket/repository/repository.py +0 -8
- hyperpocket/repository/tool_reference.py +0 -28
- hyperpocket/util/generate_slug.py +0 -4
- /hyperpocket/{tool/tests → auth/kraken}/__init__.py +0 -0
- {hyperpocket-0.4.4.dist-info → hyperpocket-0.5.0.dist-info}/WHEEL +0 -0
- {hyperpocket-0.4.4.dist-info → hyperpocket-0.5.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,14 @@
|
|
1
|
+
from hyperpocket.auth.context import AuthContext
|
2
|
+
|
3
|
+
|
4
|
+
class KrakenAuthContext(AuthContext):
|
5
|
+
def to_dict(self) -> dict[str, str]:
|
6
|
+
return {
|
7
|
+
'KRAKEN_API_KEY': self.detail["KRAKEN_API_KEY"],
|
8
|
+
'KRAKEN_API_SECRET': self.detail["KRAKEN_API_SECRET"],
|
9
|
+
}
|
10
|
+
|
11
|
+
def to_profiled_dict(self, profile: str) -> dict[str, str]:
|
12
|
+
return {
|
13
|
+
f"{profile.upper()}_{self._ACCESS_TOKEN_KEY}": self.access_token,
|
14
|
+
}
|
@@ -0,0 +1,17 @@
|
|
1
|
+
from hyperpocket.auth.kraken.context import KrakenAuthContext
|
2
|
+
from hyperpocket.auth.kraken.keypair_schema import KrakenKeypairResponse
|
3
|
+
|
4
|
+
|
5
|
+
class KrakenKeypairAuthContext(KrakenAuthContext):
|
6
|
+
@classmethod
|
7
|
+
def from_kraken_keypair_response(cls, response: KrakenKeypairResponse):
|
8
|
+
description = f"Kraken Keypair Context logged in"
|
9
|
+
return cls(
|
10
|
+
access_token="",
|
11
|
+
detail={
|
12
|
+
"KRAKEN_API_KEY": response.kraken_api_key,
|
13
|
+
"KRAKEN_API_SECRET": response.kraken_api_secret,
|
14
|
+
},
|
15
|
+
description=description,
|
16
|
+
expires_at=None,
|
17
|
+
)
|
@@ -0,0 +1,97 @@
|
|
1
|
+
from typing import Optional
|
2
|
+
from urllib.parse import urljoin, urlencode
|
3
|
+
|
4
|
+
from hyperpocket.auth import AuthProvider
|
5
|
+
from hyperpocket.auth.ahrefs.token_schema import AhrefsTokenRequest
|
6
|
+
from hyperpocket.auth.context import AuthContext
|
7
|
+
from hyperpocket.auth.handler import AuthHandlerInterface
|
8
|
+
from hyperpocket.auth.kraken.keypair_context import KrakenKeypairAuthContext
|
9
|
+
from hyperpocket.auth.kraken.keypair_schema import KrakenKeypairRequest, KrakenKeypairResponse
|
10
|
+
from hyperpocket.config import config
|
11
|
+
from hyperpocket.futures import FutureStore
|
12
|
+
|
13
|
+
|
14
|
+
class KrakeyKeypairAuthHandler(AuthHandlerInterface):
|
15
|
+
name: str = "kraken-keypair"
|
16
|
+
description: str = (
|
17
|
+
"This handler is used to authenticate users using the Kraken keypair."
|
18
|
+
)
|
19
|
+
scoped: bool = False
|
20
|
+
|
21
|
+
_TOKEN_URL: str = urljoin(
|
22
|
+
config().public_base_url + "/",
|
23
|
+
f"{config().callback_url_rewrite_prefix}/auth/kraken/keypair",
|
24
|
+
)
|
25
|
+
|
26
|
+
@staticmethod
|
27
|
+
def provider() -> AuthProvider:
|
28
|
+
return AuthProvider.KRAKEN
|
29
|
+
|
30
|
+
@staticmethod
|
31
|
+
def provider_default() -> bool:
|
32
|
+
return True
|
33
|
+
|
34
|
+
@staticmethod
|
35
|
+
def recommended_scopes() -> set[str]:
|
36
|
+
return set()
|
37
|
+
|
38
|
+
def prepare(
|
39
|
+
self,
|
40
|
+
auth_req: AhrefsTokenRequest,
|
41
|
+
thread_id: str,
|
42
|
+
profile: str,
|
43
|
+
future_uid: str,
|
44
|
+
*args,
|
45
|
+
**kwargs,
|
46
|
+
) -> str:
|
47
|
+
redirect_uri = urljoin(
|
48
|
+
config().public_base_url + "/",
|
49
|
+
f"{config().callback_url_rewrite_prefix}/auth/kraken/keypair/callback",
|
50
|
+
)
|
51
|
+
url = self._make_auth_url(
|
52
|
+
auth_req=auth_req, redirect_uri=redirect_uri, state=future_uid
|
53
|
+
)
|
54
|
+
FutureStore.create_future(
|
55
|
+
future_uid,
|
56
|
+
data={
|
57
|
+
"redirect_uri": redirect_uri,
|
58
|
+
"thread_id": thread_id,
|
59
|
+
"profile": profile,
|
60
|
+
},
|
61
|
+
)
|
62
|
+
|
63
|
+
return f"User needs to authenticate using the following URL: {url}"
|
64
|
+
|
65
|
+
async def authenticate(
|
66
|
+
self, auth_req: KrakenKeypairRequest, future_uid: str, *args, **kwargs
|
67
|
+
) -> AuthContext:
|
68
|
+
future_data = FutureStore.get_future(future_uid)
|
69
|
+
kraken_api_keypair = await future_data.future
|
70
|
+
|
71
|
+
response = KrakenKeypairResponse(
|
72
|
+
kraken_api_key=kraken_api_keypair["kraken_api_key"],
|
73
|
+
kraken_api_secret=kraken_api_keypair["kraken_api_secret"],
|
74
|
+
)
|
75
|
+
context = KrakenKeypairAuthContext.from_kraken_keypair_response(response)
|
76
|
+
|
77
|
+
return context
|
78
|
+
|
79
|
+
async def refresh(
|
80
|
+
self, auth_req: KrakenKeypairRequest, context: AuthContext, *args, **kwargs
|
81
|
+
) -> AuthContext:
|
82
|
+
raise Exception("Kraken keypair doesn't support refresh")
|
83
|
+
|
84
|
+
def _make_auth_url(
|
85
|
+
self, auth_req: KrakenKeypairRequest, redirect_uri: str, state: str
|
86
|
+
):
|
87
|
+
params = {
|
88
|
+
"redirect_uri": redirect_uri,
|
89
|
+
"state": state,
|
90
|
+
}
|
91
|
+
auth_url = f"{self._TOKEN_URL}?{urlencode(params)}"
|
92
|
+
return auth_url
|
93
|
+
|
94
|
+
def make_request(
|
95
|
+
self, auth_scopes: Optional[list[str]] = None, **kwargs
|
96
|
+
) -> KrakenKeypairRequest:
|
97
|
+
return KrakenKeypairRequest()
|
hyperpocket/auth/provider.py
CHANGED
@@ -2,7 +2,6 @@ from typing import Optional
|
|
2
2
|
from urllib.parse import urljoin, urlencode
|
3
3
|
|
4
4
|
from hyperpocket.auth import AuthProvider
|
5
|
-
from hyperpocket.auth.ahrefs.token_schema import AhrefsTokenRequest
|
6
5
|
from hyperpocket.auth.context import AuthContext
|
7
6
|
from hyperpocket.auth.handler import AuthHandlerInterface
|
8
7
|
from hyperpocket.auth.valyu.token_context import ValyuTokenAuthContext
|
@@ -37,7 +36,7 @@ class ValyuTokenAuthHandler(AuthHandlerInterface):
|
|
37
36
|
|
38
37
|
def prepare(
|
39
38
|
self,
|
40
|
-
auth_req:
|
39
|
+
auth_req: ValyuTokenRequest,
|
41
40
|
thread_id: str,
|
42
41
|
profile: str,
|
43
42
|
future_uid: str,
|
hyperpocket/builtin.py
CHANGED
@@ -12,7 +12,7 @@ def get_builtin_tools(pocket_auth: PocketAuth) -> List[Tool]:
|
|
12
12
|
Builtin Tool can access to Pocket Core.
|
13
13
|
"""
|
14
14
|
|
15
|
-
def __get_current_thread_session_state(thread_id: str = "default") -> str:
|
15
|
+
async def __get_current_thread_session_state(thread_id: str = "default") -> str:
|
16
16
|
"""
|
17
17
|
This tool retrieves the current session state list for the specified thread.
|
18
18
|
|
@@ -31,7 +31,7 @@ def get_builtin_tools(pocket_auth: PocketAuth) -> List[Tool]:
|
|
31
31
|
|
32
32
|
This tool ensures transparency about the current session but must respect user-driven intent and should never be called automatically or without a specific user request.
|
33
33
|
"""
|
34
|
-
session_list = pocket_auth.list_session_state(thread_id)
|
34
|
+
session_list = await pocket_auth.list_session_state(thread_id)
|
35
35
|
return str(session_list)
|
36
36
|
|
37
37
|
def __delete_session(
|
@@ -58,7 +58,7 @@ def get_builtin_tools(pocket_auth: PocketAuth) -> List[Tool]:
|
|
58
58
|
return str(is_deleted)
|
59
59
|
|
60
60
|
builtin_tools = [
|
61
|
-
from_func(func=__get_current_thread_session_state),
|
61
|
+
from_func(func=__get_current_thread_session_state, afunc=__get_current_thread_session_state),
|
62
62
|
from_func(func=__delete_session),
|
63
63
|
]
|
64
64
|
|
hyperpocket/cli/__main__.py
CHANGED
@@ -2,7 +2,6 @@ import click
|
|
2
2
|
|
3
3
|
from hyperpocket.cli.eject import eject
|
4
4
|
from hyperpocket.cli.pull import pull
|
5
|
-
from hyperpocket.cli.sync import sync
|
6
5
|
from hyperpocket.cli.eject import eject
|
7
6
|
from hyperpocket.cli.auth_token import create_token_auth_template
|
8
7
|
from hyperpocket.cli.auth_oauth2 import create_oauth2_auth_template
|
@@ -28,7 +27,6 @@ devtool.add_command(build_tool)
|
|
28
27
|
devtool.add_command(export_tool)
|
29
28
|
|
30
29
|
cli.add_command(pull)
|
31
|
-
cli.add_command(sync)
|
32
30
|
cli.add_command(eject)
|
33
31
|
|
34
32
|
cli()
|
hyperpocket/config/logger.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
import logging
|
2
2
|
import os
|
3
|
+
import pathlib
|
3
4
|
from logging.handlers import RotatingFileHandler
|
4
5
|
from pathlib import Path
|
5
6
|
|
@@ -27,9 +28,7 @@ class ColorFormatter(logging.Formatter):
|
|
27
28
|
|
28
29
|
|
29
30
|
def get_logger():
|
30
|
-
|
31
|
-
package_path = Path(os.path.dirname(hyperpocket.__file__))
|
32
|
-
log_dir = package_path / ".log"
|
31
|
+
log_dir = pathlib.Path(os.getcwd()) / ".log"
|
33
32
|
os.makedirs(log_dir, exist_ok=True)
|
34
33
|
log_file = log_dir / "pocket.log"
|
35
34
|
if not log_file.exists():
|
@@ -25,7 +25,6 @@ class FutureStore(object):
|
|
25
25
|
f"the future already exists. the existing future is returned. uid: {uid}"
|
26
26
|
)
|
27
27
|
return future
|
28
|
-
|
29
28
|
loop = asyncio.get_running_loop()
|
30
29
|
future = loop.create_future()
|
31
30
|
future_data = FutureData(future=future, data=data)
|
@@ -42,7 +41,13 @@ class FutureStore(object):
|
|
42
41
|
if not future_data:
|
43
42
|
raise ValueError(f"Future not found for uid={uid}")
|
44
43
|
if not future_data.future.done():
|
45
|
-
|
44
|
+
# if the future loop is running, it should be executed in same event loop
|
45
|
+
loop = future_data.future.get_loop()
|
46
|
+
if loop.is_running():
|
47
|
+
loop.call_soon_threadsafe(future_data.future.set_result, value)
|
48
|
+
# if the future loop is not running, it can be executed from anywhere.
|
49
|
+
else:
|
50
|
+
future_data.future.set_result(value)
|
46
51
|
|
47
52
|
def delete_future(self, uid: str):
|
48
53
|
self.futures.pop(uid, None)
|
hyperpocket/pocket_auth.py
CHANGED
@@ -78,7 +78,7 @@ class PocketAuth(object):
|
|
78
78
|
handler = self.find_handler_instance(auth_handler_name, auth_provider)
|
79
79
|
return handler.make_request(auth_scopes, **kwargs)
|
80
80
|
|
81
|
-
def check(
|
81
|
+
async def check(
|
82
82
|
self,
|
83
83
|
auth_req: AuthenticateRequest,
|
84
84
|
auth_handler_name: Optional[str] = None,
|
@@ -112,12 +112,12 @@ class PocketAuth(object):
|
|
112
112
|
"""
|
113
113
|
handler = self.find_handler_instance(auth_handler_name, auth_provider)
|
114
114
|
session = self.session_storage.get(handler.provider(), thread_id, profile)
|
115
|
-
auth_state = self.get_session_state(session=session, auth_req=auth_req)
|
115
|
+
auth_state = await self.get_session_state(session=session, auth_req=auth_req)
|
116
116
|
|
117
117
|
return auth_state
|
118
118
|
|
119
119
|
@staticmethod
|
120
|
-
def get_session_state(
|
120
|
+
async def get_session_state(
|
121
121
|
session: Optional[BaseSessionValue], auth_req: Optional[AuthenticateRequest]
|
122
122
|
) -> AuthState:
|
123
123
|
if not session:
|
@@ -125,6 +125,8 @@ class PocketAuth(object):
|
|
125
125
|
|
126
126
|
if session.auth_resolve_uid:
|
127
127
|
future_data = FutureStore.get_future(session.auth_resolve_uid)
|
128
|
+
# it yields before checking future's state, because the future is being resolved on another thread's event loop.
|
129
|
+
await asyncio.sleep(0)
|
128
130
|
if future_data is not None and future_data.future.done():
|
129
131
|
return AuthState.RESOLVED
|
130
132
|
|
@@ -140,7 +142,7 @@ class PocketAuth(object):
|
|
140
142
|
|
141
143
|
return AuthState.SKIP_AUTH
|
142
144
|
|
143
|
-
def prepare(
|
145
|
+
async def prepare(
|
144
146
|
self,
|
145
147
|
auth_req: AuthenticateRequest,
|
146
148
|
auth_handler_name: Optional[str] = None,
|
@@ -166,7 +168,7 @@ class PocketAuth(object):
|
|
166
168
|
Returns:
|
167
169
|
Optional[str]: authentication URL
|
168
170
|
"""
|
169
|
-
auth_state = self.check(
|
171
|
+
auth_state = await self.check(
|
170
172
|
auth_req=auth_req,
|
171
173
|
auth_handler_name=auth_handler_name,
|
172
174
|
auth_provider=auth_provider,
|
@@ -263,7 +265,7 @@ class PocketAuth(object):
|
|
263
265
|
Returns:
|
264
266
|
AuthContext: authentication context
|
265
267
|
"""
|
266
|
-
auth_state = self.check(
|
268
|
+
auth_state = await self.check(
|
267
269
|
auth_req=auth_req,
|
268
270
|
auth_handler_name=auth_handler_name,
|
269
271
|
auth_provider=auth_provider,
|
@@ -353,7 +355,7 @@ class PocketAuth(object):
|
|
353
355
|
|
354
356
|
return session.auth_context
|
355
357
|
|
356
|
-
def list_session_state(
|
358
|
+
async def list_session_state(
|
357
359
|
self, thread_id: str, auth_provider: Optional[AuthProvider] = None
|
358
360
|
):
|
359
361
|
session_list = self.session_storage.get_by_thread_id(
|
@@ -361,7 +363,7 @@ class PocketAuth(object):
|
|
361
363
|
)
|
362
364
|
session_state_list = []
|
363
365
|
for session in session_list:
|
364
|
-
state = self.get_session_state(session=session, auth_req=None)
|
366
|
+
state = await self.get_session_state(session=session, auth_req=None)
|
365
367
|
|
366
368
|
session_state_list.append(
|
367
369
|
{
|