brickly-sdk 0.1.0__tar.gz
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.
- brickly_sdk-0.1.0/.gitignore +7 -0
- brickly_sdk-0.1.0/PKG-INFO +180 -0
- brickly_sdk-0.1.0/README.md +171 -0
- brickly_sdk-0.1.0/brickly/__init__.py +31 -0
- brickly_sdk-0.1.0/brickly/api.py +601 -0
- brickly_sdk-0.1.0/brickly/errors.py +36 -0
- brickly_sdk-0.1.0/brickly/protocol.py +44 -0
- brickly_sdk-0.1.0/brickly/runtime.py +247 -0
- brickly_sdk-0.1.0/brickly/system.py +100 -0
- brickly_sdk-0.1.0/pyproject.toml +16 -0
- brickly_sdk-0.1.0/tests/test_sdk.py +521 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: brickly-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python SDK for Brickly brick runtimes.
|
|
5
|
+
Author: Brickly
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
|
|
10
|
+
# brickly-sdk-python
|
|
11
|
+
|
|
12
|
+
Python SDK for Brickly brick runtimes. It speaks BPP over stdin/stdout and keeps stdout reserved for protocol messages.
|
|
13
|
+
|
|
14
|
+
## Quick Start
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
from brickly import BricklyRuntime
|
|
18
|
+
|
|
19
|
+
brick = BricklyRuntime("com.example.python")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@brick.on_command("hello")
|
|
23
|
+
def hello(ctx, input):
|
|
24
|
+
ctx.progress(0.5, "working...")
|
|
25
|
+
ctx.chunk("hello\n")
|
|
26
|
+
return {"ok": True, "input": input}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
brick.run()
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The SDK handles:
|
|
33
|
+
|
|
34
|
+
- `host.hello` -> `runtime.ready`
|
|
35
|
+
- `runtime.ping` -> `runtime.pong`
|
|
36
|
+
- `command.invoke` dispatch
|
|
37
|
+
- `command.cancel` via `ctx.cancel_event`, `ctx.is_cancelled()`, and `ctx.on_cancel(...)`
|
|
38
|
+
- `command.progress`, `command.chunk`, and `command.output`
|
|
39
|
+
- `host.*` request id allocation and `host.result` / `host.error` routing
|
|
40
|
+
- `runtime.shutdown` -> optional shutdown hook -> `runtime.bye`
|
|
41
|
+
- `ui.create_browser_window`, `WindowHandle.call(...)`, and event routing for `window.*`
|
|
42
|
+
- `events.publish(...)` and `events.on(...)`
|
|
43
|
+
- `brick.platform.system.*` / `ctx.platform.system.*`, plus `brick.system.*` / `ctx.system.*` aliases
|
|
44
|
+
- `brick.invoke(...)` / `ctx.invoke(...)` for child cross-brick command calls inside command scope
|
|
45
|
+
- `brick.invoke_root(...)` for root cross-brick command calls outside command scope
|
|
46
|
+
- `brick.invoke_stream(...)` / `ctx.invoke_stream(...)` for streaming child cross-brick command calls
|
|
47
|
+
- `brick.open_session(...)` / `ctx.open_session(...)` for stateful cross-brick sessions
|
|
48
|
+
- `brick.platform.clipboard.read_content()` / `set_content(...)`
|
|
49
|
+
|
|
50
|
+
## Logging
|
|
51
|
+
|
|
52
|
+
`brick.log(...)` writes plain messages to stderr. Stdout is reserved for BPP protocol messages. The Brickly host log center attaches the brick id, source, stream, and scope metadata when collecting stderr, so brick code should not add a `[brickId]` prefix by itself.
|
|
53
|
+
|
|
54
|
+
## Window Example
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
from brickly import BricklyRuntime
|
|
58
|
+
|
|
59
|
+
brick = BricklyRuntime("com.example.window")
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@brick.on_command("open")
|
|
63
|
+
def open_window(ctx, input):
|
|
64
|
+
win = ctx.ui.create_browser_window("ui/index.html", {"width": 640, "height": 480})
|
|
65
|
+
win.on("closed", lambda payload: brick.log("closed", payload))
|
|
66
|
+
win.set_title("Hello from Python")
|
|
67
|
+
return {"windowId": win.id}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
brick.run()
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Cross-Brick Invoke
|
|
74
|
+
|
|
75
|
+
Inside a command handler, use `ctx.invoke` to call another brick command. The SDK automatically sends the current `parentRequestId`, so the host attaches the child call to the same invocation graph. Brickly starts, reuses, and recycles the target brick instance automatically. Pass `profile_id` when the target brick should run with a specific Profile; omit it to use the target brick's default Profile.
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
result = ctx.invoke(
|
|
79
|
+
"com.brickly.openai",
|
|
80
|
+
"chat",
|
|
81
|
+
{"prompt": "hello"},
|
|
82
|
+
profile_id="work",
|
|
83
|
+
)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Use `invoke_stream` when the target command emits progress, chunks, or named outputs before its final result. The SDK sends `host.invoke` with `stream: true` and yields events in host order:
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
for event in ctx.invoke_stream("com.brickly.openai", "chat", {"prompt": "hello"}):
|
|
90
|
+
if event["type"] == "progress":
|
|
91
|
+
ctx.progress(event.get("progress", 0), event.get("message"))
|
|
92
|
+
elif event["type"] == "chunk":
|
|
93
|
+
ctx.chunk(event.get("chunk"))
|
|
94
|
+
elif event["type"] == "output":
|
|
95
|
+
ctx.output(event["name"], event.get("value"))
|
|
96
|
+
elif event["type"] == "result":
|
|
97
|
+
return event["result"]
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
If the host returns `host.error`, `invoke_stream` raises `BppError`, matching normal `invoke` error handling.
|
|
101
|
+
|
|
102
|
+
To create a top-level cross-brick call outside command scope, use the explicit root API:
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
result = brick.invoke_root(
|
|
106
|
+
"com.brickly.openai",
|
|
107
|
+
"chat",
|
|
108
|
+
{"prompt": "hello"},
|
|
109
|
+
profile_id="work",
|
|
110
|
+
)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
The caller manifest must declare the target brick and allowed commands in `dependencies`:
|
|
114
|
+
|
|
115
|
+
```json
|
|
116
|
+
"dependencies": {
|
|
117
|
+
"com.brickly.openai": {
|
|
118
|
+
"commands": ["chat"]
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Platform System API
|
|
124
|
+
|
|
125
|
+
`brick.platform.system.*`, `brick.system.*`, `ctx.platform.system.*`, and `ctx.system.*` call host system capabilities through BPP `host.platform.system.*`:
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
@brick.on_command("show-app-info")
|
|
129
|
+
def show_app_info(ctx, _input):
|
|
130
|
+
return {
|
|
131
|
+
"appName": ctx.system.get_app_name(),
|
|
132
|
+
"appVersion": ctx.system.get_app_version(),
|
|
133
|
+
"userData": ctx.system.get_path("userData"),
|
|
134
|
+
"isMacOS": ctx.system.is_macos(),
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Current methods are `show_notification`, `shell_open_path`, `shell_trash_item`, `shell_show_item_in_folder`, `shell_open_external`, `shell_beep`, `get_native_id`, `get_app_name`, `get_app_version`, `get_path`, `get_file_icon`, `read_current_folder_path`, `read_current_browser_url`, `is_dev`, `is_macos`, `is_windows`, and `is_linux`.
|
|
139
|
+
|
|
140
|
+
`get_path()` supports `home`, `appData`, `assets`, `userData`, `sessionData`, `temp`, `exe`, `module`, `desktop`, `documents`, `downloads`, `music`, `pictures`, `videos`, `recent`, `logs`, and `crashDumps`. Runtime calls are still checked by manifest permissions on the host side: notifications require `os.notification`; Shell capabilities require `os.exec`; app info, paths, native ID, platform checks, and current folder path reads require `os.env`; file icons require `fs.read`. `read_current_folder_path()` works when Finder is frontmost on macOS or Explorer is frontmost on Windows; it raises `CURRENT_FOLDER_UNAVAILABLE` when no readable foreground file-manager folder is available. `read_current_browser_url()` is reserved and currently returns `UNSUPPORTED_PLATFORM`.
|
|
141
|
+
|
|
142
|
+
## Platform Clipboard API
|
|
143
|
+
|
|
144
|
+
`brick.platform.clipboard.*` and `ctx.platform.clipboard.*` call host clipboard capabilities through BPP `host.platform.clipboard.*`:
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
@brick.on_command("replace-clipboard")
|
|
148
|
+
def replace_clipboard(ctx, _input):
|
|
149
|
+
previous = ctx.platform.clipboard.read_content()
|
|
150
|
+
updated = ctx.platform.clipboard.set_content({"kind": "text", "text": "Updated by Python"})
|
|
151
|
+
return {"previous": previous, "updated": updated}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Current methods are `read_content()` and `set_content(content)`.
|
|
155
|
+
|
|
156
|
+
## Cross-Brick Sessions
|
|
157
|
+
|
|
158
|
+
Use `ctx.open_session` when the target brick keeps in-memory state that must survive across multiple command calls. Calls through the same session are routed to the same target brick instance, and each `session.invoke` automatically carries the current `parentRequestId`, until `close()` is called or the caller brick instance exits.
|
|
159
|
+
|
|
160
|
+
```python
|
|
161
|
+
session = ctx.open_session("com.brickly.openai", profile_id="work")
|
|
162
|
+
|
|
163
|
+
try:
|
|
164
|
+
session.invoke("start-thread", {"title": "Draft"})
|
|
165
|
+
reply = session.invoke("chat", {"prompt": "continue the thread"})
|
|
166
|
+
finally:
|
|
167
|
+
session.close()
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
`profile_id` is the target brick Profile ID. Session invokes are checked against the caller manifest's `dependencies[target].commands` on every command call.
|
|
171
|
+
|
|
172
|
+
## Errors
|
|
173
|
+
|
|
174
|
+
Raise `BppError` to preserve a specific Brickly error code:
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
from brickly import BppError
|
|
178
|
+
|
|
179
|
+
raise BppError("INVALID_INPUT", "url is required")
|
|
180
|
+
```
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# brickly-sdk-python
|
|
2
|
+
|
|
3
|
+
Python SDK for Brickly brick runtimes. It speaks BPP over stdin/stdout and keeps stdout reserved for protocol messages.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```python
|
|
8
|
+
from brickly import BricklyRuntime
|
|
9
|
+
|
|
10
|
+
brick = BricklyRuntime("com.example.python")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@brick.on_command("hello")
|
|
14
|
+
def hello(ctx, input):
|
|
15
|
+
ctx.progress(0.5, "working...")
|
|
16
|
+
ctx.chunk("hello\n")
|
|
17
|
+
return {"ok": True, "input": input}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
brick.run()
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The SDK handles:
|
|
24
|
+
|
|
25
|
+
- `host.hello` -> `runtime.ready`
|
|
26
|
+
- `runtime.ping` -> `runtime.pong`
|
|
27
|
+
- `command.invoke` dispatch
|
|
28
|
+
- `command.cancel` via `ctx.cancel_event`, `ctx.is_cancelled()`, and `ctx.on_cancel(...)`
|
|
29
|
+
- `command.progress`, `command.chunk`, and `command.output`
|
|
30
|
+
- `host.*` request id allocation and `host.result` / `host.error` routing
|
|
31
|
+
- `runtime.shutdown` -> optional shutdown hook -> `runtime.bye`
|
|
32
|
+
- `ui.create_browser_window`, `WindowHandle.call(...)`, and event routing for `window.*`
|
|
33
|
+
- `events.publish(...)` and `events.on(...)`
|
|
34
|
+
- `brick.platform.system.*` / `ctx.platform.system.*`, plus `brick.system.*` / `ctx.system.*` aliases
|
|
35
|
+
- `brick.invoke(...)` / `ctx.invoke(...)` for child cross-brick command calls inside command scope
|
|
36
|
+
- `brick.invoke_root(...)` for root cross-brick command calls outside command scope
|
|
37
|
+
- `brick.invoke_stream(...)` / `ctx.invoke_stream(...)` for streaming child cross-brick command calls
|
|
38
|
+
- `brick.open_session(...)` / `ctx.open_session(...)` for stateful cross-brick sessions
|
|
39
|
+
- `brick.platform.clipboard.read_content()` / `set_content(...)`
|
|
40
|
+
|
|
41
|
+
## Logging
|
|
42
|
+
|
|
43
|
+
`brick.log(...)` writes plain messages to stderr. Stdout is reserved for BPP protocol messages. The Brickly host log center attaches the brick id, source, stream, and scope metadata when collecting stderr, so brick code should not add a `[brickId]` prefix by itself.
|
|
44
|
+
|
|
45
|
+
## Window Example
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
from brickly import BricklyRuntime
|
|
49
|
+
|
|
50
|
+
brick = BricklyRuntime("com.example.window")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@brick.on_command("open")
|
|
54
|
+
def open_window(ctx, input):
|
|
55
|
+
win = ctx.ui.create_browser_window("ui/index.html", {"width": 640, "height": 480})
|
|
56
|
+
win.on("closed", lambda payload: brick.log("closed", payload))
|
|
57
|
+
win.set_title("Hello from Python")
|
|
58
|
+
return {"windowId": win.id}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
brick.run()
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Cross-Brick Invoke
|
|
65
|
+
|
|
66
|
+
Inside a command handler, use `ctx.invoke` to call another brick command. The SDK automatically sends the current `parentRequestId`, so the host attaches the child call to the same invocation graph. Brickly starts, reuses, and recycles the target brick instance automatically. Pass `profile_id` when the target brick should run with a specific Profile; omit it to use the target brick's default Profile.
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
result = ctx.invoke(
|
|
70
|
+
"com.brickly.openai",
|
|
71
|
+
"chat",
|
|
72
|
+
{"prompt": "hello"},
|
|
73
|
+
profile_id="work",
|
|
74
|
+
)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Use `invoke_stream` when the target command emits progress, chunks, or named outputs before its final result. The SDK sends `host.invoke` with `stream: true` and yields events in host order:
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
for event in ctx.invoke_stream("com.brickly.openai", "chat", {"prompt": "hello"}):
|
|
81
|
+
if event["type"] == "progress":
|
|
82
|
+
ctx.progress(event.get("progress", 0), event.get("message"))
|
|
83
|
+
elif event["type"] == "chunk":
|
|
84
|
+
ctx.chunk(event.get("chunk"))
|
|
85
|
+
elif event["type"] == "output":
|
|
86
|
+
ctx.output(event["name"], event.get("value"))
|
|
87
|
+
elif event["type"] == "result":
|
|
88
|
+
return event["result"]
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
If the host returns `host.error`, `invoke_stream` raises `BppError`, matching normal `invoke` error handling.
|
|
92
|
+
|
|
93
|
+
To create a top-level cross-brick call outside command scope, use the explicit root API:
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
result = brick.invoke_root(
|
|
97
|
+
"com.brickly.openai",
|
|
98
|
+
"chat",
|
|
99
|
+
{"prompt": "hello"},
|
|
100
|
+
profile_id="work",
|
|
101
|
+
)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
The caller manifest must declare the target brick and allowed commands in `dependencies`:
|
|
105
|
+
|
|
106
|
+
```json
|
|
107
|
+
"dependencies": {
|
|
108
|
+
"com.brickly.openai": {
|
|
109
|
+
"commands": ["chat"]
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Platform System API
|
|
115
|
+
|
|
116
|
+
`brick.platform.system.*`, `brick.system.*`, `ctx.platform.system.*`, and `ctx.system.*` call host system capabilities through BPP `host.platform.system.*`:
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
@brick.on_command("show-app-info")
|
|
120
|
+
def show_app_info(ctx, _input):
|
|
121
|
+
return {
|
|
122
|
+
"appName": ctx.system.get_app_name(),
|
|
123
|
+
"appVersion": ctx.system.get_app_version(),
|
|
124
|
+
"userData": ctx.system.get_path("userData"),
|
|
125
|
+
"isMacOS": ctx.system.is_macos(),
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Current methods are `show_notification`, `shell_open_path`, `shell_trash_item`, `shell_show_item_in_folder`, `shell_open_external`, `shell_beep`, `get_native_id`, `get_app_name`, `get_app_version`, `get_path`, `get_file_icon`, `read_current_folder_path`, `read_current_browser_url`, `is_dev`, `is_macos`, `is_windows`, and `is_linux`.
|
|
130
|
+
|
|
131
|
+
`get_path()` supports `home`, `appData`, `assets`, `userData`, `sessionData`, `temp`, `exe`, `module`, `desktop`, `documents`, `downloads`, `music`, `pictures`, `videos`, `recent`, `logs`, and `crashDumps`. Runtime calls are still checked by manifest permissions on the host side: notifications require `os.notification`; Shell capabilities require `os.exec`; app info, paths, native ID, platform checks, and current folder path reads require `os.env`; file icons require `fs.read`. `read_current_folder_path()` works when Finder is frontmost on macOS or Explorer is frontmost on Windows; it raises `CURRENT_FOLDER_UNAVAILABLE` when no readable foreground file-manager folder is available. `read_current_browser_url()` is reserved and currently returns `UNSUPPORTED_PLATFORM`.
|
|
132
|
+
|
|
133
|
+
## Platform Clipboard API
|
|
134
|
+
|
|
135
|
+
`brick.platform.clipboard.*` and `ctx.platform.clipboard.*` call host clipboard capabilities through BPP `host.platform.clipboard.*`:
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
@brick.on_command("replace-clipboard")
|
|
139
|
+
def replace_clipboard(ctx, _input):
|
|
140
|
+
previous = ctx.platform.clipboard.read_content()
|
|
141
|
+
updated = ctx.platform.clipboard.set_content({"kind": "text", "text": "Updated by Python"})
|
|
142
|
+
return {"previous": previous, "updated": updated}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Current methods are `read_content()` and `set_content(content)`.
|
|
146
|
+
|
|
147
|
+
## Cross-Brick Sessions
|
|
148
|
+
|
|
149
|
+
Use `ctx.open_session` when the target brick keeps in-memory state that must survive across multiple command calls. Calls through the same session are routed to the same target brick instance, and each `session.invoke` automatically carries the current `parentRequestId`, until `close()` is called or the caller brick instance exits.
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
session = ctx.open_session("com.brickly.openai", profile_id="work")
|
|
153
|
+
|
|
154
|
+
try:
|
|
155
|
+
session.invoke("start-thread", {"title": "Draft"})
|
|
156
|
+
reply = session.invoke("chat", {"prompt": "continue the thread"})
|
|
157
|
+
finally:
|
|
158
|
+
session.close()
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
`profile_id` is the target brick Profile ID. Session invokes are checked against the caller manifest's `dependencies[target].commands` on every command call.
|
|
162
|
+
|
|
163
|
+
## Errors
|
|
164
|
+
|
|
165
|
+
Raise `BppError` to preserve a specific Brickly error code:
|
|
166
|
+
|
|
167
|
+
```python
|
|
168
|
+
from brickly import BppError
|
|
169
|
+
|
|
170
|
+
raise BppError("INVALID_INPUT", "url is required")
|
|
171
|
+
```
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from .api import (
|
|
2
|
+
BricklyRuntime,
|
|
3
|
+
CommandContext,
|
|
4
|
+
EventEnvelope,
|
|
5
|
+
EventsApi,
|
|
6
|
+
UiApi,
|
|
7
|
+
WebContentsApi,
|
|
8
|
+
WindowHandle,
|
|
9
|
+
)
|
|
10
|
+
from .errors import BppError, payload_to_error
|
|
11
|
+
from .protocol import PROTOCOL_VERSION, PlatformSystemPathName
|
|
12
|
+
from .runtime import BppTransport
|
|
13
|
+
from .system import ClipboardApi, PlatformApi, SystemApi
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"BppError",
|
|
17
|
+
"BppTransport",
|
|
18
|
+
"BricklyRuntime",
|
|
19
|
+
"ClipboardApi",
|
|
20
|
+
"CommandContext",
|
|
21
|
+
"EventEnvelope",
|
|
22
|
+
"EventsApi",
|
|
23
|
+
"PlatformApi",
|
|
24
|
+
"PROTOCOL_VERSION",
|
|
25
|
+
"PlatformSystemPathName",
|
|
26
|
+
"SystemApi",
|
|
27
|
+
"UiApi",
|
|
28
|
+
"WebContentsApi",
|
|
29
|
+
"WindowHandle",
|
|
30
|
+
"payload_to_error",
|
|
31
|
+
]
|