Skip to content
Musher Docs

TypeScript SDK Concepts

This page explains the core concepts behind the TypeScript SDK's object model. Understanding these will help you choose the right function for each use case.

Resolve vs Pull

The SDK offers two ways to interact with a bundle:

  • resolve() — fetches manifest metadata only. No asset content is downloaded. Returns a BundleResolveOutput.
  • pull() — fetches metadata and downloads all asset content, verifying each asset's SHA-256 checksum. Returns a Bundle.

Use resolve() when you only need to check which version a reference points to, list what assets exist, or validate a bundle reference — without paying the cost of downloading files.

typescript
import { pull, resolve } from "@musher-dev/musher-sdk";

// Metadata only — no downloads
const result = await resolve("acme/code-review-kit:^1.0");
console.log(`Version: ${result.version}`);
console.log(`Assets: ${result.assets.length}`);

// Full download — assets verified and cached locally
const bundle = await pull("acme/code-review-kit:^1.0");

for (const fh of bundle.files()) {
  console.log(fh.text());
}

Manifest Layers vs Typed Handles

These two concepts represent different views of the same bundle content, depending on whether you used resolve() or pull().

Manifest layers

A BundleResolveOutput contains metadata about each asset: its logical path, asset type, digest, and size. Layers are metadata — they tell you what's in the bundle without giving you the content.

Typed handles

A Bundle provides typed accessor methods that return handle objects for each asset category:

  • bundle.files()FileHandle[]
  • bundle.skills()SkillHandle[]
  • bundle.prompts()PromptHandle[]
  • bundle.toolsets()ToolsetHandle[]
  • bundle.agentSpecs()AgentSpecHandle[]

Each handle gives you the actual content via properties like .content or methods like .text() and .bytes(). Handles are the primary way you interact with bundle assets after pulling.

typescript
const bundle = await pull("acme/code-review-kit");

// Typed handle access
for (const skill of bundle.skills()) {
  console.log(`${skill.name}: ${skill.description}`);
  console.log(skill.content);
}

// Single asset lookup (throws BundleAssetNotFoundError if not found)
const toolset = bundle.toolset("lint-rules");
console.log(toolset.content);

Client Architecture

The SDK provides two ways to make calls:

Module-level functions

pull() and resolve() use a shared global client internally. This is the simplest path and works for most applications.

typescript
import { pull, resolve, configure } from "@musher-dev/musher-sdk";

// Optional: customize global settings
configure({ cacheDir: "/custom/cache" });

// Uses the global client
const bundle = await pull("acme/code-review-kit");

MusherClient

MusherClient lets you create instances with independent configuration. Use this when you need multiple clients pointed at different registries, or when you want isolated configuration.

typescript
import { MusherClient } from "@musher-dev/musher-sdk";

const client = new MusherClient({
  apiKey: "mush_...",
  baseUrl: "https://api.musher.dev",
});

const bundle = await client.pull("acme/code-review-kit");

Credential Resolution

The SDK discovers credentials automatically by checking four sources in order, using the first one found:

  1. Programmatic — pass a token directly via configure() or the MusherClient constructor
  2. MUSHER_API_KEY environment variable — simplest option for CI and local development
  3. OS keyring (musher/<host>) — set by the Musher CLI after musher auth login
  4. Credential file (~/.local/share/musher/credentials/<host>/api-key) — file-based fallback (must have strict permissions)

Selection and Filtering

Use bundle.select() to create a filtered view that only includes specific assets. The returned Selection has the same typed accessor methods as Bundle.

typescript
const bundle = await pull("acme/code-review-kit");

// Filter to specific assets
const selection = bundle.select({
  skills: ["code-review", "security-scan"],
  prompts: ["review-template"],
});

// Only selected assets are returned
for (const skill of selection.skills()) {
  console.log(skill.name);
}

// Original bundle is unchanged
console.log(bundle.skills().length); // all skills