Web SDK reference
Requests, typed services, events, cancellation, and connection lifecycle in @trove/plugin-sdk.
Verified September 11, 2026
On this page
Install and connect#
This reference covers SDK 0.0.1-alpha.1 and Bridge protocol v1. The CLI supplies the local kit: use trove new for new projects or the adoption wizard for an existing app.
import { host } from "@trove/plugin-sdk";
await host.ready();
console.log(host.context);
ready() returns Promise<void> and shares the transport connection. Every call also waits for readiness. Imports are lazy; default Host calls and context access require a Trove WebView.
| Context field | Meaning |
|---|---|
curioId, curioVersion | Curio ID and manifest version |
instanceId | Current UI instance |
apiVersion | 1 |
mode | "development" or "production" |
Context is read-only and identifies the caller bound by the Host.
Request and service clients#
Use host.xxx for Host capabilities, and acquire cross-Curio references by Curio ID before calling methods directly. This example requires generated types, a private handler, and the installed example provider:
import { host } from "@trove/plugin-sdk";
import { createClient as createDevice } from "./generated/backend.js";
const { text } = await host.clipboard.readText();
await host.storage.set({ key: "draft", value: text });
// Optional private client; its name belongs to your application.
const device = createDevice();
const local = await device.uppercase({ text: "hello" });
const TextTools = await host.curios.get("dev.example.text-tools");
console.log((await TextTools.uppercase({ text: local.text })).text);
Authors control their internal UI ↔ Backend organization. Device, api and backend are application variable names, not protocol requirements. The optional private generated client routes to @self. Existing HTTP/WebSocket or application abstractions may remain; see internal communication.
Methods without business parameters omit {}; CallOptions is the final argument. Contract-generated TypeScript does not replace runtime validation, and full JSON Schema validation of arbitrary business payloads is not enabled in the default Host.
Timeouts and cancellation#
CallOptions accepts timeoutMs?: number and signal?: AbortSignal as the final argument. A method without business parameters takes options alone. Defaults come from the contract, falling back to 30 seconds; file dialogs use five minutes. Explicit timeouts range from 1 to 2,147,483,647 ms, subject to Host limits.
import { host } from "@trove/plugin-sdk";
const controller = new AbortController();
const request = host.runtime.getHostInfo({
timeoutMs: 5_000,
signal: controller.signal,
});
// Connect controller.abort() to a cancel button or component cleanup.
console.log(await request);
A call’s total budget includes connection, cold start and execution. Reverse calls inherit the remaining budget. Timeout, cancellation and disconnection end the wait. Execution cancellation is cooperative, does not undo completed side effects, and does not replay requests.
Events and cleanup#
This uses the progress topic from the service guide; the provider must implement it:
import { createClient } from "./generated/text-tools.js";
const curio = createClient();
const controller = new AbortController();
const subscription = await curio.events.progress(
{ taskId: "preview" },
event => console.info(event.progress),
{ signal: controller.signal },
);
// Release when the owning screen or task is finished.
await subscription.close();
console.log((await subscription.closed).code);
TroveSubscription exposes id, idempotent close(): Promise<void>, and closed: Promise<{ code: string; message?: string }>. The closed promise reports the terminal reason separately from business payloads and settles on close, cancellation or connection loss.
An AbortSignal cancels establishment or closes an established subscription. Abort or close during component cleanup, including asynchronous setup. Do not disconnect a shared singleton for every component. Use a custom transport when a test needs its own connection.
Structured errors#
import { host, TroveError } from "@trove/plugin-sdk";
try {
await host.runtime.getHostInfo();
} catch (error) {
if (error instanceof TroveError) {
console.error(error.code, error.message, error.retryable, error.traceId);
} else {
console.error(error);
}
}
TroveError extends Error with code, message, optional data, retryable (false by default), and optional traceId. Branch on stable codes, show recovery steps, and correlate diagnostics by trace ID. Retryable does not trigger an automatic retry.
Custom clients and testing#
The main entry exports host, createHost, TroveError and public types. Import dynamic protocol tools and test adapters separately from @trove/plugin-sdk/transport:
import { createHost } from "@trove/plugin-sdk";
import { createMockBridge, createTransport } from "@trove/plugin-sdk/transport";
const transport = createTransport({
bridge: createMockBridge({
"host.clipboard.readText": () => ({ text: "test text" }),
}),
});
const host = createHost(transport);
try {
console.log((await host.clipboard.readText()).text);
} finally {
transport.disconnect();
}
createTransport({ bridge?, context?, onListenerError? }) owns a connection and can be passed to createHost or a generated createClient. Disconnect rejects pending calls and releases subscriptions; that transport cannot be revived. Custom adapters implement TroveBridge connect/send/disconnect and protocol v1. See mock limitations.
Migrate from SDK 0.1#
trove kit
npm install
trove generate
trove check
Use your project’s package manager for install. A Host-only UI without bindings can skip generate.
| Previous usage | Current usage |
|---|---|
trove.call("host.clipboard", "readText", {}) | host.clipboard.readText() |
trove.self.call("uppercase", params) | Generate a private client, then backend.uppercase(params) |
trove.service(serviceId).call("uppercase", params) | await host.curios.get(curioId), then curio.uppercase(params) |
createTroveClient / main-entry mock imports | createTransport / createMockBridge from transport |
getPaths().logs and disk logs | Current-run debug console and dev terminal |
Host, SDK, and Rust Kit currently remain at 0.0.1-alpha.1. Content digests distinguish new kit builds with the same version; reinstall dependencies, regenerate, rebuild, and package after updating. Add contract and bindings when adopting a private client; bindings.services[].curio enables ID inference. Keep the Curio ID so existing installed data retains its identity. Rust is the authored contract source; TypeScript is an optional generated client, not a second authored contract.
The old serviceId and new curioId are not interchangeable. For service dev.example.text-tools.files, acquire host.curios.get("dev.example.text-tools") and call its files member. A root service equal to the Curio ID keeps direct methods. Existing standalone generated service clients retain service-ID routing.
Curio references and lifecycle#
| Method | Semantics |
|---|---|
has(id, options?) | Exists in the effective installed/development registry; true even when disabled |
status(id, options?) | Snapshot without starting; missing providers have state not-installed |
get(id, options?) | Validate and construct a public capability reference; no startup or UI |
start(id, options?) | Validate and start a public Backend, wait for ready, return status; no UI |
All methods are asynchronous. CurioStatus contains id, installed, enabled, hasBackend, version (null if absent), and state. States are not-installed / disabled / stopped / starting / running / failed / cooldown. Snapshots can become stale immediately; handle errors on the actual call too.
import { host } from "@trove/plugin-sdk";
if (await host.curios.has("dev.example.text-tools")) {
const snapshot = await host.curios.status("dev.example.text-tools");
console.log(snapshot.state, snapshot.enabled);
await host.curios.start("dev.example.text-tools", { timeoutMs: 10_000 });
}
A reference owns neither a process nor a dedicated connection. Name, compose and share it freely; release its subscriptions when their tasks end. Calls made after a restart route through Host again. Failed in-flight calls are not replayed, and old subscriptions do not resume automatically. Reacquire after installing a new version to refresh the capability description and check version requirements.
An installed public provider keeps its Backend running when its window closes. Configure idleTimeoutMs to reclaim it after calls/subscriptions become idle; zero disables idle reclamation. Host exit, disable and uninstall end the provider. Closing a development window instead ends that development session. Discarding a reference cannot stop a process other Curios use.