npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

multibot-sdk

v0.6.0

Published

Headless multi-bot host: local HTTP gateway, group chat, third-party LLMs, directory-scoped tools.

Readme

sdk-bots

Headless multi-bot host: a local HTTP gateway, group-chat orchestration, and directory-scoped tools. No Electron UI. Models come from you (OpenRouter / local OpenAI-compatible / Claude Code / Codex).

npm package: multibot-sdk (sdk-bots was already taken on the registry).

License / provenance: derived from an unofficial reconstruction of a commercial product (Grok Bot / Cursor by Anysphere). Published as UNLICENSED (source-available for evaluation) — see NOTICE.md and LICENSE. Not affiliated with Anysphere.

Install

pnpm add multibot-sdk
# npm install multibot-sdk

Requires Node >= 22 (node:sqlite). Native modules tree-sitter / tree-sitter-bash ship prebuilds for macOS arm64 and common Linux.

import { startHost, SdkBotsClient } from "multibot-sdk";

const host = await startHost();          // data at ~/.sdk-bots (never ~/.cursor)
const sdk = host.client;

const a1 = await sdk.createAgent({ name: "researcher", description: "research" });
const a2 = await sdk.createAgent({ name: "writer", description: "writes" });
const group = await sdk.createGroup({ name: "war room", memberIds: [a1.agent.id, a2.agent.id] });

await sdk.setHostSettings({ inferenceProvider: "openrouter" });
await sdk.sendPrompt({ agentId: group.agent.id, prompt: "@researcher pick a plan" });

const dispose = sdk.subscribe(ev => console.log(ev.channel, ev.payload));

Console (when the host is running): http://127.0.0.1:7331/ if you set SAND_HOST_PORT=7331.

Demo:

NODE_OPTIONS="--use-system-ca" npm run example:group-chat
NODE_OPTIONS="--use-system-ca" npm run example:group-chat -- 帮我想一个周末徒步计划

See examples/README.md.

startHost() options: { dataDir?, port?, token?, startupTimeoutMs? }. Discovery is <dataDir>/gateway.json (pid-stamped; stale files are ignored).

Sandbox

There is a local box exec-daemon (loopback, default port 1337). File tools map /workspace → ~/.sdk-bots/box-workspace. Paths that escape that directory are rejected. That is the intended sandbox: restrict to a directory, not a VM.

Shell starts with cwd inside that folder. It is still a normal shell, so treat the host as a trusted local process and keep the gateway on loopback.

Tools and third-party models

Turns use this host's tool loop, not a plug-in of some other agent runtime (LangGraph, CrewAI, …). You swap the model:

| inferenceProvider | transport | tools | |---|---|---| | openrouter | OpenAI-compatible /chat/completions (local freeroute or OpenRouter cloud) | SendMessage, SendToAgent, Shell, Read, Write, Grep, … | | claude-code | Claude Agent SDK / CLI | Claude's tools + optional MCP | | codex | Codex Responses API | Codex tool surface | | mock (SAND_AGENT_MOCK_RESPONSE) | no network | scripted SendMessage / tool calls |

Set localToolPermission: "always" in host settings.json (or setHostSettings) so file/shell tools do not wait on a UI prompt. The user-visible reply is still SendMessage.

Group chat: @name addresses one member; omit @ or use @所有人 for everyone.

Zero-credential mock

process.env.SAND_AGENT_MOCK_RESPONSE = "MOCK-REPLY: hello from the mock model";

Plain string, or {"sendMessage": "..."} / {"toolCalls": [...]} (see parseSandMockScript).

Tests

npm run test:unit      # no host
npm run test:smoke     # boot + CRUD + SSE
npm run test:e2e       # mock group-chat loop
npm run test:integration # isolated host per case

Host-requiring scripts build the daemon/worker bundles first (pre hooks).

If a parent shell injects a NODE_OPTIONS preload, run tests with NODE_OPTIONS="--use-system-ca" so recursive fs.rm is not intercepted.

Build

npm run build          # clean + daemon + workers + tsc (JS + public d.ts)
npm run typecheck

Generated (gitignored): dist/box-exec-daemon/main.cjs, dist/host-workers/*.cjs — built into the single dist/ output; nothing generated lives inside src/.

Environment

| var | purpose | |---|---| | SAND_DATA_ROOT | host data root (default ~/.sdk-bots) | | SAND_GATEWAY_TOKEN | gateway auth token | | SAND_HOST_PORT | gateway port | | SAND_GATEWAY_BIND_HOST | bind host (default 127.0.0.1) | | SAND_OPENROUTER_BASE_URL | default http://127.0.0.1:3080/freeroute/v1 | | SAND_OPENROUTER_MODEL | default auto locally | | OPENROUTER_API_KEY | required only for official OpenRouter cloud | | SAND_LOCAL_CHAT_TIMEOUT_MS | per-request timeout for local OpenAI-compatible inference (default 120000) | | SAND_AGENT_MOCK_RESPONSE | mock inference script | | SAND_BOX_EXEC_DAEMON_ENTRY | daemon bundle path | | SAND_USE_EXISTING_BOX_EXEC_DAEMON | 1 = do not spawn the daemon | | SAND_BOX_EXEC_DAEMON_HOST | host connects here (default 127.0.0.1; non-loopback = remote box, no local spawn) | | SAND_BOX_EXEC_DAEMON_BIND_HOST | daemon listen address (default loopback; 0.0.0.0 to accept remote clients) | | SAND_BOX_EXEC_DAEMON_PORT | daemon port (default 1337) | | SAND_BOX_EXEC_DAEMON_AUTH_TOKEN | daemon bearer token (default local) | | SAND_LOCAL_EXEC_GATEWAY_URL | standalone multibot-host --local-exec-daemon attach URL | | SAND_LOCAL_EXEC_ADVERTISE_HOST | host written into the local-exec connection file (defaults to loopback when the gateway binds 0.0.0.0) | | SAND_GOAL_STALL_LIMIT | goal-guard stall limit: stop a routine after this many consecutive cycles with no ledger update (default 3) | | SAND_SWARM_COLLABORATION | force unattended-swarm collaboration rules for every bot (default off; per-agent opt-in is the SWARM-v1 marker in a bot's description) | | SAND_SWARM_BOARD | shared task board path handed to swarm-mode bots (default <data root>/swarm/TASKS.md) |

Goal loop (routine that iterates until the objective is met)

A cron routine becomes a goal loop by dropping a goal.json next to its automation.json (<data root>/agents/<agentId>/automations/<routineId>/goal.json). The engine then:

  • injects a <goal_guard> block into every wake prompt (objective, acceptance checklist, cycle number);
  • advances cycle on each scheduled fire and records lastFiredAt;
  • stops the loop by itself once status is achieved/blocked/abandoned, once every acceptance item is proven, or after SAND_GOAL_STALL_LIMIT cycles with no ledger update — and disarms the routine, leaving a run record that says why.

The model owns objective, acceptance[].done, status and updatedAt; the engine owns cycle, stallCount and lastFiredAt. A cycle without a ledger write counts as no progress. Routines without a goal.json behave exactly as before. See examples/verify-goal-loop.mjs for an end-to-end check.

Layout

  • src/sdk — SdkBotsClient + startHost() (public entry)
  • src/bootstrap — headless CLI bootstrap + shared host composition (composition.ts)
  • src/host — gateway, group chat, inference (recovered runtime)
  • src/lib — recovered library shelf (agent tools, agent-exec, chat-inference, …)
  • src/box-exec-daemon — sandbox process for Shell/Read/Write (the remote computer is this box; bind/connect via SAND_BOX_EXEC_DAEMON_*)
  • src/host/local-exec — in-process ExternalShell/ExternalRead provider backed by the same box daemon (also multibot-host --local-exec-daemon for a standalone SSE attach)
  • src/proto — generated protobuf closures (generated/ + redacted/; machine-owned, never hand-edit)
  • src/shared — shared kernel: contracts (host-extensions), path policy (sand-paths), backend clients
  • test/unit — no host; test/integration — isolated host per case

Strict layering: sdk → bootstrap → host → {lib, proto, shared}; shared never imports host.

CLI (host process): npx multibot-host after installing this package, or npm start in this repo.

Status

Headless boot, 37 host extensions, gateway /health + POST /api/* + SSE, mock turns, and live OpenRouter/freeroute turns are exercised in-tree. See CHANGELOG.md.

This is not MIT/Apache. Evaluate locally; do not assume you may ship it as a dependency in a commercial product until provenance is reviewed.

Roadmap: web Grok-bots on dsh

Inference rides on the local dsh freeroute gateway (http://127.0.0.1:3080/freeroute/v1, model auto) by default — verified end-to-end including tool round-trips. The web UI ships as a dsh Cordis plugin (collapsible Bots panel). Full architecture, gateway/SSE API reference, plugin spec, and roadmap: ../dsh-bots/DEVELOPMENT.md.