A Dagger SDK for authoring Dagger modules and generating typed clients in TypeScript.
This module implements the Dagger CLI 1.0 SDK contract: the engine calls its
initModule and initClient functions when you run dagger module init and
dagger api client init, and its @generate functions when you run dagger generate. It also exposes targetRuntime ("typescript"), so modules it
creates run on the built-in TypeScript runtime.
Backed by github.com/dagger/polyfill.
Install the TypeScript SDK into your workspace:
dagger sdk install typescriptThis installs github.com/dagger/typescript-sdk as [modules.dagger-typescript-sdk]
and registers it under the user-facing name typescript, so dagger module init typescript and dagger api client init typescript dispatch to it.
Commands that produce a Changeset print the diff and prompt for confirmation
before writing anything. Pass --auto-apply to skip the prompt.
Create a TypeScript SDK module under the default .dagger/modules/<name>/:
dagger module init typescript my-modulePick a different location with --path:
dagger module init typescript my-module --path some/dir/my-modulePick a runtime (node is the default; bun and deno are also supported):
dagger module init typescript my-module --runtime bun
dagger module init typescript my-module --runtime denoinitModule seeds src/index.ts plus runtime-specific config files:
node/bun→package.json,tsconfig.jsondeno→deno.json
If package.json, tsconfig.json, or deno.json already exist at the target
path, init merges Dagger-required keys into them rather than overwriting — your
scripts, path aliases, unstable flags, and other custom settings are preserved.
The engine owns the module's config; the SDK only contributes the template and
config files above. Run dagger generate afterwards to produce the generated
SDK bindings.
Pick a starter with --template (default is a small working module, empty
is a bare @object class):
dagger module init typescript my-module --template emptymodule init accepts configuration flags written into the module's
package.json (or deno.json for Deno modules):
dagger module init typescript my-module \
--package-manager pnpm@8.15.4 \
--base-image node:23.2.0-alpineBoth flags are optional. By default no packageManager field is written and no
base image override is set.
--package-manager accepts the Node-standard name@version syntax (e.g.
npm@10.7.0, pnpm@8.15.4, yarn@1.22.22). It is only valid with the Node
runtime; Bun and Deno bundle their own.
Run dagger sdk module-options typescript to list the flags the installed
version accepts.
Generate a TypeScript client bound to a module at a target path:
dagger api client init typescript ./lib/client .dagger/modules/apiThe positional arguments are the output <path> and the target <module> (a
workspace-relative path or a canonical module ref). Pass --dev to bind the
local development client instead of a pinned release:
dagger api client init typescript ./lib/client .dagger/modules/api --devThe engine records the client (generator + directory) in workspace config and the target module config; the SDK generates the files.
A client is a self-contained, scoped npm package holding:
dagger.gen.ts— the core API types<module>.gen.ts— one per module in the bound module's closurepackage.json,tsconfig.json— pinned to the bound module's engine version
It binds exactly one module and serves it through Workspace.moduleSource, so
it resolves from any plain client session rather than only from a module
runtime. If you point @dagger.io/dagger at a local bundle (e.g. "./sdk"),
regeneration preserves that instead of resetting it to the version pin.
Regenerate every registered module and client in the workspace:
dagger generateGeneration is anchored at your current directory, not the workspace root: running it from a subdirectory regenerates the project you're in and the projects beneath it, and leaves the rest of the workspace alone.
Each module's local dependency closure is staged before its own codegen runs, so a module that imports another local module always generates against up-to-date bindings — including dependencies owned by a different SDK.
The SDK also exposes auxiliary functions for working with existing modules,
callable directly with dagger call. They are addressed by the workspace
install name (dagger-typescript-sdk by default):
dagger call dagger-typescript-sdk <function> [flags]Read current configuration:
dagger call dagger-typescript-sdk mod --path my-module config package-manager
dagger call dagger-typescript-sdk mod --path my-module config base-imageChange configuration with config set — pass either flag, or both in a single
call. Each returns a Changeset so you confirm the diff before anything is
written:
dagger call dagger-typescript-sdk mod --path my-module config set --package-manager pnpm@8.15.4
dagger call dagger-typescript-sdk mod --path my-module config set --base-image node:23.2.0-alpine
dagger call dagger-typescript-sdk mod --path my-module config set \
--package-manager pnpm@8.15.4 --base-image node:23.2.0-alpineUnset stays as separate commands:
dagger call dagger-typescript-sdk mod --path my-module config unset-package-manager
dagger call dagger-typescript-sdk mod --path my-module config unset-base-imagepackage-manager is only supported on Node modules; Bun and Deno bundle their
own and the SDK rejects the flag on those runtimes. base-image writes to
deno.json for Deno modules and to package.json otherwise — matching where
the engine reads it from.
--path may point anywhere inside the module; mod walks up to the nearest
enclosing module config. Pass --find-up=false to address a module root
directly.
Config always resolves through the module's source directory, which the
module config's source field can move away from the module root — the layout
dagger setup migration produces, where the config lives in
.dagger/modules/<name>/ and points back at pre-existing code:
# reads and writes ci/package.json, not .dagger/modules/my-module/package.json
dagger call dagger-typescript-sdk mod \
--path .dagger/modules/my-module --find-up=false config package-manager# Generate a single module's SDK files
dagger call dagger-typescript-sdk mod --path my-module generate
# Every TypeScript SDK module in scope, as a cwd-relative path
dagger call dagger-typescript-sdk modules path
# Generate all of them; equivalent to what `dagger generate` runs
dagger call dagger-typescript-sdk generate-all-module
dagger call dagger-typescript-sdk generate-all-clientmodules returns the modules this SDK manages — the
[[modules.<sdk>.as-sdk.modules]] entries the engine owns — intersected with
the ones visible from your current directory. A module is discovered through the
directory holding its config, so a module whose source points elsewhere is
found at its config path, not from inside its source tree.
To exclude a directory tree from module generation, drop an empty
.dagger-typescript-sdk-skip-generate file at or above the module root. Useful
for fixtures, vendored modules, or anything you don't want regenerated in bulk.
touch some/fixture/.dagger-typescript-sdk-skip-generateThe marker only affects module generation; registered clients are unaffected.
Run the end-to-end checks:
dagger checkChecks live in .dagger/modules/e2e, one file per SDK surface (lookup,
discovery, init, config, generate, client), sharing the assertions in
util.dang and driving the fixture tree under .dagger/modules/e2e/fixtures.
List them with dagger check -l, or run one group with
dagger check "e-2-e:config:*".
See typescript-sdk.dang for the full type surface.