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

playclaw-sdk

v1.1.0

Published

Connect any AI agent to PlayClaw — the professional audit playground. One bridge. Any model.

Downloads

200

Readme

playclaw-sdk

Connect any AI agent to PlayClaw — the professional audit playground.

npm install playclaw-sdk

The 5-line version

const { PlayclawBridge } = require('playclaw-sdk');

const bridge = new PlayclawBridge({ token: 'PC-XXXX-XXXX-XXXX' });

bridge.onMessage(async (message, context) => {
  return await myAgent.reply(message);
});

bridge.connect();

That's it. The SDK handles the connection, sessions, turn limits, error recovery, and auto-reconnect.


What this SDK does

PlayClaw audits AI agents by running structured conversations through your agent. The SDK is the bridge between PlayClaw's platform and your agent code.

Each time someone clicks "Start Audit" in PlayClaw, a session begins. The SDK:

  • Detects the new session and fires onSessionStart
  • Routes each message from PlayClaw to your onMessage handler
  • Sends your agent's reply back to PlayClaw
  • Enforces the turn limit (default: 5)
  • Fires onSessionEnd with the full conversation history
  • Reconnects automatically if the connection drops

Full API reference

new PlayclawBridge(options)

| Option | Type | Default | Description | |--------|------|---------|-------------| | token | string | required | Your PC-XXXX PlayClaw token | | auditTurns | number | 5 | Max turns per audit session | | autoReconnect | boolean | true | Reconnect if channel drops | | logLevel | 'debug'\|'info'\|'warn'\|'error'\|'silent' | 'info' | Console log verbosity | | logger | function(level, message, meta) | null | Custom log output function | | rateLimit | RateLimitOptions | null | Enable built-in rate limiting | | supabaseUrl | string | PlayClaw default | Override for self-hosted setups | | supabaseKey | string | PlayClaw default | Override for self-hosted setups |

Hooks

All hooks return this for chaining.

.onMessage(handler) ← required

bridge.onMessage(async (message, context) => {
  // message: string — what the user sent
  // context.sessionId   : 'sess_abc123'
  // context.turnNumber  : 1, 2, 3, 4, or 5
  // context.totalTurns  : 5
  // context.isLastTurn  : true on turn 5
  // context.history     : [{ role, content, timestamp }, ...]
  return "your reply as a string";
});

.onSessionStart(handler)

bridge.onSessionStart((sessionId) => {
  myAgent.resetMemory(); // new audit = fresh state
});

.onSessionEnd(handler)

bridge.onSessionEnd((sessionId, history) => {
  database.save({ sessionId, history });
});

.onError(handler)

bridge.onError((error, sessionId) => {
  monitoring.report(error); // bridge stays alive
});

.onConnect(handler) / .onDisconnect(handler)

bridge.onConnect(() => console.log("Live"));
bridge.onDisconnect(() => console.log("Reconnecting..."));

Middleware

// Transform messages BEFORE your handler sees them
bridge.use(async (message, context) => {
  return sanitize(message);
});

// Transform replies BEFORE they're sent to PlayClaw
bridge.useReply(async (reply, context) => {
  return translate(reply, "en");
});

Middlewares run in registration order. Throwing inside a middleware cancels the message.

State & Introspection

bridge.isConnected  // boolean
bridge.sessionInfo  // { sessionId, turnCount, auditTurns, isExhausted }
bridge.stats        // { sessionsHandled, messagesHandled, errorsCount, reconnectsCount }

Rate Limiting

const bridge = new PlayclawBridge({
  token: 'PC-...',
  rateLimit: {
    maxPerWindow:  60,      // max 60 messages per minute (global)
    windowMs:      60_000,  // 1-minute window
    maxConcurrent: 1,       // max 1 in-flight call per session
  }
});

Or as middleware for custom logic:

const { PlayclawRateLimiter } = require('playclaw-sdk');
const limiter = new PlayclawRateLimiter({ maxPerWindow: 30, windowMs: 60_000 });
bridge.use(limiter.middleware());

Real-world examples

OpenAI (GPT-4o)

const { PlayclawBridge } = require('playclaw-sdk');
const OpenAI = require('openai');

const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const bridge = new PlayclawBridge({ token: process.env.PC_TOKEN });

let history = [];

bridge.onSessionStart(() => { history = []; });

bridge.onMessage(async (message, context) => {
  history.push({ role: "user", content: message });

  const res = await openai.chat.completions.create({
    model: "gpt-4o",
    messages: [
      { role: "system", content: context.isLastTurn ? "Give a conclusive answer." : "Be helpful." },
      ...history,
    ],
  });

  const reply = res.choices[0].message.content;
  history.push({ role: "assistant", content: reply });
  return reply;
});

bridge.connect();

Anthropic (Claude)

const { PlayclawBridge } = require('playclaw-sdk');
const Anthropic = require('@anthropic-ai/sdk');

const anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
const bridge = new PlayclawBridge({ token: process.env.PC_TOKEN });

let history = [];

bridge.onSessionStart(() => { history = []; });

bridge.onMessage(async (message) => {
  history.push({ role: "user", content: message });

  const res = await anthropic.messages.create({
    model: "claude-opus-4-6",
    max_tokens: 1024,
    messages: history,
  });

  const reply = res.content[0].text;
  history.push({ role: "assistant", content: reply });
  return reply;
});

bridge.connect();

Any HTTP agent (n8n, Flowise, local LLM)

const { PlayclawBridge } = require('playclaw-sdk');

const bridge = new PlayclawBridge({ token: process.env.PC_TOKEN });

bridge.onMessage(async (message) => {
  const res = await fetch("https://my-agent.internal/chat", {
    method: "POST",
    headers: { "Content-Type": "application/json", "Authorization": `Bearer ${process.env.AGENT_KEY}` },
    body: JSON.stringify({ message }),
  });
  const data = await res.json();
  return data.reply;
});

bridge.connect();

Multiple PlayClaw accounts (multi-tenant)

const { PlayclawBridge } = require('playclaw-sdk');

const tokens = process.env.PC_TOKENS.split(",");

const bridges = tokens.map((token) => {
  const bridge = new PlayclawBridge({ token });
  bridge.onMessage(async (message) => await myAgent.reply(message));
  return bridge;
});

await Promise.all(bridges.map((b) => b.connect()));
console.log(`${bridges.length} bridges connected.`);

Logging

By default the SDK logs to stdout at info level with timestamps and color.

2026-03-23T14:00:01.123Z INFO  [playclaw:PC-TEST] Bridge connected — listening for audit messages
2026-03-23T14:00:02.456Z INFO  [playclaw:PC-TEST] Audit session started { sessionId: 'sess_abc' }
2026-03-23T14:00:05.789Z INFO  [playclaw:PC-TEST] Audit session ended   { sessionId: 'sess_abc', messages: 10 }

Levels: debug | info | warn | error | silent

// Verbose mode (see every message processed)
new PlayclawBridge({ token: 'PC-...', logLevel: 'debug' });

// No output
new PlayclawBridge({ token: 'PC-...', logLevel: 'silent' });

// Pipe to your own logger (winston, pino, etc.)
new PlayclawBridge({
  token: 'PC-...',
  logger: (level, message, meta) => winston.log(level, message, meta),
});

Running tests

node test/bridge.test.js

Tests run without any real Supabase connection using the built-in MockTransport.


Advanced: build your own transport

If you want to use a different backend (custom WebSocket server, etc.):

const { PlayclawBridge, PlayclawTransport } = require('playclaw-sdk');

class MyCustomTransport extends PlayclawTransport {
  async connect() { /* your connection logic */ }
  async send(event, payload) { /* your send logic */ }
}

const bridge = new PlayclawBridge({ token: 'PC-...' });
bridge._transport = new MyCustomTransport('PC-...');
bridge.connect();

License

MIT — playclaw.info