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 aBundleResolveOutput.pull()— fetches metadata and downloads all asset content, verifying each asset's SHA-256 checksum. Returns aBundle.
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.
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.
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.
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.
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:
- Programmatic — pass a token directly via
configure()or theMusherClientconstructor MUSHER_API_KEYenvironment variable — simplest option for CI and local development- OS keyring (
musher/<host>) — set by the Musher CLI aftermusher auth login - 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.
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