Putnami
DocsGitHub

Licensed under FSL-1.1-MIT

Getting Started
Concepts
How To
Build A Web App
Build An Api Service
Share Code Between Projects
Configure Your App
Add Persistence
Add Authentication
Add Background Jobs
Principles
Tooling & Workspace
Workspace Overview
Cli
Jobs & Commands
SDK
Error Handling
Extensions
Typescript
Go
Python
Docker
Ci
Frameworks
Typescript
OverviewWebReact RoutingForms And ActionsStatic FilesApiErrors And ResponsesConfigurationLoggingHttp And MiddlewareDependency InjectionPlugins And LifecycleSessionsAuthPersistenceEventsStorageCachingWebsocketsTestingHealth ChecksTelemetryProto GrpcSmart Client
Go
OverviewHttpDependency InjectionPlugins And LifecycleConfigurationSecurityPersistenceErrorsEventsStorageCachingLoggingTelemetryGrpcService ClientsValidationOpenapiTesting
Platform
  1. DocsSeparator
  2. Tooling & WorkspaceSeparator
  3. SDK

SDK

@putnami/sdk is the core library that powers the Putnami CLI. It provides the command system, job execution runner, workspace/project discovery, caching, and utilities that extension authors use to extend Putnami.

What it is used for

Use the SDK when you need to:

  • Build custom Putnami extensions (commands + jobs)
  • Automate workspace operations in code
  • Reuse job orchestration, dependency resolution, and caching

Most users interact with the SDK indirectly through the CLI.

Install

Install at the workspace root:

bun add @putnami/sdk

Core concepts

Workspace

The workspace represents the root orchestration unit and is accessed via WorkspaceService:

import { ProjectsService, WorkspaceService } from '@putnami/sdk';

const workspace = WorkspaceService.getInstance();
const description = workspace.describe();

const projectsService = ProjectsService.getInstance();
const projects = projectsService.listProjects();

Projects

Projects are individual apps/libraries in the workspace:

const project = projectsService.getProject('my-app');
console.log(project?.name);
console.log(project?.path);

Commands

Commands define the CLI surface. Use the fluent model API to define arguments and flags, then bind them to a CommandLine.

import { CommandLine, projectJobModel, Flag, type InferCommandModel } from '@putnami/sdk';

const buildModel = projectJobModel.extend({
  description: 'Build project',
  flags: {
    target: Flag.string({ flag: '--target', description: 'Build target', default: 'bun' }),
  },
});

export type BuildOptions = InferCommandModel<typeof buildModel>;

export default new CommandLine('build')
  .description('Build project')
  .category('build')
  .relatedCommands('test', 'lint')
  .model(buildModel)
  .jobRunner();

Commands support metadata for enhanced help output:

  • .category(cat) — groups the command in help output (build, test, deploy, workspace, project, extension, utility)
  • .relatedCommands(...cmds) — shows related commands in help
  • .seeAlso(...refs) — shows documentation links in help

Help generation

The HelpGeneratorService generates structured help data for commands:

import { HelpGeneratorService } from '@putnami/sdk';

const helpGenerator = HelpGeneratorService.getInstance();
const helpData = await helpGenerator.generateHelp(command, 'putnami build', {
  format: 'text',
  includeExamples: true,
  includeRelated: true,
  groupByCategory: true,
});

The service supports:

  • Category grouping — subcommands organized by CommandCategory
  • Dynamic examples — command-defined examples with fallback generation
  • Related commands — cross-references between commands
  • Dual output — structured data for both text and JSON rendering

Jobs

Jobs are executed as subprocesses using the JSONL event protocol. The SDK provides runJobCommandCli for building job CLI scripts:

#!/usr/bin/env bun
import {
  runJobCommandCli,
  standardJobModel,
  Flag,
  emitLog,
  type InferCommandModel,
  type JobCommandContext,
  type JobResult,
} from '@putnami/sdk';

const buildCliModel = standardJobModel.extend({
  name: 'build',
  description: 'Build project',
  flags: {
    minify: Flag.boolean({ default: true }),
  },
});

async function runBuild(
  options: InferCommandModel<typeof buildCliModel>,
  context: JobCommandContext
): Promise<JobResult> {
  if (!context.project) return { status: 'SKIP' };
  emitLog('info', `Building ${context.project.name}`);
  // do work here
  return { status: 'OK' };
}

if (import.meta.main) {
  runJobCommandCli({
    model: buildCliModel,
    extension: '@my/extension',
    job: 'build',
    run: runBuild,
  });
}

Job results return a status: OK, FAILED, or SKIP.

Creating commands and jobs

1) Define options

// src/build/options.ts
import { projectJobModel, Flag, type InferCommandModel } from '@putnami/sdk';

export const buildModel = projectJobModel.extend({
  description: 'Build options',
  flags: {
    minify: Flag.boolean({ default: true, description: 'Minify output' }),
  },
});

export type BuildOptions = InferCommandModel<typeof buildModel>;

2) Wire command to the job execution runner

// src/build/command.ts
import { CommandLine } from '@putnami/sdk';
import { buildModel } from './options';

export default new CommandLine('build').model(buildModel).jobRunner();

3) Implement the job CLI script

// bin/build.ts
#!/usr/bin/env bun
import {
  runJobCommandCli,
  standardJobModel,
  Flag,
  emitLog,
  type InferCommandModel,
  type JobCommandContext,
  type JobResult,
} from '@putnami/sdk';

const buildCliModel = standardJobModel.extend({
  name: 'build',
  description: 'Build project',
  flags: {
    minify: Flag.boolean({ default: true }),
  },
});

async function runBuild(
  options: InferCommandModel<typeof buildCliModel>,
  context: JobCommandContext
): Promise<JobResult> {
  if (!context.project) return { status: 'SKIP' };
  emitLog('info', `Building ${context.project.name}`);
  // Build logic here
  return { status: 'OK' };
}

if (import.meta.main) {
  runJobCommandCli({
    model: buildCliModel,
    extension: '@my/extension',
    job: 'build',
    run: runBuild,
  });
}

Extension wiring

putnami.extension.json

Declare commands and jobs in putnami.extension.json:

{
  "commands": {
    "build": "./build"
  },
  "jobs": {
    "build": {
      "kind": "command",
      "command": "bun",
      "args": ["@my/extension:build"],
      "cwd": "{projectRoot}",
      "filePatterns": ["src/**/*", "package.json"]
    }
  }
}

package.json exports

Export commands and job CLI scripts:

{
  "bin": {
    "my-extension-build": "./bin/build"
  },
  "exports": {
    "./build": "./src/build/command.ts"
  }
}

Job dependencies

Use dependsOn on pipeline steps in putnami.extension.json:

  • "stepId" depends on an intra-pipeline step
  • "^job" runs the job on upstream dependencies
  • "*job" runs it on downstream dependents
  • "/job" runs a workspace-level job

Job orchestration, execution trees, caching, and scope resolution are documented in 03-jobs-&-commands.md.

Caching

Caching behavior is documented in 03-jobs-&-commands.md.

Service discovery and watch

The SDK provides service discovery and multi-service watch capabilities for putnami serve:

Project runsWith config

Declare runtime service dependencies in a project's package.json:

{
  "putnami": {
    "runsWith": ["catalogue-api", "profile-api"]
  }
}

When putnami serve web-app is run, the system discovers all declared services, allocates ports, and starts them alongside the primary project.

Service discovery API

import { discoverServices, PortAllocator, type DiscoveredService } from '@putnami/sdk';

// Discover all services for a project
const services: DiscoveredService[] = discoverServices('web-app');
// Returns: [{ name: 'web-app', path: '...', primary: true }, { name: 'catalogue-api', ... }, ...]

// Allocate ports: per-project config → auto-assign from base
const allocator = new PortAllocator(3000);
const assignments = allocator.allocate(services);

See core/sdk/doc/SERVICE-DISCOVERY.md for full API reference.

See also

  • core/sdk/doc/COMMAND-SYSTEM.md
  • core/sdk/doc/JOB-SYSTEM.md
  • core/sdk/doc/HOOK-COMMANDS.md - JSONL protocol for subprocess execution
  • core/sdk/doc/CACHING.md
  • core/sdk/doc/SERVICE-DISCOVERY.md - Multi-service discovery and port allocation
  • core/sdk/doc/WATCH-MODE.md - Watch mode architecture

Next steps

  • Previous: Jobs & commands
  • Next: Extensions

On this page

  • SDK
  • What it is used for
  • Install
  • Core concepts
  • Workspace
  • Projects
  • Commands
  • Help generation
  • Jobs
  • Creating commands and jobs
  • 1) Define options
  • 2) Wire command to the job execution runner
  • 3) Implement the job CLI script
  • Extension wiring
  • putnami.extension.json
  • package.json exports
  • Job dependencies
  • Caching
  • Service discovery and watch
  • Project runsWith config
  • Service discovery API
  • See also
  • Next steps