Skip to content

SYMBaiEX/OpenController

OpenController

CI Release License: MIT Security Policy Code of Conduct

OpenController is an open-source TypeScript SDK for giving AI agents a real controller interface.

Instead of wiring one-off scripts into every game, simulator, overlay, emulator, or browser app, OpenController gives agents a small, typed API that behaves like a gamepad: press buttons, move sticks, pull triggers, run combos, stream state, record replays, receive host feedback, and visualize exactly what happened.

The goal is simple: make controller input a clean, inspectable, reusable software primitive for local agents, tests, accessibility tools, and experiments.

import { createController } from "@opencontroller/core";

const controller = await createController({
  profile: "xbox",
  adapter: "dry-run"
});

await controller.press("A", 100);
await controller.press("RT", { durationMs: 120, pressure: 0.35 });
await controller.moveStick("LEFT", { x: 0, y: -1 }, 500);
await controller.trigger("RT", 0.5, 200);
await controller.neutral();
await controller.disconnect();

Why OpenController

AI agents need a better input layer than brittle app-specific scripts. A controller is already the shared language for games, emulators, robotics simulators, browser experiments, overlays, accessibility tooling, and testing harnesses.

OpenController turns that language into a reusable controller layer:

  • agent code can express intent as controller actions
  • apps can consume input through a stable state stream
  • replays can show exactly what the agent did
  • safety policies can constrain dangerous or repetitive input
  • native bridge processes can translate state into virtual device reports

The long-term aim is full virtual controller emulation with a clean npm SDK on top: agent logic in TypeScript, host integration through adapters, and native device bridges where the operating system requires them.

What You Get

  • Typed controller runtime for Xbox, PlayStation, Switch, generic HID, and keyboard/mouse-style profiles
  • Button helper options for analog pressure, including trigger pressure mapped into XInput/HID report bytes
  • Explicit persistent-state helpers for held buttons, held sticks, analog triggers, and exact D-pad directions
  • Atomic state patches for applying a whole agent decision as one controller update
  • Cardinal and diagonal D-pad commands mapped into combined controller state and XInput/HID report bits
  • PlayStation touchpad and PlayStation/Switch motion state commands for browser, replay, and WebSocket integrations
  • Virtual device battery and connection-health status for agents, replay logs, WebSocket streams, and native bridge JSONL extensions
  • Host rumble/light output tracking in state.feedback, replay logs, and controller.onFeedback(...)
  • Safety guardrails for rate limits, max hold durations, disabled buttons, repeated input loops, and neutral-on-error behavior
  • Replay logs for commands, state snapshots, annotations, and errors
  • Adapter model with dry-run, WebSocket, XInput report, native bridge, and native process output backends
  • XInput-compatible binary report encoding for native virtual-device bridges
  • Canonical HID gamepad report descriptor and input report encoder
  • PlayStation extended HID report bytes for touchpad contacts and motion vectors
  • Versioned JSONL native bridge protocol with XInput/HID payloads and optional profile-specific HID payloads plus connect/disconnect lifecycle messages and touchpad/motion/status extensions
  • Controller hub for managing multiple virtual controllers
  • React and OBS-friendly overlays for showing controller state
  • CLI commands for doctor, native backend doctor, native bridge smoke tests, dry-run tests, overlay, replay, and starter action maps
  • A playable two-agent browser fighting game demo

OpenController is designed for local agents, accessibility tooling, testing, research, plugins, emulators, stream overlays, and controlled single-player experiments. It is not intended for anti-cheat bypasses, stealth automation, or online competitive game automation.

Start Here

Clone the repo, install dependencies, and run the flagship OpenController demo:

bun install
bun run dev:fighter

Open the game:

http://127.0.0.1:5173/

Open the controller telemetry panel:

http://127.0.0.1:5173/controllers

Run the release checks:

bun run lint
bun run typecheck
bun run test
bun run build
bun run pack:check

Run a headless benchmark duel:

bun --cwd examples/agent-fighter headless --duration-ms 15000

Where It Stands

OpenController is ready to use from source today. The core runtime, package layout, CLI, overlays, examples, docs, CI, release notes, and npm package manifests are in place.

The SDK surface is complete enough for local builds, demos, browser games, WebSocket integrations, overlays, replay capture, touchpad/motion state, HID report streams, host rumble/light feedback, and native bridge prototyping. It is not yet a full cross-platform native virtual controller driver stack.

The current emulation boundary is the adapter layer, XInput-compatible binary report encoding, descriptor-backed HID gamepad reports, PlayStation extended HID report bytes for touchpad/motion data, Switch HID motion reports, a versioned JSONL protocol for native bridge processes, the first Linux uinput bridge package, Windows VHF host bridge helpers, and macOS DriverKit host bridge helpers. Touchpad and motion commands flow through the runtime, replay logs, dry-run, WebSocket state stream, native bridge extensions, and profile HID payloads today. Virtual device battery and connection-health status flows through state snapshots, replay logs, WebSocket streams, and native bridge extensions. Host rumble and light feedback flows back through HID adapters, native bridge feedback JSONL, and controller.onFeedback(...).

The next milestone is turning those host bridge surfaces into signed, installable native device flows and mapping more host feedback into physical platform APIs.

If you are evaluating it for another project, use it now for controller-state or command-stream integrations. Linux users can start testing the uinput bridge. Windows users can inspect VHF/HID assets, legacy ViGEmBus compatibility, and XUSB report mapping. macOS users can generate DriverKit HID assets, wrap a signed host bridge process, and check local signing/tool readiness.

Run OpenController Agent Fighter

OpenController Agent Fighter is the flagship demo: two autonomous agents drive two separate OpenController channels and fight inside a browser game. The game reads controller state only, so the agents interact through the same input surface a player would.

bun install
bun run dev:fighter

Open the game:

http://127.0.0.1:5173/

Open the controller telemetry panel:

http://127.0.0.1:5173/controllers

Agents start stopped by default. Use the telemetry panel to start, stop, and reset the duel.

If OPENAI_API_KEY is present, OpenController Agent Fighter uses the OpenAI Responses API for agent decisions. Without a key, local policies drive the same controller channels, so the demo still works offline.

Packages

Package Purpose
@opencontroller/core Controller runtime, profiles, adapters, safety, state, replay logs
@opencontroller/overlay React overlays, canvas rendering helpers, OBS browser-source server
@opencontroller/cli Doctor, test, overlay, replay, and init commands
@opencontroller/native One-import native host bridge adapter selection for Linux, Windows, and macOS
@opencontroller/native-linux-uinput Linux /dev/uinput bridge helper and event mapping
@opencontroller/native-windows-virtual-gamepad Windows VHF/HID assets, XUSB helpers, and legacy ViGEmBus diagnostics
@opencontroller/native-macos-driverkit macOS DriverKit HID assets, host bridge adapter factory, and local authoring diagnostics

Install

OpenController is published on npm under the @opencontroller scope.

npm install @opencontroller/core

For overlays:

npm install @opencontroller/core @opencontroller/overlay

For CLI workflows:

npm install -D @opencontroller/cli

For cross-platform native bridge work:

npm install @opencontroller/core @opencontroller/native

For Linux native bridge work:

npm install @opencontroller/core @opencontroller/native-linux-uinput

For Windows virtual gamepad compatibility work:

npm install @opencontroller/core @opencontroller/native-windows-virtual-gamepad

For macOS DriverKit virtual HID work:

npm install @opencontroller/core @opencontroller/native-macos-driverkit

Source packages in this repository use workspace links for local development. External projects should install the npm packages shown above.

Security And Safety

OpenController is designed for local agents, accessibility tooling, testing, research, emulators, stream overlays, and controlled single-player experiments. It is not intended for anti-cheat bypasses, stealth automation, credential theft, or online competitive game automation.

Native setup helpers generate reviewed assets, plans, and commands. They do not silently install unsigned drivers, alter system permissions, or perform privileged changes without explicit user action.

Please read SECURITY.md before reporting vulnerabilities, CODE_OF_CONDUCT.md before participating, and CONTRIBUTING.md for the release-check and contribution flow.

SDK Surface

The core package exports:

  • createController for a single virtual controller runtime
  • createControllerHub for multi-controller sessions
  • built-in profiles for common controller families
  • dry-run, WebSocket, and XInput report adapters
  • native process bridge adapter for helper processes
  • native bridge JSONL protocol helpers with direct HID report bytes
  • safety policies and replay logging
  • XInput report helpers from @opencontroller/core/hid
  • HID gamepad report descriptor and report helpers from @opencontroller/core/hid
  • PlayStation extended HID report descriptor and report helpers from @opencontroller/core/hid
  • bridge helpers from @opencontroller/core/bridge
  • profile, action-map, and browser-friendly entry points

For npm consumers, @opencontroller/core is the main package. Add overlays, CLI workflows, or native host bridge packages only when your app needs them.

The unified native package adds:

  • createNativeHostBridgeAdapter for selecting the host bridge backend for the current platform
  • resolveNativeHostBridgeBackend and defaultNativeHostBridgePath for install and packaging workflows
  • backend aliases for Linux uinput, Windows VHF, and macOS DriverKit

The Linux native package adds:

  • OpenController JSONL to Linux event mapping
  • host diagnostics for /dev/uinput, module, and permission readiness
  • C helper source for /dev/uinput
  • build helper for producing opencontroller-uinput-bridge
  • profile HID parsing for PlayStation touchpad reports and Switch motion reports before falling back to generic HID/XInput payloads
  • Linux FF_RUMBLE feedback handling with native bridge feedback JSONL
  • Linux EV_LED player-indicator feedback through the shared "hid-gamepad-lights" native bridge payload
  • setup helper for compiling the bridge and printing reviewed udev-rule commands

The Windows native package adds:

  • VHF-ready HID descriptor, input report helpers, Switch/PlayStation report profiles, and rumble/light feedback contract
  • INF, WDK C source, host bridge C source, C-array asset generators, and a host bridge adapter factory for a maintained Windows VHF driver path
  • generated VHF source templates that capture HID output reports and emit native bridge feedback JSONL
  • setup helper for staging reviewed VHF driver/host source files and install/test commands without privileged changes
  • XUSB report helpers
  • legacy ViGEmBus service diagnostics
  • opencontroller-windows-gamepad-doctor

The macOS native package adds:

  • DriverKit-ready HID descriptor, input report helpers, Switch/PlayStation report profiles, and rumble/light output report codecs
  • Info.plist and entitlement templates for a virtual HID gamepad dext
  • C++ DriverKit source and byte-array asset generation, including setReport rumble/light capture plus copyRumbleReport and copyLightReport hooks for signed host bridges
  • host bridge adapter factory for a signed DriverKit host process
  • setup helper for staging reviewed DriverKit/host source files and activation/test commands without privileged changes
  • opencontroller-macos-driverkit-doctor

Core API

const controller = await createController({
  id: "player-1",
  profile: "xbox",
  adapter: "websocket",
  url: "ws://127.0.0.1:5173/controller",
  replay: {
    enabled: true,
    source: "my-agent"
  },
  safety: {
    maxCommandsPerSecond: 20,
    maxButtonHoldMs: 1000,
    disabledButtons: ["GUIDE", "START"]
  }
});

await controller.press("X", {
  durationMs: 80,
  context: { intent: "light_attack" }
});
await controller.press("RT", { durationMs: 120, pressure: 0.5 });
await controller.combo(["A", "X"], 60, 20, { intent: "jump_attack" });
await controller.moveStick("LEFT", { x: 1, y: 0 }, 250);
await controller.setButton("LB", true);
await controller.setStick("LEFT", { x: 0.75, y: -0.2 });
await controller.setTrigger("RT", 0.4);
await controller.setDpad("UP_RIGHT");
await controller.setStatus({
  battery: { level: 0.72, charging: true, wired: false },
  connection: { quality: 0.95, latencyMs: 6, packetLoss: 0 }
});
await controller.setState({
  buttons: { LB: true },
  triggers: { RT: 0.2 },
  sticks: { LEFT: { x: 0.5, y: -0.2 } },
  dpad: "NEUTRAL",
  status: { battery: { low: false } }
});
await controller.setButton("LB", false);
await controller.neutral();

Use press, moveStick, trigger, dpad, and combo for timed actions that return to neutral automatically. Use setButton, setStick, setTrigger, and setDpad when an agent needs to hold an exact controller state across planning steps. Use setStatus for virtual battery and connection-health telemetry. Use setState when one agent tick should update several controls or status fields atomically and avoid intermediate controller states.

Multiple Controllers

import { createControllerHub } from "@opencontroller/core";

const hub = await createControllerHub({
  controllers: [
    { id: "player-1", profile: "xbox", adapter: "dry-run" },
    { id: "player-2", profile: "xbox", adapter: "dry-run" }
  ]
});

await hub.get("player-1").press("A", 80);
await hub.get("player-2").press("X", 80);

console.log(hub.states());
await hub.disconnectAll();

XInput Reports

import {
  XInputReportAdapter,
  createController,
  decodeXInputReport
} from "@opencontroller/core";

const adapter = new XInputReportAdapter({
  onReport(report) {
    const decoded = decodeXInputReport(report.bytes);
    console.log(decoded);
  }
});

const controller = await createController({ profile: "xbox", adapter });
await controller.press("A", 80);

XInput reports are the handoff point for native bridge processes. They do not install a driver by themselves, but they provide the packed state a driver or local bridge needs.

HID Gamepad Reports

import { HidGamepadReportAdapter } from "@opencontroller/core";
import {
  encodeHidGamepadReport,
  encodeHidGamepadLightReport,
  encodeHidGamepadRumbleReport,
  hidGamepadReportDescriptor
} from "@opencontroller/core/hid";

const descriptor = hidGamepadReportDescriptor;
const bytes = encodeHidGamepadReport(controller.getState());
const rumble = encodeHidGamepadRumbleReport({
  weakMotor: 0.25,
  strongMotor: 0.8
});
const lights = encodeHidGamepadLightReport({
  red: 0.1,
  green: 0.4,
  blue: 1,
  brightness: 0.75,
  playerIndex: 1,
  playerLightMask: 0b0010
});

const adapter = new HidGamepadReportAdapter({
  onReport({ bytes }) {
    // Send bytes directly to an in-process virtual HID bridge.
    console.log(bytes);
  },
  onFeedback(event) {
    if (event.type === "rumble") {
      console.log("host rumble", event.weakMotor, event.strongMotor);
    }
    if (event.type === "lights") {
      console.log("host lights", event.red, event.green, event.blue);
    }
  }
});

HID reports are the handoff point for descriptor-backed virtual device APIs. The report shape includes 16 buttons, four signed stick axes, and two trigger axes. OpenController also defines compact vendor output reports for rumble channels and light/player indicators so native drivers have shared feedback contracts to implement. Native helpers can also send rumble and light feedback back to agents through controller.onFeedback(...); the runtime stores the latest host output in controller.getState().feedback and replay logs. In-process HID adapters can surface the same events through adapter.receiveOutputReport(bytes). See HID Gamepad Reports.

Native Bridge JSONL

import { NativeBridgeAdapter, createController } from "@opencontroller/core";

const adapter = new NativeBridgeAdapter({
  includeState: false,
  write(line) {
    process.stdout.write(line);
  }
});

const controller = await createController({
  id: "player-1",
  profile: "xbox",
  adapter,
  replay: false
});

await controller.press("A", 80);
await controller.disconnect();

Each line is a versioned bridge message containing the controller id, profile, diagnostic report fields, and base64-encoded XInput/HID report bytes. Native bridge processes can consume the same stream over stdio, pipes, sockets, or any ordered byte transport.

Native Process Helpers

import {
  NativeProcessBridgeAdapter,
  createController
} from "@opencontroller/core";

const controller = await createController({
  id: "player-1",
  profile: "xbox",
  adapter: new NativeProcessBridgeAdapter({
    command: "/home/me/.opencontroller/bin/opencontroller-uinput-bridge",
    includeState: false,
    supportsRumble: true
  }),
  replay: false
});

controller.onFeedback((event) => {
  if (event.type === "rumble") {
    console.log("rumble", event.weakMotor, event.strongMotor);
  }
  if (event.type === "lights") {
    console.log("lights", event.red, event.green, event.blue);
  }
});

await controller.press("A", 80);
await controller.disconnect();

Linux projects can use the package-level helper instead of wiring the process adapter manually:

import { createController } from "@opencontroller/core";
import {
  createLinuxUinputBridgeAdapter
} from "@opencontroller/native-linux-uinput";

const controller = await createController({
  id: "player-1",
  profile: "xbox",
  adapter: createLinuxUinputBridgeAdapter(),
  replay: false
});

This adapter spawns a helper process, streams native bridge JSONL to stdin, sends a disconnect message, closes stdin, surfaces non-zero helper exits, and can parse helper stdout feedback JSONL for host-side rumble and opt-in light events.

macOS DriverKit projects can use the same native process pattern once a signed host app/bridge is available:

import { createController } from "@opencontroller/core";
import {
  createMacosDriverKitHostBridgeAdapter
} from "@opencontroller/native-macos-driverkit";

const controller = await createController({
  id: "player-1",
  profile: "xbox",
  adapter: createMacosDriverKitHostBridgeAdapter({
    controllerId: "player-1",
    driverBundleIdentifier: "com.opencontroller.driverkit.virtual-gamepad",
    driverClassName: "OpenControllerVirtualGamepadDriver"
  }),
  replay: false
});

The default macOS bridge path is ~/Library/Application Support/OpenController/bin/OpenControllerDriverKitHostBridge. The generated DriverKit source stores HID rumble and light output reports so a signed host bridge can publish opencontroller.bridge.feedback JSONL back to controller.onFeedback(...).

For application code that should run on whichever native backend is installed for the current host, use the unified native package:

import { createController } from "@opencontroller/core";
import { createNativeHostBridgeAdapter } from "@opencontroller/native";

const controller = await createController({
  id: "player-1",
  profile: "xbox",
  adapter: createNativeHostBridgeAdapter(),
  replay: false
});

Linux uinput

bun --cwd packages/native-linux-uinput build
bun packages/native-linux-uinput/dist/bin/build-helper.js
opencontroller-linux-uinput-setup
opencontroller-linux-uinput-doctor
opencontroller bridge --id player-1 | ~/.opencontroller/bin/opencontroller-uinput-bridge
opencontroller bridge --id player-1 | ~/.opencontroller/bin/opencontroller-uinput-bridge --controller-id player-1
opencontroller bridge --id player-1 | ~/.opencontroller/bin/opencontroller-uinput-bridge --dry-run

The Linux helper reads the native bridge JSONL stream, creates an OpenController Virtual Gamepad through /dev/uinput, emits Linux gamepad events, and destroys the device when the stream closes. It prefers the direct HID payload and keeps XInput fallback for older streams. Dry-run mode decodes the same stream without opening /dev/uinput. Use --controller-id or OPENCONTROLLER_CONTROLLER_ID when a helper reads a shared stream so one virtual device only reacts to its assigned controller. Real device mode requires Linux and write access to /dev/uinput. Games that use evdev FF_RUMBLE can send weak/strong rumble back through helper stdout as opencontroller.bridge.feedback, which the SDK exposes through controller.onFeedback(...). Hosts that toggle Linux player LEDs also surface as "hid-gamepad-lights" feedback with the current player light mask.

The Windows VHF and macOS DriverKit host bridge adapters use the same controllerId option and OPENCONTROLLER_CONTROLLER_ID environment contract, so one helper process can safely own one virtual controller even when agents share a bridge stream.

React Overlay

import { ControllerOverlay } from "@opencontroller/overlay";

export function GamepadHud({ state }) {
  return <ControllerOverlay profile="xbox" state={state} />;
}

CLI

opencontroller doctor
opencontroller test --profile xbox --adapter dry-run
opencontroller overlay --profile xbox --port 4317
opencontroller replay ./replays/session/events.jsonl
opencontroller bridge --id player-1
opencontroller native doctor --backend current
opencontroller native doctor --backend all --json
opencontroller native setup --backend current
opencontroller native setup --backend windows-vhf --output ./opencontroller-windows-vhf
opencontroller native setup --backend windows-vhf --report-profile playstation
opencontroller native setup --backend macos-driverkit --report-profile playstation
opencontroller native setup --backend windows-vhf --report-profile switch
opencontroller native setup --backend macos-driverkit --report-profile switch
opencontroller native test --backend linux-uinput --dry-run --id player-1
opencontroller native test --backend current
opencontroller-windows-vhf-setup --output ./opencontroller-windows-vhf
opencontroller-macos-driverkit-setup --output ./opencontroller-macos-driverkit
opencontroller init

Use opencontroller native doctor --backend current --check in setup scripts when you want a non-zero exit code unless the host's native backend is ready. The command can also target linux-uinput, windows-virtual-gamepad, or macos-driverkit directly.

Use opencontroller native setup to prepare the current host backend through one CLI entrypoint. It dispatches to the Linux uinput helper build, Windows VHF kit generator, or macOS DriverKit kit generator while keeping privileged system changes explicit and reviewed.

Use opencontroller native test after a backend is installed to push a small button, stick, trigger, and neutral sequence through the selected native host bridge. On Linux, add --dry-run to validate JSONL/HID decoding before opening /dev/uinput; the Linux helper filter is pinned to the emitted controller ID.

Architecture

Commands flow through a predictable pipeline before they reach an adapter:

Controller API
  -> Command queue
  -> Safety guard
  -> Profile normalization
  -> State store
  -> Replay logger
  -> Adapter

Adapters receive normalized controller commands. That keeps the runtime stable while different targets decide how to consume input.

Current adapters:

  • dry-run: updates state, safety, and replay logs without touching a real device
  • websocket: streams normalized controller commands to an app, game, bridge, or emulator
  • xinput-report: turns controller state into 12-byte XInput gamepad reports for native bridge processes
  • hid-gamepad-report: turns controller state into 13-byte descriptor-backed HID gamepad reports
  • hid-playstation-extended-report: turns PlayStation state into 47-byte HID reports with touchpad and motion bytes
  • hid-switch-extended-report: turns Switch state into 31-byte HID reports with motion bytes
  • native-bridge: emits versioned JSONL messages with XInput, generic HID, and profile HID report payloads for native bridge processes
  • NativeProcessBridgeAdapter: streams JSONL directly to a helper process stdin

Runtime adapters can also opt into full state synchronization. This is the important boundary for virtual controller emulation: native drivers generally want the current complete gamepad state, not only an event like "A was pressed." Process-backed native helpers can additionally opt into feedback events so games and host drivers can send haptics back to AI agents.

Native bridge streams start with an opencontroller.bridge.connect message for each controller before state reports begin. The message carries the controller ID, profile, available report formats, helper feedback channels, and virtual device identity so native bridges can create or select the right device before input bytes arrive.

Every adapter exposes capability metadata through controller.capabilities(). Agents can inspect supported profiles, command types, output formats, report formats, touchpad/motion/status support, feedback types, transport, and virtual-device kind before selecting a backend:

const capabilities = controller.capabilities();

if (
  capabilities.supportsVirtualDevice &&
  capabilities.virtualDeviceKind === "os-virtual-gamepad"
) {
  console.log("native gamepad path ready", capabilities.reportFormats);
}
if (capabilities.supportsDeviceStatus) {
  console.log("status snapshots available", controller.getState().status);
}
if (capabilities.feedbackTypes.includes("rumble")) {
  controller.onFeedback((event) => {
    console.log("latest host output", event.type, controller.getState().feedback);
  });
}

Examples

bun --cwd examples/basic-dry-run dev
bun run dev:fighter
bun --cwd examples/agent-fighter headless --duration-ms 15000
bun --cwd examples/agent-fighter headless --matches 5 --duration-ms 10000 --output ./opencontroller-agent-fighter-series.json
bun --cwd examples/agent-fighter headless --matches 3 --duration-ms 10000 --min-decisions-per-player 10 --min-total-damage 1
bun --cwd examples/react-overlay dev
bun --cwd examples/obs-overlay dev
bun --cwd examples/websocket-bridge dev
bun --cwd examples/native-bridge-jsonl dev

Example folders:

  • examples/basic-dry-run: minimal controller API usage
  • examples/agent-fighter: OpenController Agent Fighter, a two-agent browser fighting game with a gated headless match-series runner
  • examples/react-overlay: React controller visualization
  • examples/obs-overlay: local OBS browser-source overlay server
  • examples/websocket-bridge: WebSocket adapter target example
  • examples/native-bridge-jsonl: JSONL stream for native bridge authors

Development

bun install
bun run typecheck
bun run lint
bun test
bun run build
bun run pack:check

Clean generated files:

bun run clean

Release Maintenance

Before publishing a new npm package version:

bun run clean
bun run release:check
bun run npm:status
bun run publish:npm:dry-run

bun run npm:status compares every publishable workspace with the live npm registry. It exits non-zero when local packages are ahead of npm, which is the expected state immediately before a new publish.

Publish the package set in dependency order:

bun run publish:npm -- --confirm --otp 123456

If your npm account uses a granular automation token with publish 2FA bypass, omit --otp. To retry one package, pass one or more workspaces:

bun run publish:npm -- --confirm --otp 123456 --workspace packages/core

Check the live npm versions directly after publishing:

bun run npm:status

Then confirm:

  • dist files are built from the current source and included in pack output
  • package-local READMEs, bins, exports, and type declarations are present
  • examples still run from a fresh install
  • npm shows the expected package versions
  • GitHub Actions is green
  • release tags and notes match the package version

Current Status

The main branch is ready for local experiments, demos, package hardening, and integration work.

Included:

  • controller runtime
  • profile normalization
  • safety checks
  • replay logs
  • dry-run and WebSocket adapters
  • XInput binary report bridge
  • HID gamepad report descriptor and encoder
  • native bridge JSONL protocol
  • unified native host bridge adapter package
  • Linux uinput bridge package and helper source
  • Windows VHF/HID virtual gamepad source and asset helpers
  • macOS DriverKit virtual HID source, asset, and host bridge adapter helpers
  • multi-controller hub
  • React/OBS overlays
  • CLI workflows
  • docs and examples
  • OpenController Agent Fighter demo and gated headless match-series runner

Not included yet:

  • signed Windows/macOS native virtual HID drivers
  • game-specific perception
  • broad cross-platform installation testing
  • production hardening for long-running agent tournaments

Roadmap

  • Keep npm packages current under the @opencontroller scope
  • Verify Linux FF_RUMBLE across more game launchers and distributions
  • Add signed Windows virtual HID and macOS DriverKit bridge drivers
  • Add native bridge daemon templates with install and permission diagnostics
  • Add stored regression baselines for headless OpenController Agent Fighter match series
  • Export replay data to JSON, CSV, and training-friendly formats
  • Add richer telemetry dashboards for agents and controller state
  • Expand adapter examples for emulators, desktop apps, and browser games

License

MIT. See LICENSE.

About

Bun-first TypeScript SDK for AI-agent virtual controller control, overlays, safety, and replay logs.

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors