Skip to content

Getting Started

Fighter90 edited this page Jul 9, 2026 · 1 revision

Getting Started

career-ops-ui is a dashboard on top of Fighter90/career-ops. It runs inside a career-ops project as career-ops/web-ui/ and reads your cv.md, config/, and data/ from the parent folder via ../. It does not work standalone — you need the parent career-ops repo too.

Requirements

  • Node ≥ 18 (engines.node: ">=18"). Built-ins are imported with the node: prefix.
  • The parent career-ops project on disk (for real data). For development/tests you can point at a throwaway root — see Testing and QA.
  • Optional: one LLM provider API key (Anthropic / Gemini / OpenAI / Qwen / OpenRouter / GitHub Models) for the ⚡ live features. Without a key, live actions fall back to a copy-paste prompt.
  • Optional: Playwright installed in the parent's node_modules for PDF generation and liveness checks.

Production runtime dependencies are intentionally minimal: express, js-yaml, multer. No bundler, no transpiler, no TypeScript.

Install

Option 1 — one curl (sets up everything)

curl -fsSL https://raw.githubusercontent.com/Fighter90/career-ops-ui/main/bin/setup.sh | bash

Clones both repos, arranges the career-ops/web-ui/ layout, installs deps, runs the doctor, starts the server at http://127.0.0.1:4317, and opens the dashboard.

Option 2 — add the UI to an existing career-ops project

cd career-ops                                                   # your existing career-ops project
git clone https://github.com/Fighter90/career-ops-ui.git web-ui
cd web-ui
npm install
npx career-ops-ui init        # interactive: pick LLM provider + paste its key → parent career-ops/.env

The nested web-ui/ layout is exactly what lets the UI resolve ../cv.md, ../config/, ../data/. Run npm link once if you'd rather type the bare career-ops-ui <verb> instead of npx career-ops-ui <verb>.

Running the server

Command Purpose
npm start Run the server on 127.0.0.1:4317 (node server/index.mjs).
npm run dev Same with --watch (auto-restart on file change).
bash bin/start.sh One-shot launcher: installs deps if missing, validates Node ≥ 18, starts the server, opens the browser.

CLI verbs (bin/)

career-ops-ui setup    # bootstrap: install deps → doctor → run (SKIP_START=1 to stop before run)
career-ops-ui init     # pick LLM provider + paste its key (interactive)
career-ops-ui doctor   # verify Node / project / keys / Playwright (exit 0 ⇔ all required green)
career-ops-ui run      # launch the server at http://127.0.0.1:4317
career-ops-ui open     # open + raise the dashboard tab in your browser
career-ops-ui help     # list every verb

Prefix with npx if you didn't npm link. Set NO_OPEN=1 to disable auto-open (headless / CI).

How it finds the parent career-ops project

server/lib/paths.mjs::resolveProjectRoot() resolves the parent in this order:

  1. CAREER_OPS_ROOT env var (absolute, or relative to process.cwd()).
  2. .. relative to the web-ui repo — the normal career-ops/web-ui/ layout.
  3. process.cwd() — when the server is launched from inside career-ops directly.

The first candidate that contains either cv.md or portals.yml wins. If none match, the first candidate is returned and you find out through Health-page warnings. All filesystem access goes through PATHS.<thing> — the code never hardcodes ...

PATHS resolves once per process (paths.mjs computes PROJECT_ROOT at import time). This matters for tests — see Testing and QA.

First-run tips

  • Open http://127.0.0.1:4317 — the SPA boots, calls /api/health, and renders the dashboard.
  • The Health page (#/health) is the first stop: it shows Node version, whether each provider key is configured, whether the parent project resolved, and whether Playwright/js-yaml are present.
  • Set at least one provider key in #/config (App Settings) to unlock ⚡ live evaluations. It writes only KNOWN_KEYS to the parent's .env; empty string deletes a key; no restart needed.
  • On first boot the server appends a documented russian_portals: block to portals.yml if it's missing (idempotent — the one and only auto-write the UI performs).
  • Run a dry scan first: #/scan with the dry-run toggle skips writes to data/scan-history.tsv and data/last-scan.json.

Security defaults

The server binds to loopback (127.0.0.1) by default. Rate limiting and some fingerprint-hiding kick in only when you bind publicly (HOST=0.0.0.0). See Security before exposing it beyond localhost.

Clone this wiki locally