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

@getspinup/sdk

v1.3.0

Published

Fetch-based client for the Spinup public API.

Downloads

1,485

Readme

@getspinup/sdk

Fetch-based client for the Spinup public API.

Install

npm install @getspinup/sdk

First Success Workflow

Create a client with a workspace API key and default workspace slug from your own app configuration, then create a Spinup Agent and read back its environment status:

import { SpinupApiError, createSpinupClient } from "@getspinup/sdk";

const spinup = createSpinupClient({
  apiKey: process.env.SPINUP_API_KEY,
  defaultWorkspaceSlug: process.env.SPINUP_WORKSPACE,
});

try {
  const created = await spinup.agents.create({
    name: "Docs Runner",
  });

  console.log("created", created.agent.slug, created.environment.status);

  const agents = await spinup.agents.list();
  console.log(
    "agents",
    agents.agents.map((agent) => `${agent.slug}:${agent.status}`),
  );

  const current = await spinup.agents.get({
    agentSlug: created.agent.slug,
  });

  console.log("current status", current.environment.status);
} catch (error) {
  if (error instanceof SpinupApiError) {
    console.error("Spinup API error", {
      code: error.code,
      message: error.message,
      status: error.status,
    });
  } else {
    throw error;
  }
}

You can still override those values per call:

await spinup.agents.list({
  apiKey: process.env.OTHER_SPINUP_API_KEY,
  workspaceSlug: "other-workspace",
});

In Cloudflare Workers or other edge runtimes, pass bindings from the request handler's env argument instead of reading process.env at module scope. The Env type is generated from your Worker bindings:

import { createSpinupClient } from "@getspinup/sdk";

export default {
  async fetch(_request, env) {
    const spinup = createSpinupClient({
      apiKey: env.SPINUP_API_KEY,
      defaultWorkspaceSlug: env.SPINUP_WORKSPACE,
    });

    const agents = await spinup.agents.list();

    return Response.json({ agents: agents.agents });
  },
} satisfies ExportedHandler<Env>;

Public Resources

The SDK exposes workspace-scoped control-plane operations:

  • spinup.me.get()
  • spinup.workspaces.list()
  • spinup.workspaces.secrets.list()
  • spinup.workspaces.secrets.get({ secretId })
  • spinup.workspaces.secrets.create({ name, defaultProjectionName, value })
  • spinup.workspaces.secrets.update({ secretId, name, defaultProjectionName, value })
  • spinup.workspaces.secrets.delete({ secretId })
  • spinup.agents.list()
  • spinup.agents.get({ agentSlug })
  • spinup.agents.create({ name })
  • spinup.agents.update({ agentSlug, name })
  • spinup.agents.update({ agentSlug, primaryModel: { provider, model } })
  • spinup.agents.update({ agentSlug, runtimePolicy: { allowedPackageInstallationMode } })
  • spinup.agents.update({ agentSlug, runtimePolicy: { resourceLimits: { memoryMiB, diskGiB } } })
  • spinup.agents.updateHarnesses({ agentSlug, defaultHarness })
  • spinup.agents.instructions.get({ agentSlug })
  • spinup.agents.instructions.set({ agentSlug, coreInstructions })
  • spinup.agents.instructions.clear({ agentSlug })
  • spinup.agents.setupCommand.get({ agentSlug })
  • spinup.agents.setupCommand.set({ agentSlug, setupCommand })
  • spinup.agents.setupCommand.clear({ agentSlug })
  • spinup.agents.capabilities.list({ agentSlug })
  • spinup.agents.capabilities.add({ agentSlug, capability })
  • spinup.agents.capabilities.update({ agentSlug, capabilityId, capability })
  • spinup.agents.capabilities.remove({ agentSlug, capabilityId })
  • spinup.agents.runtimeKey.issue({ agentSlug })
  • spinup.agents.schedules.list({ agentSlug })
  • spinup.agents.schedules.create({ agentSlug, schedule })
  • spinup.agents.schedules.update({ agentSlug, scheduleId, schedule })
  • spinup.agents.schedules.disable({ agentSlug, scheduleId })
  • spinup.agents.schedules.delete({ agentSlug, scheduleId })
  • spinup.agents.schedules.preview({ agentSlug, schedule })
  • spinup.agents.secretBindings.list({ agentSlug })
  • spinup.agents.secretBindings.update({ agentSlug, bindings, expectedStateVersion })
  • spinup.agents.proposedChanges.list({ agentSlug, status })
  • spinup.agents.proposedChanges.get({ agentSlug, proposedChangeId })
  • spinup.agents.proposedChanges.approve({ agentSlug, proposedChangeId, expectedBaseStateVersionId, expectedPayloadHash })
  • spinup.agents.proposedChanges.reject({ agentSlug, proposedChangeId, reason })
  • spinup.agents.delete({ agentSlug, confirmationSlug })

The SDK also exposes agent-runtime operations that require an agent runtime key (sk_agent_...) scoped to the target agentId:

  • spinup.agents.status({ agentId })
  • spinup.agents.runs.list({ agentId })
  • spinup.agents.runs.create({ agentId, input, model })
  • spinup.agents.runs.get({ agentId, runId })
  • spinup.agents.runs.wait({ agentId, runId })

Use spinup.agents.runtimeKey.issue({ agentSlug }) with a workspace or personal/device control-plane key to issue or replace that one-agent runtime key. The returned plaintext apiKey is shown once, and the returned agentId is the identifier used by runtime status and run methods.

Device authorization is CLI-only. Use a dashboard-created workspace API key for application code.

Use spinup.agents.capabilities.update({ agentSlug, capabilityId, capability: { status: "disabled" } }) for reversible disable/re-enable flows. spinup.agents.capabilities.remove({ agentSlug, capabilityId }) deletes the binding from the agent state.

Install-backed command-line capabilities are governed by the agent runtime policy. The default declared mode allows supported user-declared package and remote-script install plans. Use curated to restrict installs to Spinup's recognized installer set such as Firecrawl CLI and the Loops installer, or none to block install-backed capabilities. Spinup-owned runtime tools such as ffmpeg should use installPlan.strategy: "none" with an executable validation plan; Spinup materializes the pinned Worker Host artifact on demand before validation.

await spinup.agents.update({
  agentSlug: "support-agent",
  runtimePolicy: {
    allowedPackageInstallationMode: "declared",
  },
});

await spinup.agents.capabilities.add({
  agentSlug: "support-agent",
  capability: {
    installPlan: {
      executable: "loops",
      interpreter: "bash",
      strategy: "remote_script",
      url: "https://cli.loops.so",
    },
    kind: "cli",
    name: "Loops CLI",
    secretBindingIds: ["agentsecretbinding_loops"],
    source: "https://loops.so",
    validationPlan: {
      executable: "loops",
      strategy: "executable",
    },
  },
});

For secret-backed CLIs, bind secrets by ID and project names through the relevant capability or secret-binding flow. Do not put secret values, API keys, or pasted shell installers such as curl ... | bash in capability payloads.