hyperpocket 0.3.1__py3-none-any.whl → 0.3.3__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,,