feat(FN-1318): rename CLI binary artifacts from kb to fn

- Update CLI build and runtime paths to emit and reference fn-named binaries across default and cross-target builds
- Adjust Bun binary bootstrap stubs and native patch temp/symlink prefixes to use fn naming consistently
- Update CI, release, and test-release workflows to validate and collect fn-prefixed artifacts
- Align CLI build and package config tests with fn binary expectations and add a patch changeset for @gsxdsm/fusion
This commit is contained in:
gsxdsm
2026-04-08 14:47:30 -07:00
parent 65e98af3b5
commit c1918c6819
11 changed files with 58 additions and 53 deletions

View File

@@ -0,0 +1,5 @@
---
"@gsxdsm/fusion": patch
---
Rename standalone binary output from `kb` to `fn` to match the product rename.

View File

@@ -40,4 +40,4 @@ jobs:
run: pnpm --filter @gsxdsm/fusion build:exe run: pnpm --filter @gsxdsm/fusion build:exe
- name: Verify binary exists - name: Verify binary exists
run: test -f packages/cli/dist/kb run: test -f packages/cli/dist/fn

View File

@@ -29,16 +29,16 @@ jobs:
include: include:
- os: ubuntu-latest - os: ubuntu-latest
target: bun-linux-x64 target: bun-linux-x64
binary: kb-linux-x64 binary: fn-linux-x64
- os: macos-latest - os: macos-latest
target: bun-darwin-arm64 target: bun-darwin-arm64
binary: kb-darwin-arm64 binary: fn-darwin-arm64
- os: macos-13 - os: macos-13
target: bun-darwin-x64 target: bun-darwin-x64
binary: kb-darwin-x64 binary: fn-darwin-x64
- os: windows-latest - os: windows-latest
target: bun-windows-x64 target: bun-windows-x64
binary: kb-windows-x64.exe binary: fn-windows-x64.exe
steps: steps:
- name: Checkout - name: Checkout
@@ -133,7 +133,7 @@ jobs:
- name: Collect release files - name: Collect release files
run: | run: |
mkdir release-files mkdir release-files
find artifacts -type f \( -name "kb-*" -o -name "*.sha256" \) -exec cp {} release-files/ \; find artifacts -type f \( -name "fn-*" -o -name "*.sha256" \) -exec cp {} release-files/ \;
ls -la release-files/ ls -la release-files/
- name: Create GitHub Release - name: Create GitHub Release

View File

@@ -19,16 +19,16 @@ jobs:
include: include:
- os: ubuntu-latest - os: ubuntu-latest
target: bun-linux-x64 target: bun-linux-x64
binary: kb-linux-x64 binary: fn-linux-x64
- os: macos-latest - os: macos-latest
target: bun-darwin-arm64 target: bun-darwin-arm64
binary: kb-darwin-arm64 binary: fn-darwin-arm64
- os: macos-13 - os: macos-13
target: bun-darwin-x64 target: bun-darwin-x64
binary: kb-darwin-x64 binary: fn-darwin-x64
- os: windows-latest - os: windows-latest
target: bun-windows-x64 target: bun-windows-x64
binary: kb-windows-x64.exe binary: fn-windows-x64.exe
steps: steps:
- name: Checkout - name: Checkout
@@ -133,7 +133,7 @@ jobs:
- name: Combine artifacts - name: Combine artifacts
run: | run: |
mkdir combined mkdir combined
find artifacts -type f \( -name "kb-*" -o -name "*.sha256" \) -exec cp {} combined/ \; find artifacts -type f \( -name "fn-*" -o -name "*.sha256" \) -exec cp {} combined/ \;
ls -la combined/ ls -la combined/
- name: Upload combined archive - name: Upload combined archive

View File

@@ -1,8 +1,8 @@
#!/usr/bin/env bun #!/usr/bin/env bun
/** /**
* Bun compile build script for the `kb` CLI. * Bun compile build script for the `fn` CLI.
* *
* Produces a single self-contained executable at packages/cli/dist/kb * Produces a single self-contained executable at packages/cli/dist/fn
* with the dashboard client assets co-located at packages/cli/dist/client/. * with the dashboard client assets co-located at packages/cli/dist/client/.
* *
* Usage: * Usage:
@@ -55,20 +55,20 @@ function targetToPrebuildName(target: BunTarget): string {
/** /**
* Map a Bun target identifier to the output binary name. * Map a Bun target identifier to the output binary name.
* e.g. "bun-linux-x64" → "kb-linux-x64", "bun-windows-x64" → "kb-windows-x64.exe" * e.g. "bun-linux-x64" → "fn-linux-x64", "bun-windows-x64" → "fn-windows-x64.exe"
*/ */
function binaryNameForTarget(target: BunTarget): string { function binaryNameForTarget(target: BunTarget): string {
// "bun-linux-x64" → "linux-x64" // "bun-linux-x64" → "linux-x64"
const suffix = target.replace(/^bun-/, ""); const suffix = target.replace(/^bun-/, "");
const isWindows = target.includes("windows"); const isWindows = target.includes("windows");
return `kb-${suffix}${isWindows ? ".exe" : ""}`; return `fn-${suffix}${isWindows ? ".exe" : ""}`;
} }
/** /**
* Determine the default binary name for the current platform (no cross-compile). * Determine the default binary name for the current platform (no cross-compile).
*/ */
function defaultBinaryName(): string { function defaultBinaryName(): string {
return process.platform === "win32" ? "kb.exe" : "kb"; return process.platform === "win32" ? "fn.exe" : "fn";
} }
/** /**
@@ -272,7 +272,7 @@ const { targets } = parseArgs();
const clientAssetMode = ensureClientAssets(); const clientAssetMode = ensureClientAssets();
if (targets === null) { if (targets === null) {
// Default: build for current platform → dist/kb // Default: build for current platform → dist/fn
const outBinary = join(outDir, defaultBinaryName()); const outBinary = join(outDir, defaultBinaryName());
const ok = compileBinary(outBinary, "bun", false); const ok = compileBinary(outBinary, "bun", false);
if (!ok) process.exit(1); if (!ok) process.exit(1);

View File

@@ -21,7 +21,7 @@ const SUPPORTED_TARGETS = [
function expectedBinaryName(target: string): string { function expectedBinaryName(target: string): string {
const suffix = target.replace(/^bun-/, ""); const suffix = target.replace(/^bun-/, "");
const isWindows = target.includes("windows"); const isWindows = target.includes("windows");
return `kb-${suffix}${isWindows ? ".exe" : ""}`; return `fn-${suffix}${isWindows ? ".exe" : ""}`;
} }
/** /**
@@ -44,8 +44,8 @@ describe("build-exe-cross: single target", () => {
}); });
}, 180_000); }, 180_000);
it("produces dist/kb-linux-x64", () => { it("produces dist/fn-linux-x64", () => {
const bin = join(distDir, "kb-linux-x64"); const bin = join(distDir, "fn-linux-x64");
expect(existsSync(bin)).toBe(true); expect(existsSync(bin)).toBe(true);
expect(statSync(bin).size).toBeGreaterThan(0); expect(statSync(bin).size).toBeGreaterThan(0);
}); });
@@ -64,8 +64,8 @@ describe("build-exe-cross: windows target has .exe extension", () => {
}); });
}, 180_000); }, 180_000);
it("produces dist/kb-windows-x64.exe", () => { it("produces dist/fn-windows-x64.exe", () => {
const bin = join(distDir, "kb-windows-x64.exe"); const bin = join(distDir, "fn-windows-x64.exe");
expect(existsSync(bin)).toBe(true); expect(existsSync(bin)).toBe(true);
expect(statSync(bin).size).toBeGreaterThan(0); expect(statSync(bin).size).toBeGreaterThan(0);
}); });
@@ -143,8 +143,8 @@ describe("build-exe-cross: default (no args) backward compatibility", () => {
}); });
}, 180_000); }, 180_000);
it("produces dist/kb (no platform suffix)", () => { it("produces dist/fn (no platform suffix)", () => {
const defaultName = process.platform === "win32" ? "kb.exe" : "kb"; const defaultName = process.platform === "win32" ? "fn.exe" : "fn";
const bin = join(distDir, defaultName); const bin = join(distDir, defaultName);
expect(existsSync(bin)).toBe(true); expect(existsSync(bin)).toBe(true);
expect(statSync(bin).size).toBeGreaterThan(0); expect(statSync(bin).size).toBeGreaterThan(0);

View File

@@ -6,8 +6,8 @@ import { mkdtempSync, rmSync } from "node:fs";
import { tmpdir } from "node:os"; import { tmpdir } from "node:os";
const cliRoot = join(import.meta.dirname!, "..", ".."); const cliRoot = join(import.meta.dirname!, "..", "..");
const outBinary = join(cliRoot, "dist", process.platform === "win32" ? "kb.exe" : "kb"); const outBinary = join(cliRoot, "dist", process.platform === "win32" ? "fn.exe" : "fn");
const binaryName = process.platform === "win32" ? "kb.exe" : "kb"; const binaryName = process.platform === "win32" ? "fn.exe" : "fn";
const clientDir = join(cliRoot, "dist", "client"); const clientDir = join(cliRoot, "dist", "client");
const runtimeDir = join(cliRoot, "dist", "runtime"); const runtimeDir = join(cliRoot, "dist", "runtime");
@@ -16,7 +16,7 @@ const runtimeDir = join(cliRoot, "dist", "runtime");
* and runtime/ assets — no package.json. Returns the dir path and a cleanup function. * and runtime/ assets — no package.json. Returns the dir path and a cleanup function.
*/ */
function createIsolatedDir(): { dir: string; binary: string; cleanup: () => void } { function createIsolatedDir(): { dir: string; binary: string; cleanup: () => void } {
const dir = mkdtempSync(join(tmpdir(), "kb-iso-")); const dir = mkdtempSync(join(tmpdir(), "fn-iso-"));
cpSync(outBinary, join(dir, binaryName), { recursive: true }); cpSync(outBinary, join(dir, binaryName), { recursive: true });
cpSync(clientDir, join(dir, "client"), { recursive: true }); cpSync(clientDir, join(dir, "client"), { recursive: true });
// Copy runtime native assets alongside binary // Copy runtime native assets alongside binary

View File

@@ -51,7 +51,7 @@ describe("CI workflow (.github/workflows/ci.yml)", () => {
}); });
it("verifies binary exists after build", () => { it("verifies binary exists after build", () => {
expect(content).toContain("test -f packages/cli/dist/kb"); expect(content).toContain("test -f packages/cli/dist/fn");
}); });
it("includes pnpm test step", () => { it("includes pnpm test step", () => {

View File

@@ -37,12 +37,12 @@ describe("CLI package.json publishing config", () => {
it("does not include bare 'dist' entry or globs that would match Bun binaries", () => { it("does not include bare 'dist' entry or globs that would match Bun binaries", () => {
const bunBinaryNames = [ const bunBinaryNames = [
"kb", "fn",
"kb-linux-x64", "fn-linux-x64",
"kb-linux-arm64", "fn-linux-arm64",
"kb-darwin-x64", "fn-darwin-x64",
"kb-darwin-arm64", "fn-darwin-arm64",
"kb-windows-x64.exe", "fn-windows-x64.exe",
]; ];
// No bare "dist" entry that would include everything // No bare "dist" entry that would include everything
expect(pkg.files).not.toContain("dist"); expect(pkg.files).not.toContain("dist");
@@ -51,8 +51,8 @@ describe("CLI package.json publishing config", () => {
for (const bin of bunBinaryNames) { for (const bin of bunBinaryNames) {
expect(entry).not.toBe(`dist/${bin}`); expect(entry).not.toBe(`dist/${bin}`);
} }
// No wildcard like "dist/kb*" that would match binaries // No wildcard like "dist/fn*" that would match binaries
expect(entry).not.toMatch(/^dist\/kb/); expect(entry).not.toMatch(/^dist\/fn/);
} }
}); });

View File

@@ -24,11 +24,11 @@ if (isBunBinary) {
if (!existsSync(localPkg)) { if (!existsSync(localPkg)) {
// Write a minimal package.json to a temp dir and redirect PI_PACKAGE_DIR // Write a minimal package.json to a temp dir and redirect PI_PACKAGE_DIR
const tmp = mkdtempSync(join(tmpdir(), "kb-pkg-")); const tmp = mkdtempSync(join(tmpdir(), "fn-pkg-"));
writeFileSync( writeFileSync(
join(tmp, "package.json"), join(tmp, "package.json"),
JSON.stringify( JSON.stringify(
{ name: "kb", version: "0.1.0", type: "module", piConfig: { name: "kb", configDir: ".fusion" } }, { name: "fn", version: "0.1.0", type: "module", piConfig: { name: "fn", configDir: ".fusion" } },
null, null,
2, 2,
) + "\n", ) + "\n",

View File

@@ -8,7 +8,7 @@
* where bundled code runs. Node-pty looks for native modules at: * where bundled code runs. Node-pty looks for native modules at:
* /$bunfs/root/prebuilds/<platform>-<arch>/pty.node * /$bunfs/root/prebuilds/<platform>-<arch>/pty.node
* *
* This module creates a real directory structure at /tmp/kb-bunfs-<pid>/ that mirrors * This module creates a real directory structure at /tmp/fn-bunfs-<pid>/ that mirrors
* the expected structure, then attempts to create a symlink from /$bunfs/root to that * the expected structure, then attempts to create a symlink from /$bunfs/root to that
* temp directory (on macOS/Linux) so node-pty can find the native assets. * temp directory (on macOS/Linux) so node-pty can find the native assets.
*/ */
@@ -64,9 +64,9 @@ function cleanupStaleBunfsLinks(): void {
if (stats.isSymbolicLink()) { if (stats.isSymbolicLink()) {
const target = readlinkSync(bunfsRoot); const target = readlinkSync(bunfsRoot);
// If the target is a temp dir that no longer exists, remove the stale link // If the target is a temp dir that no longer exists, remove the stale link
if (target.includes("kb-bunfs-") && !existsSync(target)) { if (target.includes("fn-bunfs-") && !existsSync(target)) {
rmSync(bunfsRoot); rmSync(bunfsRoot);
console.log("[kb-native-patch] Cleaned up stale /$bunfs/root symlink"); console.log("[fn-native-patch] Cleaned up stale /$bunfs/root symlink");
} }
} }
} }
@@ -79,7 +79,7 @@ function cleanupStaleBunfsLinks(): void {
* Set up the native module resolution structure. * Set up the native module resolution structure.
* *
* Creates: * Creates:
* /tmp/kb-bunfs-<pid>/kb/prebuilds/<platform>-<arch>/ * /tmp/fn-bunfs-<pid>/fn/prebuilds/<platform>-<arch>/
* ├── pty.node * ├── pty.node
* └── spawn-helper (Unix only) * └── spawn-helper (Unix only)
* *
@@ -89,7 +89,7 @@ function cleanupStaleBunfsLinks(): void {
export function setupNativeResolution(): { success: boolean; nativeDir: string | null } { export function setupNativeResolution(): { success: boolean; nativeDir: string | null } {
const nativeDir = findStagedNativeDir(); const nativeDir = findStagedNativeDir();
if (!nativeDir) { if (!nativeDir) {
console.warn("[kb-native-patch] No native assets found, terminal will be unavailable"); console.warn("[fn-native-patch] No native assets found, terminal will be unavailable");
return { success: false, nativeDir: null }; return { success: false, nativeDir: null };
} }
@@ -102,9 +102,9 @@ export function setupNativeResolution(): { success: boolean; nativeDir: string |
process.env.FUSION_NATIVE_ASSETS_PATH = nativeDir; process.env.FUSION_NATIVE_ASSETS_PATH = nativeDir;
// Create the fake bunfs structure // Create the fake bunfs structure
const tmpRoot = join(tmpdir(), `kb-bunfs-${process.pid}`); const tmpRoot = join(tmpdir(), `fn-bunfs-${process.pid}`);
const kbDir = join(tmpRoot, "kb"); const fnDir = join(tmpRoot, "fn");
const prebuildsDir = join(kbDir, "prebuilds"); const prebuildsDir = join(fnDir, "prebuilds");
const platformDir = join(prebuildsDir, basename(nativeDir)); const platformDir = join(prebuildsDir, basename(nativeDir));
try { try {
@@ -138,23 +138,23 @@ export function setupNativeResolution(): { success: boolean; nativeDir: string |
} }
} }
// Create new symlink pointing to our temp kb directory // Create new symlink pointing to our temp fn directory
// We want /$bunfs/root -> /tmp/kb-bunfs-<pid>/kb // We want /$bunfs/root -> /tmp/fn-bunfs-<pid>/fn
// So that /$bunfs/root/prebuilds/<platform>/pty.node resolves correctly // So that /$bunfs/root/prebuilds/<platform>/pty.node resolves correctly
symlinkSync(kbDir, bunfsRoot); symlinkSync(fnDir, bunfsRoot);
bunfsSymlinkPath = bunfsRoot; bunfsSymlinkPath = bunfsRoot;
console.log("[kb-native-patch] Created /$bunfs/root symlink for native module resolution"); console.log("[fn-native-patch] Created /$bunfs/root symlink for native module resolution");
} catch (symlinkErr) { } catch (symlinkErr) {
// Symlink creation failed (likely permission denied) - not fatal // Symlink creation failed (likely permission denied) - not fatal
// The terminal service will try alternative loading methods // The terminal service will try alternative loading methods
console.log("[kb-native-patch] Could not create /$bunfs/root symlink (permissions), using fallback"); console.log("[fn-native-patch] Could not create /$bunfs/root symlink (permissions), using fallback");
} }
} }
console.log("[kb-native-patch] Native assets staged at:", tmpRoot); console.log("[fn-native-patch] Native assets staged at:", tmpRoot);
return { success: true, nativeDir }; return { success: true, nativeDir };
} catch (err) { } catch (err) {
console.error("[kb-native-patch] Failed to setup native resolution:", err); console.error("[fn-native-patch] Failed to setup native resolution:", err);
return { success: false, nativeDir: null }; return { success: false, nativeDir: null };
} }
} }