hyperpocket 0.3.1__py3-none-any.whl → 0.3.3__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/provider.py +1 -0
- hyperpocket/auth/x/__init__.py +0 -0
- hyperpocket/auth/x/context.py +13 -0
- hyperpocket/auth/x/oauth2_context.py +29 -0
- hyperpocket/auth/x/oauth2_handler.py +165 -0
- hyperpocket/auth/x/oauth2_schema.py +18 -0
- hyperpocket/cli/__main__.py +3 -0
- hyperpocket/cli/codegen/tool/__init__.py +3 -0
- hyperpocket/cli/codegen/tool/tool_main_template.py +48 -0
- hyperpocket/cli/tool.py +121 -0
- hyperpocket/config/auth.py +6 -0
- hyperpocket/server/auth/x.py +16 -0
- hyperpocket/tool/wasm/README.md +2 -2
- {hyperpocket-0.3.1.dist-info → hyperpocket-0.3.3.dist-info}/METADATA +120 -47
- {hyperpocket-0.3.1.dist-info → hyperpocket-0.3.3.dist-info}/RECORD +17 -8
- {hyperpocket-0.3.1.dist-info → hyperpocket-0.3.3.dist-info}/WHEEL +0 -0
- {hyperpocket-0.3.1.dist-info → hyperpocket-0.3.3.dist-info}/entry_points.txt +0 -0
hyperpocket/auth/provider.py
CHANGED
File without changes
|
@@ -0,0 +1,13 @@
|
|
1
|
+
from hyperpocket.auth.context import AuthContext
|
2
|
+
|
3
|
+
|
4
|
+
class XAuthContext(AuthContext):
|
5
|
+
_ACCESS_TOKEN_KEY: str = "X_AUTH_TOKEN"
|
6
|
+
|
7
|
+
def to_dict(self) -> dict[str, str]:
|
8
|
+
return {self._ACCESS_TOKEN_KEY: self.access_token}
|
9
|
+
|
10
|
+
def to_profiled_dict(self, profile: str) -> dict[str, str]:
|
11
|
+
return {
|
12
|
+
f"{profile.upper()}_{self._ACCESS_TOKEN_KEY}": self.access_token,
|
13
|
+
}
|
@@ -0,0 +1,29 @@
|
|
1
|
+
from datetime import datetime, timedelta, timezone
|
2
|
+
from typing import Optional
|
3
|
+
|
4
|
+
from pydantic import Field
|
5
|
+
|
6
|
+
from hyperpocket.auth.x.context import XAuthContext
|
7
|
+
from hyperpocket.auth.x.oauth2_schema import XOAuth2Response
|
8
|
+
|
9
|
+
|
10
|
+
class XOAuth2AuthContext(XAuthContext):
|
11
|
+
refresh_token: Optional[str] = Field(default=None, description="Refresh token")
|
12
|
+
|
13
|
+
@classmethod
|
14
|
+
def from_x_oauth2_response(cls, response: XOAuth2Response) -> "XOAuth2AuthContext":
|
15
|
+
description = f"X OAuth2 Context logged in with {response.scope} scopes"
|
16
|
+
now = datetime.now(tz=timezone.utc)
|
17
|
+
|
18
|
+
if response.expires_in:
|
19
|
+
expires_at = now + timedelta(seconds=response.expires_in)
|
20
|
+
else:
|
21
|
+
expires_at = None
|
22
|
+
|
23
|
+
return cls(
|
24
|
+
access_token=response.access_token,
|
25
|
+
refresh_token=response.refresh_token,
|
26
|
+
description=description,
|
27
|
+
expires_at=expires_at,
|
28
|
+
detail=response,
|
29
|
+
)
|
@@ -0,0 +1,165 @@
|
|
1
|
+
import base64
|
2
|
+
import traceback
|
3
|
+
from typing import Optional
|
4
|
+
from urllib.parse import urlencode, urljoin, quote
|
5
|
+
from uuid import uuid4
|
6
|
+
|
7
|
+
import httpx
|
8
|
+
|
9
|
+
from hyperpocket.auth.context import AuthContext
|
10
|
+
from hyperpocket.auth.x.oauth2_context import XOAuth2AuthContext
|
11
|
+
from hyperpocket.auth.x.oauth2_schema import (
|
12
|
+
XOAuth2Request,
|
13
|
+
XOAuth2Response,
|
14
|
+
)
|
15
|
+
from hyperpocket.auth.handler import AuthHandlerInterface, AuthProvider
|
16
|
+
from hyperpocket.config import config
|
17
|
+
from hyperpocket.futures import FutureStore
|
18
|
+
|
19
|
+
|
20
|
+
class XOAuth2AuthHandler(AuthHandlerInterface):
|
21
|
+
_X_AUTH_URL = "https://x.com/i/oauth2/authorize"
|
22
|
+
_X_TOKEN_URL = "https://api.x.com/2/oauth2/token"
|
23
|
+
|
24
|
+
name: str = "x-oauth2"
|
25
|
+
description: str = "This handler is used to authenticate users using X OAuth."
|
26
|
+
scoped: bool = True
|
27
|
+
|
28
|
+
@staticmethod
|
29
|
+
def provider() -> AuthProvider:
|
30
|
+
return AuthProvider.X
|
31
|
+
|
32
|
+
@staticmethod
|
33
|
+
def provider_default() -> bool:
|
34
|
+
return True
|
35
|
+
|
36
|
+
@staticmethod
|
37
|
+
def recommended_scopes() -> set[str]:
|
38
|
+
return set()
|
39
|
+
|
40
|
+
def prepare(
|
41
|
+
self,
|
42
|
+
auth_req: XOAuth2Request,
|
43
|
+
thread_id: str,
|
44
|
+
profile: str,
|
45
|
+
future_uid: str,
|
46
|
+
*args,
|
47
|
+
**kwargs,
|
48
|
+
) -> str:
|
49
|
+
redirect_uri = urljoin(
|
50
|
+
config().public_base_url + "/",
|
51
|
+
f"{config().callback_url_rewrite_prefix}/auth/x/oauth2/callback",
|
52
|
+
)
|
53
|
+
code_verifier = "challenge" # TODO: implement code_verifier
|
54
|
+
code_challenge = code_verifier # TODO: implement code_challenge with sha256
|
55
|
+
auth_url = self._make_auth_url(
|
56
|
+
auth_req, redirect_uri, future_uid, code_challenge, "plain"
|
57
|
+
)
|
58
|
+
|
59
|
+
FutureStore.create_future(
|
60
|
+
future_uid,
|
61
|
+
data={
|
62
|
+
"redirect_uri": redirect_uri,
|
63
|
+
"thread_id": thread_id,
|
64
|
+
"profile": profile,
|
65
|
+
"code_verifier": code_verifier,
|
66
|
+
},
|
67
|
+
)
|
68
|
+
|
69
|
+
return f"User needs to authenticate using the following URL: {auth_url}"
|
70
|
+
|
71
|
+
async def authenticate(
|
72
|
+
self, auth_req: XOAuth2Request, future_uid: str, *args, **kwargs
|
73
|
+
) -> AuthContext:
|
74
|
+
future_data = FutureStore.get_future(future_uid)
|
75
|
+
auth_code = await future_data.future
|
76
|
+
|
77
|
+
async with httpx.AsyncClient() as client:
|
78
|
+
basic_token = f"{auth_req.client_id}:{auth_req.client_secret}"
|
79
|
+
basic_token_encoded = base64.b64encode(basic_token.encode()).decode()
|
80
|
+
resp = await client.post(
|
81
|
+
url=self._X_TOKEN_URL,
|
82
|
+
data={
|
83
|
+
"code": auth_code,
|
84
|
+
# "client_id": auth_req.client_id,
|
85
|
+
# "client_secret": auth_req.client_secret,
|
86
|
+
"redirect_uri": future_data.data["redirect_uri"],
|
87
|
+
"code_verifier": future_data.data["code_verifier"],
|
88
|
+
"grant_type": "authorization_code",
|
89
|
+
},
|
90
|
+
headers={
|
91
|
+
"Authorization": f"Basic {basic_token_encoded}",
|
92
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
93
|
+
},
|
94
|
+
)
|
95
|
+
|
96
|
+
if resp.status_code != 200:
|
97
|
+
raise Exception(
|
98
|
+
f"failed to authenticate. status_code : {resp.status_code}, {resp.json()}"
|
99
|
+
)
|
100
|
+
|
101
|
+
resp_json = resp.json()
|
102
|
+
auth_response = XOAuth2Response(**resp_json)
|
103
|
+
return XOAuth2AuthContext.from_x_oauth2_response(auth_response)
|
104
|
+
|
105
|
+
async def refresh(
|
106
|
+
self, auth_req: XOAuth2Request, context: AuthContext, *args, **kwargs
|
107
|
+
) -> AuthContext:
|
108
|
+
x_context: XOAuth2AuthContext = context
|
109
|
+
last_oauth2_resp: XOAuth2Response = x_context.detail
|
110
|
+
refresh_token = x_context.refresh_token
|
111
|
+
if refresh_token is None:
|
112
|
+
raise Exception(
|
113
|
+
f"refresh token is None. last_oauth2_resp: {last_oauth2_resp}"
|
114
|
+
)
|
115
|
+
|
116
|
+
async with httpx.AsyncClient() as client:
|
117
|
+
resp = await client.post(
|
118
|
+
url=self._X_TOKEN_URL,
|
119
|
+
data={
|
120
|
+
"client_id": auth_req.client_id,
|
121
|
+
"client_secret": auth_req.client_secret,
|
122
|
+
"refresh_token": refresh_token,
|
123
|
+
"grant_type": "refresh_token",
|
124
|
+
},
|
125
|
+
)
|
126
|
+
|
127
|
+
if resp.status_code != 200:
|
128
|
+
raise Exception(
|
129
|
+
f"failed to authenticate. status_code : {resp.status_code}"
|
130
|
+
)
|
131
|
+
|
132
|
+
resp_json = resp.json()
|
133
|
+
if "refresh_token" not in resp_json:
|
134
|
+
resp_json["refresh_token"] = refresh_token
|
135
|
+
|
136
|
+
response = XOAuth2Response(**resp_json)
|
137
|
+
return XOAuth2AuthContext.from_google_oauth2_response(response)
|
138
|
+
|
139
|
+
def _make_auth_url(
|
140
|
+
self,
|
141
|
+
auth_req: XOAuth2Request,
|
142
|
+
redirect_uri: str,
|
143
|
+
state: str,
|
144
|
+
code_challenge: str,
|
145
|
+
code_challenge_method: str,
|
146
|
+
) -> str:
|
147
|
+
params = {
|
148
|
+
"client_id": auth_req.client_id,
|
149
|
+
"redirect_uri": redirect_uri,
|
150
|
+
"response_type": "code",
|
151
|
+
"scope": " ".join(auth_req.auth_scopes),
|
152
|
+
"code_challenge": code_challenge,
|
153
|
+
"code_challenge_method": code_challenge_method,
|
154
|
+
"state": state,
|
155
|
+
}
|
156
|
+
return f"{self._X_AUTH_URL}?{urlencode(params)}"
|
157
|
+
|
158
|
+
def make_request(
|
159
|
+
self, auth_scopes: Optional[list[str]] = None, **kwargs
|
160
|
+
) -> XOAuth2Request:
|
161
|
+
return XOAuth2Request(
|
162
|
+
auth_scopes=auth_scopes,
|
163
|
+
client_id=config().auth.x.client_id,
|
164
|
+
client_secret=config().auth.x.client_secret,
|
165
|
+
)
|
@@ -0,0 +1,18 @@
|
|
1
|
+
from typing import Optional
|
2
|
+
|
3
|
+
from pydantic import Field
|
4
|
+
|
5
|
+
from hyperpocket.auth.schema import AuthenticateRequest, AuthenticateResponse
|
6
|
+
|
7
|
+
|
8
|
+
class XOAuth2Request(AuthenticateRequest):
|
9
|
+
client_id: str
|
10
|
+
client_secret: str
|
11
|
+
|
12
|
+
|
13
|
+
class XOAuth2Response(AuthenticateResponse):
|
14
|
+
access_token: str
|
15
|
+
expires_in: int
|
16
|
+
refresh_token: Optional[str] = Field(default=None)
|
17
|
+
scope: str
|
18
|
+
token_type: str
|
hyperpocket/cli/__main__.py
CHANGED
@@ -6,6 +6,7 @@ from hyperpocket.cli.sync import sync
|
|
6
6
|
from hyperpocket.cli.eject import eject
|
7
7
|
from hyperpocket.cli.auth_token import create_token_auth_template
|
8
8
|
from hyperpocket.cli.auth_oauth2 import create_oauth2_auth_template
|
9
|
+
from hyperpocket.cli.tool import create_tool_template, build_tool
|
9
10
|
|
10
11
|
@click.group()
|
11
12
|
def cli():
|
@@ -20,6 +21,8 @@ def devtool():
|
|
20
21
|
cli.add_command(devtool)
|
21
22
|
devtool.add_command(create_token_auth_template)
|
22
23
|
devtool.add_command(create_oauth2_auth_template)
|
24
|
+
devtool.add_command(create_tool_template)
|
25
|
+
devtool.add_command(build_tool)
|
23
26
|
|
24
27
|
cli.add_command(pull)
|
25
28
|
cli.add_command(sync)
|
@@ -0,0 +1,48 @@
|
|
1
|
+
from jinja2 import Template
|
2
|
+
|
3
|
+
def get_tool_main_template() -> Template:
|
4
|
+
return Template('''import json
|
5
|
+
import os
|
6
|
+
import sys
|
7
|
+
|
8
|
+
from pydantic import BaseModel, Field
|
9
|
+
|
10
|
+
token = os.getenv('AUTH_PROVIDER_TOKEN')
|
11
|
+
|
12
|
+
|
13
|
+
class {{ capitalized_tool_name }}Request(BaseModel):
|
14
|
+
param_1: str = Field(description="")
|
15
|
+
|
16
|
+
|
17
|
+
def {{ tool_name }}(req: {{ capitalized_tool_name }}Request):
|
18
|
+
"""
|
19
|
+
Example of tool function
|
20
|
+
response = requests.delete(
|
21
|
+
url=f"https://www.googleapis.com/calendar/v3/calendars/{req.calendar_id}/events/{req.event_id}",
|
22
|
+
params={
|
23
|
+
"sendUpdates": req.send_updates
|
24
|
+
},
|
25
|
+
headers={
|
26
|
+
"Authorization": f"Bearer {token}",
|
27
|
+
}
|
28
|
+
)
|
29
|
+
|
30
|
+
if response.status_code != 200:
|
31
|
+
return f"failed to delete calendar events. error : {response.text}"
|
32
|
+
|
33
|
+
return f"successfully deleted calendar events {req.event_id}"
|
34
|
+
"""
|
35
|
+
return
|
36
|
+
|
37
|
+
|
38
|
+
def main():
|
39
|
+
req = json.load(sys.stdin.buffer)
|
40
|
+
req_typed = {{ capitalized_tool_name }}Request.model_validate(req)
|
41
|
+
response = {{ tool_name }}(req_typed)
|
42
|
+
|
43
|
+
print(response)
|
44
|
+
|
45
|
+
|
46
|
+
if __name__ == '__main__':
|
47
|
+
main()
|
48
|
+
''')
|
hyperpocket/cli/tool.py
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
import os
|
2
|
+
import shutil
|
3
|
+
import subprocess
|
4
|
+
from pathlib import Path
|
5
|
+
|
6
|
+
import click
|
7
|
+
|
8
|
+
from hyperpocket.cli.codegen.tool import get_tool_main_template
|
9
|
+
|
10
|
+
|
11
|
+
@click.command()
|
12
|
+
@click.argument('tool_name', type=str)
|
13
|
+
def create_tool_template(tool_name, language="python"):
|
14
|
+
"""Create a tool template with the specified tool name."""
|
15
|
+
|
16
|
+
# Validate tool_name
|
17
|
+
if not tool_name.islower() or not tool_name.replace('_', '').isalpha():
|
18
|
+
raise ValueError("tool_name must be lowercase and contain only letters and underscores")
|
19
|
+
|
20
|
+
tool_directory_name = tool_name.replace('_', '-')
|
21
|
+
capitalized_tool_name = ''.join([word.capitalize() for word in tool_name.split('_')])
|
22
|
+
|
23
|
+
# Create tool directory
|
24
|
+
print(f"Generating tool package directory for {tool_name}")
|
25
|
+
cwd = Path.cwd()
|
26
|
+
tool_path = cwd / tool_directory_name
|
27
|
+
tool_path.mkdir(parents=True, exist_ok=True)
|
28
|
+
|
29
|
+
# Create tool module directory
|
30
|
+
print(f"Generating tool module directory for {tool_name}")
|
31
|
+
tool_module_path = tool_path / tool_name
|
32
|
+
tool_module_path.mkdir(parents=True, exist_ok=True)
|
33
|
+
|
34
|
+
# Create uv or poetry init
|
35
|
+
print(f"Generating pyproject.toml file for {tool_name}")
|
36
|
+
if shutil.which("uv"):
|
37
|
+
subprocess.run(["uv", "init"], cwd=tool_path, check=True)
|
38
|
+
os.remove(tool_path / "hello.py")
|
39
|
+
elif shutil.which("poetry"):
|
40
|
+
subprocess.run(["poetry", "init", "--no-interaction"], cwd=tool_path, check=True)
|
41
|
+
else:
|
42
|
+
raise ValueError("uv or poetry must be installed to generate tool project")
|
43
|
+
|
44
|
+
# Create __init__.py file
|
45
|
+
init_file = tool_module_path / '__init__.py'
|
46
|
+
if not init_file.exists():
|
47
|
+
init_file.write_text(f'''from .__main__ import main
|
48
|
+
|
49
|
+
__all__ = ["main"]
|
50
|
+
''')
|
51
|
+
|
52
|
+
# Create __main__.py file
|
53
|
+
print(f"Generating tool main file for {tool_name}")
|
54
|
+
main_file = tool_module_path / '__main__.py'
|
55
|
+
if not main_file.exists():
|
56
|
+
main_content = get_tool_main_template().render(
|
57
|
+
capitalized_tool_name=capitalized_tool_name,
|
58
|
+
tool_name=tool_name
|
59
|
+
)
|
60
|
+
main_file.write_text(main_content)
|
61
|
+
|
62
|
+
# Create config.toml
|
63
|
+
print(f"Generating config.toml file for {tool_name}")
|
64
|
+
config_file = tool_path / 'config.toml'
|
65
|
+
if not config_file.exists():
|
66
|
+
config_file.write_text(f'''name = "{tool_name}"
|
67
|
+
description = ""
|
68
|
+
language = "{language}"
|
69
|
+
|
70
|
+
[auth]
|
71
|
+
auth_provider = ""
|
72
|
+
auth_handler = ""
|
73
|
+
scopes = []
|
74
|
+
''')
|
75
|
+
|
76
|
+
# Create .gitignore
|
77
|
+
print(f"Generating .gitignore file for {tool_name}")
|
78
|
+
gitignore_file = tool_path / '.gitignore'
|
79
|
+
gitignore_file.touch()
|
80
|
+
|
81
|
+
# Create README.md
|
82
|
+
print(f"Generating README.md file for {tool_name}")
|
83
|
+
readme_file = tool_path / 'README.md'
|
84
|
+
if not readme_file.exists():
|
85
|
+
readme_file.write_text(f'# {tool_name}\n\n')
|
86
|
+
|
87
|
+
# Create schema.json
|
88
|
+
print(f"Generating schema.json file for {tool_name}")
|
89
|
+
schema_file = tool_path / 'schema.json'
|
90
|
+
schema_file.touch()
|
91
|
+
|
92
|
+
|
93
|
+
@click.command()
|
94
|
+
@click.argument('tool_path', type=str, required=False)
|
95
|
+
def build_tool(tool_path):
|
96
|
+
"""Build the tool at the specified path or current directory."""
|
97
|
+
|
98
|
+
cwd = Path.cwd()
|
99
|
+
|
100
|
+
# Determine the tool directory
|
101
|
+
if tool_path is None:
|
102
|
+
if not (cwd / 'config.toml').exists():
|
103
|
+
raise ValueError("Current working directory must be a tool directory")
|
104
|
+
else:
|
105
|
+
potential_path = Path(tool_path)
|
106
|
+
if (cwd / potential_path).exists():
|
107
|
+
cwd = cwd / potential_path
|
108
|
+
elif potential_path.exists():
|
109
|
+
cwd = potential_path
|
110
|
+
else:
|
111
|
+
raise ValueError(f"Tool path '{tool_path}' does not exist")
|
112
|
+
|
113
|
+
# Build the tool
|
114
|
+
print(f"Building tool in {cwd}")
|
115
|
+
if shutil.which("uv"):
|
116
|
+
subprocess.run(["uv", "build"], cwd=cwd, check=True)
|
117
|
+
os.remove(cwd / "dist/.gitignore")
|
118
|
+
elif shutil.which("poetry"):
|
119
|
+
subprocess.run(["poetry", "build"], cwd=cwd, check=True)
|
120
|
+
else:
|
121
|
+
raise ValueError("Tool must be a poetry or uv project")
|
hyperpocket/config/auth.py
CHANGED
@@ -27,11 +27,17 @@ class CalendlyAuthConfig(BaseAuthConfig):
|
|
27
27
|
client_secret: str
|
28
28
|
|
29
29
|
|
30
|
+
class XAuthConfig(BaseAuthConfig):
|
31
|
+
client_id: str
|
32
|
+
client_secret: str
|
33
|
+
|
34
|
+
|
30
35
|
class AuthConfig(BaseModel):
|
31
36
|
slack: Optional[SlackAuthConfig] = None
|
32
37
|
google: Optional[GoogleAuthConfig] = None
|
33
38
|
github: Optional[GithubAuthConfig] = None
|
34
39
|
calendly: Optional[CalendlyAuthConfig] = None
|
40
|
+
x: Optional[XAuthConfig] = None
|
35
41
|
use_prebuilt_auth: bool = Field(default=True)
|
36
42
|
|
37
43
|
|
@@ -0,0 +1,16 @@
|
|
1
|
+
from fastapi import APIRouter, Request
|
2
|
+
from starlette.responses import HTMLResponse
|
3
|
+
|
4
|
+
from hyperpocket.futures import FutureStore
|
5
|
+
|
6
|
+
x_auth_router = APIRouter(prefix="/x")
|
7
|
+
|
8
|
+
|
9
|
+
@x_auth_router.get("/oauth2/callback")
|
10
|
+
async def x_oauth2_callback(request: Request, state: str, code: str):
|
11
|
+
try:
|
12
|
+
FutureStore.resolve_future(state, code)
|
13
|
+
except ValueError:
|
14
|
+
return HTMLResponse(content="failed")
|
15
|
+
|
16
|
+
return HTMLResponse(content="success")
|
hyperpocket/tool/wasm/README.md
CHANGED
@@ -117,8 +117,8 @@ from_git('https://github.com/your-organization/your-repository/tree/main',tool_v
|
|
117
117
|
})
|
118
118
|
```
|
119
119
|
|
120
|
-
2. Injecting
|
121
|
-
|
120
|
+
2. Injecting tool_vars by settings.toml
|
121
|
+
Hyperpocket checks the `settings.toml` from the agent code directory.
|
122
122
|
|
123
123
|
## WasmTool
|
124
124
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: hyperpocket
|
3
|
-
Version: 0.3.
|
3
|
+
Version: 0.3.3
|
4
4
|
Summary: Building AI agent with hyperpocket tool in a flash
|
5
5
|
Project-URL: Homepage, https://vessl-ai.github.io/hyperpocket
|
6
6
|
Project-URL: Repository, https://github.com/vessl-ai/hyperpocket
|
@@ -193,32 +193,32 @@ Pocket provides way to use end user auth easily.
|
|
193
193
|
|
194
194
|
- Supported methods
|
195
195
|
|
196
|
-
|
197
|
-
|
198
|
-
|
196
|
+
- [x] OAuth
|
197
|
+
- [x] Token
|
198
|
+
- [ ] Basic Auth (Username, Password)
|
199
199
|
|
200
200
|
- Supported OAuth Providers
|
201
201
|
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
202
|
+
- [x] Google
|
203
|
+
- [x] GitHub
|
204
|
+
- [x] Slack
|
205
|
+
- [x] Reddit
|
206
|
+
- [x] Calendly
|
207
|
+
- [ ] Facebook
|
208
|
+
- [ ] X (Previously Twitter)
|
209
|
+
- [ ] LinkedIn
|
210
|
+
- [ ] Discord
|
211
|
+
- [ ] Zoom
|
212
|
+
- [ ] Microsoft
|
213
|
+
- [ ] Spotify
|
214
|
+
- [ ] Twitch
|
215
215
|
|
216
216
|
- Supported Token Providers
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
217
|
+
- [x] Notion
|
218
|
+
- [x] Slack
|
219
|
+
- [x] Linear
|
220
|
+
- [x] Gumloop
|
221
|
+
- [x] Github
|
222
222
|
|
223
223
|
You can manage your auths in request-wise level. (e.g. you can use different auths for different requests)
|
224
224
|
|
@@ -340,15 +340,15 @@ client_secret = "" # your slack client secret
|
|
340
340
|
|
341
341
|
- While creating your github OAuth app, configuring your app's `Authorization callback URL` is different for your
|
342
342
|
development environment and production environment.
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
343
|
+
- For local testing environment, you can use `https://localhost:8001/proxy/auth/<provider>/callback` for TLS enabled
|
344
|
+
redirect url. (ex. `https://localhost:8001/proxy/auth/github/callback`)
|
345
|
+
- **Note**: Default port for hyperpocket dev server is `8000`. If you are using a different port, make sure to
|
346
|
+
replace `8000` with your actual port number.
|
347
|
+
- **Note**: But for easy dev experience, you can use TLS proxy on port `8001` provided out-of-the-box.
|
348
|
+
- You can change the `proxy` prefix in settings.toml to your desired prefix with
|
349
|
+
`callback_url_rewrite_prefix` key.
|
350
|
+
- For production environment, you can use `https://yourdomain.com/auth/github/callback`
|
351
|
+
- **Note**: Make sure to replace `yourdomain.com` with your actual domain name that this app will be hosted on.
|
352
352
|
|
353
353
|
#### How to integrate SLACK OAuth app
|
354
354
|
|
@@ -359,19 +359,92 @@ client_secret = "" # your slack client secret
|
|
359
359
|
- Redirect URLs :
|
360
360
|
`{public_server_protocol}://{public_hostname}:[{public_server_port}]/{callback_url_rewrite_prefix}/auth/slack/oauth2/callback`
|
361
361
|
- Scopes : What you want to request to user.
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
3. Set your Slack APP Client ID / Client Secret in
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
362
|
+
- Recommended scopes :
|
363
|
+
- channels:history,
|
364
|
+
- channels:read,
|
365
|
+
- chat:write,
|
366
|
+
- groups:history,
|
367
|
+
- groups:read,
|
368
|
+
- im:history,
|
369
|
+
- mpim:history,
|
370
|
+
- reactions:read,
|
371
|
+
- reactions:write,
|
372
|
+
|
373
|
+
3. Set your Slack APP Client ID / Client Secret in `$HOME/.pocket/settings.toml`
|
374
|
+
|
375
|
+
#### How to start adding a new token auth
|
376
|
+
|
377
|
+
1. Generate boilerplate codes for token-based auth services ?
|
378
|
+
|
379
|
+
```
|
380
|
+
# service_name should be lowercase including underscore
|
381
|
+
poetry run hyperpocket devtool create-token-auth-template {service_name}
|
382
|
+
```
|
383
|
+
|
384
|
+
It will generate boilerplate code lines for a new token-based auth service
|
385
|
+
|
386
|
+
2. Extend AuthProvider enum to add your new auth provider.
|
387
|
+
|
388
|
+
```python
|
389
|
+
class AuthProvider(Enum):
|
390
|
+
SERVICE = 'service'
|
391
|
+
```
|
392
|
+
|
393
|
+
3. Specify auth provider for tools
|
394
|
+
|
395
|
+
1) github repo or local
|
396
|
+
|
397
|
+
```toml
|
398
|
+
[auth]
|
399
|
+
auth_provider = "{service_name}"
|
400
|
+
auth_handler = "{service_name}-token"
|
401
|
+
scopes = []
|
402
|
+
```
|
403
|
+
|
404
|
+
2. function_tool
|
405
|
+
|
406
|
+
```python
|
407
|
+
@function_tool(
|
408
|
+
auth_provider=AuthProvider.SERVICE
|
409
|
+
)
|
410
|
+
def my_function(**kwargs):
|
411
|
+
```
|
412
|
+
|
413
|
+
#### How to Start Developing a New Tool
|
414
|
+
|
415
|
+
1. Generate Boilerplate Template for the Tool
|
416
|
+
|
417
|
+
```bash
|
418
|
+
# tool_name must be lowercase and can include underscores
|
419
|
+
poetry run hyperpocket devtool create-tool-template your_own_tool
|
420
|
+
```
|
421
|
+
|
422
|
+
This command will generate the boilerplate directory and files for a new tool.
|
423
|
+
|
424
|
+
2. Configure the `config.toml`
|
425
|
+
|
426
|
+
Define the language, `auth_provider`, scopes, and other required settings in the `config.toml` file.
|
427
|
+
|
428
|
+
```toml
|
429
|
+
# Example configuration
|
430
|
+
name = "google_delete_calendar_events"
|
431
|
+
description = "Delete Google Calendar events"
|
432
|
+
language = "python"
|
433
|
+
|
434
|
+
[auth]
|
435
|
+
auth_provider = "google"
|
436
|
+
scopes = ["https://www.googleapis.com/auth/calendar"]
|
437
|
+
```
|
438
|
+
|
439
|
+
3. Develop the Tool Logic
|
440
|
+
|
441
|
+
Implement the `request_model` and the necessary functions for your tool's logic in the `__main__.py` file.
|
442
|
+
|
443
|
+
4. Build Your Tool
|
444
|
+
|
445
|
+
Use the Hyperpocket CLI to build your tool.
|
446
|
+
|
447
|
+
```bash
|
448
|
+
# Specify the tool_path or run the command inside the tool's directory
|
449
|
+
poetry run hyperpocket devtool build-tool ./your-own-tool
|
450
|
+
```
|
@@ -10,7 +10,7 @@ hyperpocket/auth/README.md,sha256=zn4QqnFZCA_4X3x8Wb6lE3OP5otYxpByZaCiUkBvaNs,11
|
|
10
10
|
hyperpocket/auth/__init__.py,sha256=pO8M6SAuq0EPqi848_Iy650wqaLekx98e3RRnEAM_r0,607
|
11
11
|
hyperpocket/auth/context.py,sha256=m-j2gDYUKBMsiakLHsu9thhM4dYyFiXP0Wp0S_iC0bU,1303
|
12
12
|
hyperpocket/auth/handler.py,sha256=5cusl9ANEyG3gORVFjqh709txC0alw6eKtxgV6wjf6k,6683
|
13
|
-
hyperpocket/auth/provider.py,sha256=
|
13
|
+
hyperpocket/auth/provider.py,sha256=Fe5kYX093sd9K0fakPVXYqXL6oIhdPU530W8pkVzZHU,515
|
14
14
|
hyperpocket/auth/schema.py,sha256=pl4oRTNj8PdqQg6UVPWf8ei2uYQ4DtOmmD58cVFMYQw,537
|
15
15
|
hyperpocket/auth/calendly/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
16
16
|
hyperpocket/auth/calendly/context.py,sha256=oUQzLy_w5NPvbXjFPOhLD-ggONVSy9VVpFabvX4ZpsY,408
|
@@ -59,13 +59,19 @@ hyperpocket/auth/slack/oauth2_schema.py,sha256=eu2jF2thmaz66AKcnivTvIBLTbeF-a-xQ
|
|
59
59
|
hyperpocket/auth/slack/token_context.py,sha256=GUqMcPUleo8O9YRNMjOrMiSlHb-OOKwlrO_syCow55w,437
|
60
60
|
hyperpocket/auth/slack/token_handler.py,sha256=6L_HiDQ2wpz9x65QUB9t-d5KOpADeG-zZVgEMHu3MUE,2765
|
61
61
|
hyperpocket/auth/slack/token_schema.py,sha256=mAwOtRO2tW5OtDzbllDJtR_uhLD8ShQAjezkAZnAgd0,207
|
62
|
+
hyperpocket/auth/x/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
63
|
+
hyperpocket/auth/x/context.py,sha256=5Ur9GI8og49E-8FToKlqR3TtPn6vWAdu1boLqYxJL8k,399
|
64
|
+
hyperpocket/auth/x/oauth2_context.py,sha256=q2HB14WENz2LU7Qe7KkE7-AtsEOtzXIj7n8alykac8M,955
|
65
|
+
hyperpocket/auth/x/oauth2_handler.py,sha256=kEnsbL1YIeIQUlMVPIZYqOswk9_t-sNGiaixj43M1Yc,5634
|
66
|
+
hyperpocket/auth/x/oauth2_schema.py,sha256=PKeaHccpxGLJjEd8RxP2wVcrIPCE-W9HEtP8EMzU4To,401
|
62
67
|
hyperpocket/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
63
|
-
hyperpocket/cli/__main__.py,sha256=
|
68
|
+
hyperpocket/cli/__main__.py,sha256=T6qnVngoWYmHZONloDafbkTLcVKyZXkIwa9e2wKdnLs,720
|
64
69
|
hyperpocket/cli/auth_oauth2.py,sha256=gGQCS8AvaVHvs4Oe6fS7tw2ks_N67-8newinaDlywbw,4680
|
65
70
|
hyperpocket/cli/auth_token.py,sha256=gmT-jVMvZ1Ck-1F_yFUChcd9HMtFHNTaUvj_332usjE,4809
|
66
71
|
hyperpocket/cli/eject.py,sha256=Te1YhDONk_VCgZ6l2HjLqONJrn04EfPk19sD6D5SOyY,636
|
67
72
|
hyperpocket/cli/pull.py,sha256=3xCyTmdbP77l7awNyCY63_ZmUhIlX1PE3apwReXNYco,549
|
68
73
|
hyperpocket/cli/sync.py,sha256=OO5zFrLOMXk7TSAFEew04ZzW4HLoQ1uVb80UKvIJha0,517
|
74
|
+
hyperpocket/cli/tool.py,sha256=r9dtmU805z5OEs059Zvpki_Vtbc4U6bzC_EK-pl98Nk,3950
|
69
75
|
hyperpocket/cli/codegen/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
70
76
|
hyperpocket/cli/codegen/auth/__init__.py,sha256=wtm3vX2Kk79tyFkguV5oV82_YoJ3w-Clqu2izwE65Vg,959
|
71
77
|
hyperpocket/cli/codegen/auth/auth_context_template.py,sha256=89HHp5byHhLazkwtR8bsymg7IBKfp2ltdGikcD5aplY,570
|
@@ -76,8 +82,10 @@ hyperpocket/cli/codegen/auth/auth_token_context_template.py,sha256=GzpsCY6DlGXu-
|
|
76
82
|
hyperpocket/cli/codegen/auth/auth_token_handler_template.py,sha256=t30JZFTCYCcXWFop4CVEABEtttjJn-F6tcS-ZOQCh-Y,3158
|
77
83
|
hyperpocket/cli/codegen/auth/auth_token_schema_template.py,sha256=ZJRcT-0QIIxltkDCgVz7zKl3aObX__bMi33s7_ZxI5k,429
|
78
84
|
hyperpocket/cli/codegen/auth/server_auth_template.py,sha256=-S3bqlqsXMytOdrIG68_np3LfU5_Ydy7z-Z0fqfCE8I,1478
|
85
|
+
hyperpocket/cli/codegen/tool/__init__.py,sha256=ZJgrXJKwbxkOYz-e_cInlVIKTGEOFchvDVHUdo3LNMU,92
|
86
|
+
hyperpocket/cli/codegen/tool/tool_main_template.py,sha256=AlqGMc2xRPPM4ypShFeh7TBzex0rMlQOZjRKFwfofTo,1126
|
79
87
|
hyperpocket/config/__init__.py,sha256=gYMHdExdPYScBB8mihSp1yJ9H-RJ8FdNz5mEFubFNAo,138
|
80
|
-
hyperpocket/config/auth.py,sha256=
|
88
|
+
hyperpocket/config/auth.py,sha256=5Z8Q01TNvWO1x0ZjCXc6nF1qe6-FFuiV2y7sxXYRq2c,910
|
81
89
|
hyperpocket/config/git.py,sha256=CGbY7J1LyN4ccZr9CFGAQwwhjC9kvdU0On_nsAsZ1FQ,362
|
82
90
|
hyperpocket/config/logger.py,sha256=T5LESHvQfl2q1F-7FjsVfrz9fWz6jxPo7u6Jml3CQjc,2804
|
83
91
|
hyperpocket/config/session.py,sha256=1ng1mARv4R_07SF5CkZWLgX9pNeXTcM0knCRw07Fpms,816
|
@@ -101,6 +109,7 @@ hyperpocket/server/auth/notion.py,sha256=NUKwCK61UADhO4FmFpNYjh1vsDzFZoFRDHdcMIB
|
|
101
109
|
hyperpocket/server/auth/reddit.py,sha256=UloBQNEdhU1_eXaF8qVSQ5ivq5xtWINsHvBDGwgFSLM,443
|
102
110
|
hyperpocket/server/auth/slack.py,sha256=frMNtBEfe3fbeNzsQ8udapeau45NZmS8guATmS46qyA,710
|
103
111
|
hyperpocket/server/auth/token.py,sha256=Yq5Ym-uEO_3cBpQOsmCBuqtFIdImrNcVUgF5ozs5NHk,1763
|
112
|
+
hyperpocket/server/auth/x.py,sha256=CYCD_ajBY6Jt04E2bSEBZFRRIUZmNjF2gn6F0ZV5XuA,450
|
104
113
|
hyperpocket/server/tool/__init__.py,sha256=khNLe3H2W7WXKQlHjXuuvd9R87eHOAZhDsQmjDcbYsg,210
|
105
114
|
hyperpocket/server/tool/wasm.py,sha256=VJyp6RGsq8llKT_sY6DhV52wsETu-W9bzJ7C9wC17Oo,1698
|
106
115
|
hyperpocket/server/tool/dto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -118,7 +127,7 @@ hyperpocket/tool/function/__init__.py,sha256=2zV5ojuzvv38B9J-5l6XomZ3x-8FTC0-Bow
|
|
118
127
|
hyperpocket/tool/function/annotation.py,sha256=mHm_TN2X7xmivBZ8_tbnsaGt_GUQZ-l2IPv0PudZKb8,982
|
119
128
|
hyperpocket/tool/function/tool.py,sha256=FVEn8i3HJaG2S-LuBgRc4DJUYWWplsGs0nRmuVstLZQ,6280
|
120
129
|
hyperpocket/tool/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
121
|
-
hyperpocket/tool/wasm/README.md,sha256=
|
130
|
+
hyperpocket/tool/wasm/README.md,sha256=DjHtzOS4oe4Mk39M8BfxE0N_hqds32KXx2OWJ2cJOGo,3866
|
122
131
|
hyperpocket/tool/wasm/__init__.py,sha256=t6KwkMY0pwZheCf9Q-9Kt7HGHQU7U--b9TTKgNegWUY,72
|
123
132
|
hyperpocket/tool/wasm/browser.py,sha256=JWn34P1ybPJCogBB85BePWFXM4AqOI2wSbIb5LcCXTs,1867
|
124
133
|
hyperpocket/tool/wasm/invoker.py,sha256=jgW3XqdbAXgDjTlURmD2Xeh5qpjZgvB6PYzNlcdvfQc,1431
|
@@ -135,7 +144,7 @@ hyperpocket/util/flatten_json_schema.py,sha256=iuNBEmMSKFtPi-uqo6fb3RWN0koHOAihW
|
|
135
144
|
hyperpocket/util/function_to_model.py,sha256=TXUs-qPbzL8C9-qqpz4Ad4D9MOPP61n_p0iPU6SoBeM,2318
|
136
145
|
hyperpocket/util/get_objects_from_subpackage.py,sha256=4mR_S8eaJSdU68YfCkiXeIcXxb6q7LjFGsY_IHeNIZw,929
|
137
146
|
hyperpocket/util/json_schema_to_model.py,sha256=PqI87pU5dWwcrQWB8eQxRdfgAEvvC1x_DKZnhcsRV-o,3586
|
138
|
-
hyperpocket-0.3.
|
139
|
-
hyperpocket-0.3.
|
140
|
-
hyperpocket-0.3.
|
141
|
-
hyperpocket-0.3.
|
147
|
+
hyperpocket-0.3.3.dist-info/METADATA,sha256=SyPQKiQ-LsCfP9vHGwHw-PQOz_wlwjh2o2lSSngYn8w,12958
|
148
|
+
hyperpocket-0.3.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
149
|
+
hyperpocket-0.3.3.dist-info/entry_points.txt,sha256=KpBleaYr0SaENXOa-dFvJ_cvFCHYFEQ4LMl11ShAcBI,61
|
150
|
+
hyperpocket-0.3.3.dist-info/RECORD,,
|
File without changes
|
File without changes
|