Skip to content

release: 0.4.0 — fix missing SDK names in PyPI 0.3.0, control serve + HIL, localized labels - #12

Merged
cmjang merged 22 commits into
mainfrom
release/0.4.0
Nov 4, 2026
Merged

cmjang merged 22 commits into
mainfrom
release/0.4.0

Conversation

@cmjang

@cmjang cmjang commented Nov 4, 2026

Copy link
Copy Markdown
Contributor

变更

发布 botlab-python 0.4.0,修掉 PyPI 上 0.3.0 的最高优先问题:pip install botlab-python 后跑任何内置模板都会 ImportError,因为 0.3.0 缺 read_config / declare_control / control_profile / action。

  • 合入 merge/local-wip-20261028(control serve/descriptor、Control v1 协议、HIL 设备链与 gateway、打包面、docs)。
  • 从 BotLab main(bfaade5)重新镜像 src/botlab/sdk 与 src/botlab/_presets.py,逐字节一致(仅 _runtime 按约定不镜像)。镜像带上了 BotLab 侧的 {en, zh} 标签支持。
  • 合入 feat: accept {en, zh} tables for parameter and key mapping labels #11(参数与按键映射接受 {en, zh} 文本表)。BotLab main 的内置模板已经用本地化标签,0.4.0 必须同时接受它们,否则模板在 CLI 里报 label must be a non-empty string。
  • 版本升到 0.4.0(pyproject.toml、src/botlab/__init__.py、uv.lock),README 中的发布示例同步到 0.4.0。
  • 修掉 WIP 分支继承的 ruff 问题(import 顺序、UP035、UP022)。

验证

  • uv run ruff check . → All checks passed
  • uv run pytest → 1201 passed, 10 skipped, 2 deselected(含跨语言 digest 夹具 PINNED_DIGEST_HEX = cf149d18…,与 BotLab pythonRelease.test.ts 同一 pin)
  • uv run python -m botlab._runtime.golden_check tests/fixtures/processors.json → 54 cases replayed
  • 真实模板冒烟(0.4.0 可编辑安装,BotLab main 的 public/templates):
    • botlab validate 全通的:g1-gmtrack、microduck(read_config + control_profile + declare_control + 本地化标签全链路)
    • 其余模板已越过 import 与声明阶段,只停在共享模型路径(models/…、templates/… 需要站点根,与本次 SDK 无关)
    • read_config / declare_control / control_profile / action 均在 botlab.__all__ 中可导入

发布前置(未包含在本 PR)

  • wheel 必须带 src/botlab/ui 快照:需在 BotLab main 上 npm run build + npm run build:viewer 后 botlab ui sync、botlab ui export,再过 scripts/verify_wheel.py 与浏览器验收。
  • PyPI 上传凭据不在仓库/CI 里(无发布 workflow、无 TWINE_*),需人工 twine upload / uv publish。
  • GitHub v0.4.0 release 需附 botlab-browser-runtime-0.4.0.zip,否则 botlab run 拉不到浏览器运行时。

carlvon17 and others added 22 commits October 23, 2026 20:06
Run a project's ONNX models on a Linux target while the browser keeps
MuJoCo, the signal graph and project Python — model-service HIL, not
full-controller deployment or hard real-time scheduling.

- hil/: SSH target discovery, inspection and provisioning (generic-linux
  and rdk-x5 profiles), a versioned build → deploy → load → run
  lifecycle over verified SSH forwarding, and the target-side inference
  agent; new `botlab-agent` entry point and a `target` extra
  (numpy + onnxruntime only)
- CLI: `botlab target list/add/inspect/remove/discover/identity/
  provision/projects`, `botlab deploy --target`, `botlab agent`, and
  `botlab run --hil <target>`; HIL runs pin the host to loopback and
  require a browser runtime advertising hilProtocol v1
- server: HIL endpoints behind a per-session token with Host and origin
  checks
- browser adapters: `botlab run --browser-adapter MODULE:FUNCTION
  --inference-endpoint HOST:PORT` runs a project-supplied launcher
  (BrowserRunContext) with the SDK-managed web runtime; physics stays
  in the browser
- hardening: browser runs bind 127.0.0.1 by default (--host 0.0.0.0 for
  LAN disables HIL), UI asset traversal checks, dot-files and tmp/
  excluded from local-project payloads and debug watches, project loads
  serialized under a lock for asynchronous HIL builds
- README: browser-first run flow (--native opt-in) and the HIL guide

Co-Authored-By: Claude Code <noreply@anthropic.com>
`botlab control` is a robot-independent client: a project supplies its
own loopback middleware, capability declarations and execution backend,
and the CLI discovers it, binds instances and sessions, and submits
identity-preserving commands — without importing project code or any
robot adapter. The first adapter ships in the MicroDuck example project.

- control/: stdlib-only HTTP transport, private project-scoped
  middleware discovery, durable request receipts for retries, and
  capability-driven subcommands (instances, capabilities, sessions,
  state, connection, call, move, action, wait, retry, stop)
- dispatch `botlab control` ahead of argparse so REMAINDER arguments
  reach the project CLI untouched
- docs/control-protocol.md is the canonical protocol spec; the contract
  test runs against an unrelated joint-only stdlib middleware to prove
  the API is not MicroDuck-shaped

Co-Authored-By: Claude Code <noreply@anthropic.com>
The protocol core now owns the frozen envelope field set, command statuses,
project_key and strict envelope validation with injectable errors; the client
consumes these instead of local duplicates. /info declares the extension
negotiation surface (empty for local v1). docs/control-hosting.md freezes the
M0 hosting decisions: eight-field envelope, extension negotiation, restart
and deduplication semantics, dependency rules and the HIL acceptance split.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Endpoints now accept HTTPS origins (loopback HTTP keeps its explicit-port
rule; plaintext HTTP never leaves the loopback). `botlab control enroll`
probes a remote middleware, pins its reported identity and instance into a
private remote descriptor, and every later command - including wait/retry -
reuses the existing descriptor, receipt and restart-refusal semantics.
Remote identity comparison uses the enrollment pin instead of the local
checkout path; re-enrollment replaces an endpoint's previous descriptor and
instance reuse across endpoints fails with instance_conflict. Tokens arrive
via BOTLAB_CONTROL_TOKEN or a hidden prompt, never argv; private CAs are
supported through BOTLAB_CONTROL_CA_BUNDLE. Acceptance runs over real TLS
with a generated dev CA in tests/test_control_remote.py.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Board agents gain an outbound mode: botlab-agent connect dials a device
gateway (ws:// loopback, wss:// anywhere) and serves the same dispatch
loop as TCP mode, redialing after lease termination or link loss. The
gateway relays opaque HIL v1 frames between /agent/<id> and /device/<id>
and never parses them, so the inference path inherits no Control-command
semantics — no receipts, no retry, no task table.

- gateway: one shared bearer token (operator channel, no tokenless
  mode), exclusive coordinator pairing (device busy), takeover on
  re-registration closing the displaced pairing, either side dropping
  releases the HIL lease like a TCP disconnect; compression off, links
  dial directly (no ambient proxy).
- agent connect + serve_stream extraction: TCP handler and outbound mode
  share one loop; the lease owner is the stream, fixing release identity.
- host: transport "gateway" targets via a shared _open_stream helper;
  token only from BOTLAB_HIL_TOKEN, never in target settings.
- endpoint rule mirrors Control M1: ws:// stays loopback with an
  explicit port, wss:// accepts any host (gateway_endpoint).
- botlab target bench: serial-inference latency report (RTT percentiles,
  effective Hz, budget verdict) for the real-time acceptance tier; the
  functional tier is covered by tests/test_hil_gateway.py (loopback and
  real-TLS e2e, exclusive pairing, takeover, stale-session rejection).

Co-Authored-By: Claude Code <noreply@anthropic.com>
`botlab control connection` (and remote enroll) now include an
`agent_prompt` field: ready-to-paste instructions for the user's AI.
Written for an already-prepared environment (botlab-python installed,
middleware running) so it teaches no install steps, pins the working
directory and --project discipline, and encodes the operational rules
proven out on MicroDuck: terminal-state sequencing, parse failure !=
not sent, capabilities-bounded args, timeout/retry-id semantics, and
hidden/pause readiness. The pairing token is never embedded; it stays
behind `connection --show-token`, and the new test asserts both that
and the absence of install instructions.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Remote descriptors pin the serving host's project path from /info;
the AI assistant the prompt addresses runs botlab from this checkout,
so both connection() and enroll() now pass self.project.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Browsers cannot set Authorization headers on a WebSocket handshake, so the
gateway now accepts the same shared bearer credential as one leading
{"type": "gateway-auth", "token": ...} text control frame on both channels
before the binary relay begins. The header path is unchanged; any text frame
after authentication remains a protocol error, and the relay still never
parses HIL frames. Bad or malformed frames fail exactly like a bad header
(1008), a silent client times out after 5 s.

Co-Authored-By: Claude Code <noreply@anthropic.com>
…pairing guidance

agent_prompt() now lives in botlab.control.prompts (stdlib-pure, like the
protocol core) so third-party middleware can serve byte-identical text to
the browser panel. The copy gains an unpaired branch: when state shows the
browser not paired, the AI relays the websocket URL and token from
`connection --show-token` through chat only — never files, logs or commits.
Also documents the optional Origin-gated `agent-prompt` extension endpoint.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The browser panel ships the handoff prompt as fixed UI copy instead of
fetching it from the middleware; remove the endpoint convention until
something actually serves it.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The handoff prompt now lives only as fixed copy in the browser's
CLI-control panel, so connection/enroll stop carrying an agent_prompt
field and the shared prompts module has no consumer left.

Co-Authored-By: Claude Code <noreply@anthropic.com>
…panel prompt

Co-Authored-By: Claude Code <noreply@anthropic.com>
The board runs gateway + agent on its own loopback and the host forwards
port 50060 with a single ssh -N -L; both ws:// legs stay loopback and the
cross-machine hop rides SSH. The domain-server runbook shrinks to a
pointer to hil.md "Gateway targets" for remote boards.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Boards in scenario B often have no egress at all; sharing the assistant
host's network or finding Wi-Fi is not worth it. The runbook now builds
the wheel on the assistant host, downloads dependency wheels tagged for
the board venv's exact Python, scps both over, and installs with
--no-index --find-links.

Also record the real floor: HIL needs Python >=3.11 because
websockets>=17.1 declares Requires-Python >=3.11 and the dial passes the
17.1-only legacy=True flag (16.x raises TypeError). Boards with older
system Pythons (or without ensurepip) get a standalone CPython instead.
New pitfalls indexed: cp/ABI tag mismatch surfaces as
"from versions: none", and downgrading websockets to 16.x is not a fix.

Co-Authored-By: Claude Code <noreply@anthropic.com>
A coordinator can compile for minutes before its first frame; the 120s
read timeout is not a lease failure. Keep waiting for traffic instead of
dropping the gateway link and the device registration while the browser
still works.

Co-Authored-By: Claude Code <noreply@claude-code>
与 BotLab 85a71b6 逐字节同步:新增 _config.py(read_config 受限 YAML 解析)、
_run_end.py,_declaration/_robot/_resources 等更新;sync_sdk.py --botlab
显式路径重同步后 20 模块 cmp 校验一致。

Co-Authored-By: Claude Code <noreply@anthropic.com>
含 AI 助手 HIL 部署 runbook 文档、control serve 服务与描述符、wheel 校验
及 CI 更新等并行工作线的未提交改动。

Co-Authored-By: Claude Code <noreply@anthropic.com>
# Conflicts:
#	src/botlab/sdk/_robot.py
#	tests/test_host.py
#	tests/test_project.py
- sync src/botlab/sdk from BotLab main (adds _localized_text, imu-sensor
  observation data already in the WIP merge) and refresh _presets.py
- bump version to 0.4.0 and lock metadata
- fix ruff findings inherited from the WIP branch (import order, UP035/UP022)
@cmjang
cmjang merged commit daa00e9 into main Nov 4, 2026
3 checks passed
carlvon17 added a commit that referenced this pull request Nov 5, 2026
release/0.4.0 was squash-merged as #12 (62 files, 14418+/526-); the remaining
tree delta vs this branch is exactly #13 (68+/46-) plus the v3 SDK sync
(1034+/217-), i.e. 1102+/263- with zero drift. Recorded with -s ours so the
branch history is reachable from main and the ref can be retired; also covers
merge/local-wip-20261028 and feat/localized-labels (both ancestors of this
branch).

Co-Authored-By: Claude Code <noreply@anthropic.com>
@carlvon17
carlvon17 deleted the release/0.4.0 branch November 5, 2026 11:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants