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

@agentworkforce/factory-cloud

v0.1.4

Published

The Cloudflare baseline a Factory garden deploys: the Worker, the Container image, the Durable Object classes and their migrations, and the dispatch config template.

Readme

factory-cloud

Deployment home for the AgentWorkforce Factory control process. Factory source remains in the public AgentWorkforce/factory repo; this repo consumes its published @agent-relay/factory package.

This repository is PRIVATE. The published npm package is what a garden consumes. It is the baseline that every Factory instance deploys, and a garden installs it as @agentworkforce/factory-cloud at a pinned version — no repository credential at all. See consuming this baseline.

This repository was briefly public on 2026-09-03 to remove that credential, and was made private again the same day once the package became the distribution path. If you are reading a copy of this file that says "this repository is public, and that is load-bearing", it predates that change.

Be clear about what the package discloses. The tarball carries src/, container/, config/, scripts/ and test/ — publishing it publicly discloses the same source a public repository would. What stays private is the git history, the issues and pull requests, and the branches. The package is a deliberate, versioned artifact rather than a live repository; it is not a smaller disclosure of the code itself.

It holds no secrets and no account-scoped identifiers, and scripts/check-public-baseline.mjs fails CI if one appears — that check is named for the era that introduced it and still earns its keep, because the package is public even though the repository is not. Credentials live in Actions secrets and Cloudflare, and reach the runtime as --var/secret bindings.

Tracking issue: factory#268.

Runtime shape

One Cloudflare Worker always addresses one named factory-primary Container, and wrangler.jsonc caps that Container class at max_instances: 1. The Container runs the long-lived Factory event subscription and timers. Factory uses --backend relay, so agents run on enrolled fleet nodes in a fresh sandbox per agent. The persistent dispatcher and ephemeral agents are separate lifecycles.

The deployed compatibility mode deliberately does not sleep: it keeps the 24-hour inactivity window, renews that window, and addresses the same singleton from a minute recovery cron. Container disk is ephemeral. Separate named Durable Objects retain the version-3 Factory state document and the rotating RelayAuth path-token pair. Claims and leases must report the cloudflare-do backend before live start is admitted; the entrypoint refuses to start against the file store.

An additive event-driven path is implemented but hard-disabled in the deploy workflow. Relayfile's existing signed outbound webhook can wake the stable singleton, and the existing state Durable Object can retain a chunked, integrity-checked mirror generation so resume does not require an immediate cold rebuild. Receiver/snapshot shadowing and short sleep have separate flags; sleep fails closed unless the dispatcher is idle, reconciliation is complete, and a ready snapshot exists. The outbound subscription and reconciliation remain the fallback and correctness paths until live acceptance evidence is complete. See event-driven wake and durable resume.

Factory is embedded through the host-neutral @agent-relay/factory/cli entrypoint. container/factory.mjs injects the Cloudflare adapter; the public Factory package contains no Cloudflare selection policy.

Configuration

Factory resolves exactly one contract: the path passed to --config, otherwise ./factory.config.json in its working directory. It does not search target repositories or layer contracts. The image renders config/factory.config.template.json and passes the exact resolved path to every command. See config resolution.

The workflow requires exactly four GitHub Actions secrets and one non-secret repository variable. Each command prompts for its value without putting it in shell history:

gh secret set CLOUDFLARE_API_TOKEN --repo AgentWorkforce/factory-cloud
gh secret set AGENT_RELAY_WORKSPACE_KEY --repo AgentWorkforce/factory-cloud
gh secret set FACTORY_RELAYAUTH_ACCESS_TOKEN --repo AgentWorkforce/factory-cloud
gh secret set FACTORY_RELAYAUTH_REFRESH_TOKEN --repo AgentWorkforce/factory-cloud
gh variable set FACTORY_SLACK_CHANNEL --repo AgentWorkforce/factory-cloud

There is deliberately no Factory GitHub token. github.identity is pinned to app; private reads and every GitHub write must cross the server-side App boundary. The Cloudflare token deploys infrastructure and is not passed into the Container. The RelayAuth pair seeds the credential Durable Object and is also never passed into the Container; only the Container-private factory-auth.do access endpoint crosses that boundary. Never commit a secret, account id, workspace key, or connection string here. This repository is public; scripts/check-public-baseline.mjs refuses those shapes in CI, and it is a backstop, not permission to try.

The disabled event receiver additionally expects FACTORY_RELAYFILE_WEBHOOK_SECRET as a Worker-only secret when an operator reaches the shadow-doorbell stage. It is intentionally absent from the current deployment workflow: this repository does not mint, rotate, or enable it, and it is never forwarded to the Container.

Consuming this baseline

A garden depends on a published version of this package, not on a checkout of this repository. That is the whole dependency:

// a garden's baseline.json
{ "package": "@agentworkforce/factory-cloud", "version": "0.1.0" }

The garden's deploy workflow turns that into an exact npm dependency and installs it; it does not commit a package.json of its own. See AgentWorkforce/hoopsheet-garden's .github/actions/fetch-baseline.

No token:, no persist-credentials, no repository access. That is the point: while the baseline was a private repository, every garden job needed a read credential to resolve it, and the only shapes that satisfied it were a fine-grained PAT or a GitHub App installation token — infrastructure a config-only repository should not need. hoopsheet-garden failed CI six consecutive times on exactly that step.

Why an exact version rather than a range: a running garden must not change because the baseline moved. ^ or ~ moves by definition. Bumping a garden onto a newer baseline is then an explicit, reviewable, one-line edit in that garden's repository.

The package is a build context, not a library. A garden does not import it; it runs wrangler deploy against it. That is why the tarball ships wrangler.jsonc, Dockerfile, .dockerignore, src/, container/, config/ and scripts/ rather than a compiled entry point, and why wrangler is a real dependency rather than a devDependency — npm does not install a dependency's devDependencies, and the pinned wrangler version is part of the deployment contract scripts/check-cloudflare-contract.mjs asserts.

scripts/check-publishable-package.mjs refuses to publish a tarball missing any path the deploy needs. It derives that list from the Dockerfile's COPY lines and from wrangler.jsonc, so it cannot drift when a COPY is added, and it asserts against the real npm pack output rather than the files array — files is an input to packing, not its result.

Why a package rather than a container image, which is the obvious alternative:

  • A garden needs the Worker script, which wrangler bundles from local source (src/index.mjs plus its siblings). No registry image supplies that, so an image cannot replace this dependency however it is published.
  • Cloudflare Containers pull only from AWS ECR, Docker Hub, Google Artifact Registry, or Cloudflare's own managed registry. GHCR is rejected outright by wrangler's registry allowlist, so "publish the image to GitHub Packages" is not an available design.

What a garden repository therefore contains, in full: an instance.json overlay, a baseline.json naming this package and an exact version, and a deploy workflow. No Worker, no Dockerfile, no wrangler.jsonc. See config/instances/README.md and AgentWorkforce/hoopsheet-garden.

Publishing a new baseline version

Use the Publish baseline to npm Actions workflow (.github/workflows/publish.yml). It is the supported path because a release is no longer just an npm publish: it also builds the prebuilt Worker bundle, builds and pushes the Container image to Cloudflare's managed registry, and writes the deploy manifest — so a garden can provision over the Cloudflare REST API without running wrangler or Docker itself. See docs/prebuilt-deploy-artifacts.md. Leave dry_run on for the first run against a new scope: it validates the bundle and manifest without pushing an image or publishing.

Publishing by hand skips the image push and the artifact build. A bare npm publish ships a tarball with no dist/ (no worker bundle, no manifest) and no pushed image, so a garden's REST provisioner has nothing to deploy — even though check-publishable-package.mjs passes, because dist/ is optional in the pack. If you must release manually, reproduce what the workflow does:

npm ci
npm run check                         # the same gate ci.yml runs
npm version <patch|minor|major>
npm run deploy:build                  # prebuilt Worker bundle -> dist/worker/
# build + push the Container image, capture <name>@sha256:<digest>:
npx wrangler containers build . -t "agentworkforce-factory-cloud:<new version>" --push
node scripts/generate-deploy-manifest.mjs --release \
  --worker-bundle-dir dist/worker \
  --image-ref "<name>@sha256:<digest>" --image-digest "sha256:<digest>" \
  --out dist/factory-cloud-deploy-manifest.json
npm publish                           # now the tarball carries dist/
npm view @agentworkforce/factory-cloud@<new version> version

The image build+push needs CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID. The last line is not ceremony. npm publish exiting 0 is not evidence the registry serves the version — a release nobody can install is not a release, and that assertion is the one step a human is most likely to skip.

prepublishOnly runs scripts/check-publishable-package.mjs on the way out, so a tarball missing a path the deploy needs cannot be published even if someone publishes without running npm run check first. That guard is the reason this manual path is tolerable rather than reckless.

An NPM_TOKEN repository secret is already provisioned for the workflow this section used to describe. Writing it is open work, and the decisions it has to settle are real ones: whether a release is gated on the Factory drift check, and whether main moves before or after the registry confirms the version. Until it lands, the commands above are the whole procedure.

More than one Factory

This repository is the baseline. A second Factory — a "garden" — deploys this same Worker, Container, Durable Object classes and migrations as its own Cloudflare Worker service, and differs only by a config overlay in config/instances/. It is selected at container boot by the FACTORY_INSTANCE_SLUG Worker variable; absent, the baseline renders exactly as before.

Isolation comes from the Worker service name (wrangler deploy --name), which is what scopes a Durable Object namespace and the Container. It does not come from renaming bindings, classes, migrations, or the stable factory-primary object key — those stay identical across instances on purpose.

See config/instances/README.md for what is config and what is inherited, and AgentWorkforce/hoopsheet-garden for a worked example.

Observability

The resolved config explicitly enables Factory's bounded Cloud reporter under the operator-facing instance name factory-khaliq-cloud. Events first enter Factory's local outbox, then the authenticated Cloud Factory ledger. Cloud owns the downstream fan-out: sanitized operational failures go to PostHog, while the Factory health sweep turns stale heartbeats and explicit failures into the shared NightCTO evidence contract.

PostHog and NightCTO credentials therefore remain in AgentWorkforce/cloud; they are not duplicated into this deployment. Reporter or sink failure is fail-open for orchestration: it may leave telemetry pending or lost across a Container replacement, but it must never stop Factory dispatching. An explicit config key proves deployment intent, not delivery; completion still requires a real event observed in the Cloud ledger and both downstream systems.

The authenticated /evidence payload also carries dispatchContract, an allowlisted projection of the rendered config the running Factory was started with — the repo roster, the label/prefix gate, batchSize and mergePolicy. A successful read carries state: "available"; a read or parse failure remains visible as state: "unavailable" with a fixed reason code rather than disappearing during JSON serialization. The template is baked into the image at build time, so a checkout only describes the commit you are reading; this is the read that answers what is deployed. It is authenticated because the resolved contract names every repository Factory may write to. See config resolution.

Version drift

factory-version.json is the image's single Factory package pin. scripts/check-factory-drift.mjs fails closed if that pin is behind npm or the registry cannot be checked. CI runs it on pushes, pull requests, and daily; bump-factory.yml moves the pin automatically. This prevents the deployment repo from becoming a new place for a package pin to rot.

Deploy

deploy.yml is manual. Its default is a start-disabled deployment that builds the image and runs the real built CLI inside the Container. A live request still fails closed until Factory reports the Durable Object SQLite backend. The workflow returns the host exit codes for status and run-once --dry-run. It explicitly deploys FACTORY_EVENT_DRIVEN_ENABLED=0 and FACTORY_EVENT_DRIVEN_SLEEP_ENABLED=0, preserving the working bridge.

The SQLite namespace is intentionally not auto-initialized. Before the first live start, run the create-only import command inside the named Container and feed it the current v3 state document on stdin:

node /opt/factory-host/container/import-state.mjs - < current-state.json

The command uses the Container-only factory-state.do route, validates the document with the installed Factory package, reads it back, and prints only its revision, byte count, and SHA-256. A second import fails with HTTP 409. Restart the Container after import so its preflight runs against initialized state.

The credential Durable Object rotates the pre-minted fixed workspace-path pair before first use, atomically stores each new pair, and returns only its access token through the Container-private endpoint. Replacement Containers reload the newest pair from the Durable Object, not ephemeral disk or host-local login. The deploy remains gated on a published Factory package that consumes FACTORY_CLOUD_ACCESS_TOKEN_URL, and private repository read/write must land through the GitHub App. A successful infrastructure deploy is therefore a rehearsal, not proof that #268 is done.

Completion requires a cloud-originated private-repo dispatch, remote agent spawn, App-authored PR, restart-without-redispatch proof, and a fresh exit-code check that the laptop Factory remains stopped. See the cutover design and acceptance bar.