diff --git a/package.json b/package.json index 7679a550..3dad1838 100644 --- a/package.json +++ b/package.json @@ -28,13 +28,13 @@ "clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"", "build": "npm run clean && npm run build:app && tsc -p tsconfig.build.json", "build:app": "vite build", - "dev": "node scripts/dev-server.mjs", + "dev": "tsx watch --clear-screen=false src/cli.ts serve", "postinstall": "node scripts/fix-node-pty-permissions.mjs", "prepack": "npm run build", "schema:config": "tsx scripts/generate-config-schema.ts", "start": "node dist/cli.js serve", - "test": "tsx src/user-config.test.ts && tsx src/config.test.ts && tsx src/onboarding.test.ts && tsx src/cli-workspace.test.ts && tsx src/request-meta.test.ts && tsx src/incoming-artifacts.test.ts && tsx src/artifact-download.test.ts && tsx src/ui/card-types.test.ts && tsx src/ui/tool-result.test.ts && tsx src/ui/patch-display.test.ts && tsx src/apply-patch.test.ts && tsx src/process-platform.test.ts && tsx src/process-sessions.test.ts && tsx src/mcp-sessions.test.ts && tsx src/server-shutdown.test.ts && tsx src/local-agent-config.test.ts && tsx src/local-agent-catalog.test.ts && tsx src/local-agent-presentation.test.ts && tsx src/local-agent-runtime.test.ts && tsx src/local-agent-daemon-lifecycle.test.ts && tsx src/local-agent-daemon-protocol.test.ts && tsx src/local-agent-daemon.test.ts && tsx src/local-agent-codex.test.ts && tsx src/local-agent-opencode.test.ts && tsx src/local-agent-acp.test.ts && tsx src/local-agent-grok.test.ts && tsx src/local-agent-pi-sandbox.test.ts && tsx src/local-agent-pi.test.ts && tsx src/local-agent-claude.test.ts && tsx src/local-agent-adapters.test.ts && tsx src/local-agent-availability.test.ts && tsx src/local-agent-profiles.test.ts && tsx src/local-agent-targets.test.ts && tsx src/local-agent-store.test.ts && tsx src/local-agent-manager.test.ts && tsx src/roots.test.ts && tsx src/skills.test.ts && tsx src/workspaces.test.ts && tsx src/workspace-conversation.test.ts && tsx src/review-checkpoints.test.ts && tsx src/server.test.ts && tsx src/oauth-store.test.ts && tsx src/cli-show-changes.test.ts && tsx src/cli.test.ts", - "typecheck": "tsx src/config-schema.test.ts && tsc -p tsconfig.json --noEmit" + "test": "tsx --test --test-concurrency=1 \"src/**/*.test.ts\"", + "typecheck": "tsc -p tsconfig.json --noEmit" }, "keywords": [], "author": "", diff --git a/scripts/dev-server.mjs b/scripts/dev-server.mjs deleted file mode 100644 index 5585bdae..00000000 --- a/scripts/dev-server.mjs +++ /dev/null @@ -1,120 +0,0 @@ -import { spawn } from "node:child_process"; -import { readdirSync, statSync, watch } from "node:fs"; -import { join, resolve } from "node:path"; -import { fileURLToPath } from "node:url"; - -const repoRoot = resolve(fileURLToPath(new URL("..", import.meta.url))); -const watchRoots = ["src"].map((entry) => join(repoRoot, entry)); -const restartDelayMs = 750; -const crashDelayMs = 1500; - -let child; -let restartTimer; -let stoppingForRestart = false; -let shuttingDown = false; - -function log(message) { - console.error(`[devspace:dev] ${message}`); -} - -function start() { - stoppingForRestart = false; - child = spawn("npx", ["tsx", "src/cli.ts", "serve"], { - cwd: repoRoot, - env: process.env, - stdio: "inherit", - }); - - child.on("exit", (code, signal) => { - child = undefined; - if (shuttingDown) return; - if (stoppingForRestart) return; - - log(`server exited (${signal ?? code ?? "unknown"}); restarting in ${crashDelayMs}ms`); - scheduleRestart(crashDelayMs); - }); -} - -function scheduleRestart(delayMs = restartDelayMs) { - clearTimeout(restartTimer); - restartTimer = setTimeout(restart, delayMs); -} - -function restart() { - if (shuttingDown) return; - clearTimeout(restartTimer); - - if (!child) { - start(); - return; - } - - stoppingForRestart = true; - child.once("exit", () => { - if (!shuttingDown) start(); - }); - child.kill("SIGTERM"); - - setTimeout(() => { - if (child && stoppingForRestart) child.kill("SIGKILL"); - }, 3000).unref(); -} - -function watchDirectory(root) { - const watchers = []; - const seen = new Set(); - - function addDirectory(dir) { - if (seen.has(dir)) return; - seen.add(dir); - - const watcher = watch(dir, (event, filename) => { - if (!filename) { - scheduleRestart(); - return; - } - - const path = join(dir, filename.toString()); - if (event === "rename") maybeAddDirectory(path); - scheduleRestart(); - }); - watchers.push(watcher); - - for (const entry of readdirSync(dir)) { - maybeAddDirectory(join(dir, entry)); - } - } - - function maybeAddDirectory(path) { - try { - const stats = statSync(path); - if (stats.isDirectory()) addDirectory(path); - } catch { - // The file may have been deleted between the watch event and stat call. - } - } - - addDirectory(root); - return watchers; -} - -function shutdown() { - shuttingDown = true; - clearTimeout(restartTimer); - if (!child) return process.exit(0); - - child.once("exit", () => process.exit(0)); - child.kill("SIGTERM"); - setTimeout(() => process.exit(1), 3000).unref(); -} - -for (const signal of ["SIGINT", "SIGTERM"]) { - process.on(signal, shutdown); -} - -for (const root of watchRoots) { - watchDirectory(root); -} - -log("watching src; server restarts on changes and after crashes"); -start();