Skip to content

Commit fa1a8c7

Browse files
committed
docs(sandbox): add connection workflows and official references to skill
1 parent 3e8df95 commit fa1a8c7

2 files changed

Lines changed: 165 additions & 3 deletions

File tree

skills/bailian-sandbox/SKILL.md

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ metadata:
77
description: >-
88
阿里云百炼 Sandbox 沙箱实例与模版生命周期管理入口:用户要创建、查询、连接、暂停、恢复或释放百炼沙箱,
99
或查看内置基础镜像、上传模版挂载文件、创建、更新、查询、删除沙箱模版、查看模版构建状态时,使用 `bl sandbox`。
10-
仅覆盖百炼 Sandbox 管控面;不用于宿主执行沙箱设置、E2B 官方云资源或沙箱内命令执行与文件传输。
10+
用户要访问已创建实例、打开 WebShell、连接 browser-use 浏览器自动化或 VNC 实时画面时,先获取连接信息,再连接数据面。
11+
不用于宿主执行沙箱设置、E2B 官方云资源或通用文件传输。
1112
agents.yaml 托管 Agent / Session / Environment 管理交给 bailian-managed-agent。
1213
官方安装:`bl skill init`(与共享协议 bailian-protocol 同装)。
1314
---
@@ -21,7 +22,7 @@ Before running `bl`, read the shared [bailian-protocol](../bailian-protocol/SKIL
2122
- Manage Sandbox instances and templates through Bailian's E2B-compatible REST control plane. No E2B SDK or E2B API key is required; authentication uses the Bailian API Key as an Authorization Bearer token.
2223
- Resolve Base URL through the same CLI chain as Managed Agent: `--base-url` > `DASHSCOPE_BASE_URL` > login/profile `base_url`. Use an origin such as `https://workspace.cn-beijing.maas.aliyuncs.com`; the CLI strips URL paths/query/fragment and appends `/api/v1/agentstudio/sandbox` for lifecycle operations, or `/api/v1/agentstudio/files` for template file uploads. The saved API Key is reused. Profile capability fallback follows the shared protocol for both the key and Base URL.
2324
- If no Base URL is configured, resolve the workspace from `--workspace-id`, then `BAILIAN_WORKSPACE_ID`, then configured `workspace_id`, and use `https://{workspace_id}.cn-beijing.maas.aliyuncs.com/api/v1/agentstudio/sandbox`. With a configured Base URL, the workspace flag is optional. The service currently supports `cn-beijing` and requires prior Sandbox SLR authorization.
24-
- No `agents.yaml` or local IaC state is required. `connect` returns instance connection information; it does not open an interactive shell. Do not invent commands for executing code or transferring files inside the sandbox.
25+
- No `agents.yaml` or local IaC state is required. `get` / `connect` return instance connection information; neither opens an interactive shell or browser. For WebShell, CDP, or VNC, use that information with the runtime APIs described below. Do not invent `bl sandbox exec`, `webshell`, or browser subcommands.
2526

2627
## Choose the operation
2728

@@ -42,11 +43,19 @@ To save the origin in an isolated Profile, use `bl auth login --config sandbox -
4243

4344
For built-in template images, discover the preset ID or Chinese name through `sandbox official-images`, then pass it to template create/update with `--image`. This fills both `fromImage` and `imageName`; `--image-name` alone remains a display name, not an image selector. Presets override the body's image fields; explicit `--from-image` / `--image-name` override the corresponding preset fields. Presets are pinned cn-beijing image URLs, not a live catalog or an availability guarantee for other environments. Omitting `--image` does not change existing defaults or update a template's image implicitly. See the generated reference for the catalog and exact flags.
4445

46+
## Connect to an instance
47+
48+
For requests to access a running instance, open WebShell, control a browser, or show its live desktop, read [assets/instance-connections.md](assets/instance-connections.md). It contains the connection workflow, API payloads, verification steps, and official documentation links.
49+
50+
- Obtain fresh JSON with `bl sandbox get --sandbox-id <id> --show-credentials --output json`. Use `envdAccessToken` as `X-Access-Token`; the control-plane Bailian API Key is a different credential.
51+
- WebShell uses the returned `envdUrl` and envd PTY RPCs. The official browser template uses port 3000 on `{port}-{sandboxID}.{domain}` for CDP and VNC. These ports and paths come from the template contract, not fields returned by `get`; do not apply them to other images without checking their documentation.
52+
- When the user asks to open or access the instance, complete the connection and verify a terminal command or rendered browser page. Returning connection JSON or a successful health check alone does not complete that request. A local proxy URL is a host-side access page, not a URL returned by the Sandbox API.
53+
4554
## Operational boundaries
4655

4756
- Mutating commands act on remote resources. Only perform the requested operation and scope; read-only discovery does not authorize creating, pausing, resuming, or deleting resources.
4857
- Instance and template deletion are high-risk. Follow the shared protocol: show the exact target and risk, then wait for explicit confirmation before adding `--yes`. Treat `requires_confirmation` as a stop signal, not a reason to retry automatically.
49-
- Connection credentials are redacted by default. Use `--show-credentials` only when the user explicitly needs the connection tokens, and keep them out of chat summaries, logs, and committed files.
58+
- Connection credentials are redacted by default. A request to connect to or access the instance needs the tokens: use `--show-credentials` for that operation, capture the result privately, and keep tokens out of chat summaries, logs, URLs, and committed files.
5059
- Template create/update wait by polling the build-status endpoint, not template details. `--async` returns after the submission response with `templateID` / `buildID`; it does not mean the build is ready. Use those IDs with `template build-status` to check completion.
5160
- Global `--timeout` limits HTTP requests and total template-build polling. `--instance-timeout` sets instance lifetime; these are different limits. A polling timeout does not prove the remote build failed or stopped; check its status before submitting another build.
5261
- `--body` accepts a JSON object inline or through `@path`; explicit flags override body fields. For local template mounts, first use `bl sandbox file upload`: it sends multipart `file` and fixed `source=sandbox_template` directly to `/api/v1/agentstudio/files` with the Bailian API Key. Use the returned `id` as `mntConfig[].originFileId`, with `mountPath` and optional `originFileName`, in template create/update `--body`. The upload and template must use the same workspace. This is not the temporary OSS upload from `bl file upload` or a transfer into a running instance.
@@ -63,3 +72,4 @@ Refer to sibling skills by name: read them if installed; otherwise use the comma
6372

6473
- [bailian-protocol](../bailian-protocol/SKILL.md) — shared execution protocol, installed with `bl skill init`
6574
- [reference/](reference/index.md) — generated command reference
75+
- [Instance connections](assets/instance-connections.md) — WebShell, browser CDP, VNC, and official runtime documentation
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
# Connect to a Sandbox instance
2+
3+
Use this guide when the user asks to access an instance, open WebShell, automate its browser, or view the live desktop. Lifecycle operations stay in `bl sandbox`; runtime access uses the returned connection information with HTTP, CDP, or VNC clients.
4+
5+
## Official documentation and endpoint ownership
6+
7+
- [Bailian Sandbox SDK](https://docs.agent.bailian.aliyun.com/zh/sandbox/sdk) ([Markdown](https://docs.agent.bailian.aliyun.com/zh/sandbox/sdk.md)) — separates control-plane operations from runtime access; documents command execution through `POST /process.Process/Start`.
8+
- [Browser template](https://help.aliyun.com/en/functioncompute/browser-template) — the **WebSocket endpoints** section defines port 3000, `/health`, `/ws/automation`, `/ws/livestream`, and `X-Access-Token` authentication.
9+
- [Use Browser Use Sandbox](https://help.aliyun.com/en/functioncompute/use-browser-use-sandbox) — browser readiness, Puppeteer / BrowserUse integration, page navigation, and screenshots.
10+
- [Connect protocol](https://connectrpc.com/docs/protocol/) — streaming envelopes, unary JSON requests, and end-of-stream errors used by envd.
11+
12+
The browser paths below belong to the official browser image, not to every Sandbox template. Custom images or gateways may have different ports and paths; check their contract before constructing a URL. The envd PTY payloads below were verified with Bailian `envdVersion=0.2.4`; use the returned version when diagnosing compatibility changes.
13+
14+
## 1. Obtain connection information
15+
16+
Reuse the requested instance. Create one only when creation is part of the user's request. Follow the shared protocol before running `bl`; use the generated command reference for lifecycle flags.
17+
18+
Capture the credential-bearing result in a private temporary directory:
19+
20+
```bash
21+
umask 077
22+
sandbox_connection_dir="$(mktemp -d)"
23+
bl sandbox get --sandbox-id <sandbox-id> --show-credentials --output json > "$sandbox_connection_dir/connection.json"
24+
```
25+
26+
Check the command exit code before reading the file. `create` and `connect` can also return connection fields with `--show-credentials`, but neither command opens a terminal or browser. Without that flag, tokens are `[REDACTED]` and cannot authenticate a runtime request.
27+
28+
| Returned field | Use |
29+
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
30+
| `envdUrl` | Runtime base URL for WebShell / process RPCs; use it directly. |
31+
| `envdAccessToken` | Value for the runtime `X-Access-Token` header; not the Bailian API Key. |
32+
| `sandboxID`, `domain` | Construct a documented service host as `{port}-{sandboxID}.{domain}`. Use `sandboxID`, not `bailianSandboxId` or `templateID`. |
33+
| `state`, `endAt`, `envdVersion` | Check readiness, expiry, and the runtime version. |
34+
| `trafficAccessToken` | Separate optional credential; do not substitute it for `envdAccessToken` in the flows below. |
35+
36+
Require a running instance and a usable token. If it is paused, resume only within the requested scope, then fetch fresh connection information. If it has expired or no longer exists, report that state rather than retrying an old token or silently creating a replacement. Do not hardcode a previously observed domain, instance ID, or token.
37+
38+
## 2. Check the intended service
39+
40+
For the official browser template, set `browserHost = "3000-" + sandboxID + "." + domain`.
41+
42+
| Capability | Address | Transport |
43+
| ------------------------- | ----------------------------------- | -------------------------- |
44+
| envd readiness | `{envdUrl}/health` | HTTPS GET |
45+
| WebShell / shell commands | `{envdUrl}/process.Process/Start` | HTTPS POST, Connect stream |
46+
| Browser readiness | `https://{browserHost}/health` | HTTPS GET |
47+
| Browser automation | `wss://{browserHost}/ws/automation` | CDP over WebSocket |
48+
| Live browser desktop | `wss://{browserHost}/ws/livestream` | VNC / RFB over WebSocket |
49+
50+
Send `X-Access-Token: <envdAccessToken>` on each request. The control-plane `Authorization: Bearer <Bailian API Key>` is not this data-plane header. Do not send the Bailian API Key to runtime service hosts.
51+
52+
This Python example uses only the standard library and keeps the token out of command arguments and output. It checks envd and, for a browser instance, the browser service:
53+
54+
```bash
55+
python3 - "$sandbox_connection_dir/connection.json" <<'PYTHON'
56+
import json
57+
import sys
58+
import urllib.request
59+
from pathlib import Path
60+
61+
connection = json.loads(Path(sys.argv[1]).read_text())
62+
token = connection.get("envdAccessToken")
63+
if not token or token == "[REDACTED]":
64+
raise SystemExit("Fetch connection information with --show-credentials first")
65+
browser_host = f"3000-{connection['sandboxID']}.{connection['domain']}"
66+
for base_url in [connection["envdUrl"], f"https://{browser_host}"]:
67+
request = urllib.request.Request(
68+
base_url.rstrip("/") + "/health",
69+
headers={"X-Access-Token": token},
70+
)
71+
with urllib.request.urlopen(request, timeout=15) as response:
72+
print(base_url, response.status, response.read(1024).decode())
73+
PYTHON
74+
```
75+
76+
An authenticated `/health` response of 200 establishes service readiness only. A root-path `/` response of 404 does not prove that the runtime is unavailable; these endpoints need not serve an HTML homepage. For a newly starting browser service, retry health checks with a bounded deadline before opening CDP or VNC.
77+
78+
## 3. Open WebShell through envd PTY
79+
80+
Use a terminal client or a host-side page with a terminal emulator such as xterm.js. Reuse an available envd client instead of recreating the protocol when possible.
81+
82+
1. POST to `{envdUrl}/process.Process/Start` with `X-Access-Token`, `Connect-Protocol-Version: 1`, `Content-Type: application/connect+json`, and `connect-content-encoding: identity`.
83+
2. Encode the JSON payload below in a Connect envelope: one zero flags byte, a four-byte big-endian payload length, then the UTF-8 JSON bytes. This is not a plain JSON POST.
84+
85+
```json
86+
{
87+
"process": {
88+
"cmd": "/bin/bash",
89+
"args": ["--noprofile", "--norc", "-i"],
90+
"envs": { "TERM": "xterm-256color" }
91+
},
92+
"pty": { "size": { "cols": 100, "rows": 30 } }
93+
}
94+
```
95+
96+
Choose a shell available in the image; the browser image used here has `/bin/bash`. Consume the response incrementally instead of waiting for the interactive process to finish:
97+
98+
- Save `event.start.pid` as the process handle.
99+
- Decode Base64 `event.data.pty` into bytes and write them to the terminal; preserve multibyte characters across chunks. For non-PTY processes, output may use `stdout` / `stderr` instead.
100+
- Handle `event.end.exitCode`. Frames whose flags contain `0x02` are end-of-stream frames; report their `error` even when HTTP status is 200. Buffer incomplete frames across network chunks.
101+
102+
Subsequent requests use `Content-Type: application/json` and the same token and protocol-version headers, without a streaming envelope:
103+
104+
| Action | POST path under `envdUrl` | JSON body |
105+
| ------------------------------------------- | ----------------------------- | --------------------------------------------------------------- |
106+
| Send keyboard input | `/process.Process/SendInput` | `{"process":{"pid":123},"input":{"pty":"cHdkDQ=="}}` |
107+
| Resize the terminal | `/process.Process/Update` | `{"process":{"pid":123},"pty":{"size":{"cols":100,"rows":30}}}` |
108+
| Terminate this shell on explicit disconnect | `/process.Process/SendSignal` | `{"process":{"pid":123},"signal":"SIGNAL_SIGKILL"}` |
109+
110+
Replace `123` with the returned PID. `cHdkDQ==` is Base64 for `pwd` followed by a carriage return. Serialize keyboard input requests to preserve their order. A WebShell frontend maps terminal input to `SendInput`, terminal resize to `Update`, and streamed output back to the terminal. Abort streaming requests and clean up only the shell process created by that client when disconnecting; do not kill the sandbox or the browser stack.
111+
112+
Verify the interactive path by typing `pwd` in the terminal and observing the result and a new prompt. A successful `Start` response alone does not verify keyboard input. In a page that wraps global `fetch`, use an unwrapped streaming client or a local proxy if the wrapper buffers the stream.
113+
114+
## 4. Connect browser automation through CDP
115+
116+
Use a WebSocket client that supports custom headers. With `puppeteer-core` available in a scratch project, save the following as `connect-browser.mjs` there and run `node connect-browser.mjs <connection.json> <target-url> <screenshot.png>`. The browser runs in the sandbox; no local Chromium installation or E2B SDK is needed for this direct connection.
117+
118+
```javascript
119+
import { readFile } from "node:fs/promises";
120+
import puppeteer from "puppeteer-core";
121+
122+
const [connectionPath, targetUrl, screenshotPath] = process.argv.slice(2);
123+
if (!connectionPath || !targetUrl || !screenshotPath) {
124+
throw new Error("Expected connection.json, target URL, and screenshot path");
125+
}
126+
const connection = JSON.parse(await readFile(connectionPath, "utf8"));
127+
const browserHost = `3000-${connection.sandboxID}.${connection.domain}`;
128+
const browser = await puppeteer.connect({
129+
browserWSEndpoint: `wss://${browserHost}/ws/automation`,
130+
headers: { "X-Access-Token": connection.envdAccessToken },
131+
});
132+
try {
133+
const page = await browser.newPage();
134+
await page.goto(targetUrl, { waitUntil: "domcontentloaded", timeout: 30000 });
135+
await page.screenshot({ path: screenshotPath });
136+
console.log(JSON.stringify({ url: page.url(), title: await page.title(), screenshotPath }));
137+
} finally {
138+
browser.disconnect();
139+
}
140+
```
141+
142+
Run the health/token checks first. Use the user's requested target; if they only ask for an access check, `https://example.com` is a suitable sample. Verify the page title and screenshot. Disconnecting the client leaves the remote browser available; do not copy tutorial cleanup that closes the browser or kills a sandbox the user wants to keep using.
143+
144+
## 5. Show the live browser desktop through VNC
145+
146+
Connect a noVNC client to `wss://{browserHost}/ws/livestream`. This endpoint carries VNC frames, not CDP messages or an HTML page. A WebSocket handshake alone does not establish a working desktop; verify that the client finishes the RFB handshake and renders the remote browser.
147+
148+
Browser-native WebSocket cannot attach `X-Access-Token` headers. For a user-facing page, use a header-capable bridge: browser noVNC → local WebSocket proxy → sandbox VNC endpoint. Keep the token on the proxy side and attach it only to the sandbox request. Do not put it in page source or query parameters to work around header support.
149+
150+
For a temporary local viewer, bind to `127.0.0.1`, check the WebSocket Origin, and forward only the selected instance's service. Serve the needed noVNC assets from an available dependency or a scratch install. The same local approach can bridge an xterm.js WebShell page to envd. Keep the proxy running while the user is using the page; stop its connections when the instance expires or the user finishes.
151+
152+
Report the actual local page URL separately from the remote service URL. Open the page when requested, verify the connected state and visible desktop or terminal output, and retain the usable tab for the user. State that a loopback URL works only on this machine and report the instance's actual expiry from `endAt`. Do not treat `127.0.0.1` pages as addresses returned by `sandbox get`.

0 commit comments

Comments
 (0)