Skip to content

Script API Reference

All functions and objects available in the Muxit script sandbox. All calls are synchronous — no await needed (await still works if you prefer).

Drag-and-drop

Every primitive on this page is also draggable. Open the Connectors panel — the Script API section at the top lists delay, ai, say, log.*, emit, on, stream, ask.*, and the others. Drop one into a script editor to insert the call.

Device Access

APIDescription
connector(name)Get a device proxy by name
device(name)Alias for connector()
javascript
const psu = connector("psu");
psu.voltage                  // read property (no parens)
psu.voltage = 12             // write property (assignment)
psu.reset()                  // execute action
psu.rampTo(24)               // call custom method

Logging

APIDescription
log.info(msg)Info-level log
log.warn(msg)Warning-level log
log.error(msg)Error-level log
log.debug(msg)Debug-level log
console.log(msg)Alias for log.info()

Events

APIDescription
emit(event, data)Publish event (namespaced as script:<event>)
on(event, handler)Subscribe to events (auto-cleaned on stop)
stream(connector, stream, data)Emit stream data to dashboards

Timing

APIDescription
delay(ms)Sleep (abortable on stop). Always use in loops to avoid excessive CPU usage
timestamp()Current ISO 8601 UTC string

AI

APIDescriptionTier
ai(prompt)Single-shot LLM call, returns string responseFree (with local LLM) / Maker (with Muxit-managed)
ai(prompt, image)LLM call with base64 JPEG image — the model sees the picture and answers in textFree (with local LLM) / Maker (with Muxit-managed)
ai(prompt, images[])LLM call with several images for comparison — array of base64 strings or { image, label } objectsFree (with local LLM) / Maker (with Muxit-managed)
say(text, opts?)Send message to Chat Panel; spoken aloud when the speaker toggle in the top status strip is on. opts.emotion hints at delivery style.Free

ai() routes through the active LLM provider configured in Settings → AI Services. When pointed at a local LLM (Ollama, LM Studio, any OpenAI-compatible endpoint) it works on every tier including Free — bring-your-own model, no Muxit credits consumed. When pointed at the Muxit-managed cloud proxy it requires a Maker subscription; Free-tier users get a clean AI_REQUIRES_PRO error (legacy error name — the actual gating tier is Maker). The image overload runs on the same gate as the active provider; what's gated to Pro is the agentic Vision AI tool surface in the Chat Panel and MCP (take_snapshot, identify_objects, OpenCV trackers, spatial mapping) — not the explicit ai(prompt, image) call from a script. say() uses browser text-to-speech and is available on all tiers.

say() details:

say(text, opts?) queues text for the dashboard's text-to-speech engine and shows it as a script bubble in the chat panel. Calls from any script land in a single FIFO queue on the dashboard so they can never overlap or replay as a burst — utterances finish in the order they were emitted.

opts.emotion is a hint string ("excited", "sad", "whisper", "serious", …). The browser TTS engine approximates emotions with rate/pitch tweaks; server-side TTS providers (added in a later release) honour them natively.

js
say("Reading complete.")
say("Calibration done!", { emotion: "excited" })
say("Limit switch triggered.", { emotion: "serious" })

On Free? You have two ways to use ai() without paying for Muxit-managed credits: (1) point Muxit's chat / ai() at a local LLM (Ollama, LM Studio, any OpenAI-compatible endpoint) in Settings → AI Services — it runs on every tier; or (2) use Muxit's MCP server with your own AI client (Claude Desktop, Claude Code, ChatGPT) — that exposes the same connectors and scripts but drives them from outside the Muxit window. See the AI guide.

ai() details:

  • Returns a text string (non-streaming, ~1024 max tokens)
  • Uses the Scripts & tools model configured in Settings → AI Services — set it to point scripts and image recognition at a different (e.g. local, image-optimized) model than the chat assistant. Leave it blank to fall back to the chat model
  • No tool calling — for quick inference only (classify, extract, describe)
  • No conversation history — each call is independent
  • image parameter: base64-encoded JPEG (e.g., from connector('webcam').snapshot)
  • Multiple images: pass an array as the second argument to attach several frames in one call — useful for "what changed?" (before/after) or "is this like A or B?" (reference matching). Items are either bare base64 strings or { image, label?, detail? } objects; a label is sent as a short text block right before its image so the model can refer to it by name. Up to 8 images per call (extras are dropped with a script-log warning).

Where do the images come from? Any source that yields base64 JPEG:

  • Live cameraconnector('webcam').snapshot (or the Onvif driver).
  • Reference image from disk — the FileAccess driver: connector('fileaccess').openBinary('refs/good.jpg').read() returns plain base64, ready to pass straight to ai(). Prefer JPEG reference files; PNG/WebP also work (the MIME type is detected automatically).
js
// Text inference
const result = ai("Is 85°C too hot for this motor?");

// Vision analysis (single image)
const snap = connector('webcam').snapshot;
const analysis = ai("Is the part in the correct position?", snap);

// Has anything changed vs the previous snapshot?
const before = connector('webcam').snapshot;
delay(2000);
const after = connector('webcam').snapshot;
const diff = ai("Did anything move or change between these two photos?", [before, after]);

// Is the workpiece like reference A or B? (labelled comparison)
const fa = connector('fileaccess');
const verdict = ai("Does the current part match A (good) or B (rejected)?", [
  { image: fa.openBinary('refs/good.jpg').read(),     label: "A: good" },
  { image: fa.openBinary('refs/rejected.jpg').read(), label: "B: rejected" },
  { image: connector('webcam').snapshot,              label: "Current" },
]);

User Interaction

Block the script until a dashboard user answers, or the timeout fires.

APIDescription
ask.confirm(message, opts?)Yes/No prompt — returns true / false
ask.choose(message, choices, opts?)Pick one of the strings in choices
ask.text(message, opts?)Free-form text — returns the user's string

Options (opts):

FieldDescription
timeoutMilliseconds (≥ 1000) to wait before falling back to default / throwing
defaultValue returned if the timeout expires. Omit to throw PROMPT_TIMEOUT
requireObserverWhen true, the timeout clock only ticks while ≥ 1 dashboard is subscribed
  • Return values are indistinguishable from human answers — consult script logs if you need to know whether a default was used.
  • An omitted timeout waits indefinitely (until the script is stopped).
  • timeout < 1000 is rejected at call time as a likely typo (seconds vs milliseconds).
js
// Yes/no with a 30 second timeout, defaulting to "no".
if (!ask.confirm('Home all axes before run?', { timeout: 30000, default: false })) {
    log.warn('User declined — skipping homing');
}

// Force operator attendance: clock pauses if all dashboards disconnect.
const port = ask.choose('Select instrument port', ['COM1','COM2','COM3'], {
    requireObserver: true,
    timeout: 120000,
    default: 'COM1',
});

// No default → throws PROMPT_TIMEOUT if nobody answers.
const label = ask.text('Run label?', { timeout: 60000 });

File I/O

The sandbox has no fs, require, or import — for file access, use a FileAccess connector (the default workspace ships one named files, sandboxed to workspace/data/).

Bind the path once with .openText(path) (or .openBinary(path) for base64 I/O) and call methods on the returned handle — the rest of the script doesn't repeat the path:

APIDescription
connector('files').openText(path, delimiter?)Returns a TextFileHandle
connector('files').openBinary(path)Returns a BinaryFileHandle (base64 I/O)
f.write(content)Overwrite. String or array (joined as a CSV row)
f.append(content)Append. String or array. null / undefined is a no-op
f.read() / f.readLines()Read as UTF-8 text or split into lines
f.exists / f.size / f.info()Metadata
f.rename(newPath) / f.delete()Mutations (delete is idempotent; rename updates the handle in place)
connector('files').list(path?)Returns { files, dirs } for a directory
js
const files = connector('files');
const csv = files.openText('temps.csv');
csv.write('time,temp_c\n');
while (script.running) {
  csv.append([timestamp(), connector('sensor').temperature]);  // array → CSV row, auto-quoted
  await delay(1000);
}

Open temps.csv in a tab — the chart/table refreshes automatically each time the script appends a row. Full action list in the FileAccess driver reference.

Script Lifecycle

APIDescription
script.runningtrue while active, false after stop
script.nameThis script's filename (without .js)

Pressing Stop interrupts the V8 engine synchronously — statements after the main loop, and finally blocks, are not guaranteed to run. Put setup and safe-state writes before the loop and design each iteration to be safe to abort.

Standard Globals

Math, JSON, Date, parseInt, parseFloat, isNaN, isFinite, Number, String, Boolean, Array, Object, Map, Set, Promise

Not Available

require, import, fs, process, net, http, child_process, eval, Proxy, Reflect, global, globalThis.

For filesystem access, use a FileAccess connector instead of require('fs').


For detailed explanations and examples, see the Script Guide.

Muxit — Hardware Orchestration Platform