feat(KB-107): merge kb/kb-107

- feat(KB-107): complete Step 5 — add changeset for behavioral change
- test(KB-107): complete Step 4 — update tests to remove --no-open flag references
- feat(KB-107): complete Step 3 — remove --no-open from pi extension dashboard spawn
- feat(KB-107): complete Step 2 — simplify dashboard command, remove openBrowser function
- feat(KB-107): complete Step 1 — remove --no-open flag from CLI entry point
This commit is contained in:
gsxdsm
2026-03-30 07:34:10 -07:00
parent fe91b082d1
commit 4eccd036a8
6 changed files with 15 additions and 21 deletions

View File

@@ -0,0 +1,7 @@
---
"@dustinbyrne/kb": patch
---
Remove automatic browser-opening behavior on dashboard startup
The dashboard no longer automatically opens a browser window when starting. Users must manually navigate to the URL shown in the console output. The `--no-open` flag has been removed as it is now the default behavior.

View File

@@ -89,7 +89,7 @@ describe("build-exe", () => {
const port = 14040 + Math.floor(Math.random() * 1000); const port = 14040 + Math.floor(Math.random() * 1000);
try { try {
const output = await new Promise<string>((resolve, reject) => { const output = await new Promise<string>((resolve, reject) => {
const child = spawn(binary, ["dashboard", "--no-open", "-p", String(port)], { const child = spawn(binary, ["dashboard", "-p", String(port)], {
cwd: dir, cwd: dir,
stdio: ["ignore", "pipe", "pipe"], stdio: ["ignore", "pipe", "pipe"],
}); });

View File

@@ -102,11 +102,10 @@ async function main() {
const portIdxShort = args.indexOf("-p"); const portIdxShort = args.indexOf("-p");
const pi = portIdx !== -1 ? portIdx : portIdxShort; const pi = portIdx !== -1 ? portIdx : portIdxShort;
const port = pi !== -1 ? parseInt(args[pi + 1], 10) : 4040; const port = pi !== -1 ? parseInt(args[pi + 1], 10) : 4040;
const open = !args.includes("--no-open");
const paused = args.includes("--paused"); const paused = args.includes("--paused");
const dev = args.includes("--dev"); const dev = args.includes("--dev");
const interactive = args.includes("--interactive"); const interactive = args.includes("--interactive");
await runDashboard(port, { open, paused, dev, interactive }); await runDashboard(port, { paused, dev, interactive });
break; break;
} }

View File

@@ -117,7 +117,7 @@ describe("runDashboard — AuthStorage & ModelRegistry wiring", () => {
it("passes authStorage and modelRegistry to createServer", async () => { it("passes authStorage and modelRegistry to createServer", async () => {
const { createServer } = await import("@kb/dashboard"); const { createServer } = await import("@kb/dashboard");
await runDashboard(0, { open: false }); await runDashboard(0, {});
expect(createServer).toHaveBeenCalledTimes(1); expect(createServer).toHaveBeenCalledTimes(1);
const serverOpts = (createServer as ReturnType<typeof vi.fn>).mock.calls[0][1]; const serverOpts = (createServer as ReturnType<typeof vi.fn>).mock.calls[0][1];
@@ -128,7 +128,7 @@ describe("runDashboard — AuthStorage & ModelRegistry wiring", () => {
it("creates AuthStorage via AuthStorage.create()", async () => { it("creates AuthStorage via AuthStorage.create()", async () => {
const { AuthStorage } = await import("@mariozechner/pi-coding-agent"); const { AuthStorage } = await import("@mariozechner/pi-coding-agent");
await runDashboard(0, { open: false }); await runDashboard(0, {});
expect(AuthStorage.create).toHaveBeenCalledTimes(1); expect(AuthStorage.create).toHaveBeenCalledTimes(1);
}); });
@@ -136,7 +136,7 @@ describe("runDashboard — AuthStorage & ModelRegistry wiring", () => {
it("creates ModelRegistry with the authStorage instance", async () => { it("creates ModelRegistry with the authStorage instance", async () => {
const { ModelRegistry } = await import("@mariozechner/pi-coding-agent"); const { ModelRegistry } = await import("@mariozechner/pi-coding-agent");
await runDashboard(0, { open: false }); await runDashboard(0, {});
expect(ModelRegistry).toHaveBeenCalledTimes(1); expect(ModelRegistry).toHaveBeenCalledTimes(1);
expect(ModelRegistry).toHaveBeenCalledWith(mockAuthStorage); expect(ModelRegistry).toHaveBeenCalledWith(mockAuthStorage);

View File

@@ -1,4 +1,4 @@
import { exec, execSync } from "node:child_process"; import { execSync } from "node:child_process";
import type { AddressInfo } from "node:net"; import type { AddressInfo } from "node:net";
import { createInterface } from "node:readline"; import { createInterface } from "node:readline";
import { TaskStore } from "@kb/core"; import { TaskStore } from "@kb/core";
@@ -7,14 +7,6 @@ import { createServer, GitHubClient } from "@kb/dashboard";
import { TriageProcessor, TaskExecutor, Scheduler, AgentSemaphore, WorktreePool, aiMergeTask, UsageLimitPauser, PRIORITY_MERGE, scanIdleWorktrees, cleanupOrphanedWorktrees, NtfyNotifier, PrMonitor, PrCommentHandler } from "@kb/engine"; import { TriageProcessor, TaskExecutor, Scheduler, AgentSemaphore, WorktreePool, aiMergeTask, UsageLimitPauser, PRIORITY_MERGE, scanIdleWorktrees, cleanupOrphanedWorktrees, NtfyNotifier, PrMonitor, PrCommentHandler } from "@kb/engine";
import { AuthStorage, ModelRegistry } from "@mariozechner/pi-coding-agent"; import { AuthStorage, ModelRegistry } from "@mariozechner/pi-coding-agent";
function openBrowser(url: string): void {
const cmd =
process.platform === "darwin" ? `open "${url}"`
: process.platform === "win32" ? `start "" "${url}"`
: `xdg-open "${url}"`;
exec(cmd, () => {});
}
/** /**
* Prompt the user for a port number interactively. * Prompt the user for a port number interactively.
* Shows "Port [4040]: " and accepts user input or Enter for default. * Shows "Port [4040]: " and accepts user input or Enter for default.
@@ -192,7 +184,7 @@ export async function processPullRequestMergeTask(
return "merged"; return "merged";
} }
export async function runDashboard(port: number, opts: { open?: boolean; paused?: boolean; dev?: boolean; interactive?: boolean } = {}) { export async function runDashboard(port: number, opts: { paused?: boolean; dev?: boolean; interactive?: boolean } = {}) {
// Handle interactive port selection // Handle interactive port selection
let selectedPort = port; let selectedPort = port;
if (opts.interactive) { if (opts.interactive) {
@@ -648,9 +640,5 @@ export async function runDashboard(port: number, opts: { open?: boolean; paused?
console.log(` File watcher: ✓ active`); console.log(` File watcher: ✓ active`);
console.log(` Press Ctrl+C to stop`); console.log(` Press Ctrl+C to stop`);
console.log(); console.log();
if (opts.open !== false) {
openBrowser(`http://localhost:${actualPort}`);
}
}); });
} }

View File

@@ -866,7 +866,7 @@ export default function kbExtension(pi: ExtensionAPI) {
const port = trimmed ? parseInt(trimmed, 10) || 4040 : 4040; const port = trimmed ? parseInt(trimmed, 10) || 4040 : 4040;
// Find the kb binary: prefer local node_modules, then global // Find the kb binary: prefer local node_modules, then global
const child = spawn("kb", ["dashboard", "--port", String(port), "--no-open"], { const child = spawn("kb", ["dashboard", "--port", String(port)], {
cwd: ctx.cwd, cwd: ctx.cwd,
stdio: ["ignore", "pipe", "pipe"], stdio: ["ignore", "pipe", "pipe"],
detached: false, detached: false,