hyperpocket 0.3.7__py3-none-any.whl → 0.4.0__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/weaviate/context.py +12 -0
- hyperpocket/auth/weaviate/token_context.py +11 -0
- hyperpocket/auth/weaviate/token_handler.py +68 -0
- hyperpocket/auth/weaviate/token_schema.py +7 -0
- hyperpocket/cli/eject.py +2 -7
- hyperpocket/cli/pull.py +2 -7
- hyperpocket/config/settings.py +2 -1
- hyperpocket/pocket_core.py +41 -68
- hyperpocket/pocket_main.py +37 -16
- hyperpocket/repository/__init__.py +3 -4
- hyperpocket/repository/repository.py +6 -41
- hyperpocket/repository/tool_reference.py +28 -0
- hyperpocket/server/auth/weaviate.py +27 -0
- hyperpocket/server/server.py +127 -61
- hyperpocket/session/in_memory.py +13 -3
- hyperpocket/tool/__init__.py +0 -3
- hyperpocket/tool/dock/__init__.py +3 -0
- hyperpocket/tool/dock/dock.py +34 -0
- hyperpocket/tool/function/__init__.py +1 -1
- hyperpocket/tool/function/tool.py +62 -32
- hyperpocket/tool/tool.py +1 -9
- hyperpocket/tool_like.py +2 -1
- hyperpocket/util/generate_slug.py +4 -0
- hyperpocket/util/json_schema_to_model.py +5 -1
- {hyperpocket-0.3.7.dist-info → hyperpocket-0.4.0.dist-info}/METADATA +4 -1
- {hyperpocket-0.3.7.dist-info → hyperpocket-0.4.0.dist-info}/RECORD +30 -36
- hyperpocket/cli/sync.py +0 -17
- hyperpocket/repository/lock.py +0 -240
- hyperpocket/repository/lockfile.py +0 -62
- hyperpocket/server/tool/__init__.py +0 -10
- hyperpocket/server/tool/dto/script.py +0 -33
- hyperpocket/server/tool/wasm.py +0 -46
- hyperpocket/tool/wasm/README.md +0 -166
- hyperpocket/tool/wasm/__init__.py +0 -3
- hyperpocket/tool/wasm/browser.py +0 -63
- hyperpocket/tool/wasm/invoker.py +0 -41
- hyperpocket/tool/wasm/script.py +0 -134
- hyperpocket/tool/wasm/templates/__init__.py +0 -35
- hyperpocket/tool/wasm/templates/node.py +0 -87
- hyperpocket/tool/wasm/templates/python.py +0 -93
- hyperpocket/tool/wasm/tool.py +0 -163
- /hyperpocket/{server/tool/dto → auth/weaviate}/__init__.py +0 -0
- {hyperpocket-0.3.7.dist-info → hyperpocket-0.4.0.dist-info}/WHEEL +0 -0
- {hyperpocket-0.3.7.dist-info → hyperpocket-0.4.0.dist-info}/entry_points.txt +0 -0
hyperpocket/tool/wasm/README.md
DELETED
@@ -1,166 +0,0 @@
|
|
1
|
-
# Wasm(WebAssembly)
|
2
|
-
|
3
|
-
---
|
4
|
-
|
5
|
-
## How To Use
|
6
|
-
|
7
|
-
### plain url
|
8
|
-
|
9
|
-
load the tool by providing a GitHub URL
|
10
|
-
|
11
|
-
```python
|
12
|
-
from hyperpocket import Pocket
|
13
|
-
|
14
|
-
pocket = Pocket(tools=[
|
15
|
-
'https://github.com/your-organization/your-repository/tree/main',
|
16
|
-
])
|
17
|
-
```
|
18
|
-
|
19
|
-
- in this case, read tool code from `main` branch by default
|
20
|
-
|
21
|
-
### from_git
|
22
|
-
|
23
|
-
- using `from_git`, you can specify branch and tool code path
|
24
|
-
|
25
|
-
```python
|
26
|
-
from hyperpocket import Pocket
|
27
|
-
from hyperpocket.tool import from_git
|
28
|
-
|
29
|
-
pocket = Pocket(tools=[
|
30
|
-
from_git("https://github.com/your-organization/your-repository", "branch-name", "your/tool/code/path"),
|
31
|
-
])
|
32
|
-
```
|
33
|
-
|
34
|
-
### from_local
|
35
|
-
|
36
|
-
- using `from_local`, you can load a tool by specifying a local path
|
37
|
-
|
38
|
-
```python
|
39
|
-
from hyperpocket import Pocket
|
40
|
-
from hyperpocket.tool import from_local
|
41
|
-
|
42
|
-
pocket = Pocket(tools=[
|
43
|
-
from_local("your/local/tool/code/path")
|
44
|
-
])
|
45
|
-
```
|
46
|
-
|
47
|
-
## WasmToolRequest
|
48
|
-
|
49
|
-
The class to initiate WasmTool
|
50
|
-
|
51
|
-
WasmToolRequest offer those build method:
|
52
|
-
|
53
|
-
- `from_local`
|
54
|
-
- `from_git`
|
55
|
-
|
56
|
-
Each `WasmToolRequest` has it's own `Lock` object
|
57
|
-
|
58
|
-
Those `Lock` object is managed by `Lockfile` in the `Pocket` class
|
59
|
-
|
60
|
-
```mermaid
|
61
|
-
classDiagram
|
62
|
-
class WasmToolRequest {
|
63
|
-
+Lock lock
|
64
|
-
+ from_git()
|
65
|
-
+ from_local()
|
66
|
-
}
|
67
|
-
|
68
|
-
class Lock{
|
69
|
-
+key()
|
70
|
-
+sync()
|
71
|
-
}
|
72
|
-
|
73
|
-
class GitLock{
|
74
|
-
+key()
|
75
|
-
+sync()
|
76
|
-
}
|
77
|
-
|
78
|
-
class LocalLock{
|
79
|
-
+key()
|
80
|
-
+sync()
|
81
|
-
}
|
82
|
-
|
83
|
-
class Lockfile {
|
84
|
-
+dict[tuple,Lock] locks
|
85
|
-
|
86
|
-
+ add_lock()
|
87
|
-
+ get_lock()
|
88
|
-
+ sync()
|
89
|
-
+ write()
|
90
|
-
}
|
91
|
-
|
92
|
-
ToolRequest <|.. WasmToolRequest : Implementation
|
93
|
-
Lock o-- WasmToolRequest : 1..1
|
94
|
-
Lock <|.. GitLock : Implementation
|
95
|
-
Lock <|.. LocalLock : Implementation
|
96
|
-
Lock o-- Lockfile : n..1
|
97
|
-
Lockfile o-- Pocket : 1..1
|
98
|
-
```
|
99
|
-
|
100
|
-
### Inject tool variables
|
101
|
-
|
102
|
-
If the user specifies [tool_vars] in the `config.toml` of the tool's repository, which is allowed to be injected dynamically when the user develops an agent, it can be injected through the following steps.
|
103
|
-
|
104
|
-
```toml
|
105
|
-
# config.toml of a tool
|
106
|
-
|
107
|
-
[tool_vars]
|
108
|
-
config1 = "config1"
|
109
|
-
config2 = "config2"
|
110
|
-
```
|
111
|
-
|
112
|
-
1. Injecting tool_vars when importing tool in code.
|
113
|
-
|
114
|
-
```python
|
115
|
-
from_git('https://github.com/your-organization/your-repository/tree/main',tool_vars = {
|
116
|
-
"config1": "modified_config1"
|
117
|
-
})
|
118
|
-
```
|
119
|
-
|
120
|
-
2. Injecting tool_vars by settings.toml
|
121
|
-
Hyperpocket checks the `settings.toml` from the agent code directory.
|
122
|
-
|
123
|
-
## WasmTool
|
124
|
-
|
125
|
-
```python
|
126
|
-
class WasmTool(Tool):
|
127
|
-
_invoker: WasmInvoker = None
|
128
|
-
pkg_lock: Lock = None
|
129
|
-
rel_path: str
|
130
|
-
runtime: ScriptRuntime = None
|
131
|
-
json_schema: Optional[dict] = None
|
132
|
-
readme: Optional[str] = None
|
133
|
-
```
|
134
|
-
|
135
|
-
- `_invoker`: A class for executing WASM.
|
136
|
-
- `pkg_lock`: The lock class for the tool.
|
137
|
-
- Used for determining the package path where the current WASM code is stored.
|
138
|
-
- `rel_path`: The relative path to the location of the WASM code within the package.
|
139
|
-
- `runtime`: The runtime language of the WASM code.
|
140
|
-
- `json_schema`: The JSON schema for the WASM tool.
|
141
|
-
- Information read from schema.json.
|
142
|
-
- `readme`: The README information.
|
143
|
-
|
144
|
-
## WasmInvoker
|
145
|
-
|
146
|
-
A class for running actual WASM
|
147
|
-
|
148
|
-
1. Combines runtime information, authentication details, and body content to render HTML.
|
149
|
-
2. Stores the rendered HTML in memory.
|
150
|
-
3. Launches a browser to execute the WASM.
|
151
|
-
4. The browser sends a request to the server to retrieve the rendered HTML.
|
152
|
-
5. Executes code specific to each runtime within the HTML and returns the result.
|
153
|
-
|
154
|
-
```mermaid
|
155
|
-
sequenceDiagram
|
156
|
-
participant WasmTool as WasmTool (Includes Server)
|
157
|
-
participant Browser as Browser (Executes WASM Runtime)
|
158
|
-
|
159
|
-
|
160
|
-
WasmTool->>WasmTool: Render HTML and Store in Memory
|
161
|
-
WasmTool->>Browser: Launch Browser
|
162
|
-
Browser->>WasmTool: Request Rendered HTML
|
163
|
-
WasmTool->>Browser: Provide Rendered HTML
|
164
|
-
Browser->>Browser: Execute Injected WASM Runtime Script
|
165
|
-
Browser->>WasmTool: Return Execution Result
|
166
|
-
```
|
hyperpocket/tool/wasm/browser.py
DELETED
@@ -1,63 +0,0 @@
|
|
1
|
-
import asyncio
|
2
|
-
|
3
|
-
from playwright.async_api import (
|
4
|
-
BrowserContext,
|
5
|
-
Page,
|
6
|
-
Playwright,
|
7
|
-
Route,
|
8
|
-
async_playwright,
|
9
|
-
)
|
10
|
-
|
11
|
-
|
12
|
-
class InvokerBrowser(object):
|
13
|
-
_instance: "InvokerBrowser" = None
|
14
|
-
_lock = asyncio.Lock()
|
15
|
-
playwright: Playwright
|
16
|
-
browser_context: BrowserContext
|
17
|
-
|
18
|
-
def __init__(self):
|
19
|
-
raise RuntimeError("Use InvokerBrowser.get_instance() instead")
|
20
|
-
|
21
|
-
async def _async_init(self):
|
22
|
-
self.playwright = await async_playwright().start()
|
23
|
-
self.browser_context = await self.playwright.chromium.launch_persistent_context(
|
24
|
-
headless=True,
|
25
|
-
args=[
|
26
|
-
"--disable-web-security=True",
|
27
|
-
],
|
28
|
-
user_data_dir="/tmp/chrome_dev_user",
|
29
|
-
)
|
30
|
-
|
31
|
-
@classmethod
|
32
|
-
async def get_instance(cls):
|
33
|
-
if not cls._instance:
|
34
|
-
async with cls._lock:
|
35
|
-
if cls._instance is None:
|
36
|
-
instance = cls.__new__(cls)
|
37
|
-
await instance._async_init()
|
38
|
-
cls._instance = instance
|
39
|
-
return cls._instance
|
40
|
-
|
41
|
-
async def new_page(self) -> Page:
|
42
|
-
page = await self.browser_context.new_page()
|
43
|
-
|
44
|
-
async def _hijack_route(route: Route):
|
45
|
-
response = await route.fetch()
|
46
|
-
body = await response.body()
|
47
|
-
await route.fulfill(
|
48
|
-
response=response,
|
49
|
-
body=body,
|
50
|
-
headers={
|
51
|
-
**response.headers,
|
52
|
-
"Cross-Origin-Opener-Policy": "same-origin",
|
53
|
-
"Cross-Origin-Embedder-Policy": "require-corp",
|
54
|
-
"Cross-Origin-Resource-Policy": "cross-origin",
|
55
|
-
},
|
56
|
-
)
|
57
|
-
|
58
|
-
await page.route("**/*", _hijack_route)
|
59
|
-
return page
|
60
|
-
|
61
|
-
async def teardown(self):
|
62
|
-
await self.browser_context.close()
|
63
|
-
await self.playwright.stop()
|
hyperpocket/tool/wasm/invoker.py
DELETED
@@ -1,41 +0,0 @@
|
|
1
|
-
import asyncio
|
2
|
-
import json
|
3
|
-
import uuid
|
4
|
-
from typing import Any
|
5
|
-
from urllib.parse import urljoin
|
6
|
-
|
7
|
-
from hyperpocket.config import config
|
8
|
-
from hyperpocket.futures import FutureStore
|
9
|
-
from hyperpocket.tool.wasm.browser import InvokerBrowser
|
10
|
-
from hyperpocket.tool.wasm.script import Script, ScriptRuntime, ScriptStore
|
11
|
-
from hyperpocket.tool.wasm.templates import render
|
12
|
-
|
13
|
-
|
14
|
-
class WasmInvoker(object):
|
15
|
-
def invoke(
|
16
|
-
self, tool_path: str, runtime: ScriptRuntime, body: Any, envs: dict, **kwargs
|
17
|
-
) -> str:
|
18
|
-
loop = asyncio.get_running_loop()
|
19
|
-
return loop.run_until_complete(
|
20
|
-
self.ainvoke(tool_path, runtime, body, envs, **kwargs)
|
21
|
-
)
|
22
|
-
|
23
|
-
async def ainvoke(
|
24
|
-
self, tool_path: str, runtime: ScriptRuntime, body: Any, envs: dict, **kwargs
|
25
|
-
) -> str:
|
26
|
-
uid = str(uuid.uuid4())
|
27
|
-
html = render(runtime.value, uid, envs, json.dumps(body))
|
28
|
-
script = Script(
|
29
|
-
id=uid, tool_path=tool_path, rendered_html=html, runtime=runtime
|
30
|
-
)
|
31
|
-
ScriptStore.add_script(script=script)
|
32
|
-
future_data = FutureStore.create_future(uid=uid)
|
33
|
-
browser = await InvokerBrowser.get_instance()
|
34
|
-
page = await browser.new_page()
|
35
|
-
url = urljoin(
|
36
|
-
config().internal_base_url + "/", f"tools/wasm/scripts/{uid}/browse"
|
37
|
-
)
|
38
|
-
await page.goto(url)
|
39
|
-
stdout = await future_data.future
|
40
|
-
await page.close()
|
41
|
-
return stdout
|
hyperpocket/tool/wasm/script.py
DELETED
@@ -1,134 +0,0 @@
|
|
1
|
-
import base64
|
2
|
-
import enum
|
3
|
-
import pathlib
|
4
|
-
from typing import Optional
|
5
|
-
|
6
|
-
import toml
|
7
|
-
from pydantic import BaseModel
|
8
|
-
|
9
|
-
from hyperpocket.config import pocket_logger
|
10
|
-
|
11
|
-
|
12
|
-
class ScriptRuntime(enum.Enum):
|
13
|
-
Node = "node"
|
14
|
-
Python = "python"
|
15
|
-
Wasm = "wasm"
|
16
|
-
|
17
|
-
|
18
|
-
_RuntimePackageFiles = {
|
19
|
-
ScriptRuntime.Node: ["dist/index.js"],
|
20
|
-
ScriptRuntime.Python: ["main.py", "requirements.txt"],
|
21
|
-
ScriptRuntime.Wasm: ["dist/index.wasm"],
|
22
|
-
}
|
23
|
-
|
24
|
-
|
25
|
-
class ScriptFileNodeContent(BaseModel):
|
26
|
-
contents: str
|
27
|
-
|
28
|
-
|
29
|
-
class ScriptFileNode(BaseModel):
|
30
|
-
directory: Optional[dict[str, "ScriptFileNode"]] = None
|
31
|
-
file: Optional[ScriptFileNodeContent] = None
|
32
|
-
|
33
|
-
@classmethod
|
34
|
-
def create_file_tree(cls, path: str, contents: str) -> dict[str, "ScriptFileNode"]:
|
35
|
-
path_split = path.split("/")
|
36
|
-
if len(path_split) == 1:
|
37
|
-
return {
|
38
|
-
path_split[0]: ScriptFileNode(
|
39
|
-
file=ScriptFileNodeContent(contents=contents)
|
40
|
-
)
|
41
|
-
}
|
42
|
-
node = cls.create_file_tree("/".join(path_split[1:]), contents)
|
43
|
-
return {path_split[0]: ScriptFileNode(directory=node)}
|
44
|
-
|
45
|
-
@staticmethod
|
46
|
-
def merge(
|
47
|
-
a: dict[str, "ScriptFileNode"], b: [str, "ScriptFileNode"]
|
48
|
-
) -> dict[str, "ScriptFileNode"]:
|
49
|
-
for k, v in b.items():
|
50
|
-
if k in a:
|
51
|
-
if a[k].directory and v.directory:
|
52
|
-
a[k].directory = ScriptFileNode.merge(a[k].directory, v.directory)
|
53
|
-
elif a[k].file and v.file:
|
54
|
-
a[k].file = v.file
|
55
|
-
else:
|
56
|
-
a[k] = v
|
57
|
-
return a
|
58
|
-
|
59
|
-
|
60
|
-
class Script(BaseModel):
|
61
|
-
id: str
|
62
|
-
tool_path: str
|
63
|
-
rendered_html: str
|
64
|
-
runtime: ScriptRuntime
|
65
|
-
|
66
|
-
def load_file_tree(self) -> dict[str, ScriptFileNode]:
|
67
|
-
relpaths = _RuntimePackageFiles[self.runtime]
|
68
|
-
file_tree = dict()
|
69
|
-
for p in relpaths:
|
70
|
-
filepath = pathlib.Path(self.tool_path) / p
|
71
|
-
with filepath.open("r") as f:
|
72
|
-
contents = f.read().encode("utf-8")
|
73
|
-
encoded_bytes = base64.b64encode(contents)
|
74
|
-
encoded_str = encoded_bytes.decode()
|
75
|
-
file_tree = ScriptFileNode.merge(
|
76
|
-
file_tree, ScriptFileNode.create_file_tree(p, encoded_str)
|
77
|
-
)
|
78
|
-
return file_tree
|
79
|
-
|
80
|
-
@property
|
81
|
-
def package_name(self) -> Optional[str]:
|
82
|
-
if self.runtime != ScriptRuntime.Python:
|
83
|
-
return
|
84
|
-
pyproject = toml.load(pathlib.Path(self.tool_path) / "pyproject.toml")
|
85
|
-
if "project" in pyproject:
|
86
|
-
name = pyproject["project"]["name"]
|
87
|
-
if "tool" in pyproject and "poetry" in pyproject["tool"]:
|
88
|
-
name = pyproject["tool"]["poetry"]["name"]
|
89
|
-
if not name:
|
90
|
-
raise ValueError("Could not find package name")
|
91
|
-
return name.replace("-", "_")
|
92
|
-
|
93
|
-
@property
|
94
|
-
def entrypoint(self) -> str:
|
95
|
-
pocket_logger.info(self.tool_path)
|
96
|
-
if self.runtime == ScriptRuntime.Node:
|
97
|
-
return "dist/index.js"
|
98
|
-
elif self.runtime == ScriptRuntime.Wasm:
|
99
|
-
return "dist/main.wasm"
|
100
|
-
pyproject = toml.load(pathlib.Path(self.tool_path) / "pyproject.toml")
|
101
|
-
version = None
|
102
|
-
if "project" in pyproject:
|
103
|
-
version = pyproject["project"]["version"]
|
104
|
-
elif "tool" in pyproject and "poetry" in pyproject["tool"]:
|
105
|
-
version = pyproject["tool"]["poetry"]["version"]
|
106
|
-
else:
|
107
|
-
raise ValueError("Could not find package version")
|
108
|
-
wheel_name = f"{self.package_name}-{version}-py3-none-any.whl"
|
109
|
-
wheel_path = pathlib.Path(self.tool_path) / "dist" / wheel_name
|
110
|
-
if not wheel_path.exists():
|
111
|
-
raise ValueError(f"Wheel file {wheel_path} does not exist")
|
112
|
-
return wheel_name
|
113
|
-
|
114
|
-
def dist_file_path(self, file_name: str) -> str:
|
115
|
-
return str(pathlib.Path(self.tool_path) / "dist" / file_name)
|
116
|
-
|
117
|
-
|
118
|
-
class _ScriptStore(object):
|
119
|
-
scripts: dict[str, Script] = {}
|
120
|
-
|
121
|
-
def __init__(self):
|
122
|
-
self.rendered_html = {}
|
123
|
-
|
124
|
-
def add_script(self, script: Script):
|
125
|
-
if script.id in self.scripts:
|
126
|
-
raise ValueError("Script id already exists")
|
127
|
-
self.scripts[script.id] = script
|
128
|
-
|
129
|
-
def get_script(self, script_id: str) -> Script:
|
130
|
-
# ValueError exception is intentional
|
131
|
-
return self.scripts[script_id]
|
132
|
-
|
133
|
-
|
134
|
-
ScriptStore = _ScriptStore()
|
@@ -1,35 +0,0 @@
|
|
1
|
-
import base64
|
2
|
-
import json
|
3
|
-
|
4
|
-
from jinja2 import DictLoader, Environment
|
5
|
-
|
6
|
-
from hyperpocket.tool.wasm.templates.node import node_template
|
7
|
-
from hyperpocket.tool.wasm.templates.python import python_template
|
8
|
-
|
9
|
-
TemplateEnvironments = Environment(
|
10
|
-
loader=DictLoader(
|
11
|
-
{
|
12
|
-
"python.html": python_template,
|
13
|
-
"node.html": node_template,
|
14
|
-
}
|
15
|
-
),
|
16
|
-
autoescape=False,
|
17
|
-
)
|
18
|
-
|
19
|
-
|
20
|
-
def render(
|
21
|
-
language: str, script_id: str, env: dict[str, str], body: str, **kwargs
|
22
|
-
) -> str:
|
23
|
-
env_json = json.dumps(env)
|
24
|
-
template = TemplateEnvironments.get_template(f"{language.lower()}.html")
|
25
|
-
body_bytes = body.encode("utf-8")
|
26
|
-
body_b64_bytes = base64.b64encode(body_bytes)
|
27
|
-
body_b64 = body_b64_bytes.decode("ascii")
|
28
|
-
return template.render(
|
29
|
-
**{
|
30
|
-
"SCRIPT_ID": script_id,
|
31
|
-
"ENV_JSON": env_json,
|
32
|
-
"BODY_JSON_B64": body_b64,
|
33
|
-
}
|
34
|
-
| kwargs
|
35
|
-
)
|
@@ -1,87 +0,0 @@
|
|
1
|
-
node_template = """
|
2
|
-
<!DOCTYPE html>
|
3
|
-
<html lang="en">
|
4
|
-
<head>
|
5
|
-
<meta charset="UTF-8">
|
6
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
7
|
-
<title>PyScript Offline</title>
|
8
|
-
</head>
|
9
|
-
<body>
|
10
|
-
<script type="module">
|
11
|
-
function loadConfig() {
|
12
|
-
globalThis.toolConfigs = {
|
13
|
-
envs: `{{ ENV_JSON }}`,
|
14
|
-
body: `{{ BODY_JSON_B64 }}`,
|
15
|
-
scriptID: `{{ SCRIPT_ID }}`
|
16
|
-
}
|
17
|
-
}
|
18
|
-
import { WebContainer } from 'https://esm.run/@webcontainer/api@1.5.1';
|
19
|
-
function decodeContent(content) {
|
20
|
-
return Uint8Array.from(atob(content), c => c.charCodeAt(0));
|
21
|
-
}
|
22
|
-
function decodeFileTree(filetree) {
|
23
|
-
const decoded = {};
|
24
|
-
for (const [key, value] of Object.entries(filetree)) {
|
25
|
-
if (value.file) {
|
26
|
-
decoded[key] = {
|
27
|
-
file: {
|
28
|
-
contents: decodeContent(value.file.contents)
|
29
|
-
}
|
30
|
-
}
|
31
|
-
} else if (value.directory) {
|
32
|
-
decoded[key] = {
|
33
|
-
directory: decodeFileTree(value.directory)
|
34
|
-
}
|
35
|
-
} else if (value.symlink) {
|
36
|
-
decoded[key] = {
|
37
|
-
symlink: value.symlink
|
38
|
-
}
|
39
|
-
}
|
40
|
-
}
|
41
|
-
return decoded;
|
42
|
-
}
|
43
|
-
async function main() {
|
44
|
-
loadConfig();
|
45
|
-
const b64FilesResp = await fetch(`/tools/wasm/scripts/${globalThis.toolConfigs.scriptID}/file_tree`);
|
46
|
-
const b64Files = await b64FilesResp.json();
|
47
|
-
const files = decodeFileTree(b64Files.tree);
|
48
|
-
const webcontainer = await WebContainer.boot();
|
49
|
-
|
50
|
-
await webcontainer.mount(files)
|
51
|
-
const envs = JSON.parse(globalThis.toolConfigs.envs)
|
52
|
-
envs['DEPLOYED'] = 'true'
|
53
|
-
const runProcess = await webcontainer.spawn('node', ['dist/index.js'], {
|
54
|
-
output: true,
|
55
|
-
env: envs,
|
56
|
-
});
|
57
|
-
const stdin = runProcess.input.getWriter();
|
58
|
-
const decodedBytes = atob(globalThis.toolConfigs.body);
|
59
|
-
await (async () => {
|
60
|
-
await stdin.ready
|
61
|
-
await stdin.write(decodedBytes);
|
62
|
-
})()
|
63
|
-
let stdout = '';
|
64
|
-
runProcess.output.pipeTo(
|
65
|
-
new WritableStream({
|
66
|
-
write(chunk) {
|
67
|
-
stdout += chunk;
|
68
|
-
}
|
69
|
-
})
|
70
|
-
)
|
71
|
-
await runProcess.exit;
|
72
|
-
if (stdout.startsWith(decodedBytes)) {
|
73
|
-
stdout = stdout.slice(decodedBytes);
|
74
|
-
}
|
75
|
-
await fetch(`/tools/wasm/scripts/${globalThis.toolConfigs.scriptID}/done`, {
|
76
|
-
method: 'POST',
|
77
|
-
headers: {
|
78
|
-
'Content-Type': 'application/json'
|
79
|
-
},
|
80
|
-
body: JSON.stringify({ stdout })
|
81
|
-
});
|
82
|
-
}
|
83
|
-
main();
|
84
|
-
</script>
|
85
|
-
</body>
|
86
|
-
</html>
|
87
|
-
"""
|
@@ -1,93 +0,0 @@
|
|
1
|
-
python_template = """
|
2
|
-
<!DOCTYPE html>
|
3
|
-
<html lang="en">
|
4
|
-
<head>
|
5
|
-
<meta charset="UTF-8">
|
6
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
7
|
-
<title>PyScript Offline</title>
|
8
|
-
<script src="https://cdn.jsdelivr.net/pyodide/v0.26.4/full/pyodide.js"></script>
|
9
|
-
</head>
|
10
|
-
<body>
|
11
|
-
<script type="module">
|
12
|
-
function loadConfig() {
|
13
|
-
globalThis.toolConfigs = {
|
14
|
-
envs: `{{ ENV_JSON }}`,
|
15
|
-
body: `{{ BODY_JSON_B64 }}`,
|
16
|
-
scriptID: `{{ SCRIPT_ID }}`
|
17
|
-
}
|
18
|
-
}
|
19
|
-
async function _main() {
|
20
|
-
// load the script configs
|
21
|
-
loadConfig();
|
22
|
-
|
23
|
-
// get entrypoint wheel
|
24
|
-
const entrypointResp = await fetch(`/tools/wasm/scripts/${globalThis.toolConfigs.scriptID}/entrypoint`);
|
25
|
-
const { package_name: packageName, entrypoint } = await entrypointResp.json();
|
26
|
-
|
27
|
-
// initialize pyodide
|
28
|
-
const pyodide = await loadPyodide({
|
29
|
-
env: JSON.parse(globalThis.toolConfigs.envs),
|
30
|
-
});
|
31
|
-
await pyodide.loadPackage("micropip");
|
32
|
-
await pyodide.loadPackage("ssl");
|
33
|
-
const micropip = pyodide.pyimport("micropip");
|
34
|
-
await micropip.install(entrypoint);
|
35
|
-
await micropip.install("pyodide-http")
|
36
|
-
|
37
|
-
let emitted = false;
|
38
|
-
const decodedBytes = atob(globalThis.toolConfigs.body);
|
39
|
-
pyodide.setStdin({
|
40
|
-
stdin: () => {
|
41
|
-
if (emitted) {
|
42
|
-
return null;
|
43
|
-
}
|
44
|
-
emitted = true;
|
45
|
-
return decodedBytes;
|
46
|
-
},
|
47
|
-
autoEOF: true,
|
48
|
-
})
|
49
|
-
let stdout = "";
|
50
|
-
let stderr = "";
|
51
|
-
pyodide.setStdout({
|
52
|
-
batched: (x) => { stdout += x; },
|
53
|
-
})
|
54
|
-
pyodide.setStderr({
|
55
|
-
batched: (x) => { stderr += x; },
|
56
|
-
})
|
57
|
-
await pyodide.runPythonAsync(`
|
58
|
-
import pyodide_http
|
59
|
-
pyodide_http.patch_all()
|
60
|
-
|
61
|
-
import ${packageName}
|
62
|
-
${packageName}.main()
|
63
|
-
`);
|
64
|
-
console.log(stdout)
|
65
|
-
await fetch(`/tools/wasm/scripts/${globalThis.toolConfigs.scriptID}/done`, {
|
66
|
-
method: 'POST',
|
67
|
-
headers: {
|
68
|
-
'Content-Type': 'application/json'
|
69
|
-
},
|
70
|
-
body: JSON.stringify({ stdout, stderr })
|
71
|
-
});
|
72
|
-
}
|
73
|
-
|
74
|
-
async function main() {
|
75
|
-
try {
|
76
|
-
await _main();
|
77
|
-
} catch (e) {
|
78
|
-
console.error(e);
|
79
|
-
await fetch(`/tools/wasm/scripts/${globalThis.toolConfigs.scriptID}/done`, {
|
80
|
-
method: 'POST',
|
81
|
-
headers: {
|
82
|
-
'Content-Type': 'application/json'
|
83
|
-
},
|
84
|
-
body: JSON.stringify({ error: e.message })
|
85
|
-
});
|
86
|
-
}
|
87
|
-
}
|
88
|
-
|
89
|
-
main();
|
90
|
-
</script>
|
91
|
-
</body>
|
92
|
-
</html>
|
93
|
-
"""
|