hyperpocket 0.1.10__py3-none-any.whl → 0.2.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/__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 +23 -7
- 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.10.dist-info → hyperpocket-0.2.0.dist-info}/METADATA +11 -5
- hyperpocket-0.2.0.dist-info/RECORD +137 -0
- hyperpocket-0.1.10.dist-info/RECORD +0 -137
- {hyperpocket-0.1.10.dist-info → hyperpocket-0.2.0.dist-info}/WHEEL +0 -0
- {hyperpocket-0.1.10.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