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

@animated-waffle/server

v0.3.0

Published

Server-side TypeScript SDK for the Animated Waffle /v1 API.

Downloads

449

Readme

@animated-waffle/server

Typed server-side access to the Animated Waffle /v1 API. This package uses the runtime's native fetch, has no runtime dependencies, and must not be bundled into a browser because it holds a server credential: either a workspace key or a revocable Agent-authoring key. It requires Node.js 20.3 or newer.

pnpm add @animated-waffle/server

Setup

import { WaffleServer } from '@animated-waffle/server'

const waffle = new WaffleServer({
  apiKey: process.env.ANIMATED_WAFFLE_API_KEY!,
})

Production is the default API origin. Tests and non-production integrations may set apiOrigin or inject fetch.

Browser session tokens

Keep the workspace key on the server and return only the short-lived token to the browser:

const grant = await waffle.createSessionToken({
  agentId: '22222222-2222-4222-8222-222222222222',
  endUserId: '55555555-5555-4555-8555-555555555555',
})

endUserId is one stable UUID for the product's end user. It is required when the published Agent uses memory, Calendar, or documents.

Developer Feedback grants

Developer Feedback is a creator-only development path. After the browser has an actual persisted session.id, a creator-authenticated backend may request a five-minute grant for that exact Agent Session:

const grant = await waffle.createDeveloperFeedbackGrant({
  agentId,
  sourceSessionId,
})

return Response.json({
  endpoint: grant.endpoint,
  token: grant.token,
  expires_at: grant.expiresAt,
  source_session_id: grant.sourceSessionId,
})

The backend must authenticate the caller as the creator represented by its user-bound workspace key before calling this method. Never put the awp_ key in browser code. An ordinary Agent session token cannot request or refresh a feedback grant.

Local Character Director

Use the separate @animated-waffle/cli package for interactive local-Agent login and the waffle-agent executable. This package only owns the typed server SDK.

The SDK and CLI accept the same camelCase authoring data. A complete SDK workflow is:

const feedbackId = crypto.randomUUID()
let round = await waffle.addAgentAuthoringFeedback({
  sourceSessionId,
  feedback: {
    id: feedbackId,
    scope: 'message',
    targetMessageId: assistantMessageId,
    action: 'change',
    dimension: 'instructions',
    note: 'Sound warmer.',
  },
})
round = await waffle.submitAgentAuthoringRound(round.id, round.lockVersion)
round = await waffle.saveAgentAuthoringCandidate(round.id, round.lockVersion, {
  updatedPersona: round.baseRevision.persona,
  updatedPrompt: 'Answer warmly in one concise sentence.',
  summary: 'Added warmth while preserving brevity.',
  changes: [{ area: 'instructions', summary: 'Added warmth.' }],
  preserved: ['One concise sentence.'],
  conflicts: [],
  outsidePrompt: [],
  preserveScenario: {
    label: 'Preserve brevity',
    userMessage: 'Summarize your day.',
    expectedBehavior: 'Answer in one sentence.',
  },
  addressedFeedbackIds: [feedbackId],
  unresolvedFeedback: [],
})
round = await waffle.replayAgentAuthoringCandidate(
  round.id,
  round.lockVersion,
  round.proposal!.candidateHash,
)
round = await waffle.createAgentAuthoringDraft(
  round.id,
  round.lockVersion,
  round.proposal!.candidateHash,
)
console.log(round.dashboardUrl)

feedback.id is a caller-owned idempotency key: generate one UUID for each feedback item. If the response is ambiguous, retry with the same ID and exact payload; reusing the ID for different feedback returns 409. Candidate, replay, and draft writes use the latest lockVersion; after an ambiguous result, reread the Round and reconcile instead of retrying blindly. An ambiguous submit may reuse its original Round and lock: once submitted, it returns the current later Round state without another mutation. Run the CLI help for every JSON shape.

The repository workflow for Codex and Claude-style Agents lives at .agents/skills/animated-waffle-character-director/SKILL.md. The CLI can browse authorized Sessions, manage feedback, save/replay a Candidate, and create an immutable draft. It has no publish or delete command.

Messages and documents

Message and document writes require a caller-owned idempotency key:

const idempotencyKey = crypto.randomUUID()
const turn = await waffle.sendMessage({
  agentId,
  endUserId,
  text: 'What is on my list?',
  userName: 'Space',
  timeZone: 'Asia/Shanghai',
  idempotencyKey,
})

If the caller loses the response and retries the same logical write, it must reuse the same key. Generate a new key for a new write. The SDK deliberately does not retry requests, so it never guesses whether two writes are the same. The same rule applies to createDocument, updateDocument, and deleteDocument.

Agent drafts and publication

createAgent and updateAgent save a draft. They never publish implicitly:

const draft = await waffle.updateAgent(agentId, {
  instructions: 'Keep answers concise.',
})
const live = await waffle.publishAgent(draft.id)

Errors and deadlines

Every SDK-originated failure is a WaffleServerError. HTTP failures preserve the API's code, retryable, and request_id as requestId. Transport errors have no status; use code === 'request_timeout' or code === 'network_error'.

import { WaffleServerError } from '@animated-waffle/server'

try {
  await waffle.getAgent(agentId, { timeoutMs: 5_000 })
} catch (error) {
  if (error instanceof WaffleServerError) {
    console.error(error.code, error.status, error.requestId)
  }
}

Ordinary requests default to 20 seconds; sendMessage defaults to 120 seconds. Every method accepts { signal, timeoutMs } as its final argument.

API surface

  • Catalog: listVoices, listAvatars.
  • Agents: listAgents, getAgent, createAgent, updateAgent, publishAgent.
  • Shared documents: ensureDocumentSet.
  • Sessions: createSessionToken, createDeveloperFeedbackGrant, createRealtimeTranscriptionToken.
  • Local Character Director: getAgentAuthoringStatus, Session/Round browse, feedback Round mutation, Candidate save/replay, and draft creation.
  • Conversation: sendMessage, listMessages.
  • Documents: listDocuments, listAllDocuments, getDocument, createDocument, updateDocument, deleteDocument.
  • Calendar: createCalendarAuthorization, getCalendarConnection.

Public inputs and responses use camelCase; the SDK owns conversion to the API's current wire format. There is intentionally no raw-request escape hatch.