hyperpocket 0.1.9__py3-none-any.whl → 0.2.0__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- hyperpocket/__init__.py +4 -4
- hyperpocket/auth/__init__.py +12 -7
- hyperpocket/auth/calendly/oauth2_handler.py +24 -17
- hyperpocket/auth/calendly/oauth2_schema.py +3 -1
- hyperpocket/auth/context.py +2 -1
- hyperpocket/auth/github/oauth2_handler.py +13 -8
- hyperpocket/auth/github/token_handler.py +27 -21
- hyperpocket/auth/google/context.py +1 -3
- hyperpocket/auth/google/oauth2_context.py +1 -1
- hyperpocket/auth/google/oauth2_handler.py +22 -17
- hyperpocket/auth/gumloop/token_context.py +1 -4
- hyperpocket/auth/gumloop/token_handler.py +48 -20
- hyperpocket/auth/gumloop/token_schema.py +2 -1
- hyperpocket/auth/handler.py +21 -6
- hyperpocket/auth/linear/token_context.py +2 -5
- hyperpocket/auth/linear/token_handler.py +45 -21
- hyperpocket/auth/notion/context.py +2 -2
- hyperpocket/auth/notion/token_context.py +2 -4
- hyperpocket/auth/notion/token_handler.py +45 -21
- hyperpocket/auth/notion/token_schema.py +0 -1
- hyperpocket/auth/reddit/oauth2_handler.py +9 -10
- hyperpocket/auth/reddit/oauth2_schema.py +0 -2
- hyperpocket/auth/schema.py +4 -1
- hyperpocket/auth/slack/oauth2_context.py +3 -1
- hyperpocket/auth/slack/oauth2_handler.py +55 -35
- hyperpocket/auth/slack/token_context.py +2 -4
- hyperpocket/auth/slack/token_handler.py +42 -19
- hyperpocket/builtin.py +4 -2
- hyperpocket/cli/__main__.py +4 -2
- hyperpocket/cli/auth.py +59 -28
- hyperpocket/cli/codegen/auth/auth_context_template.py +3 -2
- hyperpocket/cli/codegen/auth/auth_token_context_template.py +3 -2
- hyperpocket/cli/codegen/auth/auth_token_handler_template.py +6 -5
- hyperpocket/cli/codegen/auth/auth_token_schema_template.py +3 -2
- hyperpocket/cli/codegen/auth/server_auth_template.py +3 -2
- hyperpocket/cli/pull.py +5 -5
- hyperpocket/config/__init__.py +3 -8
- hyperpocket/config/auth.py +3 -1
- hyperpocket/config/logger.py +20 -15
- hyperpocket/config/session.py +4 -2
- hyperpocket/config/settings.py +19 -2
- hyperpocket/futures/__init__.py +1 -1
- hyperpocket/futures/futurestore.py +3 -2
- hyperpocket/pocket_auth.py +171 -84
- hyperpocket/pocket_core.py +51 -33
- hyperpocket/pocket_main.py +122 -93
- hyperpocket/prompts.py +2 -2
- hyperpocket/repository/__init__.py +1 -1
- hyperpocket/repository/lock.py +47 -33
- hyperpocket/repository/lockfile.py +2 -2
- hyperpocket/repository/repository.py +1 -1
- hyperpocket/server/__init__.py +1 -1
- hyperpocket/server/auth/github.py +2 -1
- hyperpocket/server/auth/linear.py +1 -3
- hyperpocket/server/auth/notion.py +2 -5
- hyperpocket/server/auth/slack.py +1 -3
- hyperpocket/server/auth/token.py +17 -11
- hyperpocket/server/proxy.py +29 -13
- hyperpocket/server/server.py +75 -31
- hyperpocket/server/tool/dto/script.py +15 -10
- hyperpocket/server/tool/wasm.py +14 -11
- hyperpocket/session/__init__.py +6 -2
- hyperpocket/session/in_memory.py +44 -24
- hyperpocket/session/interface.py +42 -24
- hyperpocket/session/redis.py +48 -31
- hyperpocket/tool/__init__.py +10 -10
- hyperpocket/tool/function/__init__.py +1 -5
- hyperpocket/tool/function/annotation.py +11 -9
- hyperpocket/tool/function/tool.py +37 -27
- hyperpocket/tool/tool.py +59 -36
- hyperpocket/tool/wasm/__init__.py +1 -1
- hyperpocket/tool/wasm/browser.py +15 -10
- hyperpocket/tool/wasm/invoker.py +16 -16
- hyperpocket/tool/wasm/script.py +27 -14
- hyperpocket/tool/wasm/templates/__init__.py +22 -15
- hyperpocket/tool/wasm/templates/node.py +2 -2
- hyperpocket/tool/wasm/templates/python.py +2 -2
- hyperpocket/tool/wasm/tool.py +27 -14
- hyperpocket/tool_like.py +3 -3
- hyperpocket/util/__init__.py +1 -1
- hyperpocket/util/extract_func_param_desc_from_docstring.py +33 -14
- hyperpocket/util/find_all_leaf_class_in_package.py +4 -3
- hyperpocket/util/find_all_subclass_in_package.py +4 -2
- hyperpocket/util/flatten_json_schema.py +10 -6
- hyperpocket/util/function_to_model.py +33 -12
- hyperpocket/util/get_objects_from_subpackage.py +1 -1
- hyperpocket/util/json_schema_to_model.py +14 -5
- {hyperpocket-0.1.9.dist-info → hyperpocket-0.2.0.dist-info}/METADATA +29 -24
- hyperpocket-0.2.0.dist-info/RECORD +137 -0
- hyperpocket-0.1.9.dist-info/RECORD +0 -137
- {hyperpocket-0.1.9.dist-info → hyperpocket-0.2.0.dist-info}/WHEEL +0 -0
- {hyperpocket-0.1.9.dist-info → hyperpocket-0.2.0.dist-info}/entry_points.txt +0 -0
hyperpocket/pocket_main.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
import asyncio
|
2
|
-
from
|
2
|
+
from concurrent.futures import ThreadPoolExecutor
|
3
|
+
from typing import Any, List, Optional, Union
|
3
4
|
|
4
5
|
from hyperpocket.config import pocket_logger
|
5
6
|
from hyperpocket.pocket_auth import PocketAuth
|
@@ -12,30 +13,38 @@ class Pocket(object):
|
|
12
13
|
server: PocketServer
|
13
14
|
core: PocketCore
|
14
15
|
|
15
|
-
def __init__(
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
16
|
+
def __init__(
|
17
|
+
self,
|
18
|
+
tools: list[ToolLike],
|
19
|
+
auth: PocketAuth = None,
|
20
|
+
lockfile_path: Optional[str] = None,
|
21
|
+
force_update: bool = False,
|
22
|
+
use_profile: bool = False,
|
23
|
+
):
|
21
24
|
self.use_profile = use_profile
|
22
|
-
|
23
25
|
self.core = PocketCore(
|
24
26
|
tools=tools,
|
25
27
|
auth=auth,
|
26
28
|
lockfile_path=lockfile_path,
|
27
|
-
force_update=force_update
|
29
|
+
force_update=force_update,
|
28
30
|
)
|
29
|
-
|
30
31
|
self.server = PocketServer()
|
31
32
|
self.server.run(self.core)
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
33
|
+
try:
|
34
|
+
self.server.wait_initialized()
|
35
|
+
except Exception as e:
|
36
|
+
pocket_logger.error(f"Failed to initialize pocket server. error : {e}")
|
37
|
+
self._teardown_server()
|
38
|
+
|
39
|
+
def invoke(
|
40
|
+
self,
|
41
|
+
tool_name: str,
|
42
|
+
body: Any,
|
43
|
+
thread_id: str = "default",
|
44
|
+
profile: str = "default",
|
45
|
+
*args,
|
46
|
+
**kwargs,
|
47
|
+
) -> str:
|
39
48
|
"""
|
40
49
|
Invoke Tool synchronously
|
41
50
|
|
@@ -48,15 +57,19 @@ class Pocket(object):
|
|
48
57
|
Returns:
|
49
58
|
str: tool result
|
50
59
|
"""
|
51
|
-
|
52
|
-
|
60
|
+
return asyncio.run(
|
61
|
+
self.ainvoke(tool_name, body, thread_id, profile, *args, **kwargs)
|
62
|
+
)
|
53
63
|
|
54
|
-
async def ainvoke(
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
64
|
+
async def ainvoke(
|
65
|
+
self,
|
66
|
+
tool_name: str,
|
67
|
+
body: Any,
|
68
|
+
thread_id: str = "default",
|
69
|
+
profile: str = "default",
|
70
|
+
*args,
|
71
|
+
**kwargs,
|
72
|
+
) -> str:
|
60
73
|
"""
|
61
74
|
Invoke Tool asynchronously
|
62
75
|
|
@@ -79,12 +92,15 @@ class Pocket(object):
|
|
79
92
|
)
|
80
93
|
return result
|
81
94
|
|
82
|
-
def invoke_with_state(
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
95
|
+
def invoke_with_state(
|
96
|
+
self,
|
97
|
+
tool_name: str,
|
98
|
+
body: Any,
|
99
|
+
thread_id: str = "default",
|
100
|
+
profile: str = "default",
|
101
|
+
*args,
|
102
|
+
**kwargs,
|
103
|
+
) -> tuple[str, bool]:
|
88
104
|
"""
|
89
105
|
Invoke Tool with state synchronously
|
90
106
|
State indicates whether this tool is paused or not.
|
@@ -101,27 +117,33 @@ class Pocket(object):
|
|
101
117
|
"""
|
102
118
|
try:
|
103
119
|
loop = asyncio.new_event_loop()
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
pocket_logger.warning("Can't execute sync def in event loop. use nest-asyncio")
|
120
|
+
except RuntimeError:
|
121
|
+
pocket_logger.warning(
|
122
|
+
"Can't execute sync def in event loop. use nest-asyncio"
|
123
|
+
)
|
109
124
|
|
110
125
|
import nest_asyncio
|
126
|
+
|
111
127
|
loop = asyncio.new_event_loop()
|
112
128
|
nest_asyncio.apply(loop=loop)
|
113
129
|
|
114
|
-
|
115
|
-
|
130
|
+
result = loop.run_until_complete(
|
131
|
+
self.ainvoke_with_state(
|
132
|
+
tool_name, body, thread_id, profile, *args, **kwargs
|
133
|
+
)
|
134
|
+
)
|
116
135
|
|
117
136
|
return result
|
118
137
|
|
119
|
-
async def ainvoke_with_state(
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
138
|
+
async def ainvoke_with_state(
|
139
|
+
self,
|
140
|
+
tool_name: str,
|
141
|
+
body: Any,
|
142
|
+
thread_id: str = "default",
|
143
|
+
profile: str = "default",
|
144
|
+
*args,
|
145
|
+
**kwargs,
|
146
|
+
) -> tuple[str, bool]:
|
125
147
|
"""
|
126
148
|
Invoke Tool with state synchronously
|
127
149
|
State indicates whether this tool is paused or not.
|
@@ -140,10 +162,10 @@ class Pocket(object):
|
|
140
162
|
PocketServerOperations.CALL,
|
141
163
|
args,
|
142
164
|
{
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
165
|
+
"tool_name": tool_name,
|
166
|
+
"body": body,
|
167
|
+
"thread_id": thread_id,
|
168
|
+
"profile": profile,
|
147
169
|
**kwargs,
|
148
170
|
},
|
149
171
|
)
|
@@ -152,10 +174,11 @@ class Pocket(object):
|
|
152
174
|
|
153
175
|
return result, paused
|
154
176
|
|
155
|
-
async def initialize_tool_auth(
|
156
|
-
|
157
|
-
|
158
|
-
|
177
|
+
async def initialize_tool_auth(
|
178
|
+
self,
|
179
|
+
thread_id: str = "default",
|
180
|
+
profile: str = "default",
|
181
|
+
) -> dict[str, str]:
|
159
182
|
"""
|
160
183
|
Initialize authentication for all tools.
|
161
184
|
|
@@ -177,19 +200,17 @@ class Pocket(object):
|
|
177
200
|
for provider, tools in tool_by_provider.items():
|
178
201
|
tool_name_list = [tool.name for tool in tools]
|
179
202
|
prepare = await self.prepare_in_subprocess(
|
180
|
-
tool_name=tool_name_list,
|
181
|
-
|
182
|
-
profile=profile)
|
203
|
+
tool_name=tool_name_list, thread_id=thread_id, profile=profile
|
204
|
+
)
|
183
205
|
|
184
206
|
if prepare is not None:
|
185
207
|
prepare_list[provider] = prepare
|
186
208
|
|
187
209
|
return prepare_list
|
188
210
|
|
189
|
-
async def wait_tool_auth(
|
190
|
-
|
191
|
-
|
192
|
-
) -> bool:
|
211
|
+
async def wait_tool_auth(
|
212
|
+
self, thread_id: str = "default", profile: str = "default"
|
213
|
+
) -> bool:
|
193
214
|
"""
|
194
215
|
Wait until all tool authentications are completed.
|
195
216
|
|
@@ -214,10 +235,9 @@ class Pocket(object):
|
|
214
235
|
|
215
236
|
waiting_futures.append(
|
216
237
|
self.authenticate_in_subprocess(
|
217
|
-
tool_name=tools[0].name,
|
218
|
-
|
219
|
-
|
220
|
-
))
|
238
|
+
tool_name=tools[0].name, thread_id=thread_id, profile=profile
|
239
|
+
)
|
240
|
+
)
|
221
241
|
|
222
242
|
await asyncio.gather(*waiting_futures)
|
223
243
|
|
@@ -227,56 +247,65 @@ class Pocket(object):
|
|
227
247
|
pocket_logger.error("authentication time out.")
|
228
248
|
raise e
|
229
249
|
|
230
|
-
async def prepare_in_subprocess(
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
250
|
+
async def prepare_in_subprocess(
|
251
|
+
self,
|
252
|
+
tool_name: Union[str, List[str]],
|
253
|
+
thread_id: str = "default",
|
254
|
+
profile: str = "default",
|
255
|
+
*args,
|
256
|
+
**kwargs,
|
257
|
+
):
|
235
258
|
prepare = await self.server.call_in_subprocess(
|
236
259
|
PocketServerOperations.PREPARE_AUTH,
|
237
260
|
args,
|
238
261
|
{
|
239
|
-
|
240
|
-
|
241
|
-
|
262
|
+
"tool_name": tool_name,
|
263
|
+
"thread_id": thread_id,
|
264
|
+
"profile": profile,
|
242
265
|
**kwargs,
|
243
266
|
},
|
244
267
|
)
|
245
268
|
|
246
269
|
return prepare
|
247
270
|
|
248
|
-
async def authenticate_in_subprocess(
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
271
|
+
async def authenticate_in_subprocess(
|
272
|
+
self,
|
273
|
+
tool_name: str,
|
274
|
+
thread_id: str = "default",
|
275
|
+
profile: str = "default",
|
276
|
+
*args,
|
277
|
+
**kwargs,
|
278
|
+
):
|
253
279
|
credentials = await self.server.call_in_subprocess(
|
254
280
|
PocketServerOperations.AUTHENTICATE,
|
255
281
|
args,
|
256
282
|
{
|
257
|
-
|
258
|
-
|
259
|
-
|
283
|
+
"tool_name": tool_name,
|
284
|
+
"thread_id": thread_id,
|
285
|
+
"profile": profile,
|
260
286
|
**kwargs,
|
261
287
|
},
|
262
288
|
)
|
263
289
|
|
264
290
|
return credentials
|
265
291
|
|
266
|
-
async def tool_call_in_subprocess(
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
292
|
+
async def tool_call_in_subprocess(
|
293
|
+
self,
|
294
|
+
tool_name: str,
|
295
|
+
body: Any,
|
296
|
+
thread_id: str = "default",
|
297
|
+
profile: str = "default",
|
298
|
+
*args,
|
299
|
+
**kwargs,
|
300
|
+
):
|
272
301
|
result = await self.server.call_in_subprocess(
|
273
302
|
PocketServerOperations.TOOL_CALL,
|
274
303
|
args,
|
275
304
|
{
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
305
|
+
"tool_name": tool_name,
|
306
|
+
"body": body,
|
307
|
+
"thread_id": thread_id,
|
308
|
+
"profile": profile,
|
280
309
|
**kwargs,
|
281
310
|
},
|
282
311
|
)
|
@@ -290,17 +319,17 @@ class Pocket(object):
|
|
290
319
|
return self
|
291
320
|
|
292
321
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
293
|
-
if self.__dict__.get(
|
322
|
+
if self.__dict__.get("server"):
|
294
323
|
self.server.teardown()
|
295
324
|
|
296
325
|
def __del__(self):
|
297
|
-
if self.__dict__.get(
|
326
|
+
if self.__dict__.get("server"):
|
298
327
|
self.server.teardown()
|
299
328
|
|
300
329
|
def __getstate__(self):
|
301
330
|
state = self.__dict__.copy()
|
302
|
-
if
|
303
|
-
del state[
|
331
|
+
if "server" in state:
|
332
|
+
del state["server"]
|
304
333
|
return state
|
305
334
|
|
306
335
|
def __setstate__(self, state):
|
hyperpocket/prompts.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
def pocket_extended_tool_description(description: str):
|
2
|
-
return f
|
2
|
+
return f"""
|
3
3
|
This tool functions as described in the <tool-description> XML tag:
|
4
4
|
<tool-description>
|
5
5
|
{description}
|
@@ -10,4 +10,4 @@ Arguments required:
|
|
10
10
|
- 'profile': The profile of the user invoking the tool. (infer from messages; omit if unknown).
|
11
11
|
Users can request tools to be invoked in specific personas.
|
12
12
|
- 'body': Tool argument passed as a JSON 'body'.
|
13
|
-
|
13
|
+
"""
|
@@ -1,5 +1,5 @@
|
|
1
1
|
from hyperpocket.repository.lock import Lock
|
2
2
|
from hyperpocket.repository.lockfile import Lockfile
|
3
|
-
from hyperpocket.repository.repository import pull, sync
|
3
|
+
from hyperpocket.repository.repository import eject, pull, sync
|
4
4
|
|
5
5
|
__all__ = ["Lock", "Lockfile", "pull", "sync", "eject"]
|
hyperpocket/repository/lock.py
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
import abc
|
2
2
|
import pathlib
|
3
3
|
import shutil
|
4
|
-
from typing import Optional, Tuple
|
4
|
+
from typing import Optional, Tuple, ClassVar
|
5
5
|
|
6
6
|
import git
|
7
7
|
from pydantic import BaseModel, Field
|
8
|
+
from pydantic.fields import ModelPrivateAttr
|
8
9
|
|
9
|
-
from hyperpocket.config import
|
10
|
+
from hyperpocket.config import pocket_logger, settings
|
10
11
|
|
11
12
|
|
12
13
|
class Lock(BaseModel, abc.ABC):
|
@@ -34,17 +35,19 @@ class Lock(BaseModel, abc.ABC):
|
|
34
35
|
|
35
36
|
|
36
37
|
class LocalLock(Lock):
|
37
|
-
tool_source: str = Field(default=
|
38
|
+
tool_source: str = Field(default="local")
|
38
39
|
tool_path: str
|
39
40
|
|
40
41
|
def __init__(self, tool_path: str):
|
41
|
-
super().__init__(
|
42
|
+
super().__init__(
|
43
|
+
tool_source="local", tool_path=str(pathlib.Path(tool_path).resolve())
|
44
|
+
)
|
42
45
|
|
43
46
|
def __str__(self):
|
44
47
|
return f"local\t{self.tool_path}"
|
45
48
|
|
46
49
|
def key(self):
|
47
|
-
return self.tool_source, self.tool_path.rstrip(
|
50
|
+
return self.tool_source, self.tool_path.rstrip("/")
|
48
51
|
|
49
52
|
def sync(self, **kwargs):
|
50
53
|
pocket_logger.info(f"Syncing path: {self.tool_path} ...")
|
@@ -55,11 +58,12 @@ class LocalLock(Lock):
|
|
55
58
|
|
56
59
|
def toolpkg_path(self) -> pathlib.Path:
|
57
60
|
pocket_pkgs = settings.toolpkg_path
|
58
|
-
return pocket_pkgs /
|
61
|
+
return pocket_pkgs / "local" / self.tool_path[1:]
|
59
62
|
|
60
63
|
|
61
64
|
class GitLock(Lock):
|
62
|
-
|
65
|
+
_remote_cache: ClassVar[dict[str, dict[str, str]]]
|
66
|
+
tool_source: str = "git"
|
63
67
|
repository_url: str
|
64
68
|
git_ref: str
|
65
69
|
ref_sha: Optional[str] = None
|
@@ -68,17 +72,17 @@ class GitLock(Lock):
|
|
68
72
|
return f"git\t{self.repository_url}\t{self.git_ref}\t{self.ref_sha}"
|
69
73
|
|
70
74
|
def key(self):
|
71
|
-
return self.tool_source, self.repository_url.rstrip(
|
75
|
+
return self.tool_source, self.repository_url.rstrip("/"), self.git_ref
|
72
76
|
|
73
77
|
def toolpkg_path(self) -> pathlib.Path:
|
74
78
|
if not self.ref_sha:
|
75
79
|
raise ValueError("ref_sha is not set")
|
76
80
|
cleansed_url = self.repository_url
|
77
|
-
if self.repository_url.startswith(
|
81
|
+
if self.repository_url.startswith("http://"):
|
78
82
|
cleansed_url = self.repository_url[7:]
|
79
|
-
elif self.repository_url.startswith(
|
83
|
+
elif self.repository_url.startswith("https://"):
|
80
84
|
cleansed_url = self.repository_url[8:]
|
81
|
-
elif self.repository_url.startswith(
|
85
|
+
elif self.repository_url.startswith("git@"):
|
82
86
|
cleansed_url = self.repository_url[4:]
|
83
87
|
return settings.toolpkg_path / cleansed_url / self.ref_sha
|
84
88
|
|
@@ -91,12 +95,16 @@ class GitLock(Lock):
|
|
91
95
|
to align the local repository with the remote version.
|
92
96
|
"""
|
93
97
|
try:
|
94
|
-
pocket_logger.info(
|
98
|
+
pocket_logger.info(
|
99
|
+
f"Syncing git: {self.repository_url} @ ref: {self.git_ref} ..."
|
100
|
+
)
|
95
101
|
|
96
102
|
# get new sha from refs
|
97
103
|
new_sha = self._get_new_sha_if_exists_in_remote()
|
98
104
|
if new_sha is None:
|
99
|
-
raise ValueError(
|
105
|
+
raise ValueError(
|
106
|
+
f"Could not find ref {self.git_ref} in {self.repository_url}"
|
107
|
+
)
|
100
108
|
|
101
109
|
# check self.ref_sha should be updated
|
102
110
|
if self.ref_sha != new_sha:
|
@@ -111,10 +119,10 @@ class GitLock(Lock):
|
|
111
119
|
# init git repo in local and set origin url
|
112
120
|
repo = git.Repo.init(pkg_version_path)
|
113
121
|
try:
|
114
|
-
remote = repo.remote(
|
122
|
+
remote = repo.remote("origin")
|
115
123
|
remote.set_url(self.repository_url)
|
116
124
|
except ValueError:
|
117
|
-
remote = repo.create_remote(
|
125
|
+
remote = repo.create_remote("origin", self.repository_url)
|
118
126
|
|
119
127
|
# check current local commit include new_sha
|
120
128
|
# if not included, fetch and do hard reset
|
@@ -126,10 +134,12 @@ class GitLock(Lock):
|
|
126
134
|
if exist_sha is None or exist_sha != self.ref_sha:
|
127
135
|
remote.fetch(depth=1, refspec=self.ref_sha)
|
128
136
|
repo.git.checkout(new_sha)
|
129
|
-
repo.git.reset(
|
130
|
-
repo.git.clean(
|
137
|
+
repo.git.reset("--hard", new_sha)
|
138
|
+
repo.git.clean("-fd")
|
131
139
|
except Exception as e:
|
132
|
-
pocket_logger.error(
|
140
|
+
pocket_logger.error(
|
141
|
+
f"failed to sync git: {self.repository_url} @ ref: {self.git_ref}. reason : {e}"
|
142
|
+
)
|
133
143
|
raise e
|
134
144
|
|
135
145
|
def _get_new_sha_if_exists_in_remote(self):
|
@@ -143,8 +153,8 @@ class GitLock(Lock):
|
|
143
153
|
refs = git.cmd.Git().ls_remote(self.repository_url)
|
144
154
|
|
145
155
|
new_sha = None
|
146
|
-
for r in refs.split(
|
147
|
-
sha, ref = r.split(
|
156
|
+
for r in refs.split("\n"):
|
157
|
+
sha, ref = r.split("\t")
|
148
158
|
if sha == self.ref_sha:
|
149
159
|
new_sha = sha
|
150
160
|
break
|
@@ -161,16 +171,19 @@ class GitLock(Lock):
|
|
161
171
|
|
162
172
|
@classmethod
|
163
173
|
def get_git_branches(cls, repo_url):
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
+
if not hasattr(cls, "_remote_cache"):
|
175
|
+
cls._remote_cache = {}
|
176
|
+
if cls._remote_cache.get(repo_url) is None:
|
177
|
+
ls_lists = git.cmd.Git().ls_remote(repo_url)
|
178
|
+
|
179
|
+
branches = {}
|
180
|
+
for line in ls_lists.split("\n"):
|
181
|
+
sha, ref = line.split("\t")
|
182
|
+
if ref.startswith("refs/heads/"):
|
183
|
+
branch_name = ref.replace("refs/heads/", "")
|
184
|
+
branches[branch_name] = sha
|
185
|
+
cls._remote_cache[repo_url] = branches
|
186
|
+
return cls._remote_cache[repo_url]
|
174
187
|
|
175
188
|
@classmethod
|
176
189
|
def parse_repo_url(cls, repo_url: str) -> Tuple[str, str, str]:
|
@@ -195,7 +208,7 @@ class GitLock(Lock):
|
|
195
208
|
# Parse base repo URL and remaining path
|
196
209
|
tree_index = repo_path_list.index("tree")
|
197
210
|
base_repo = f"https://github.com/{'/'.join(repo_path_list[:tree_index])}"
|
198
|
-
sub_path = repo_path_list[tree_index + 1:]
|
211
|
+
sub_path = repo_path_list[tree_index + 1 :]
|
199
212
|
|
200
213
|
# Fetch branch information
|
201
214
|
branches = cls.get_git_branches(base_repo)
|
@@ -204,14 +217,15 @@ class GitLock(Lock):
|
|
204
217
|
for idx in range(1, len(sub_path) + 1):
|
205
218
|
branch_name = "/".join(sub_path[:idx])
|
206
219
|
if branch_name in branches:
|
207
|
-
directory_path =
|
220
|
+
directory_path = (
|
221
|
+
"/".join(sub_path[idx:]) if idx < len(sub_path) else None
|
222
|
+
)
|
208
223
|
return base_repo, branch_name, directory_path
|
209
224
|
|
210
225
|
# If no valid branch is found, raise an error
|
211
226
|
raise ValueError("Branch not found in repository")
|
212
227
|
|
213
228
|
def eject_to_path(self, dest_path: pathlib.Path, src_sub_path: str = None):
|
214
|
-
|
215
229
|
# clone the git repository to the target path
|
216
230
|
pocket_logger.info(
|
217
231
|
f"Ejecting git: {self.repository_url} @ ref: {self.git_ref} source in path: {src_sub_path} to {dest_path} ..."
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import pathlib
|
2
2
|
from concurrent.futures.thread import ThreadPoolExecutor
|
3
3
|
|
4
|
-
from hyperpocket.repository.lock import
|
4
|
+
from hyperpocket.repository.lock import GitLock, LocalLock, Lock
|
5
5
|
|
6
6
|
|
7
7
|
class Lockfile:
|
@@ -51,7 +51,7 @@ class Lockfile:
|
|
51
51
|
else:
|
52
52
|
locks = list(self.locks.values())
|
53
53
|
with ThreadPoolExecutor(
|
54
|
-
|
54
|
+
max_workers=min(len(locks) + 1, 100), thread_name_prefix="repository_loader"
|
55
55
|
) as executor:
|
56
56
|
executor.map(lambda lock: lock.sync(force_update=force_update), locks)
|
57
57
|
self.write()
|
hyperpocket/server/__init__.py
CHANGED
@@ -15,6 +15,7 @@ async def github_oauth2_callback(request: Request, state: str, code: str):
|
|
15
15
|
|
16
16
|
return HTMLResponse(content="success")
|
17
17
|
|
18
|
+
|
18
19
|
@github_auth_router.get("/token/callback")
|
19
20
|
async def github_token_callback(request: Request, state: str, token: str):
|
20
21
|
try:
|
@@ -22,4 +23,4 @@ async def github_token_callback(request: Request, state: str, token: str):
|
|
22
23
|
except ValueError:
|
23
24
|
return HTMLResponse(content="failed")
|
24
25
|
|
25
|
-
return HTMLResponse(content="success")
|
26
|
+
return HTMLResponse(content="success")
|
@@ -1,12 +1,9 @@
|
|
1
|
-
|
2
1
|
from fastapi import APIRouter
|
3
2
|
from starlette.responses import HTMLResponse
|
4
3
|
|
5
4
|
from hyperpocket.futures import FutureStore
|
6
5
|
|
7
|
-
notion_auth_router = APIRouter(
|
8
|
-
prefix="/notion"
|
9
|
-
)
|
6
|
+
notion_auth_router = APIRouter(prefix="/notion")
|
10
7
|
|
11
8
|
|
12
9
|
@notion_auth_router.get("/token/callback")
|
@@ -16,4 +13,4 @@ async def notion_token_callback(state: str, token: str):
|
|
16
13
|
except ValueError:
|
17
14
|
return HTMLResponse(content="failed")
|
18
15
|
|
19
|
-
return HTMLResponse(content="success")
|
16
|
+
return HTMLResponse(content="success")
|
hyperpocket/server/auth/slack.py
CHANGED