Skip to content
BreachPilot

Build System

Scripts (webui/package.json:6)

ScriptCommandPurpose
devvitedev server http://127.0.0.1:5173 strictPort + /api proxy (VITE_API_URL or DEFAULT_API http://127.0.0.1:8765)
buildtsc -b && vite buildtypecheck both project refs then bundle to webui/dist/ (outDir dist, no sourcemap, target es2020)
previewvite preview --port 5173 --strictPortserves dist with same /api proxy as dev
bundle-reportnode scripts/bundle-report.mjspost-build chunk analysis (read dist files)
testvitest runenvironment node by default, per-file jsdom opt-in

Vite (webui/vite.config.ts:1)

DEFAULT_API = "http://127.0.0.1:8765"
pkg = JSON.parse(readFileSync(package.json))
define.__APP_VERSION__ = pkg.version // "0.49.12"
plugins:[react()], resolve.alias["@"]=src
server{port:5173, strictPort, proxy{"/api":{target, changeOrigin, ws:true, secure:false}}}
preview{same proxy}, build{outDir:"dist", sourcemap:false, target:"es2020"}

target env: loadEnv(mode, cwd, "VITE_") (vite.config.ts:10) → VITE_API_URL overrides DEFAULT_API. The ws:true flag proxies WebSocket upgrades for useRunEvents.

Production serving: python main.py --web auto-builds webui/dist/ if missing (npm install && npm run build, requires Node+npm) and mounts it at / with SPA fallback in the create_app factory (docs/api.md: api.serve_webui).

TypeScript (webui/tsconfig.json, tsconfig.app.json, tsconfig.node.json)

tsconfig.json:1 is a references file: {files:[], references:[./tsconfig.app.json, ./tsconfig.node.json]}tsc -b builds both.

FileKey compilerOptionsNotes
tsconfig.app.json:2target ES2021, useDefineForClassFields, lib [ES2023, DOM, DOM.Iterable], module ESNext, skipLibCheck, moduleResolution Bundler, allowImportingTsExtensions, isolatedModules, moduleDetection force, noEmit, jsx react-jsx, strict, noUnusedLocals/Parameters, noFallthroughCasesInSwitch, baseUrl ., paths {"@/*":["src/*"]}, include ["src"]strict tree used for bundle; baseUrl/paths mirrors vite alias
tsconfig.node.jsonincludes vite.config.ts, vitest.config.ts, postcss.config.js, tailwind.config.ts; composite, allowSyntheticDefaultImports, module ESNextnode tooling only

CI scoped checks: ruff/mypy scopes listed in README §CI; tsc -b is the only typed build gate for the SPA.

Tailwind (webui/tailwind.config.ts:1)

FieldValue
importtype Config + typography(@tailwindcss/typography), animate(tailwindcss-animate)
darkMode["class"] (index.html:class="dark" toggled by lib/useTheme.ts via localStorage breachpilot.theme)
content["./index.html","./src/**/*.{ts,tsx}"]
theme.containercenter true, padding 1rem
theme.extend.typography.invert.cssprose vars prose-body/headings/links/code/pre-bg/pre-code/bullets/quotes/quote-borders → hsl(var(--*)) for SkillMarkdown in SkillsPage.tsx + docs/api.md pages
theme.extend.colors`border
theme.extend.borderRadiuslg:var(--radius) (0.5rem) etc. (radius: 0.5rem in index.css:27)
theme.extend.fontFamily.monoui-monospace,SFMono-Regular,Menlo,Consolas,monospace
plugins[typography, animate]

postcss.config.js{plugins:{tailwindcss:{}, autoprefixer:{}}}.

src/index.css (@tailwind base/components/utilities + @layer base vars :root light + .dark dark (:6/:29), @layer utilities bg-grid/grid-sm/radial-fade/glow-primary/skeleton/scrollbar-thin/animate-*). index.html has color-scheme dark light + referrer no-referrer + inline localStorage "breachpilot.theme" script to remove dark on light preference before paint.

Deps (package.json:13):

FamilyPackages
UI primitives@radix-ui/react-{checkbox,dialog,label,popover,scroll-area,select,separator,slot,switch,tabs,toast,tooltip} + class-variance-authority@0.7.0, clsx 2.1.1, tailwind-merge 2.5.4
Graphreactflow@11.11.4
Data@tanstack/react-query@5.59.16, @tanstack/react-virtual@3.14.10
Markdownreact-markdown@9.0.1, remark-gfm@4.0.0, @tailwindcss/typography@0.5.15
Icons/routinglucide-react@0.454.0, react-router-dom@6.27.0
Devtypescript 5.6.3, vite 5.4.10, @vitejs/plugin-react 4.3.3, tailwindcss 3.4.14, autoprefixer 10.4.20, vitest 2.1.8, @testing-library/{react,dom,user-event}, jsdom 29.1.1, @types/*, tailwindcss-animate 1.0.7

Vitest (webui/vitest.config.ts:1)

defineConfig({ plugins:[react()], resolve:{alias:{"@":src}},
  test:{ environment:"node", include:["src/**/*.test.{ts,tsx}"],
         setupFiles:[src/test/setup.ts] }})
  • Environment model: default node (so pure-logic tests stateShape.test.ts, campaignCheckpoint.test.ts run headless); component tests opt into jsdom via per-file /** @vitest-environment jsdom */ docblock.
  • Setup: src/test/setup.ts:1 imports @testing-library/jest-dom/vitest (matchers) + afterEach(cleanup()) for RTL. globals:false — tests import vitest globals explicitly.
  • Coverage surfaces: stateShape helpers (deriveRun, checkpoint visuals/encoding) run without DOM; SettingsPage.test.tsx and RunWizard.test.tsx are jsdom.

Execution: npm test (vitest run once), vitest watch on loopback-only env already has no live network — all subprocess/network mocked.

Output

ArtifactLocationNotes
index.htmldist/index.htmlentry with <script type=module src=/assets/...>
assets/<hash>.js/cssdist/assets/fingerprinted; __APP_VERSION__ inlined JSON.stringify(pkg.version)
Spa fallbackhandled by api. serve_webui mount (catch-all GET /{path:not api}dist/index.html)
Version labelLayout shows v{__APP_VERSION__} beta (Layout.tsx:81)

Local commands

npm install          # webui/
npm run dev          # vite 5173 + proxy; keep `python main.py --demon` running
npm run build        # tsc -b && vite build
npm run preview      # serves dist
npm test             # vitest run
npm run bundle-report

requirements.txt and pyproject.toml (AGENTS.md: pyproject + requirements sync) cover Python toolchain; webui devDependencies are not synced to Python.

source: repo docs (build sync)Edit this page on GitHub →