@sundaeswap/sprinkles
v0.8.2
Published
A TypeScript library for building interactive CLI menus and TUI applications with TypeBox schema validation
Readme
Sprinkles
Note: This is an early release (v0.x). The API may change between minor versions as we refine the library based on community feedback.
A TypeScript library for building action-centric CLI applications with TypeBox schema validation. Define your actions once, and Sprinkles exposes them through three facades: an interactive TUI, a CLI with auto-generated flags and help, and an MCP server for AI agent integration.
Architecture
Sprinkles follows an action-centric design. The core building block is an IAction — a typed, self-describing unit of work with input/output schemas:
┌──────────────┐
│ Actions │ ← define once
│ (IAction) │
└──────┬───────┘
│
┌────────────┼────────────┐
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ TUI │ │ CLI │ │ MCP │ ← three facades
│ (menu) │ │ (flags) │ │ (tools) │
└─────────┘ └─────────┘ └─────────┘- TUI — Interactive menu-driven interface. Users select actions from a menu, and Sprinkles auto-generates prompts from input schemas.
- CLI — Non-interactive command execution. Actions become subcommands with auto-generated
--flagsfrom input schemas and structured JSON output. - MCP — Model Context Protocol server. Actions are exposed as tools that AI agents can discover and call with typed JSON input/output.
Sprinkle.run() detects the mode from process.argv and dispatches automatically.
Features
- Action-centric: Define actions with TypeBox input/output schemas; get TUI, CLI, and MCP for free
- Schema-driven UI: Automatic prompt generation from TypeBox schemas
- Multi-profile support: Multiple named profiles with independent settings
- Persistent settings: JSON-based storage with BigInt support and optional encryption
- 14 built-in actions: Profile management, settings, wallet operations, and transaction handling
- Cardano integration: Built-in helpers for Blaze SDK wallet and provider management
- Type-safe: Full TypeScript support with type inference from schemas
Installation
npm install @sundaeswap/sprinklesOptional peer dependencies:
@blaze-cardano/sdk+@blaze-cardano/query— for wallet and transaction actions@modelcontextprotocol/sdk— for MCP mode
Quick Start
import { Type } from "@sinclair/typebox";
import type { TSchema } from "@sinclair/typebox";
import {
Sprinkle,
promptAndExecute,
getBuiltinActions,
NetworkSchema,
ProviderSettingsSchema,
WalletSettingsSchema,
} from "@sundaeswap/sprinkles";
import type { AnyAction, IAction, IMenu } from "@sundaeswap/sprinkles";
// 1. Define your settings schema
const AppSettings = Type.Object({
network: NetworkSchema,
provider: ProviderSettingsSchema,
wallet: WalletSettingsSchema,
});
// 2. Define a custom action
const greetAction: IAction<
{ name: string },
{ greeting: string },
TSchema
> = {
name: "greet",
description: "Say hello to someone.",
category: "app",
inputSchema: Type.Object({
name: Type.String({ description: "Person to greet" }),
}),
outputSchema: Type.Object({
greeting: Type.String(),
}),
execute: async (input) => ({
greeting: `Hello, ${input.name}!`,
}),
};
// 3. Define a TUI menu
const menu: IMenu<typeof AppSettings> = {
title: "My App",
items: [
{
title: "Greet someone",
action: async (sprinkle) => {
const result = await promptAndExecute(sprinkle, greetAction);
if (result.success) {
console.log(result.data.greeting);
} else if (result.error.code !== "USER_CANCELLED") {
console.error("Error:", result.error.message);
}
},
},
],
};
// 4. Run — mode is detected automatically
await Sprinkle.run({
type: AppSettings,
storagePath: `${process.env["HOME"]}/.config/my-app`,
menu,
actions: [
greetAction as unknown as AnyAction<typeof AppSettings>,
...getBuiltinActions<typeof AppSettings>(),
],
});This single entry point gives you:
# TUI mode (interactive)
my-app
# CLI mode
my-app greet --name Alice
my-app list-profiles
my-app get-settings
# MCP mode (AI agent)
my-app --mcp
# Help
my-app --help
my-app greet --helpActions
Defining Actions
An action is an object implementing IAction<TInput, TOutput, TSettings>:
const myAction: IAction<
{ url: string; timeout?: number },
{ status: number },
TSchema
> = {
name: "check-health",
description: "Check if a service is healthy.",
category: "ops",
inputSchema: Type.Object({
url: Type.String({ description: "Service URL" }),
timeout: Type.Optional(Type.Number({ description: "Timeout in ms", default: 5000 })),
}),
outputSchema: Type.Object({
status: Type.Number(),
}),
execute: async (input, context) => {
// context.settings gives you the current profile's settings
// context.sprinkle gives you the Sprinkle instance
const res = await fetch(input.url, { signal: AbortSignal.timeout(input.timeout ?? 5000) });
return { status: res.status };
},
};Built-in Actions
Sprinkles includes 14 built-in actions available via getBuiltinActions():
| Category | Action | Description |
|----------|--------|-------------|
| sprinkles | list-profiles | List all profiles |
| sprinkles | get-profile | Get profile metadata and settings |
| sprinkles | set-profile | Switch active profile |
| sprinkles | create-profile | Create a new profile |
| sprinkles | delete-profile | Delete a profile |
| sprinkles | get-settings | Get current settings |
| sprinkles | update-settings | Update settings |
| wallet | get-wallet-address | Get wallet address |
| wallet | get-wallet-balance | Get ADA and token balances |
| wallet | get-wallet-utxos | Get UTxO set |
| wallet | sign-transaction | Sign a transaction (hot wallet only) |
| wallet | submit-transaction | Submit a signed transaction |
| wallet | sign-and-submit | Sign and submit in one step |
| transaction | decode-transaction | Decode transaction CBOR |
Running Actions Programmatically
import { executeAction } from "@sundaeswap/sprinkles";
const result = await executeAction(myAction, { url: "https://example.com" }, context);
if (result.success) {
console.log(result.data.status);
} else {
console.error(result.error.code, result.error.message);
}Built-in Schemas
Sprinkles exports common Cardano schemas for use in your settings:
NetworkSchema
Type.Union([Type.Literal("mainnet"), Type.Literal("preview"), Type.Literal("preprod")])ProviderSettingsSchema
Type.Union([
Type.Object({
type: Type.Literal("blockfrost"),
projectId: Type.String({ minLength: 1, title: "Blockfrost Project ID" }),
}),
Type.Object({
type: Type.Literal("maestro"),
apiKey: Type.String({ minLength: 1, title: "Maestro API Key" }),
}),
])WalletSettingsSchema
Type.Union([
Type.Object({
type: Type.Literal("hot"),
privateKey: Type.String({ minLength: 1, title: "Hot Wallet Private Key", sensitive: true }),
}),
Type.Object({
type: Type.Literal("cold"),
address: Type.String({ minLength: 1, title: "Cold Wallet Address" }),
}),
])Examples
See examples/ for runnable examples:
demo-app/— Full-featured testing harness exercising all built-in actions across TUI, CLI, and MCP modesaction-example.ts— Multi-mode example with a custom actionaction-in-menu.ts— TUI menu integration patterns
License
MIT
