Skip to content

feat: add sidecar binary support for bundling external executables with CLI #170

Description

@zrosenbauer

Summary

Add a sidecar system that allows kidd-powered CLIs to define, download, and ship external binaries (e.g., rg, fd, ast-grep) alongside the compiled CLI. Similar to Tauri's sidecar concept and the existing implementation in @joggr/cli (serenity).

Current state

  • Compile infrastructure existspackages/bundler/src/compile/compile.ts already produces multi-platform binaries via bun build --compile for 7 targets
  • Config system is extensiblepackages/config/src/types.ts defines the kidd config schema
  • Download pattern existspackages/core/src/middleware/icons/install.ts demonstrates downloading + extracting from GitHub releases with platform detection
  • No sidecar concept — there is no way to declare external binaries that should ship with a compiled CLI

Proposed scope

1. Sidecar configuration (top-level in kidd.config.ts)

Define vendored binaries declaratively as a top-level concept:

export default defineConfig({
  sidecars: [
    {
      name: 'rg',
      version: '14.1.1',
      source: { repo: 'BurntSushi/ripgrep', asset: 'ripgrep-{{version}}-{{triple}}.tar.gz' },
      platforms: {
        'darwin-arm64': 'aarch64-apple-darwin',
        'darwin-x64': 'x86_64-apple-darwin',
        'linux-x64': 'x86_64-unknown-linux-gnu',
        'windows-x64': 'x86_64-pc-windows-msvc',
      },
    },
  ],
  compile: {
    name: 'my-cli',
  },
})

2. Download & packaging

  • Fetch binaries from GitHub releases (or custom URLs) during build
  • Extract and rename to strip platform suffixes (e.g., rg-darwin-arm64rg)
  • Place alongside compiled output per-platform

3. Executable permissions handling

Key insight: filesystem executable bits don't survive npm publish. Must handle via:

  • publishConfig.executableFiles in platform package.json (npm >= 7.8.0) — tells npm to set +x on listed files during install
  • postinstall script as fallback — chmod +x for environments that don't respect executableFiles
  • macOS specifics — strip quarantine xattrs (xattr -cr) and ad-hoc codesign (codesign --force --sign -)

4. Runtime resolution

Provide a utility for resolving sidecar binaries at runtime:

// Production: look next to process.execPath
// Development: look in configured vendor directory
const rgPath = resolveSidecar('rg')

5. Platform package generation (optional/future)

For npm distribution (non-compiled), generate platform-specific optional dependency packages (like @joggr/cli-darwin-arm64) that contain the correct binaries for each OS/arch combo.

Reference implementation

The serenity CLI (packages/cli in serenity) implements this pattern:

  • build/vendor.json — binary definitions with version, repo, asset template, platform triples
  • build/scripts/vendor.sh — downloads from GitHub releases with template substitution
  • build/scripts/package.sh — distributes binaries to platform dirs, handles macOS signing
  • platforms/{platform}/package.jsonpublishConfig.executableFiles for permission handling
  • src/middleware/shell.ts — runtime resolution via process.execPath dirname

Open questions

  • Should sidecar download happen at build time only, or support runtime download (lazy install)?
  • Should this live in packages/bundler or be a new packages/sidecar package?
  • How to handle binary size concerns — should sidecars be optional per-platform?
  • Should we support non-GitHub sources (direct URLs, S3, custom registries)?

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or improvement

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions