JS Driver SDK (Tier 1)
JavaScript drivers are .driver.js files in workspace/drivers/. They run in a sandboxed V8 engine with access to transport APIs for communicating with hardware.
Structure
export default {
meta: {
name: "MyDriver",
version: "1.0.0",
description: "Short description of what this driver does.",
group: "instruments", // DriverGroup: "instruments", "motion", "communication", "utilities"
// requiresSafetyGates: false, // optional, default true — set false only for drivers with no hardware / no destructive actions
properties: { voltage: { type: "number", access: "rw", unit: "V", description: "Output voltage" } },
actions: { reset: { description: "Reset device" } },
streams: ["output"],
},
async init(config) { /* set up transport, initialize device */ },
async get(property) { /* return property value */ },
async set(property, value) { /* set property value */ },
async execute(action, args) { /* execute action, return result */ },
async shutdown() { /* clean up */ },
};Connector starter template
Ship a template.js file next to your .driver.js. It is the starter connector content users see when creating a connector for your driver. The packager copies it into the .muxdriver at the package root; the server reads it at scan time. This file is required — node drivers.js build refuses to package a driver without it.
drivers/js/mydriver/
├── mydriver.driver.js
├── manifest.json
└── template.js ← starter connector content (required)Property and action descriptions
Every property and action entry supports two doc fields:
| Field | When it shows | Use it for |
|---|---|---|
description | Always — AI system prompt summary, IntelliSense hover, driver doc page | One-line summary. Keep it tight. |
details | On-demand — driver doc page ("Show details" toggle), IntelliSense hover, fetched via get_connector_schema / get_driver_schema | Short markdown: parameter enums, side effects, failure modes, truncation caps, non-obvious invariants. |
details is never included in the upfront AI system prompt — the LLM only pays for it when it explicitly fetches the full schema. Treat details as the place for everything that would take a user (or an AI agent) two round trips of trial and error to discover from the one-liner.
actions: {
scanBaud: {
description: "Close port, probe candidate baud rates, reopen at a chosen baud",
args: { portPath: "string?", candidates: "array?", durationMs: "number?", reopenAt: "string?" },
details: `\`reopenAt\` accepts \`"original"\` (default), \`"recommended"\`, or a numeric baud.
Results are ranked by printable-ratio descending, \`bytes\` as the tiebreaker.
If every candidate errors, \`recommended\` falls back to the original baud.`
},
},Markdown support: paragraphs, - bullet lists, fenced code blocks, inline bold, italic, and `code`.
Opting out of the safety gate
Set meta.requiresSafetyGates: false in your driver export when the driver has no path to physical hardware and no destructive actions (e.g. a webcam, a file-access driver). Connectors of that driver then bypass the safety gate entirely — no limit checks, no confirmations, no audit rows. Default is true. See Safety & access levels for guidance.
Available APIs
| API | Description |
|---|---|
createTcpTransport(host, port, opts) | Create TCP transport ({ delimiter, timeout }) |
createSerialTransport(path, opts) | Create serial transport ({ baudRate, delimiter, timeout }) |
__emitStream(stream, data) | Emit streaming data |
log.debug/info/warn/error(...) | Logging |
timestamp() | ISO timestamp string |
Security Sandbox
JS drivers run in an isolated ClearScript V8 engine with these restrictions:
require(),import()— blockedprocess,global— deleted fromglobalThis- No access to
fs,net,child_process, or any Node.js APIs - Path traversal —
.driver.jsfiles must reside withinworkspace/drivers/ - Name collision — JS drivers cannot shadow built-in (Tier 0) driver names