Skip to content
Masko logomasko
Docs
Documentation

TypeScript and Electron

Control a floating macOS desktop mascot from Electron main or Node.js. The SDK connects to the same Swift renderer as the CLI and displays the mascot in a separate window above other windows. It currently has no component for placing the mascot inside an Electron window, browser page, or React layout.

Install

Install the TypeScript SDK from npm:

npm install @masko/sdk

The standalone SDK supports Node.js 20+. The renderer requires macOS 14+. Install it using the terminal setup, or bundle it with your Electron app as described below.

Show a mascot

In Electron main, wait for the app to be ready:

import { app } from 'electron';
import { Masko } from '@masko/sdk';

await app.whenReady();
const ko = await Masko.mascot('ko', { size: 180 });
await ko.show();
await ko.state('working');
const play = await ko.play('celebrate');
// Optional: wait before continuing a completion-dependent step.
await ko.waitForPlay(play.play_id);

// When your feature or app closes:
await ko.dispose();

For Node.js, omit the Electron import and app.whenReady(). Keep the handle while the mascot is needed. Masko.mascot() starts or connects to the renderer automatically. Each call creates an independent instance, even if you use the same friendly name.

show(), state(), play() and prepare() return acceptance receipts immediately. Loading and animation continue in the host. Requests are ordered, so state/play can follow show while loading. Use ko.waitForRequest(receipt.request_id) for the first frame, state preparation/application, or full media preparation; ko.waitForPlay(receipt.play_id) waits through the whole performance and return. Public Ko requires no login. For Studio authoring, use the REST bridge below. Older named-character preparation requires the older backend; it is not supported by the migrated Studio routes.

Control and cleanup

CallResult
ko.state('working')Hold a state.
ko.clearState()Release the held state; an active play finishes before returning to default.
ko.play('celebrate')Perform a reaction, then return.
ko.play('celebrate', { instant: true })Skip entry transitions; play the performance and its return path.
ko.startPlay('celebrate')Alias for play(), returning an acceptance receipt.
ko.waitForRequest(id) / ko.waitForPlay(id)Explicitly wait for preparation/application or full playback.
ko.cancel()Follow the authored return path.
ko.resize(180)Set size in screen points, from 64 to 720.
ko.position(400, 100)Move using bottom-left screen coordinates.
ko.hide() / ko.show()Pause while hidden, then resume.
ko.prepare()Download all graph media.
ko.status()Inspect current state, capabilities and rendering status.
ko.dispose()Permanently remove this instance. Safe to call again.

These methods return promises. Await cleanup before quitting Electron. Disposing a mascot does not stop the host or affect another app’s instances. A crash can leave an instance running until the user closes it.

Timeouts and errors

Control methods accept { signal, timeoutMs }. Ordinary requests default to 120 seconds; waitForRequest() and waitForPlay() wait up to five minutes. Timeout or abort stops waiting, but accepted work may continue. Inspect (await ko.status()).requests for background failures and recent request progress before retrying; use cancel() or dispose() when appropriate.

Errors are MaskoError objects with code, message, and optional details. Handle busy, timeout, aborted, disposed, instance_not_found, host_unavailable, and unsupported_platform. If the user closes the mascot, only an explicit show() recreates its window.

Bundle the renderer

Build the native app with ./scripts/package.sh. Include the whole Masko Runtime.app bundle outside app.asar:

Your App.app/Contents/Resources/Masko Runtime.app

The SDK discovers that resource automatically and uses a host isolated from the CLI and other runtime installations. A replacement runtime gets a fresh host, so an older running CLI host does not block the app. With this layout, users do not need a separate Masko installation. The bundle contains the renderer; character media still downloads from the hosted version.

Local builds use the build Mac’s architecture and ad-hoc signing. Public distribution requires signing and notarizing both the nested runtime and your app. The runtime’s optional private SkyLight pinning is not suitable for Mac App Store distribution.

Connect your app’s UI

Keep the SDK in Electron main. Expose a small, validated preload bridge for your specific actions; do not pass arbitrary shell commands or privileged SDK access to renderer content. Keep authoring credentials out of shipped apps. Use public versions for anonymous delivery. For private Studio releases, your backend checks access and fetches signed delivery. This payload is not a drop-in replacement for the retired character-access URL; verify the installed runtime interface before connecting it. See application access.

The Electron example includes two independent mascots, a typed bridge, cleanup during quit, and bundled-runtime packaging. The SDK README also covers explicit connections and custom host settings.

Studio authoring

The matching local SDK and native CLI expose the Studio REST bridge:

const client = Masko.client();
const mascots = await client.studio('GET', 'mascots');
const job = await client.studio('GET', 'jobs/JOB_ID?api_version=2026-09-26');

This bridge uses your saved workspace login and requires matching client source; it has not been published to npm or the installer by this migration. Keep it in trusted tooling, never expose authoring credentials to renderer content.

Use /mascots/MASCOT_ID/generate for generation, persist the returned job ID and poll /jobs/JOB_ID?api_version=2026-09-26. Character registration, client.character(...), character plans/runs and run-based watchers target the retired backend. See Studio migration for canvas history, publication and release delivery.