feat(KB-335): rename environment variables from KB_* to FUSION_*

- Rename all KB_* environment variables to FUSION_* prefix across CLI and dashboard
- Update CLI runtime files (native-patch.ts) and build configuration
- Update dashboard server, badge-pubsub, GitHub webhooks, and terminal service
- Update test files to use new FUSION_API_KEY and related env vars
- Update documentation in README, AGENTS.md, STANDALONE.md, and dashboard README
- Update changeset files with new environment variable names
This commit is contained in:
gsxdsm
2026-03-31 17:03:23 -07:00
parent a114446844
commit ab3dff2100
16 changed files with 102 additions and 83 deletions

View File

@@ -98,8 +98,8 @@ describe("createBadgePubSub", () => {
beforeEach(() => {
process.env = { ...originalEnv };
delete process.env.KB_BADGE_PUBSUB_REDIS_URL;
delete process.env.KB_BADGE_PUBSUB_CHANNEL;
delete process.env.FUSION_BADGE_PUBSUB_REDIS_URL;
delete process.env.FUSION_BADGE_PUBSUB_CHANNEL;
});
afterEach(() => {
@@ -111,24 +111,24 @@ describe("createBadgePubSub", () => {
expect(pubsub).toBeInstanceOf(InMemoryBadgePubSub);
});
it("returns RedisBadgePubSub when KB_BADGE_PUBSUB_REDIS_URL is set", () => {
process.env.KB_BADGE_PUBSUB_REDIS_URL = "redis://localhost:6379";
it("returns RedisBadgePubSub when FUSION_BADGE_PUBSUB_REDIS_URL is set", () => {
process.env.FUSION_BADGE_PUBSUB_REDIS_URL = "redis://localhost:6379";
const pubsub = createBadgePubSub({ sourceId: "server-1" });
expect(pubsub).toBeInstanceOf(RedisBadgePubSub);
});
it("uses default channel kb:badge-updates when KB_BADGE_PUBSUB_CHANNEL is unset", () => {
process.env.KB_BADGE_PUBSUB_REDIS_URL = "redis://localhost:6379";
delete process.env.KB_BADGE_PUBSUB_CHANNEL;
it("uses default channel fusion:badge-updates when FUSION_BADGE_PUBSUB_CHANNEL is unset", () => {
process.env.FUSION_BADGE_PUBSUB_REDIS_URL = "redis://localhost:6379";
delete process.env.FUSION_BADGE_PUBSUB_CHANNEL;
const pubsub = createBadgePubSub({ sourceId: "server-1" });
expect(pubsub).toBeInstanceOf(RedisBadgePubSub);
// The channel is internal to the adapter; we verify by creating it without error
});
it("uses custom channel when KB_BADGE_PUBSUB_CHANNEL is set", () => {
process.env.KB_BADGE_PUBSUB_REDIS_URL = "redis://localhost:6379";
process.env.KB_BADGE_PUBSUB_CHANNEL = "custom-channel";
it("uses custom channel when FUSION_BADGE_PUBSUB_CHANNEL is set", () => {
process.env.FUSION_BADGE_PUBSUB_REDIS_URL = "redis://localhost:6379";
process.env.FUSION_BADGE_PUBSUB_CHANNEL = "custom-channel";
const pubsub = createBadgePubSub({ sourceId: "server-1" });
expect(pubsub).toBeInstanceOf(RedisBadgePubSub);
@@ -401,8 +401,8 @@ describe("RedisBadgePubSub (integration)", () => {
});
it("reads configuration from environment variables", () => {
process.env.KB_BADGE_PUBSUB_REDIS_URL = "redis://redis.example.com:6380";
process.env.KB_BADGE_PUBSUB_CHANNEL = "prod-badges";
process.env.FUSION_BADGE_PUBSUB_REDIS_URL = "redis://redis.example.com:6380";
process.env.FUSION_BADGE_PUBSUB_CHANNEL = "prod-badges";
const pubsub = createBadgePubSub({ sourceId: "server-1" });
expect(pubsub).toBeInstanceOf(RedisBadgePubSub);

View File

@@ -20,10 +20,10 @@ describe("GitHub Webhooks Module", () => {
beforeEach(() => {
vi.resetModules();
process.env = { ...originalEnv };
delete process.env.KB_GITHUB_APP_ID;
delete process.env.KB_GITHUB_APP_PRIVATE_KEY;
delete process.env.KB_GITHUB_APP_PRIVATE_KEY_PATH;
delete process.env.KB_GITHUB_WEBHOOK_SECRET;
delete process.env.FUSION_GITHUB_APP_ID;
delete process.env.FUSION_GITHUB_APP_PRIVATE_KEY;
delete process.env.FUSION_GITHUB_APP_PRIVATE_KEY_PATH;
delete process.env.FUSION_GITHUB_WEBHOOK_SECRET;
});
afterEach(() => {
@@ -37,14 +37,14 @@ describe("GitHub Webhooks Module", () => {
});
it("returns null when only appId is set", () => {
process.env.KB_GITHUB_APP_ID = "12345";
process.env.FUSION_GITHUB_APP_ID = "12345";
expect(getGitHubAppConfig()).toBeNull();
});
it("returns config when all required vars are set via direct key", () => {
process.env.KB_GITHUB_APP_ID = "12345";
process.env.KB_GITHUB_APP_PRIVATE_KEY = "-----BEGIN RSA PRIVATE KEY-----\ntest\n-----END RSA PRIVATE KEY-----";
process.env.KB_GITHUB_WEBHOOK_SECRET = "webhook-secret";
process.env.FUSION_GITHUB_APP_ID = "12345";
process.env.FUSION_GITHUB_APP_PRIVATE_KEY = "-----BEGIN RSA PRIVATE KEY-----\ntest\n-----END RSA PRIVATE KEY-----";
process.env.FUSION_GITHUB_WEBHOOK_SECRET = "webhook-secret";
const config = getGitHubAppConfig();
expect(config).not.toBeNull();
@@ -54,18 +54,18 @@ describe("GitHub Webhooks Module", () => {
});
it("returns null when key file cannot be read", () => {
process.env.KB_GITHUB_APP_ID = "12345";
process.env.KB_GITHUB_APP_PRIVATE_KEY_PATH = "/nonexistent/path/key.pem";
process.env.KB_GITHUB_WEBHOOK_SECRET = "webhook-secret";
process.env.FUSION_GITHUB_APP_ID = "12345";
process.env.FUSION_GITHUB_APP_PRIVATE_KEY_PATH = "/nonexistent/path/key.pem";
process.env.FUSION_GITHUB_WEBHOOK_SECRET = "webhook-secret";
expect(getGitHubAppConfig()).toBeNull();
});
it("prefers direct key over file path when both are set", () => {
process.env.KB_GITHUB_APP_ID = "12345";
process.env.KB_GITHUB_APP_PRIVATE_KEY = "direct-key-content";
process.env.KB_GITHUB_APP_PRIVATE_KEY_PATH = "/nonexistent/path/key.pem";
process.env.KB_GITHUB_WEBHOOK_SECRET = "webhook-secret";
process.env.FUSION_GITHUB_APP_ID = "12345";
process.env.FUSION_GITHUB_APP_PRIVATE_KEY = "direct-key-content";
process.env.FUSION_GITHUB_APP_PRIVATE_KEY_PATH = "/nonexistent/path/key.pem";
process.env.FUSION_GITHUB_WEBHOOK_SECRET = "webhook-secret";
const config = getGitHubAppConfig();
expect(config?.privateKey).toBe("direct-key-content");
@@ -78,9 +78,9 @@ describe("GitHub Webhooks Module", () => {
});
it("returns true when fully configured", () => {
process.env.KB_GITHUB_APP_ID = "12345";
process.env.KB_GITHUB_APP_PRIVATE_KEY = "private-key";
process.env.KB_GITHUB_WEBHOOK_SECRET = "webhook-secret";
process.env.FUSION_GITHUB_APP_ID = "12345";
process.env.FUSION_GITHUB_APP_PRIVATE_KEY = "private-key";
process.env.FUSION_GITHUB_WEBHOOK_SECRET = "webhook-secret";
expect(isGitHubAppConfigured()).toBe(true);
});

View File

@@ -103,8 +103,8 @@ export class InMemoryBadgePubSub extends EventEmitter<BadgePubSubEvents> impleme
* Uses Redis pub/sub for cross-instance message delivery.
*
* Environment variables:
* - KB_BADGE_PUBSUB_REDIS_URL: Redis connection URL
* - KB_BADGE_PUBSUB_CHANNEL: Channel name (default: "kb:badge-updates")
* - FUSION_BADGE_PUBSUB_REDIS_URL: Redis connection URL
* - FUSION_BADGE_PUBSUB_CHANNEL: Channel name (default: "fusion:badge-updates")
*/
export class RedisBadgePubSub extends EventEmitter<BadgePubSubEvents> implements BadgePubSub {
private subscriber: import("ioredis").Redis | null = null;
@@ -280,7 +280,7 @@ export function parseBadgePubSubMessage(
/**
* Create a BadgePubSub adapter based on environment configuration.
*
* If KB_BADGE_PUBSUB_REDIS_URL is set, returns a RedisBadgePubSub.
* If FUSION_BADGE_PUBSUB_REDIS_URL is set, returns a RedisBadgePubSub.
* Otherwise, returns an InMemoryBadgePubSub for local-only operation.
*/
export function createBadgePubSub(options: { sourceId: string }): BadgePubSub {
@@ -298,11 +298,11 @@ export function createBadgePubSub(options: { sourceId: string }): BadgePubSub {
}
function getRedisUrlFromEnv(): string | undefined {
return process.env.KB_BADGE_PUBSUB_REDIS_URL;
return process.env.FUSION_BADGE_PUBSUB_REDIS_URL;
}
function getChannelFromEnv(): string {
return process.env.KB_BADGE_PUBSUB_CHANNEL ?? "kb:badge-updates";
return process.env.FUSION_BADGE_PUBSUB_CHANNEL ?? "fusion:badge-updates";
}
export { getRedisUrlFromEnv, getChannelFromEnv };

View File

@@ -57,19 +57,19 @@ export interface VerificationResult {
/**
* Read GitHub App configuration from environment variables.
* Supports KB_GITHUB_APP_PRIVATE_KEY or KB_GITHUB_APP_PRIVATE_KEY_PATH.
* Supports FUSION_GITHUB_APP_PRIVATE_KEY or FUSION_GITHUB_APP_PRIVATE_KEY_PATH.
*/
export function getGitHubAppConfig(): GitHubAppConfig | null {
const appId = process.env.KB_GITHUB_APP_ID;
const webhookSecret = process.env.KB_GITHUB_WEBHOOK_SECRET;
const appId = process.env.FUSION_GITHUB_APP_ID;
const webhookSecret = process.env.FUSION_GITHUB_WEBHOOK_SECRET;
let privateKey: string | undefined;
if (process.env.KB_GITHUB_APP_PRIVATE_KEY) {
privateKey = process.env.KB_GITHUB_APP_PRIVATE_KEY;
} else if (process.env.KB_GITHUB_APP_PRIVATE_KEY_PATH) {
if (process.env.FUSION_GITHUB_APP_PRIVATE_KEY) {
privateKey = process.env.FUSION_GITHUB_APP_PRIVATE_KEY;
} else if (process.env.FUSION_GITHUB_APP_PRIVATE_KEY_PATH) {
try {
privateKey = readFileSync(process.env.KB_GITHUB_APP_PRIVATE_KEY_PATH, "utf-8");
privateKey = readFileSync(process.env.FUSION_GITHUB_APP_PRIVATE_KEY_PATH, "utf-8");
} catch {
// Failed to read key file
return null;

View File

@@ -1049,7 +1049,7 @@ function discardGitChanges(files: string[], cwd?: string): string[] {
export function createApiRoutes(store: TaskStore, options?: ServerOptions): Router {
const router = Router();
if (process.env.KB_DEBUG_PLANNING_ROUTES === "1") {
if (process.env.FUSION_DEBUG_PLANNING_ROUTES === "1") {
const planningRoutes = [
"POST /planning/start",
"POST /planning/start-streaming",

View File

@@ -57,13 +57,13 @@ export function createServer(store: TaskStore, options?: ServerOptions): ReturnT
// Serve built React app
// Resolution order:
// 1. KB_CLIENT_DIR env override (explicit)
// 1. FUSION_CLIENT_DIR env override (explicit)
// 2. Next to process.execPath (bun-compiled binary: dist/kb + dist/client/)
// 3. __dirname/../dist/client (running from src/ via tsx/ts-node)
// 4. __dirname/../client (running from dist/ after tsc)
const execDir = dirname(process.execPath);
const clientDir = process.env.KB_CLIENT_DIR
? process.env.KB_CLIENT_DIR
const clientDir = process.env.FUSION_CLIENT_DIR
? process.env.FUSION_CLIENT_DIR
: existsSync(join(execDir, "client", "index.html"))
? join(execDir, "client")
: existsSync(join(__dirname, "..", "dist", "client"))
@@ -167,7 +167,7 @@ export function createServer(store: TaskStore, options?: ServerOptions): ReturnT
app.use("/api", rateLimit(RATE_LIMITS.api));
// Planning route diagnostics for production/runtime debugging. Disabled by default.
if (process.env.KB_DEBUG_PLANNING_ROUTES === "1") {
if (process.env.FUSION_DEBUG_PLANNING_ROUTES === "1") {
app.use("/api/planning", (req, _res, next) => {
console.debug("[planning:request]", {
method: req.method,

View File

@@ -33,9 +33,9 @@ function findStagedNativeDir(): string | null {
process.arch === "x64" ? "x64" : "unknown";
const prebuildName = `${platform}-${arch}`;
// Check KB_RUNTIME_DIR env var first
if (process.env.KB_RUNTIME_DIR) {
const envPath = join(process.env.KB_RUNTIME_DIR, prebuildName);
// Check FUSION_RUNTIME_DIR env var first
if (process.env.FUSION_RUNTIME_DIR) {
const envPath = join(process.env.FUSION_RUNTIME_DIR, prebuildName);
if (existsSync(join(envPath, "pty.node"))) {
return envPath;
}
@@ -69,7 +69,7 @@ async function loadPtyModule(): Promise<typeof import("node-pty")> {
process.env.NODE_PTY_SPAWN_HELPER_DIR = nativeDir;
}
// Store reference for debugging
process.env.KB_NATIVE_ASSETS_PATH = nativeDir;
process.env.FUSION_NATIVE_ASSETS_PATH = nativeDir;
// Try to pre-load the native module using process.dlopen
// This can help when the normal require() path fails
@@ -137,7 +137,7 @@ const STRIP_ENV_VARS = [
"AUTOMAKER_API_KEY",
"NODE_PATH",
"GITHUB_TOKEN",
"KB_API_KEY",
"FUSION_API_KEY",
];
export interface TerminalSession {