hyperpocket 0.0.1__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- hyperpocket/__init__.py +7 -0
- hyperpocket/auth/README.KR.md +309 -0
- hyperpocket/auth/README.md +323 -0
- hyperpocket/auth/__init__.py +24 -0
- hyperpocket/auth/calendly/__init__.py +0 -0
- hyperpocket/auth/calendly/context.py +13 -0
- hyperpocket/auth/calendly/oauth2_context.py +25 -0
- hyperpocket/auth/calendly/oauth2_handler.py +146 -0
- hyperpocket/auth/calendly/oauth2_schema.py +16 -0
- hyperpocket/auth/context.py +38 -0
- hyperpocket/auth/github/__init__.py +0 -0
- hyperpocket/auth/github/context.py +13 -0
- hyperpocket/auth/github/oauth2_context.py +25 -0
- hyperpocket/auth/github/oauth2_handler.py +143 -0
- hyperpocket/auth/github/oauth2_schema.py +16 -0
- hyperpocket/auth/github/token_context.py +12 -0
- hyperpocket/auth/github/token_handler.py +79 -0
- hyperpocket/auth/github/token_schema.py +9 -0
- hyperpocket/auth/google/__init__.py +0 -0
- hyperpocket/auth/google/context.py +15 -0
- hyperpocket/auth/google/oauth2_context.py +31 -0
- hyperpocket/auth/google/oauth2_handler.py +137 -0
- hyperpocket/auth/google/oauth2_schema.py +18 -0
- hyperpocket/auth/handler.py +171 -0
- hyperpocket/auth/linear/__init__.py +0 -0
- hyperpocket/auth/linear/context.py +15 -0
- hyperpocket/auth/linear/token_context.py +15 -0
- hyperpocket/auth/linear/token_handler.py +68 -0
- hyperpocket/auth/linear/token_schema.py +9 -0
- hyperpocket/auth/provider.py +16 -0
- hyperpocket/auth/schema.py +19 -0
- hyperpocket/auth/slack/__init__.py +0 -0
- hyperpocket/auth/slack/context.py +15 -0
- hyperpocket/auth/slack/oauth2_context.py +40 -0
- hyperpocket/auth/slack/oauth2_handler.py +151 -0
- hyperpocket/auth/slack/oauth2_schema.py +40 -0
- hyperpocket/auth/slack/tests/__init__.py +0 -0
- hyperpocket/auth/slack/tests/test_oauth2_handler.py +32 -0
- hyperpocket/auth/slack/tests/test_token_handler.py +23 -0
- hyperpocket/auth/slack/token_context.py +14 -0
- hyperpocket/auth/slack/token_handler.py +64 -0
- hyperpocket/auth/slack/token_schema.py +9 -0
- hyperpocket/auth/tests/__init__.py +0 -0
- hyperpocket/auth/tests/test_google_oauth2_handler.py +147 -0
- hyperpocket/auth/tests/test_slack_oauth2_handler.py +147 -0
- hyperpocket/auth/tests/test_slack_token_handler.py +66 -0
- hyperpocket/cli/__init__.py +0 -0
- hyperpocket/cli/__main__.py +12 -0
- hyperpocket/cli/pull.py +18 -0
- hyperpocket/cli/sync.py +17 -0
- hyperpocket/config/__init__.py +9 -0
- hyperpocket/config/auth.py +36 -0
- hyperpocket/config/git.py +17 -0
- hyperpocket/config/logger.py +81 -0
- hyperpocket/config/session.py +35 -0
- hyperpocket/config/settings.py +62 -0
- hyperpocket/constants.py +0 -0
- hyperpocket/curated_tools.py +10 -0
- hyperpocket/external/__init__.py +7 -0
- hyperpocket/external/github_client.py +19 -0
- hyperpocket/futures/__init__.py +7 -0
- hyperpocket/futures/futurestore.py +48 -0
- hyperpocket/pocket_auth.py +344 -0
- hyperpocket/pocket_main.py +351 -0
- hyperpocket/prompts.py +15 -0
- hyperpocket/repository/__init__.py +5 -0
- hyperpocket/repository/lock.py +156 -0
- hyperpocket/repository/lockfile.py +56 -0
- hyperpocket/repository/repository.py +18 -0
- hyperpocket/server/__init__.py +3 -0
- hyperpocket/server/auth/__init__.py +15 -0
- hyperpocket/server/auth/calendly.py +16 -0
- hyperpocket/server/auth/github.py +25 -0
- hyperpocket/server/auth/google.py +16 -0
- hyperpocket/server/auth/linear.py +18 -0
- hyperpocket/server/auth/slack.py +28 -0
- hyperpocket/server/auth/token.py +51 -0
- hyperpocket/server/proxy.py +63 -0
- hyperpocket/server/server.py +178 -0
- hyperpocket/server/tool/__init__.py +10 -0
- hyperpocket/server/tool/dto/__init__.py +0 -0
- hyperpocket/server/tool/dto/script.py +15 -0
- hyperpocket/server/tool/wasm.py +31 -0
- hyperpocket/session/README.KR.md +62 -0
- hyperpocket/session/README.md +61 -0
- hyperpocket/session/__init__.py +4 -0
- hyperpocket/session/in_memory.py +76 -0
- hyperpocket/session/interface.py +118 -0
- hyperpocket/session/redis.py +126 -0
- hyperpocket/session/tests/__init__.py +0 -0
- hyperpocket/session/tests/test_in_memory.py +145 -0
- hyperpocket/session/tests/test_redis.py +151 -0
- hyperpocket/tests/__init__.py +0 -0
- hyperpocket/tests/test_pocket.py +118 -0
- hyperpocket/tests/test_pocket_auth.py +982 -0
- hyperpocket/tool/README.KR.md +68 -0
- hyperpocket/tool/README.md +75 -0
- hyperpocket/tool/__init__.py +13 -0
- hyperpocket/tool/builtins/__init__.py +0 -0
- hyperpocket/tool/builtins/example/__init__.py +0 -0
- hyperpocket/tool/builtins/example/add_tool.py +18 -0
- hyperpocket/tool/function/README.KR.md +159 -0
- hyperpocket/tool/function/README.md +169 -0
- hyperpocket/tool/function/__init__.py +9 -0
- hyperpocket/tool/function/annotation.py +30 -0
- hyperpocket/tool/function/tool.py +87 -0
- hyperpocket/tool/tests/__init__.py +0 -0
- hyperpocket/tool/tests/test_function_tool.py +266 -0
- hyperpocket/tool/tool.py +106 -0
- hyperpocket/tool/wasm/README.KR.md +144 -0
- hyperpocket/tool/wasm/README.md +144 -0
- hyperpocket/tool/wasm/__init__.py +3 -0
- hyperpocket/tool/wasm/browser.py +63 -0
- hyperpocket/tool/wasm/invoker.py +41 -0
- hyperpocket/tool/wasm/script.py +82 -0
- hyperpocket/tool/wasm/templates/__init__.py +28 -0
- hyperpocket/tool/wasm/templates/node.py +87 -0
- hyperpocket/tool/wasm/templates/python.py +75 -0
- hyperpocket/tool/wasm/tool.py +147 -0
- hyperpocket/util/__init__.py +1 -0
- hyperpocket/util/extract_func_param_desc_from_docstring.py +97 -0
- hyperpocket/util/find_all_leaf_class_in_package.py +17 -0
- hyperpocket/util/find_all_subclass_in_package.py +29 -0
- hyperpocket/util/flatten_json_schema.py +45 -0
- hyperpocket/util/function_to_model.py +46 -0
- hyperpocket/util/get_objects_from_subpackage.py +28 -0
- hyperpocket/util/json_schema_to_model.py +69 -0
- hyperpocket-0.0.1.dist-info/METADATA +304 -0
- hyperpocket-0.0.1.dist-info/RECORD +131 -0
- hyperpocket-0.0.1.dist-info/WHEEL +4 -0
- hyperpocket-0.0.1.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,66 @@
|
|
1
|
+
from unittest.async_case import IsolatedAsyncioTestCase
|
2
|
+
from urllib.parse import urlparse, parse_qs
|
3
|
+
|
4
|
+
from hyperpocket.auth import SlackTokenAuthContext
|
5
|
+
from hyperpocket.auth.slack.token_handler import SlackTokenAuthHandler
|
6
|
+
from hyperpocket.auth.slack.token_schema import SlackTokenRequest
|
7
|
+
from hyperpocket.futures import FutureStore
|
8
|
+
|
9
|
+
|
10
|
+
class TestSlackTokenAuthHandler(IsolatedAsyncioTestCase):
|
11
|
+
|
12
|
+
async def asyncSetUp(self):
|
13
|
+
self.handler: SlackTokenAuthHandler = SlackTokenAuthHandler()
|
14
|
+
self.auth_req = SlackTokenRequest()
|
15
|
+
|
16
|
+
async def test_make_auth_url(self):
|
17
|
+
auth_url = self.handler._make_auth_url(
|
18
|
+
req=self.auth_req,
|
19
|
+
redirect_uri="http://test-redirect-uri.com",
|
20
|
+
state="test-future-id"
|
21
|
+
)
|
22
|
+
parsed = urlparse(auth_url)
|
23
|
+
query_params = parse_qs(parsed.query)
|
24
|
+
base_url = f"{parsed.scheme}://{parsed.netloc}{parsed.path}"
|
25
|
+
|
26
|
+
# then
|
27
|
+
self.assertEqual(base_url, self.handler._TOKEN_URL)
|
28
|
+
self.assertEqual(query_params["state"][0], "test-future-id")
|
29
|
+
self.assertEqual(query_params["redirect_uri"][0], "http://test-redirect-uri.com")
|
30
|
+
|
31
|
+
async def test_prepare(self):
|
32
|
+
# when
|
33
|
+
prepare: str = self.handler.prepare(
|
34
|
+
auth_req=self.auth_req,
|
35
|
+
thread_id="test-prepare-thread-id",
|
36
|
+
profile="test-prepare-profile",
|
37
|
+
future_uid="test-prepare-future-uid",
|
38
|
+
)
|
39
|
+
auth_url = prepare.removeprefix("User needs to authenticate using the following URL:").strip()
|
40
|
+
future_data = FutureStore.get_future( uid="test-prepare-future-uid")
|
41
|
+
|
42
|
+
# then
|
43
|
+
self.assertTrue(auth_url.startswith(self.handler._TOKEN_URL))
|
44
|
+
self.assertIsNotNone(future_data)
|
45
|
+
self.assertEqual(future_data.data["thread_id"], "test-prepare-thread-id")
|
46
|
+
self.assertEqual(future_data.data["profile"], "test-prepare-profile")
|
47
|
+
self.assertFalse(future_data.future.done())
|
48
|
+
|
49
|
+
async def test_authenticate(self):
|
50
|
+
self.handler.prepare(
|
51
|
+
auth_req=self.auth_req,
|
52
|
+
thread_id="test-thread-id",
|
53
|
+
profile="test-profile",
|
54
|
+
future_uid="test-future-uid"
|
55
|
+
)
|
56
|
+
future_data = FutureStore.get_future( uid="test-future-uid")
|
57
|
+
future_data.future.set_result("test-token")
|
58
|
+
|
59
|
+
response: SlackTokenAuthContext = await self.handler.authenticate(
|
60
|
+
auth_req=self.auth_req,
|
61
|
+
future_uid="test-future-uid"
|
62
|
+
)
|
63
|
+
|
64
|
+
self.assertIsInstance(response, SlackTokenAuthContext)
|
65
|
+
self.assertEqual(response.access_token, "test-token")
|
66
|
+
self.assertIsNone(response.expires_at)
|
File without changes
|
hyperpocket/cli/pull.py
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
import pathlib
|
2
|
+
from typing import Optional
|
3
|
+
|
4
|
+
import click
|
5
|
+
|
6
|
+
import hyperpocket.repository as repository
|
7
|
+
|
8
|
+
|
9
|
+
@click.command()
|
10
|
+
@click.argument('url', type=str)
|
11
|
+
@click.option("--lockfile", envvar='PATHS', type=click.Path(exists=True))
|
12
|
+
@click.option("--git-ref", type=str, default='HEAD')
|
13
|
+
def pull(url: str, lockfile: Optional[pathlib.Path], git_ref: str):
|
14
|
+
if not lockfile:
|
15
|
+
lockfile = pathlib.Path.cwd() / 'pocket.lock'
|
16
|
+
if not lockfile.exists():
|
17
|
+
lockfile.touch()
|
18
|
+
repository.pull(repository.Lockfile(path=lockfile), url, git_ref)
|
hyperpocket/cli/sync.py
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
import pathlib
|
2
|
+
from typing import Optional
|
3
|
+
|
4
|
+
import click
|
5
|
+
|
6
|
+
import hyperpocket.repository as repository
|
7
|
+
|
8
|
+
|
9
|
+
@click.command()
|
10
|
+
@click.option("--lockfile", envvar='PATHS', type=click.Path(exists=True))
|
11
|
+
@click.option("--force-update", type=str, default='HEAD')
|
12
|
+
def sync(url: str, lockfile: Optional[pathlib.Path], force_update: bool):
|
13
|
+
if not lockfile:
|
14
|
+
lockfile = pathlib.Path.cwd() / 'pocket.lock'
|
15
|
+
if not lockfile.exists():
|
16
|
+
lockfile.touch()
|
17
|
+
repository.sync(repository.Lockfile(path=lockfile), force_update)
|
@@ -0,0 +1,9 @@
|
|
1
|
+
from hyperpocket.config.settings import config as _config
|
2
|
+
from hyperpocket.config.settings import settings as _settings
|
3
|
+
from hyperpocket.config.logger import pocket_logger as _pocket_logger
|
4
|
+
|
5
|
+
config = _config
|
6
|
+
secret = _settings
|
7
|
+
pocket_logger = _pocket_logger
|
8
|
+
|
9
|
+
__all__ = ["config", "secret", "pocket_logger"]
|
@@ -0,0 +1,36 @@
|
|
1
|
+
from typing import Optional
|
2
|
+
|
3
|
+
from pydantic import BaseModel, Field
|
4
|
+
|
5
|
+
|
6
|
+
class BaseAuthConfig(BaseModel):
|
7
|
+
use_recommended_scope: bool = Field(default=True)
|
8
|
+
|
9
|
+
|
10
|
+
class SlackAuthConfig(BaseAuthConfig):
|
11
|
+
client_id: str
|
12
|
+
client_secret: str
|
13
|
+
|
14
|
+
|
15
|
+
class GoogleAuthConfig(BaseAuthConfig):
|
16
|
+
client_id: str
|
17
|
+
client_secret: str
|
18
|
+
|
19
|
+
|
20
|
+
class GithubAuthConfig(BaseAuthConfig):
|
21
|
+
client_id: str
|
22
|
+
client_secret: str
|
23
|
+
|
24
|
+
class CalendlyAuthConfig(BaseAuthConfig):
|
25
|
+
client_id: str
|
26
|
+
client_secret: str
|
27
|
+
|
28
|
+
class AuthConfig(BaseModel):
|
29
|
+
slack: Optional[SlackAuthConfig] = None
|
30
|
+
google: Optional[GoogleAuthConfig] = None
|
31
|
+
github: Optional[GithubAuthConfig] = None
|
32
|
+
calendly: Optional[CalendlyAuthConfig] = None
|
33
|
+
use_prebuilt_auth: bool = Field(default=True)
|
34
|
+
|
35
|
+
|
36
|
+
DefaultAuthConfig = AuthConfig()
|
@@ -0,0 +1,17 @@
|
|
1
|
+
from typing import Optional
|
2
|
+
|
3
|
+
from pydantic import BaseModel
|
4
|
+
|
5
|
+
|
6
|
+
class GithubConfig(BaseModel):
|
7
|
+
github_token: Optional[str] = None
|
8
|
+
app_id: Optional[str] = None
|
9
|
+
app_private_key: Optional[str] = None
|
10
|
+
app_installation_id: Optional[str] = None
|
11
|
+
|
12
|
+
|
13
|
+
class GitConfig(BaseModel):
|
14
|
+
github: GithubConfig
|
15
|
+
|
16
|
+
|
17
|
+
DefaultGitConfig = GitConfig(github=GithubConfig())
|
@@ -0,0 +1,81 @@
|
|
1
|
+
import logging
|
2
|
+
import os
|
3
|
+
from logging.handlers import RotatingFileHandler
|
4
|
+
from pathlib import Path
|
5
|
+
|
6
|
+
import hyperpocket
|
7
|
+
from hyperpocket.config.settings import config
|
8
|
+
|
9
|
+
class ColorFormatter(logging.Formatter):
|
10
|
+
"""Custom formatter to add colors based on log level."""
|
11
|
+
# ANSI escape codes for text colors
|
12
|
+
LEVEL_COLORS = {
|
13
|
+
logging.DEBUG: "\033[36m", # Cyan
|
14
|
+
logging.INFO: "\033[32m", # Green
|
15
|
+
logging.WARNING: "\033[33m", # Yellow
|
16
|
+
logging.ERROR: "\033[31m", # Red
|
17
|
+
logging.CRITICAL: "\033[35m", # Magenta
|
18
|
+
}
|
19
|
+
RESET = "\033[0m"
|
20
|
+
|
21
|
+
def format(self, record):
|
22
|
+
log_color = self.LEVEL_COLORS.get(record.levelno, self.RESET)
|
23
|
+
message = super().format(record)
|
24
|
+
return f"{log_color}{message}{self.RESET}"
|
25
|
+
|
26
|
+
|
27
|
+
|
28
|
+
def get_logger():
|
29
|
+
# init log file
|
30
|
+
package_path = Path(os.path.dirname(hyperpocket.__file__))
|
31
|
+
log_dir = package_path / ".log"
|
32
|
+
os.makedirs(log_dir, exist_ok=True)
|
33
|
+
log_file = log_dir / "pocket.log"
|
34
|
+
if not log_file.exists():
|
35
|
+
print(f"created log file in {log_file}")
|
36
|
+
with open(log_file, "w"):
|
37
|
+
pass
|
38
|
+
|
39
|
+
# set log level
|
40
|
+
log_level = logging.INFO
|
41
|
+
logger = logging.getLogger("pocket_logger")
|
42
|
+
if config.log_level.lower() == "debug":
|
43
|
+
log_level = logging.DEBUG
|
44
|
+
elif config.log_level.lower() == "info":
|
45
|
+
log_level = logging.INFO
|
46
|
+
elif config.log_level.lower() == "warning":
|
47
|
+
log_level = logging.WARNING
|
48
|
+
elif config.log_level.lower() == "error":
|
49
|
+
log_level = logging.ERROR
|
50
|
+
elif config.log_level.lower() == "critical":
|
51
|
+
log_level = logging.CRITICAL
|
52
|
+
elif config.log_level.lower() == "fatal":
|
53
|
+
log_level = logging.FATAL
|
54
|
+
|
55
|
+
# set formatter
|
56
|
+
logger.setLevel(log_level)
|
57
|
+
color_formatter = ColorFormatter("[%(asctime)s] [%(levelname)s] [%(processName)s(%(process)d):%(threadName)s(%(thread)d)] [%(name)s] %(message)s",
|
58
|
+
datefmt="%Y-%m-%d %H:%M:%S")
|
59
|
+
formatter = logging.Formatter(
|
60
|
+
"[%(asctime)s] [%(levelname)s] [%(processName)s(%(process)d):%(threadName)s(%(thread)d)] [%(name)s] %(message)s",
|
61
|
+
datefmt="%Y-%m-%d %H:%M:%S"
|
62
|
+
)
|
63
|
+
|
64
|
+
# add console handler
|
65
|
+
console_handler = logging.StreamHandler()
|
66
|
+
console_handler.setLevel(log_level) # 콘솔 출력 레벨 설정
|
67
|
+
console_handler.setFormatter(color_formatter)
|
68
|
+
logger.addHandler(console_handler)
|
69
|
+
|
70
|
+
# add rotating file handler
|
71
|
+
file_handler = RotatingFileHandler(
|
72
|
+
log_file, maxBytes=5 * 1024 * 1024, backupCount=100 # 파일 크기 5MB, 백업 파일 3개
|
73
|
+
)
|
74
|
+
file_handler.setLevel(logging.DEBUG) # 파일 출력 레벨 설정
|
75
|
+
file_handler.setFormatter(formatter)
|
76
|
+
logger.addHandler(file_handler)
|
77
|
+
|
78
|
+
return logger
|
79
|
+
|
80
|
+
|
81
|
+
pocket_logger = get_logger()
|
@@ -0,0 +1,35 @@
|
|
1
|
+
from enum import Enum
|
2
|
+
from typing import Optional
|
3
|
+
|
4
|
+
from pydantic import BaseModel, Field
|
5
|
+
|
6
|
+
|
7
|
+
class SessionType(Enum):
|
8
|
+
IN_MEMORY = "in_memory"
|
9
|
+
REDIS = "redis"
|
10
|
+
|
11
|
+
|
12
|
+
class SessionConfigInMemory(BaseModel):
|
13
|
+
pass
|
14
|
+
|
15
|
+
|
16
|
+
class SessionConfigRedis(BaseModel):
|
17
|
+
class Config:
|
18
|
+
extra = "allow"
|
19
|
+
|
20
|
+
host: str = Field(default="localhost")
|
21
|
+
port: int = Field(default=6379)
|
22
|
+
db: int = Field(default=0)
|
23
|
+
|
24
|
+
|
25
|
+
class SessionConfig(BaseModel):
|
26
|
+
session_type: SessionType
|
27
|
+
in_memory: Optional[SessionConfigInMemory] = Field(default_factory=SessionConfigInMemory)
|
28
|
+
redis: Optional[SessionConfigRedis] = Field(default_factory=SessionConfigRedis)
|
29
|
+
|
30
|
+
|
31
|
+
DefaultSessionConfig = SessionConfig(
|
32
|
+
session_type=SessionType.IN_MEMORY,
|
33
|
+
in_memory=SessionConfigInMemory(),
|
34
|
+
redis=SessionConfigRedis()
|
35
|
+
)
|
@@ -0,0 +1,62 @@
|
|
1
|
+
import os
|
2
|
+
from pathlib import Path
|
3
|
+
from typing import Literal
|
4
|
+
|
5
|
+
from dynaconf import Dynaconf
|
6
|
+
from pydantic import BaseModel
|
7
|
+
|
8
|
+
from hyperpocket.config.auth import AuthConfig, DefaultAuthConfig
|
9
|
+
from hyperpocket.config.git import DefaultGitConfig, GitConfig
|
10
|
+
from hyperpocket.config.session import DefaultSessionConfig, SessionConfig
|
11
|
+
|
12
|
+
pocket_root = Path.home() / ".pocket"
|
13
|
+
if not pocket_root.exists():
|
14
|
+
os.makedirs(pocket_root)
|
15
|
+
settings_path = pocket_root / "settings.toml"
|
16
|
+
if not settings_path.exists():
|
17
|
+
with open(settings_path, "w"):
|
18
|
+
pass
|
19
|
+
secret_path = pocket_root / ".secrets.toml"
|
20
|
+
if not secret_path.exists():
|
21
|
+
with open(secret_path, "w"):
|
22
|
+
pass
|
23
|
+
toolpkg_path = pocket_root / "toolpkg"
|
24
|
+
if not toolpkg_path.exists():
|
25
|
+
os.makedirs(toolpkg_path)
|
26
|
+
|
27
|
+
settings = Dynaconf(
|
28
|
+
envvar_prefix="POCKET",
|
29
|
+
settings_files=[settings_path, secret_path],
|
30
|
+
)
|
31
|
+
|
32
|
+
for key, value in os.environ.items():
|
33
|
+
if settings.get(key) is None:
|
34
|
+
settings[key] = value
|
35
|
+
|
36
|
+
|
37
|
+
class Config(BaseModel):
|
38
|
+
internal_server_port: int = 8000
|
39
|
+
enable_local_callback_proxy: bool = True
|
40
|
+
public_hostname: str = "localhost"
|
41
|
+
public_server_protocol: str = "https"
|
42
|
+
public_server_port: int = 8001
|
43
|
+
callback_url_rewrite_prefix: str = "proxy" # should not start with a slash
|
44
|
+
log_level: str = "INFO"
|
45
|
+
auth: AuthConfig = DefaultAuthConfig
|
46
|
+
git: GitConfig = DefaultGitConfig
|
47
|
+
session: SessionConfig = DefaultSessionConfig
|
48
|
+
|
49
|
+
@property
|
50
|
+
def internal_base_url(self):
|
51
|
+
return f"http://localhost:{self.internal_server_port}"
|
52
|
+
|
53
|
+
@property
|
54
|
+
def public_base_url(self):
|
55
|
+
if self.public_server_protocol == 'https' and self.public_server_port == 443:
|
56
|
+
return f"{self.public_server_protocol}://{self.public_hostname}"
|
57
|
+
elif self.public_server_protocol == 'http' and self.public_server_port == 80:
|
58
|
+
return f"{self.public_server_protocol}://{self.public_hostname}"
|
59
|
+
return f"{self.public_server_protocol}://{self.public_hostname}:{self.public_server_port}"
|
60
|
+
|
61
|
+
|
62
|
+
config: Config = Config.model_validate({k.lower(): v for k, v in settings.items()})
|
hyperpocket/constants.py
ADDED
File without changes
|
@@ -0,0 +1,10 @@
|
|
1
|
+
from hyperpocket.tool import from_git
|
2
|
+
|
3
|
+
SLACK = [
|
4
|
+
from_git("https://github.com/vessl-ai/tool-calling", "main", "examples/slack-get-message"),
|
5
|
+
from_git("https://github.com/vessl-ai/tool-calling", "main", "examples/slack-post-message")
|
6
|
+
]
|
7
|
+
|
8
|
+
LINEAR = [
|
9
|
+
from_git("https://github.com/vessl-ai/tool-calling", "main", "examples/linear-get-issues"),
|
10
|
+
]
|
@@ -0,0 +1,19 @@
|
|
1
|
+
from github import Auth, Github, GithubIntegration
|
2
|
+
|
3
|
+
from hyperpocket.config import config
|
4
|
+
|
5
|
+
_github = None
|
6
|
+
|
7
|
+
|
8
|
+
def github_instance() -> Github:
|
9
|
+
global _github
|
10
|
+
if _github is None:
|
11
|
+
if config.git.github.github_token:
|
12
|
+
_github = Github(auth=Auth.Token(config.git.github.github_token))
|
13
|
+
elif config.git.github.app_id:
|
14
|
+
auth = Auth.AppAuth(config.git.github.app_id, config.git.github.app_private_key)
|
15
|
+
gi = GithubIntegration(auth=auth)
|
16
|
+
_github = gi.get_github_for_installation(config.git.github.app_installation_id)
|
17
|
+
else:
|
18
|
+
_github = Github()
|
19
|
+
return _github
|
@@ -0,0 +1,48 @@
|
|
1
|
+
import asyncio
|
2
|
+
import enum
|
3
|
+
from typing import Any
|
4
|
+
|
5
|
+
from hyperpocket.config import pocket_logger
|
6
|
+
|
7
|
+
|
8
|
+
class FutureData:
|
9
|
+
future: asyncio.Future
|
10
|
+
data: dict
|
11
|
+
|
12
|
+
def __init__(self, future: asyncio.Future, data: dict):
|
13
|
+
self.future = future
|
14
|
+
self.data = data
|
15
|
+
|
16
|
+
|
17
|
+
class FutureStore(object):
|
18
|
+
futures: dict[str, FutureData]
|
19
|
+
|
20
|
+
def __init__(self):
|
21
|
+
self.futures = dict()
|
22
|
+
|
23
|
+
def create_future(self, uid: str, data: dict = None) -> FutureData:
|
24
|
+
if future := self.get_future(uid) is not None:
|
25
|
+
pocket_logger.info(
|
26
|
+
f"the future already exists. the existing future is returned. uid: {uid}")
|
27
|
+
return future
|
28
|
+
|
29
|
+
loop = asyncio.get_running_loop()
|
30
|
+
future = loop.create_future()
|
31
|
+
future_data = FutureData(future=future, data=data)
|
32
|
+
|
33
|
+
self.futures[uid] = future_data
|
34
|
+
|
35
|
+
return future_data
|
36
|
+
|
37
|
+
def get_future(self, uid: str) -> FutureData:
|
38
|
+
return self.futures.get(uid, None)
|
39
|
+
|
40
|
+
def resolve_future(self, uid: str, value: Any):
|
41
|
+
future_data = self.futures.get(uid)
|
42
|
+
if not future_data:
|
43
|
+
raise ValueError(f'Future not found for uid={uid}')
|
44
|
+
if not future_data.future.done():
|
45
|
+
future_data.future.set_result(value)
|
46
|
+
|
47
|
+
def delete_future(self, uid: str):
|
48
|
+
self.futures.pop(uid, None)
|