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.
@@ -10,6 +10,7 @@ class AuthProvider(Enum):
10
10
  NOTION = "notion"
11
11
  REDDIT = "reddit"
12
12
  GUMLOOP = "gumloop"
13
+ X = "x"
13
14
 
14
15
  @classmethod
15
16
  def get_auth_provider(cls, auth_provider_name: str) -> "AuthProvider":
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
@@ -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,3 @@
1
+ from .tool_main_template import get_tool_main_template
2
+
3
+ __all__ = ["get_tool_main_template"]
@@ -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
+ ''')
@@ -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")
@@ -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")
@@ -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 envvars by settings.toml
121
- If there are remaining envvars, Hyperpocket checks the `settings.toml` from the agent code directory.
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.1
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
- - [x] OAuth
197
- - [x] Token
198
- - [ ] Basic Auth (Username, Password)
196
+ - [x] OAuth
197
+ - [x] Token
198
+ - [ ] Basic Auth (Username, Password)
199
199
 
200
200
  - Supported OAuth Providers
201
201
 
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
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
- - [x] Notion
218
- - [x] Slack
219
- - [x] Linear
220
- - [x] Gumloop
221
- - [x] Github
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
- - 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.
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
- - 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 `{WORKDIR}/settings.toml`
374
-
375
- ## Special thanks
376
-
377
- - [tott](https://x.com/tott____) for drawing the cute possum in a pocket.
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=zBuVvrp6SwGRGcfWy6mqIXabw-bfahGfsxpBi899RHs,503
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=68fOHIx2zw_k_ncR6PR8S9iZG_yQkPG7yAOj428Op_M,580
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=IsHQHiwPmO5afbmwkA1lA1P7zF7W6LmXpM4ZmUZ4c-E,795
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=QeR7mI8RD6OJ2AXdZCpzNLBW0KUVgYQ0IqCc-wVXgFs,3896
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.1.dist-info/METADATA,sha256=UEJCp-ThPh9eJOzGYyOulcb7QD7shB14LvzMm9tLnL4,11430
139
- hyperpocket-0.3.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
140
- hyperpocket-0.3.1.dist-info/entry_points.txt,sha256=KpBleaYr0SaENXOa-dFvJ_cvFCHYFEQ4LMl11ShAcBI,61
141
- hyperpocket-0.3.1.dist-info/RECORD,,
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,,