From a0dd3f5c8a1f7bb3433cef859eab76c623bb0f01 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 20 Aug 2026 17:22:21 +0000 Subject: [PATCH 1/7] Add Nix flake and package for installing the CLI Package the standalone binary with buildNpmPackage and importNpmLock so npm remains the only dependency lockfile: importNpmLock feeds the existing package-lock.json to npm offline, and bun compiles src/bin/cli.ts the same way the release workflow does. The compile itself moves into a new build:standalone npm script, so the Nix build runs the project's own build command instead of restating the bun invocation. Its output is the Bun runtime with the JavaScript bundle appended, which neither strip nor patchelf can touch without losing the bundle, so both fixup steps stay off. Only the compiled executable is installed, so nothing in the closure needs Node.js. The flake covers every platform Nixpkgs' bun runs on. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_011VWSk3QX5mCMRJ6PhsJqC1 --- .gitignore | 10 +++++++++- README.md | 19 +++++++++++++++++++ flake.lock | 27 +++++++++++++++++++++++++++ flake.nix | 25 +++++++++++++++++++++++++ package.json | 1 + package.nix | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 package.nix diff --git a/.gitignore b/.gitignore index f56945d5..c05f94ef 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,13 @@ # Build directories package +# Standalone binary compiled by npm run build:standalone +seam + +# Nix build results +result +result-* + # Shell completions generated on prepack completions @@ -209,7 +216,8 @@ $RECYCLE.BIN/ .LSOverride # Icon must end with two \r -Icon +Icon + # Thumbnails ._* diff --git a/README.md b/README.md index 589b2cff..d7099031 100644 --- a/README.md +++ b/README.md @@ -69,12 +69,31 @@ $ cd seam-bin $ makepkg -si ``` +### Nix + +Run the CLI without installing it with [Nix] with + +``` +$ nix run github:seamapi/cli +``` + +Install it into your profile with + +``` +$ nix profile install github:seamapi/cli +$ seam completion --install +``` + +The flake compiles the standalone binary from source for `x86_64-linux`, +`aarch64-linux`, and `aarch64-darwin`. + The AUR and Homebrew packages install [shell completion] themselves. After an npm or manual install, run `seam completion --install`. [aur]: https://aur.archlinux.org/packages/seam-bin [Homebrew]: https://formulae.brew.sh/cask/seam [latest GitHub release]: https://github.com/seamapi/cli/releases/latest +[Nix]: https://nixos.org/ [npm]: https://www.npmjs.com/ [Seam Wizard]: https://github.com/seamapi/wizard [shell completion]: #shell-completion diff --git a/flake.lock b/flake.lock new file mode 100644 index 00000000..a2b57527 --- /dev/null +++ b/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1787135253, + "narHash": "sha256-RD2kNWCG+Bjo6h+JVjWVNntZs2GtRoeY2xHjts/FNkA=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "ffb3c9b700e759be2ef13237c9d8f953b32a1e46", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 00000000..b0bbc1fe --- /dev/null +++ b/flake.nix @@ -0,0 +1,25 @@ +{ + description = "A command-line interface (CLI) for interacting with the Seam API."; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + }; + + outputs = + { nixpkgs, ... }: + let + # Every platform Nixpkgs' bun runs on, which is what the build needs. + systems = [ + "aarch64-darwin" + "aarch64-linux" + "x86_64-linux" + ]; + forEachSystem = f: nixpkgs.lib.genAttrs systems (system: f nixpkgs.legacyPackages.${system}); + in + { + packages = forEachSystem (pkgs: rec { + seam-cli = pkgs.callPackage ./package.nix { }; + default = seam-cli; + }); + }; +} diff --git a/package.json b/package.json index 9934ef72..7fc2a5d2 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "postbuild": "concurrently --raw --group 'node ./index.js' 'node ./bin/cli.js --version'", "build:entrypoints": "npm run build:ts", "build:ts": "tsc --project tsconfig.build.json", + "build:standalone": "npm run prepack && bun build src/bin/cli.ts --compile --minify --outfile seam", "prebuild:ts": "del 'index.*' bin lib", "postbuild:ts": "tsc-alias --project tsconfig.build.json", "typecheck": "tsc", diff --git a/package.nix b/package.nix new file mode 100644 index 00000000..36651b91 --- /dev/null +++ b/package.nix @@ -0,0 +1,46 @@ +{ + lib, + bun, + buildNpmPackage, + importNpmLock, +}: + +buildNpmPackage { + pname = "seam-cli"; + # Kept in sync with the version the build injects into the binary. + version = (lib.importJSON ./package.json).version; + + src = lib.cleanSource ./.; + + npmDeps = importNpmLock { npmRoot = ./.; }; + npmConfigHook = importNpmLock.npmConfigHook; + + # The build script compiles src/bin/cli.ts into a single-file Bun executable. + nativeBuildInputs = [ bun ]; + npmBuildScript = "build:standalone"; + + installPhase = '' + runHook preInstall + + install -Dm755 seam -t $out/bin + + runHook postInstall + ''; + + # The executable is the Bun runtime with the JavaScript bundle appended to + # it, so anything that rewrites the ELF loses the bundle: stripped, it + # silently runs as plain Bun; patched by patchelf, it segfaults. + # buildNpmPackage happens to default dontStrip, but not for this reason. + dontStrip = true; + dontPatchELF = true; + + meta = { + description = "Command-line interface (CLI) for interacting with the Seam API"; + homepage = "https://github.com/seamapi/cli"; + changelog = "https://github.com/seamapi/cli/releases"; + license = lib.licenses.mit; + mainProgram = "seam"; + # Bun compiles for the host, so this builds wherever Bun itself runs. + platforms = bun.meta.platforms; + }; +} From e6e2bd15610e4514419720b9f587fbfa833f0f24 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 20 Aug 2026 17:51:11 +0000 Subject: [PATCH 2/7] Restore the carriage returns on the Icon entry in .gitignore An earlier edit rewrote the file through a text-mode round trip, which turned the two carriage returns the macOS Icon\r\r entry needs into newlines and stopped it matching. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_011VWSk3QX5mCMRJ6PhsJqC1 --- .gitignore | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index c05f94ef..2ef0034b 100644 --- a/.gitignore +++ b/.gitignore @@ -216,8 +216,7 @@ $RECYCLE.BIN/ .LSOverride # Icon must end with two \r -Icon - +Icon # Thumbnails ._* From eeb9802881b8d4b248cf9e2246dc059903335e8d Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 20 Aug 2026 17:53:22 +0000 Subject: [PATCH 3/7] Name the Nix package seam The attribute, the derivation, and the program it installs now all agree, and nothing in Nixpkgs claims the name. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_011VWSk3QX5mCMRJ6PhsJqC1 --- flake.nix | 4 ++-- package.nix | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.nix b/flake.nix index b0bbc1fe..803e3f38 100644 --- a/flake.nix +++ b/flake.nix @@ -18,8 +18,8 @@ in { packages = forEachSystem (pkgs: rec { - seam-cli = pkgs.callPackage ./package.nix { }; - default = seam-cli; + seam = pkgs.callPackage ./package.nix { }; + default = seam; }); }; } diff --git a/package.nix b/package.nix index 36651b91..39029600 100644 --- a/package.nix +++ b/package.nix @@ -6,7 +6,7 @@ }: buildNpmPackage { - pname = "seam-cli"; + pname = "seam"; # Kept in sync with the version the build injects into the binary. version = (lib.importJSON ./package.json).version; From ec53080378184224a206fbe1e237553b56ea686f Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 20 Aug 2026 17:54:45 +0000 Subject: [PATCH 4/7] Run prepack from a prebuild:standalone hook npm chains it the same way the other build scripts here are chained, instead of an && in the script body. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_011VWSk3QX5mCMRJ6PhsJqC1 --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index acb760b3..58b5658f 100644 --- a/package.json +++ b/package.json @@ -52,9 +52,10 @@ "postbuild": "concurrently --raw --group 'node ./index.js' 'node ./bin/cli.js --version'", "build:entrypoints": "npm run build:ts", "build:ts": "tsc --project tsconfig.build.json", - "build:standalone": "npm run prepack && bun build src/bin/cli.ts --compile --minify --outfile seam", "prebuild:ts": "del 'index.*' bin lib", "postbuild:ts": "tsc-alias --project tsconfig.build.json", + "build:standalone": "bun build src/bin/cli.ts --compile --minify --outfile seam", + "prebuild:standalone": "npm run prepack", "typecheck": "tsc", "test": "vitest run --coverage", "pretest": "tsx src/index.ts", From c97a8d42743883639c117a8da4866141dd2f8b48 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 20 Aug 2026 18:10:14 +0000 Subject: [PATCH 5/7] Install the shell completion loaders with the Nix package installShellCompletion puts the same loaders that seam completion --install writes into the profile, so NixOS and Home Manager pick them up without a second step, and they still resolve to whatever schema the installed binary carries. Each installation section in the README now says what it does about completions, rather than deferring to a note under the last one. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_011VWSk3QX5mCMRJ6PhsJqC1 --- README.md | 13 +++++++++---- package.nix | 17 ++++++++++++++++- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 3aca839f..3a42458d 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,8 @@ but it does not include the Seam Wizard. $ brew install seam ``` +Both install the [shell completion] themselves. + ### Standalone binary Install the latest release on Linux and macOS with @@ -97,6 +99,8 @@ $ cd seam-bin $ makepkg -si ``` +The AUR package installs the [shell completion] itself. + ### Nix Run the CLI without installing it with [Nix] with @@ -109,14 +113,15 @@ Install it into your profile with ``` $ nix profile install github:seamapi/cli -$ seam completion --install ``` The flake compiles the standalone binary from source for `x86_64-linux`, -`aarch64-linux`, and `aarch64-darwin`. +`aarch64-linux`, and `aarch64-darwin`, and installs the [shell completion] +with it. NixOS and Home Manager load that from the profile; elsewhere, run -The AUR and Homebrew packages install [shell completion] themselves. After an -npm or manual install, run `seam completion --install`. +``` +$ seam completion --install +``` [aur]: https://aur.archlinux.org/packages/seam-bin [Homebrew]: https://formulae.brew.sh/cask/seam diff --git a/package.nix b/package.nix index 39029600..8e208ce6 100644 --- a/package.nix +++ b/package.nix @@ -1,8 +1,10 @@ { lib, + stdenv, bun, buildNpmPackage, importNpmLock, + installShellFiles, }: buildNpmPackage { @@ -16,7 +18,10 @@ buildNpmPackage { npmConfigHook = importNpmLock.npmConfigHook; # The build script compiles src/bin/cli.ts into a single-file Bun executable. - nativeBuildInputs = [ bun ]; + nativeBuildInputs = [ + bun + installShellFiles + ]; npmBuildScript = "build:standalone"; installPhase = '' @@ -27,6 +32,16 @@ buildNpmPackage { runHook postInstall ''; + # The loaders are what `seam completion --install` writes: each one asks the + # CLI for the real script the first time it completes, so the completions + # cannot drift from the schema the installed binary carries. + postInstall = lib.optionalString (stdenv.buildPlatform.canExecute stdenv.hostPlatform) '' + installShellCompletion --cmd seam \ + --bash <($out/bin/seam completion --loader bash) \ + --fish <($out/bin/seam completion --loader fish) \ + --zsh <($out/bin/seam completion --loader zsh) + ''; + # The executable is the Bun runtime with the JavaScript bundle appended to # it, so anything that rewrites the ELF loses the bundle: stripped, it # silently runs as plain Bun; patched by patchelf, it segfaults. From 891001d8cb1417fbc8dd3787cd8d523668411b0a Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 20 Aug 2026 18:15:28 +0000 Subject: [PATCH 6/7] Drop the comments that only restate the code Only the fixup settings keep one, since nothing about dontStrip and dontPatchELF says why they are there. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_011VWSk3QX5mCMRJ6PhsJqC1 --- flake.nix | 1 - package.nix | 13 +++---------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/flake.nix b/flake.nix index 803e3f38..f8a44ca3 100644 --- a/flake.nix +++ b/flake.nix @@ -8,7 +8,6 @@ outputs = { nixpkgs, ... }: let - # Every platform Nixpkgs' bun runs on, which is what the build needs. systems = [ "aarch64-darwin" "aarch64-linux" diff --git a/package.nix b/package.nix index 8e208ce6..f9992e90 100644 --- a/package.nix +++ b/package.nix @@ -9,7 +9,6 @@ buildNpmPackage { pname = "seam"; - # Kept in sync with the version the build injects into the binary. version = (lib.importJSON ./package.json).version; src = lib.cleanSource ./.; @@ -17,7 +16,6 @@ buildNpmPackage { npmDeps = importNpmLock { npmRoot = ./.; }; npmConfigHook = importNpmLock.npmConfigHook; - # The build script compiles src/bin/cli.ts into a single-file Bun executable. nativeBuildInputs = [ bun installShellFiles @@ -32,9 +30,6 @@ buildNpmPackage { runHook postInstall ''; - # The loaders are what `seam completion --install` writes: each one asks the - # CLI for the real script the first time it completes, so the completions - # cannot drift from the schema the installed binary carries. postInstall = lib.optionalString (stdenv.buildPlatform.canExecute stdenv.hostPlatform) '' installShellCompletion --cmd seam \ --bash <($out/bin/seam completion --loader bash) \ @@ -42,10 +37,9 @@ buildNpmPackage { --zsh <($out/bin/seam completion --loader zsh) ''; - # The executable is the Bun runtime with the JavaScript bundle appended to - # it, so anything that rewrites the ELF loses the bundle: stripped, it - # silently runs as plain Bun; patched by patchelf, it segfaults. - # buildNpmPackage happens to default dontStrip, but not for this reason. + # The executable is the Bun runtime with the JavaScript bundle appended, so + # rewriting the ELF loses the bundle: stripped it runs as plain Bun, + # patchelfed it segfaults. dontStrip = true; dontPatchELF = true; @@ -55,7 +49,6 @@ buildNpmPackage { changelog = "https://github.com/seamapi/cli/releases"; license = lib.licenses.mit; mainProgram = "seam"; - # Bun compiles for the host, so this builds wherever Bun itself runs. platforms = bun.meta.platforms; }; } From edd2891da915ae8a3c81b7ed732053fcdce65717 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 20 Aug 2026 18:16:43 +0000 Subject: [PATCH 7/7] Only mention completion --install where it is needed npm and a manual download need the command. The packages that install completions themselves do not need to say so. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_011VWSk3QX5mCMRJ6PhsJqC1 --- README.md | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 3a42458d..5a6959a5 100644 --- a/README.md +++ b/README.md @@ -46,8 +46,6 @@ but it does not include the Seam Wizard. $ brew install seam ``` -Both install the [shell completion] themselves. - ### Standalone binary Install the latest release on Linux and macOS with @@ -83,7 +81,7 @@ Alternatively, download a standalone binary for your platform from the Inc. Apple Developer ID and notarized by Apple, so macOS runs them without a Gatekeeper prompt. -After a manual download, install the shell completions with +After a manual download, install the [shell completion] with ``` $ seam completion --install @@ -99,8 +97,6 @@ $ cd seam-bin $ makepkg -si ``` -The AUR package installs the [shell completion] itself. - ### Nix Run the CLI without installing it with [Nix] with @@ -116,12 +112,7 @@ $ nix profile install github:seamapi/cli ``` The flake compiles the standalone binary from source for `x86_64-linux`, -`aarch64-linux`, and `aarch64-darwin`, and installs the [shell completion] -with it. NixOS and Home Manager load that from the profile; elsewhere, run - -``` -$ seam completion --install -``` +`aarch64-linux`, and `aarch64-darwin`. [aur]: https://aur.archlinux.org/packages/seam-bin [Homebrew]: https://formulae.brew.sh/cask/seam