hyperpocket 0.3.1__py3-none-any.whl → 0.3.2__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- 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.2.dist-info}/METADATA +120 -47
- {hyperpocket-0.3.1.dist-info → hyperpocket-0.3.2.dist-info}/RECORD +17 -8
- {hyperpocket-0.3.1.dist-info → hyperpocket-0.3.2.dist-info}/WHEEL +0 -0
- {hyperpocket-0.3.1.dist-info → hyperpocket-0.3.2.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.2
|
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.2.dist-info/METADATA,sha256=3TczH2IpxfxjkWNyj_ZkzwkRx6RSbk9Jt5AGqiMy1ZM,12958
|
148
|
+
hyperpocket-0.3.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
149
|
+
hyperpocket-0.3.2.dist-info/entry_points.txt,sha256=KpBleaYr0SaENXOa-dFvJ_cvFCHYFEQ4LMl11ShAcBI,61
|
150
|
+
hyperpocket-0.3.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|