跳转到内容

Quickstart

此内容尚不支持你的语言。

This page is the shape of getting started, end to end. Read Build an extension first for the model behind it.

An extension needs two things: the SDK crate and something to run async code on. Nothing else.

[dependencies]
vros-ext = "2.0"
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }

The crate version is vrOS’s own version, not the protocol’s. The wire version is a separate constant, vxp::VXP_VERSION, currently 1.7. The host accepts the current VXP major and the one before it, and every 1.x minor only adds — so an extension built against an older SDK keeps working on a newer vrOS, and simply does not see what arrived after it.

vrOS launches your binary with the endpoint, a per-spawn token, and your extension id in the environment. connect_from_env reads them and connects, retrying for a few seconds to cover the spawn race.

Then say hello. You have five seconds from launch, and nothing else is answered until you do. Say hello first, initialize after.

use vros_ext::conn::{Client, SocketConnection};
use vros_ext::event::VxpEvent;
use vros_ext::hello::{HelloReply, HelloRequest};
use vros_ext::vxp;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let conn = SocketConnection::connect_from_env().await?;
let client = Client::new(conn);
let reply = client
.hello(&HelloRequest {
id: std::env::var(vxp::ENV_EXT_ID)?,
vxp_version: vxp::VXP_VERSION.into(),
token: std::env::var(vxp::ENV_TOKEN).ok(),
})
.await?;
// The welcome carries what the user actually granted, and their locale.
let (granted, locale) = match reply {
HelloReply::Welcome { granted, locale, .. } => (granted, locale),
HelloReply::Rejected { code, message } => {
return Err(format!("hello rejected ({code:?}): {message}").into());
}
other => return Err(format!("unknown hello reply: {other:?}").into()),
};
// Do your setup here — read KV, subscribe to what you were granted,
// push your panel's words. Then park on the event stream.
let mut events = client.take_events().ok_or("no event stream")?;
while let Some(event) = events.recv().await {
match event {
VxpEvent::ShuttingDown => break,
_ => {}
}
}
Ok(())
}

Check granted before you use a capability rather than assuming your manifest was accepted whole — the user consents per capability, and a missing grant is permanent for that session.

vros-ext.toml sits beside your binary and describes it. This is what the consent screen reads, and it is read while your extension is stopped.

[extension]
id = "com.example.hello"
name = "Hello"
version = "0.1.0"
author = "Your name"
entry = "hello.exe" # the binary name; drop the .exe on Linux
restart = "on-crash"
[capabilities]
bus = ["vr.lifecycle"]
[panels]
overlay = "ui/hello.slint"

The id is lowercase reverse-DNS drawn from [a-z0-9.-], conventionally tld.author.name. It is also your install folder name, your KV namespace, and your bus prefix (ext.<id>.*), so pick it once and keep it.

An install is a folder holding the manifest, the binary it names, and any panel sources.

%LOCALAPPDATA%\vrOS\extensions\com.example.hello\ (Windows)
~/.local/share/vros/extensions/com.example.hello/ (Linux)
├─ vros-ext.toml
├─ hello.exe
└─ ui/hello.slint

Open Settings → Extensions and hit REFRESH. Your extension appears disabled, with the capabilities it asked for listed. Enable it, consent, and vrOS spawns it. The same page shows its status, its restart count, and its last error, and it is where the user removes it again.

The protocol reference covers every namespace on the wire: KV and secrets, the bus, panel properties and images, commands, settings, strings, audio, media, and face tracking, with their limits and error codes.

  • Hello is a deadline, not a formality. Miss five seconds and the host kills you and applies your restart policy.
  • Panel paths are checked at parse. Relative, forward slashes, components limited to letters, digits, dot, underscore and hyphen, ending in .slint. Traversal, absolute paths, drive letters and backslashes are refused before anything reads the disk.
  • Manifest fields target a host version. Unknown manifest fields are an error rather than a shrug, so a manifest carrying [[settings]] fails to parse on a host older than VXP 1.1, one with a number or select setting fails before VXP 1.3, and rail_label or icon fail on a host older than those fields. The one exception is width_mm on an overlay: an older host ignores it and shows the panel at its default width.
  • One JSON object per line. Batch arrays are refused, and a frame over 3 MiB is treated as corruption.
  • Say the version you speak. Send vxp::VXP_VERSION in hello, never a hard-coded string. Face updates and media status events only go to extensions that said hello with 1.5 and 1.4 or newer.
  • KV is your only durable state. Assume you will be restarted mid-anything and rebuild the rest.

The SDK crate is not on crates.io, and the repository holding it — the crate, the reference hello extension, the @vros/theme.slint module, and the authoring contract — is private today. The code is licensed MIT or Apache-2.0 at your option, and this page will link the crate and the example the moment they are public. If you want to build against it before then, say so in Discord.