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

@cat-factory/binary-generators

v0.3.77

Published

The built-in generative binary integrations (Nano Banana, Google's Gemini image models) plus the authoring seam a deployment defines its own with, written entirely through the public BinaryGeneratorRegistry seam: depends only on @cat-factory/kernel + @cat

Readme

@cat-factory/binary-generators

The generative binary integrations the platform ships, plus the seam a deployment writes its own with. These are the metered vendor APIs a binary-output step calls to produce its artifacts, as opposed to the foundational service the artifacts are then stored through.

| id | What it makes | Modalities | | ------------- | ---------------------------------------------------------------------- | ----------------------------------- | | nano-banana | Images with legible text in them, and conversational editing, up to 4K | image (image/png, image/jpeg) |

Why the platform ships one at all

defaultBinaryGeneratorRegistry() is still empty, and the reasoning that kept it empty still holds: no image API is one every organisation runs, and every one of them is metered. What changed is the Media task type. It ships a generating agent kind, a preset and the storage underneath, and its step selected nothing, so the platform's most demonstrable capability was reachable only by a deployment that first wrote an integration, an OpenAPI document and a credential declaration.

Metered is answered by the credential rather than by the registry. With no key resolved the agent is told the integration is unavailable and reports it as the reason an artifact is missing, so a deployment that ignores this package pays nothing and sees one extra row in a picker.

Using it

Every facade defaults its registry to the shipped set, so nothing is needed to get nano-banana:

await start({/* … */}) // binaryGeneratorRegistry defaults to binaryGeneratorRegistryWithBuiltins()

An injected registry REPLACES the default rather than merging with it, so a deployment adding its own integrations starts from the built-ins:

import { binaryGeneratorRegistryWithBuiltins } from '@cat-factory/node-server'

const binaryGeneratorRegistry = binaryGeneratorRegistryWithBuiltins()
binaryGeneratorRegistry.registerAll(myIntegrations)
await start({ binaryGeneratorRegistry })

That matters more here than for the gate or prompt-fragment registries, because the shipped pl_media preset SELECTS nano-banana by id: a registry without it refuses that pipeline's runs at admission (binary_output_generator_invalid) rather than degrading quietly. Dropping the shipped integration is a legitimate choice; editing that preset's step is the other half of it.

Register once per deployment, on the process that owns the registry. In mothership mode that is the mothership: a node resolves integrations over /internal/binary-generators and consults no registry of its own, so the set the pipeline builder offers and the set admission resolves are one set however far behind the node's build has drifted.

On the Worker the registry is also held process-wide (infrastructure/binaryGenerators.ts), the same way the deployment's binary artifact stores are and for the same reason: that runtime builds a container per entry point, and a binary-output step's dispatch brief is composed on the durable path, which takes no options at all. createWorker({ overrides: { binaryGeneratorRegistry } }) registers on your behalf, so a deployment using the documented seam needs to know none of that. What made this one easy to miss is that an override-less build resolves the SHIPPED set rather than nothing, so an unregistered integration is absent from a brief that otherwise looks populated.

The credential

nano-banana authenticates with GEMINI_API_KEY, an API key from Google AI Studio, sent as the x-goog-api-key header. The name is the vendor's own, so an agent reaching for google-genai finds the value where the library looks.

Set it either as a workspace capability credential (subject binary-generator, id nano-banana, key GEMINI_API_KEY) or in the environment the deployment dispatches runs from. The value travels on the job body alone and is injected into the agent's process for that job; it is never written to the checkout, the prompt or a log.

Do not add it to docs/environment-variables.md: every variable documented there is reserved by check-reserved-env-keys.mjs, which would make this key unresolvable.

Billing must be enabled on the Google project. None of these models has a free tier, so an unbilled key gets FAILED_PRECONDITION on the first call, which the agent reports as the reason nothing was generated.

Writing your own

defineBinaryGenerator is exported for exactly this, and re-exported from each facade so a deployment reaches it from the package it already depends on. It runs the platform's OWN registration rules at import (the definition schema, plus binaryGeneratorDetailIssues: the endpoint policy, the contract-set rules, the media-type classifier, the harness check and the capability-versus-accepted-values pairing), so a definition that would fail a boot fails a test instead:

import { defineBinaryGenerator, openApiContract } from '@cat-factory/binary-generators'

export const acmeImages = defineBinaryGenerator({
  id: 'acme-images',
  name: 'Acme Images',
  summary: 'One line the picker and the agent brief show.',
  description: 'What it is good at, what it is NOT for, and its cost profile.',
  modalities: ['image'],
  mediaTypes: ['image/png'],
  capabilities: ['seed', 'exact-size'],
  endpoint: 'https://api.acme.example',
  credentials: [{ key: 'ACME_IMAGE_API_KEY', usage: 'Authorization: Bearer <value>' }],
  contracts: [
    openApiContract({ contractId: 'acme-api', title: 'Acme API', document: ACME_OPENAPI }),
  ],
  guidance: 'Operating notes folded into the agent’s brief verbatim.',
})

Two rules the seam cannot check for you, both pinned per integration in the tests here:

  • A capability is a claim about the endpoint's REQUEST. Declare one only where the registered contract carries the parameter, or a step is admitted and the agent is left holding an option it cannot send. nano-banana.test.ts names each capability's evidence in the document.
  • An accepts set is a REFUSAL. State one only where the endpoint genuinely enumerates its values, and read it off the contract rather than restating it, so the two cannot drift.

Everything that does not gate an option ("good at pixel art", "cheap below 2K", "rate limited to five a second") stays prose in description and guidance, where a sentence can say what it means.