diff --git a/package.json b/package.json index b93691d2693..b32b0bd9cc2 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,8 @@ "clean": "turbo clean --log-order grouped --output-logs new-only && rimraf dist out bin .vite-port .turbo", "install:vsix": "pnpm install --frozen-lockfile && pnpm clean && pnpm vsix && node scripts/install-vsix.js", "install:vsix:nightly": "pnpm install --frozen-lockfile && pnpm clean && pnpm vsix:nightly && node scripts/install-vsix.js --nightly", + "serve": "node scripts/serve.js", + "serve:install": "node scripts/serve.js --install-only", "changeset:version": "cp CHANGELOG.md src/CHANGELOG.md && changeset version && cp -vf src/CHANGELOG.md .", "knip": "knip --include files", "evals": "dotenvx run -f packages/evals/.env.development packages/evals/.env.local -- docker compose -f packages/evals/docker-compose.yml --profile server --profile runner up --build --scale runner=0", diff --git a/scripts/serve.js b/scripts/serve.js new file mode 100644 index 00000000000..1f67c4275b1 --- /dev/null +++ b/scripts/serve.js @@ -0,0 +1,233 @@ +/** + * Serve script for Roo Code extension development + * + * This script manages code-server for web-based VS Code testing. + * + * Prerequisites: + * brew install code-server + * + * Usage: + * pnpm serve # Start code-server on port 9080 + * pnpm serve -- --port 8080 # Use a custom port + * pnpm serve -- --host 0.0.0.0 # Bind to all interfaces (for Docker/remote access) + * pnpm serve -- --auth none # Disable authentication (password|none) + * pnpm serve:install # Build and install the extension into code-server + * + * Workflow: + * 1. Run `pnpm serve:install` to build and install the extension + * 2. Run `pnpm serve` to start code-server + * 3. After code changes, run `pnpm serve:install` again and reload the window + * (Cmd+Shift+P → "Developer: Reload Window") + * + * Your password is stored in ~/.config/code-server/config.yaml + */ + +const { execSync, spawn } = require("child_process") +const fs = require("fs") +const path = require("path") +const os = require("os") + +const RESET = "\x1b[0m" +const BOLD = "\x1b[1m" +const GREEN = "\x1b[32m" +const YELLOW = "\x1b[33m" +const CYAN = "\x1b[36m" +const RED = "\x1b[31m" + +// Build vsix to a fixed path in temp directory +const VSIX_PATH = path.join(os.tmpdir(), "roo-code-serve.vsix") + +// Parse command line flags +const installOnly = process.argv.includes("--install-only") + +// Parse --port argument (default: 9080) +const DEFAULT_PORT = 9080 +function getPort() { + const portIndex = process.argv.indexOf("--port") + if (portIndex !== -1 && process.argv[portIndex + 1]) { + const port = parseInt(process.argv[portIndex + 1], 10) + if (!isNaN(port) && port > 0 && port < 65536) { + return port + } + } + return DEFAULT_PORT +} +const port = getPort() + +// Parse --host argument (default: 127.0.0.1, use 0.0.0.0 for Docker/remote access) +const DEFAULT_HOST = "127.0.0.1" +function getHost() { + const hostIndex = process.argv.indexOf("--host") + if (hostIndex !== -1 && process.argv[hostIndex + 1]) { + return process.argv[hostIndex + 1] + } + return DEFAULT_HOST +} +const host = getHost() + +// Parse --auth argument (optional, passed to code-server: "password" or "none") +function getAuth() { + const authIndex = process.argv.indexOf("--auth") + if (authIndex !== -1 && process.argv[authIndex + 1]) { + return process.argv[authIndex + 1] + } + return null +} +const auth = getAuth() + +function log(message) { + console.log(`${CYAN}[serve]${RESET} ${message}`) +} + +function logSuccess(message) { + console.log(`${GREEN}✓${RESET} ${message}`) +} + +function logWarning(message) { + console.log(`${YELLOW}⚠${RESET} ${message}`) +} + +function logError(message) { + console.error(`${RED}✗${RESET} ${message}`) +} + +function isCodeServerInstalled() { + try { + execSync("which code-server", { stdio: "pipe" }) + return true + } catch { + return false + } +} + +function ensureUserSettings() { + // code-server stores user data in ~/.local/share/code-server + const userDataDir = path.join(process.env.HOME || process.env.USERPROFILE, ".local", "share", "code-server", "User") + const settingsFile = path.join(userDataDir, "settings.json") + + // Create directory if it doesn't exist + if (!fs.existsSync(userDataDir)) { + fs.mkdirSync(userDataDir, { recursive: true }) + } + + // Read existing settings or start fresh + let settings = {} + if (fs.existsSync(settingsFile)) { + try { + settings = JSON.parse(fs.readFileSync(settingsFile, "utf8")) + } catch { + // If parsing fails, start fresh + } + } + + // Set the startup editor to none (disables welcome tab) + settings["workbench.startupEditor"] = "none" + + // Hide the secondary sidebar (auxiliary bar) + settings["workbench.auxiliaryBar.visible"] = false + + // Disable extension recommendations prompts + settings["extensions.ignoreRecommendations"] = true + + fs.writeFileSync(settingsFile, JSON.stringify(settings, null, "\t")) +} + +async function main() { + // Check if code-server is installed + log("Checking for code-server...") + if (!isCodeServerInstalled()) { + logError("code-server is not installed") + console.log("\nTo install code-server on macOS:") + console.log(` ${CYAN}brew install code-server${RESET}`) + console.log("\nFor other platforms, see: https://coder.com/docs/code-server/install") + process.exit(1) + } + logSuccess("code-server found") + + // If install-only mode, build and install the extension + if (installOnly) { + console.log(`\n${BOLD}🔧 Roo Code - Install Extension${RESET}\n`) + + // Build vsix to temp directory + log(`Building vsix to ${VSIX_PATH}...`) + try { + execSync(`pnpm vsix -- --out "${VSIX_PATH}"`, { stdio: "inherit" }) + logSuccess("Build complete") + } catch (error) { + logError("Build failed") + process.exit(1) + } + + // Install extension into code-server + log("Installing extension into code-server...") + try { + execSync(`code-server --install-extension "${VSIX_PATH}"`, { stdio: "inherit" }) + logSuccess("Extension installed") + } catch (error) { + logWarning("Extension installation had warnings (this is usually fine)") + } + + // Configure user settings to disable welcome tab + log("Configuring user settings...") + ensureUserSettings() + logSuccess("User settings configured (welcome tab disabled)") + + console.log(`\n${GREEN}✓ Extension built and installed.${RESET}`) + console.log(` If code-server is running, reload the window to pick up changes.`) + console.log(` (Cmd+Shift+P → "Developer: Reload Window")\n`) + return + } + + // Default: Start code-server + console.log(`\n${BOLD}🚀 Roo Code - code-server Development Server${RESET}\n`) + const cwd = process.cwd() + console.log(`\n${BOLD}Starting code-server...${RESET}`) + console.log(` Working directory: ${cwd}`) + console.log(` URL: ${CYAN}http://${host}:${port}${RESET}`) + if (auth === "none") { + console.log(` Auth: ${YELLOW}disabled${RESET}`) + } else { + console.log(` Password: ${YELLOW}~/.config/code-server/config.yaml${RESET}`) + } + console.log(`\n Press ${BOLD}Ctrl+C${RESET} to stop\n`) + + // Spawn code-server with: + // --bind-addr: Address to bind to + // --auth: Authentication type (password or none) + // --disable-workspace-trust: Skip workspace trust prompts + // --disable-getting-started-override: Disable welcome/getting started page + // -e: Ignore last opened directory (start fresh) + const args = ["--bind-addr", `${host}:${port}`] + if (auth) { + args.push("--auth", auth) + } + args.push("--disable-workspace-trust", "--disable-getting-started-override", "-e", cwd) + + const codeServer = spawn("code-server", args, { + stdio: "inherit", + cwd: cwd, + }) + + codeServer.on("error", (err) => { + logError(`Failed to start code-server: ${err.message}`) + process.exit(1) + }) + + codeServer.on("close", (code) => { + if (code !== 0 && code !== null) { + logError(`code-server exited with code ${code}`) + } + }) + + // Handle Ctrl+C gracefully + process.on("SIGINT", () => { + console.log("\n") + log("Shutting down code-server...") + codeServer.kill("SIGTERM") + }) +} + +main().catch((error) => { + logError(error.message) + process.exit(1) +})