Skip to content

Repository files navigation

Robonine plugins

Official plugins for the Robonine educational robotics platform, maintained by Robonine. Licensed under MIT.

Included plugins

PluginSlugDescription
ArUco detectorrobonine/arucoDetects ArUco fiducial markers in a camera feed; exposes detections to other plugins
Calibrate motorsrobonine/calibrate-motorsPlace joints in home position and save servo offsets
Calibrate robotrobonine/calibrate-robotMove joints through their full range to set encoder limits
Control robotrobonine/control-robotManually move each joint using on-screen sliders
Force sensorrobonine/force-sensorRead and display force sensor measurements
OpenCVrobonine/opencvLoads OpenCV.js and exposes it as a background service
Set motor IDsrobonine/set-motor-idsSequentially assign IDs to servos
Teleoperaterobonine/teleoperateMirror a leader arm to a follower arm with live camera feed
WebMCProbonine/webmcpExposes robot state and control to AI assistants via the Model Context Protocol

Building

npm install
npm run build

Output is written to dist/ as one .robo9 file per plugin — a gzip-compressed ESM bundle ready to be served by the platform.

To build a single plugin, pass its slug:

node --import tsx/esm scripts/buildPlugins.ts aruco

What gets bundled: everything except React. React is not bundled — plugins share the host app's React instance via window.__ROBONINE__. Plugin-local node_modules (e.g. @mcp-b/webmcp-polyfill in webmcp/) are bundled into that plugin's output.

Developing plugins

See the Plugin SDK for the full getting-started guide. This section documents the complete API surface.

Type-checking

npm install
npm run typecheck

Plugin structure

my-plugin/
src/
index.ts # re-exports manifest, PluginRoot, and optionally PluginService
manifest.ts # plugin metadata
plugin.tsx # React UI component (PluginRoot)
service.ts # optional background service (PluginService)
translations.ts # i18n strings

src/index.ts

export{manifest}from'./manifest'export{PluginRoot}from'./plugin'export{PluginService}from'./service'// omit if no serviceexport{manifestasdefault}from'./manifest'

Manifest

importtype{PluginManifest}from'@robonine/plugin-sdk'exportconstmanifest: PluginManifest={sdkVersion: '1',vendor: 'your-name',// lowercase, URL-safe namespaceslug: 'my-plugin',// lowercase, URL-safe identifiername: {en: 'My plugin',ru: 'Мой плагин'},description: {en: 'What it does.',ru: 'Что делает.'},icon: 'Wrench',// Lucide icon name, or inline SVG stringscopes: ['robot.control'],// Optional — if this plugin exposes a service to others:provides: 'my-service',// Optional — other plugins this one depends on:dependencies: [{vendor: 'robonine',slug: 'opencv'}],}

Scopes

Declare every capability your plugin needs. The platform enforces these at install and runtime.

ScopeGrants
(none)UI only, no hardware access
robot.readRead servo positions and register values
robot.controlSend position commands
robot.calibrationWrite calibration data to EEPROM
robot.configWrite servo configuration
robot.leaderSecond independent robot connection (leader role)
robot.localRequires physical local presence
camera.readAccess camera feed
installRegister a background service (PluginService)
user.authRequire user sign-in
user.readRead user name and email

PluginContext

The context prop passed to PluginRoot.

interfacePluginContext{locale: string// Robot access — pass 'default' for the main arm, 'leader' for the second arm.// Requires the robot.leader scope to use 'leader'.robot(role: 'default'|'leader'): RobotHandle// Access a service provided by another installed plugin (requires install scope on that plugin).// Returns null if the plugin is not installed or its service hasn't started.service(slug: string): unknown|null// Camera feeds available in the current session.cameras: CameraHandle[]// 3D robot visualisation component.WorldView: React.ForwardRefExoticComponent<WorldViewProps&React.RefAttributes<WorldViewApi>>// Pre-styled UI primitives matching the platform theme.ui: {Button: React.ComponentType<React.ButtonHTMLAttributes<HTMLButtonElement>&{variant?: string;className?: string}>}}

RobotHandle

interfaceRobotHandle{connection: {connected: boolean}openConnectDialog(): void// Prompts the user to confirm before starting motion. Returns true if confirmed.showSafetyWarning(): Promise<boolean>// Joint-to-servo mapping for the connected model. Null until a robot is connected.robotConfig: RobotConfig|nullservo: ServoHandle}interfaceRobotConfig{modelId: stringjointServoId: Record<string,number>}interfaceServoHandle{readJointPositions(): Promise<number[]|null>setJointPositions(positions: number[]): Promise<void>// Registers a hardware emergency stop. Returns a cleanup function to unregister.registerEmergencyStop(): ()=>void}

CameraHandle

interfaceCameraHandle{id: stringlabel: stringsource: 'local'|'remote'stream: MediaStream}

WorldView

interfaceWorldViewProps{motionMode?: 'instant'onLoad?: (joints: JointInfo[])=>void}interfaceWorldViewApi{setJoint(name: string,value: number): void}interfaceJointInfo{name: string}

PluginService

A background service runs for the lifetime of the plugin and can be accessed by other plugins via context.service(slug). Export a PluginService factory and declare provides and install in the manifest.

importtype{PluginServiceFactory}from'@robonine/plugin-sdk'exportconstPluginService: PluginServiceFactory=(ctx)=>{// ctx is PluginServiceContext (see below).// Return any object — other plugins receive it from context.service(slug).return{doSomething(){/* … */},}}

PluginServiceContext

The ctx argument passed to a PluginServiceFactory.

interfacePluginServiceContext{// Access services from other installed plugins.service(slug: string): unknown|null// High-level platform APIs available to services:getRobotPosition(role: ConnectionRole): Promise<RobotPosition|null>listConnectedRobots(): Promise<ConnectedRobot[]>listUserRobots(): Promise<UserRobot[]>listUserPaths(): Promise<MotionPath[]>readPath(id: string): Promise<MotionPath|null>stopRobot(role: ConnectionRole): Promise<void>moveToPosition(x: number,y: number,z: number,role: ConnectionRole): Promise<void>goHome(role: ConnectionRole): Promise<void>executePath(id: string,role: ConnectionRole): Promise<void>}typeConnectionRole='default'|'leader'

Localization

English (en) is the only required locale. Additional languages are optional.

// translations.tsexportconsttranslations={en: {title: 'My plugin',connectPrompt: 'Connect your robot to get started.'},ru: {title: 'Мой плагин',connectPrompt: 'Подключите робота, чтобы начать.'},}satisfiesRecord<string,Record<string,string>>
// plugin.tsxconstt=useMemo(()=>translations[context.localeaskeyoftypeoftranslations]??translations.en,[context.locale],)

Releases

Packages

Contributors

Languages