Getting Started - Fighter90/career-ops-ui GitHub Wiki

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 / Hermes / DeepSeek / GLM (Z.ai) / Kimi / MiniMax / Mistral / Grok / Together / Fireworks / Ollama) 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.

โš ๏ธ **GitHub.com Fallback** โš ๏ธ