5.6 KiB
difyctl — build
How to build, package, and ship difyctl from the dify monorepo.
Spec authority for distribution:
docs/specs/README.md. This file documents the mechanics — commands, define knobs, tarball production.
Toolchain
| Tool | Version | Source |
|---|---|---|
| Node | ^22.22.1 |
package.json#engines |
| pnpm | 10.33.0 |
package.json#packageManager |
| TypeScript | catalog pin | pnpm catalog |
vite-plus (vp) |
catalog pin | pnpm catalog — wraps Vite for tests + pack |
| oclif | catalog pin | command discovery, manifest, tarball builder |
Versions above are what the build pipeline targets. Install Node + pnpm via whatever method works for your environment.
Quick start
# inside dify/ monorepo root
pnpm install
pnpm --filter @langgenius/difyctl build
This produces:
cli/dist/— TypeScript output (commands, source map, .d.ts)cli/oclif.manifest.json— oclif command index (used at runtime to skip filesystem scan)
Scripts
| Script | What it does |
|---|---|
pnpm --filter @langgenius/difyctl build |
vp pack && oclif manifest — production bundle + manifest refresh |
pnpm --filter @langgenius/difyctl dev |
tsx bin/dev.js — runs CLI from TS source, no build step |
pnpm --filter @langgenius/difyctl test |
vp test — vitest suite (40+ files, ~400+ behavior tests) |
pnpm --filter @langgenius/difyctl lint |
eslint — antfu config + perfectionist sort + unicorn rules |
pnpm --filter @langgenius/difyctl type-check |
tsc — strict mode, noUncheckedIndexedAccess |
pnpm --filter @langgenius/difyctl manifest |
oclif manifest — refresh oclif.manifest.json only |
pnpm --filter @langgenius/difyctl readme |
oclif readme — regenerate command reference in cli/README.md |
pnpm --filter @langgenius/difyctl pack:tarballs |
oclif pack tarballs --xz --parallel — multi-target tarball matrix |
Build-time defines (vite-plus)
vite.config.ts injects a small set of constants at pack time. These replace what the Go port did via -ldflags -X:
| Define | Source | Read from |
|---|---|---|
__DIFYCTL_VERSION__ |
DIFYCTL_VERSION env var (else package.json#version) |
cli/src/version/info.ts |
__DIFYCTL_COMMIT__ |
DIFYCTL_COMMIT env var (else git rev-parse HEAD) |
cli/src/version/info.ts |
__DIFYCTL_BUILD_DATE__ |
DIFYCTL_BUILD_DATE env var (else new Date().toISOString()) |
cli/src/version/info.ts |
__DIFYCTL_CHANNEL__ |
DIFYCTL_CHANNEL env var (default stable; values: stable, beta, dev) |
cli/src/version/info.ts |
In dev (bin/dev.js), globalThis.__DIFYCTL_*__ are set from the same env vars at startup, so difyctl version reflects the local checkout without a rebuild.
Tarball production
# default — every supported (os, arch) target
pnpm --filter @langgenius/difyctl pack:tarballs
# subset — single target
pnpm --filter @langgenius/difyctl exec \
oclif pack tarballs --xz --targets=darwin-arm64
Outputs land in cli/dist/. Each tarball bundles the Node binary for that target (oclif fetches from nodejs.org), the compiled JS, and the prebuild for @napi-rs/keyring matching that target.
Supported targets (matches auth-storage.md matrix):
darwin-arm64darwin-x64linux-x64-gnulinux-arm64-gnuwin32-x64
Container image
docker build \
--build-arg VERSION=$(node -p "require('./cli/package.json').version") \
-f cli/Dockerfile \
-t ghcr.io/langgenius/difyctl:dev cli/
The Dockerfile uses node:22-alpine and npm install -g @langgenius/difyctl@${VERSION} so the CI release pipeline does not need to ship multi-arch tarballs separately for container users — it just publishes to npm and rebuilds the image.
Release flow
The dify release workflow ships a release-cli job that fans out from the dify version tag:
pnpm --filter @langgenius/difyctl buildoclif manifest(already part of build)oclif pack tarballs --xz --parallelpnpm publish --access public --tag latest(requiresNPM_TOKEN)softprops/action-gh-releaseattaches tarballs + install scripts to the GitHub releasedocker buildx build --pushfor the container image
CLI version equals dify version; parity enforced at tag time.
Local install for smoke testing
# from a fresh clone — verify the build works end to end
pnpm install
pnpm --filter @langgenius/difyctl build
pnpm --filter @langgenius/difyctl exec oclif pack tarballs --targets=$(uname -s | tr 'A-Z' 'a-z')-$(uname -m | sed 's/x86_64/x64/;s/aarch64/arm64/')
ls cli/dist/*.tar.xz
The resulting tarball is a self-contained Node + difyctl bundle — extract anywhere, point $PATH at bin/, and difyctl --version works without the host having Node installed.