Skip to content

Commit 211de97

Browse files
committed
docs: move sandbox runtime guide out of config docs
1 parent 449c1b7 commit 211de97

5 files changed

Lines changed: 306 additions & 306 deletions

File tree

docs/.vitepress/config.mjs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ export default defineConfig({
191191
{ text: "接收消息事件", link: "/guides/listen-message-event" },
192192
{ text: "发送消息", link: "/guides/send-message" },
193193
{ text: "插件配置", link: "/guides/plugin-config" },
194+
{ text: "沙盒运行时插件", link: "/guides/sandbox-runtime" },
194195
{ text: "插件 Pages", link: "/guides/plugin-pages" },
195196
{ text: "插件国际化", link: "/guides/plugin-i18n" },
196197
{ text: "调用 AI", link: "/guides/ai" },
@@ -435,6 +436,7 @@ export default defineConfig({
435436
{ text: "Listen to Message Events", link: "/guides/listen-message-event" },
436437
{ text: "Send Messages", link: "/guides/send-message" },
437438
{ text: "Plugin Configuration", link: "/guides/plugin-config" },
439+
{ text: "Sandbox Runtime Plugin", link: "/guides/sandbox-runtime" },
438440
{ text: "Plugin Pages", link: "/guides/plugin-pages" },
439441
{ text: "Plugin Internationalization", link: "/guides/plugin-i18n" },
440442
{ text: "AI", link: "/guides/ai" },

docs/en/dev/star/guides/plugin-config.md

Lines changed: 0 additions & 153 deletions
Original file line numberDiff line numberDiff line change
@@ -223,156 +223,3 @@ class ConfigPlugin(Star):
223223
When you update the Schema across different versions, AstrBot will recursively inspect the configuration items in the Schema, automatically adding default values for missing items and removing those that no longer exist.
224224

225225
Note that `default` is only applied when creating a new config file or when a field is missing from an existing config. If a field already exists in `data/config/<plugin_name>_config.json`, changing the Schema `default` later will not overwrite that saved value. This is intentional so plugin upgrades do not silently replace user-edited settings.
226-
227-
## Building a Sandbox Runtime Plugin
228-
229-
A sandbox runtime plugin teaches AstrBot how to start and connect to a sandbox service. The plugin usually contains a provider, a booter/client, a config schema, and optional tools for features such as screenshots or browser control.
230-
231-
Start with this structure:
232-
233-
```text
234-
data/plugins/<plugin_name>/
235-
main.py
236-
metadata.yaml
237-
_conf_schema.json
238-
provider.py
239-
booters/
240-
tools/
241-
```
242-
243-
Use the files like this:
244-
245-
- `main.py`: register the provider, and register any extra tools.
246-
- `provider.py`: adapt your runtime to AstrBot's sandbox provider methods.
247-
- `booters/`: put the client code that starts, connects to, and shuts down the sandbox.
248-
- `tools/`: add optional runtime tools such as screenshot, mouse, keyboard, browser, or lifecycle helpers.
249-
- `_conf_schema.json`: define the settings shown in WebUI.
250-
251-
### 1. Register the provider
252-
253-
In `main.py`, create your provider and register it when the plugin loads. Pass the plugin config into the provider so `provider.py` can read values from `_conf_schema.json`.
254-
255-
```python
256-
from astrbot.api.star import Context, Star, register
257-
from astrbot.core.computer.computer_client import (
258-
register_sandbox_provider,
259-
unregister_sandbox_provider,
260-
)
261-
262-
from .provider import MySandboxProvider
263-
264-
265-
@register("astrbot_sandbox_demo", "AstrBot Team", "Demo sandbox provider", "0.1.0")
266-
class DemoSandboxPlugin(Star):
267-
def __init__(self, context: Context, config=None) -> None:
268-
super().__init__(context)
269-
self.provider = MySandboxProvider()
270-
self.provider.plugin_config = config or {}
271-
register_sandbox_provider(self.provider, replace=True)
272-
273-
async def terminate(self) -> None:
274-
unregister_sandbox_provider(self.provider.provider_id, force=True)
275-
```
276-
277-
Use a stable name for the plugin directory, `metadata.yaml`, and `@register(...)`. Keeping them aligned makes the generated config file easy to find.
278-
279-
### 2. Implement `provider.py`
280-
281-
AstrBot calls the provider whenever it needs to create, reuse, rename, or destroy a sandbox. Implement these fields and methods:
282-
283-
- `provider_id`
284-
- `capabilities`
285-
- `tool_names`
286-
- `build_create_config(context, session_id)`
287-
- `build_connect_info(sandbox_name, config)`
288-
- `update_connect_info(record, *, sandbox_name)`
289-
- `get_idle_timeout(context, session_id)`
290-
- `create_booter(context, session_id, sandbox_id, config)`
291-
- `destroy_booter(booter, record)`
292-
293-
This is a minimal provider skeleton:
294-
295-
```python
296-
class MySandboxProvider:
297-
provider_id = "demo"
298-
capabilities = {"shell", "python", "filesystem"}
299-
tool_names = set()
300-
301-
def build_create_config(self, context, session_id):
302-
config = context.get_config(umo=session_id)
303-
sandbox_cfg = config.get("provider_settings", {}).get("sandbox", {})
304-
plugin_cfg = getattr(self, "plugin_config", None) or {}
305-
return {
306-
"endpoint_url": sandbox_cfg.get(
307-
"demo_endpoint", plugin_cfg.get("demo_endpoint", "")
308-
),
309-
"ttl": sandbox_cfg.get("demo_ttl", plugin_cfg.get("demo_ttl", 3600)),
310-
}
311-
312-
def build_connect_info(self, sandbox_name, config):
313-
return {"name": sandbox_name, **config}
314-
315-
def update_connect_info(self, record, *, sandbox_name):
316-
info = dict(record.get("connect_info") or {})
317-
info["name"] = sandbox_name
318-
return info
319-
320-
def get_idle_timeout(self, context, session_id):
321-
return 0.0
322-
323-
async def create_booter(self, context, session_id, sandbox_id, config):
324-
booter = MyBooter(**config)
325-
await booter.boot(session_id)
326-
return booter
327-
328-
async def destroy_booter(self, booter, record):
329-
await booter.shutdown()
330-
```
331-
332-
### 3. Add runtime config
333-
334-
Create `_conf_schema.json` for values that users should edit in WebUI, such as API endpoints, access tokens, profiles, image names, or timeouts.
335-
336-
```json
337-
{
338-
"demo_endpoint": {
339-
"description": "Demo API Endpoint",
340-
"type": "string",
341-
"default": "",
342-
"hint": "API endpoint for the demo sandbox service."
343-
},
344-
"demo_ttl": {
345-
"description": "Sandbox TTL",
346-
"type": "int",
347-
"default": 3600,
348-
"hint": "Sandbox lifetime in seconds."
349-
}
350-
}
351-
```
352-
353-
AstrBot stores the saved values in `data/config/<plugin_name>_config.json` and passes them to the plugin constructor as `config`.
354-
355-
If your provider still supports older values under `provider_settings.sandbox`, read them in `build_create_config()` as overrides on top of the plugin config. New provider settings should normally live in `_conf_schema.json`.
356-
357-
### 4. Add optional tools
358-
359-
If your runtime exposes extra abilities, register those tools in the plugin and list the tool names in `provider.tool_names`.
360-
361-
Common examples:
362-
363-
- screenshot tools
364-
- mouse / keyboard tools
365-
- browser tools
366-
- runtime-specific lifecycle helpers
367-
368-
AstrBot uses `tool_names` when mounting tools in sandbox mode. Make sure the names match the tools you register in `main.py`.
369-
370-
### 5. Try it locally
371-
372-
After adding the plugin under `data/plugins/<plugin_name>/`, start AstrBot and check these items:
373-
374-
- The plugin loads without import errors.
375-
- The WebUI config page shows fields from `_conf_schema.json`.
376-
- The sandbox runtime selector includes your `provider_id`.
377-
- Creating a sandbox calls `create_booter()`.
378-
- Stopping or unloading the plugin calls `terminate()` and unregisters the provider.
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
# Building a Sandbox Runtime Plugin
2+
3+
A sandbox runtime plugin teaches AstrBot how to start and connect to a sandbox service. The plugin usually contains a provider, a booter/client, a config schema, and optional tools for features such as screenshots or browser control.
4+
5+
Start with this structure:
6+
7+
```text
8+
data/plugins/<plugin_name>/
9+
main.py
10+
metadata.yaml
11+
_conf_schema.json
12+
provider.py
13+
booters/
14+
tools/
15+
```
16+
17+
Use the files like this:
18+
19+
- `main.py`: register the provider, and register any extra tools.
20+
- `provider.py`: adapt your runtime to AstrBot's sandbox provider methods.
21+
- `booters/`: put the client code that starts, connects to, and shuts down the sandbox.
22+
- `tools/`: add optional runtime tools such as screenshot, mouse, keyboard, browser, or lifecycle helpers.
23+
- `_conf_schema.json`: define the settings shown in WebUI. See [Plugin Configuration](./plugin-config.md) for the schema format.
24+
25+
## 1. Register the provider
26+
27+
In `main.py`, create your provider and register it when the plugin loads. Pass the plugin config into the provider so `provider.py` can read values from `_conf_schema.json`.
28+
29+
```python
30+
from astrbot.api.star import Context, Star, register
31+
from astrbot.core.computer.computer_client import (
32+
register_sandbox_provider,
33+
unregister_sandbox_provider,
34+
)
35+
36+
from .provider import MySandboxProvider
37+
38+
39+
@register("astrbot_sandbox_demo", "AstrBot Team", "Demo sandbox provider", "0.1.0")
40+
class DemoSandboxPlugin(Star):
41+
def __init__(self, context: Context, config=None) -> None:
42+
super().__init__(context)
43+
self.provider = MySandboxProvider()
44+
self.provider.plugin_config = config or {}
45+
register_sandbox_provider(self.provider, replace=True)
46+
47+
async def terminate(self) -> None:
48+
unregister_sandbox_provider(self.provider.provider_id, force=True)
49+
```
50+
51+
Use a stable name for the plugin directory, `metadata.yaml`, and `@register(...)`. Keeping them aligned makes the generated config file easy to find.
52+
53+
## 2. Implement `provider.py`
54+
55+
AstrBot calls the provider whenever it needs to create, reuse, rename, or destroy a sandbox. Implement these fields and methods:
56+
57+
- `provider_id`
58+
- `capabilities`
59+
- `tool_names`
60+
- `build_create_config(context, session_id)`
61+
- `build_connect_info(sandbox_name, config)`
62+
- `update_connect_info(record, *, sandbox_name)`
63+
- `get_idle_timeout(context, session_id)`
64+
- `create_booter(context, session_id, sandbox_id, config)`
65+
- `destroy_booter(booter, record)`
66+
67+
This is a minimal provider skeleton:
68+
69+
```python
70+
class MySandboxProvider:
71+
provider_id = "demo"
72+
capabilities = {"shell", "python", "filesystem"}
73+
tool_names = set()
74+
75+
def build_create_config(self, context, session_id):
76+
config = context.get_config(umo=session_id)
77+
sandbox_cfg = config.get("provider_settings", {}).get("sandbox", {})
78+
plugin_cfg = getattr(self, "plugin_config", None) or {}
79+
return {
80+
"endpoint_url": sandbox_cfg.get(
81+
"demo_endpoint", plugin_cfg.get("demo_endpoint", "")
82+
),
83+
"ttl": sandbox_cfg.get("demo_ttl", plugin_cfg.get("demo_ttl", 3600)),
84+
}
85+
86+
def build_connect_info(self, sandbox_name, config):
87+
return {"name": sandbox_name, **config}
88+
89+
def update_connect_info(self, record, *, sandbox_name):
90+
info = dict(record.get("connect_info") or {})
91+
info["name"] = sandbox_name
92+
return info
93+
94+
def get_idle_timeout(self, context, session_id):
95+
return 0.0
96+
97+
async def create_booter(self, context, session_id, sandbox_id, config):
98+
booter = MyBooter(**config)
99+
await booter.boot(session_id)
100+
return booter
101+
102+
async def destroy_booter(self, booter, record):
103+
await booter.shutdown()
104+
```
105+
106+
## 3. Add runtime config
107+
108+
Create `_conf_schema.json` for values that users should edit in WebUI, such as API endpoints, access tokens, profiles, image names, or timeouts.
109+
110+
```json
111+
{
112+
"demo_endpoint": {
113+
"description": "Demo API Endpoint",
114+
"type": "string",
115+
"default": "",
116+
"hint": "API endpoint for the demo sandbox service."
117+
},
118+
"demo_ttl": {
119+
"description": "Sandbox TTL",
120+
"type": "int",
121+
"default": 3600,
122+
"hint": "Sandbox lifetime in seconds."
123+
}
124+
}
125+
```
126+
127+
AstrBot stores the saved values in `data/config/<plugin_name>_config.json` and passes them to the plugin constructor as `config`.
128+
129+
If your provider still supports older values under `provider_settings.sandbox`, read them in `build_create_config()` as overrides on top of the plugin config. New provider settings should normally live in `_conf_schema.json`.
130+
131+
## 4. Add optional tools
132+
133+
If your runtime exposes extra abilities, register those tools in the plugin and list the tool names in `provider.tool_names`.
134+
135+
Common examples:
136+
137+
- screenshot tools
138+
- mouse / keyboard tools
139+
- browser tools
140+
- runtime-specific lifecycle helpers
141+
142+
AstrBot uses `tool_names` when mounting tools in sandbox mode. Make sure the names match the tools you register in `main.py`.
143+
144+
## 5. Try it locally
145+
146+
After adding the plugin under `data/plugins/<plugin_name>/`, start AstrBot and check these items:
147+
148+
- The plugin loads without import errors.
149+
- The WebUI config page shows fields from `_conf_schema.json`.
150+
- The sandbox runtime selector includes your `provider_id`.
151+
- Creating a sandbox calls `create_booter()`.
152+
- Stopping or unloading the plugin calls `terminate()` and unregisters the provider.

0 commit comments

Comments
 (0)