* feat(types): add jobspyIsRemote to TypeScript type definitions - Add jobspyIsRemote boolean fields to AppSettings interface - Follow three-field pattern: value, default, override - Update test fixture with default values (false) * feat(validation): add jobspyIsRemote validation schema Add Zod validation schema for jobspyIsRemote boolean setting to ensure type safety in the settings API endpoint. * feat(db): add jobspyIsRemote to database repository setting keys * feat(api): add jobspyIsRemote storage to settings API route * feat(service): add jobspyIsRemote to settings service with environment variable support * feat(jobspy): add isRemote parameter to JobSpy service interface * feat(pipeline): pass isRemote setting to JobSpy service * feat(python): add is_remote parameter to JobSpy scraper script * feat(ui): add Remote Jobs checkbox to JobSpy settings * feat(ui): add Remote badge to job display - Display Remote badge when job.isRemote === true - Position badge next to Source badge in JobHeader - Use Badge component with outline variant - Badge does not display when isRemote is false or null * docs(env): add JOBSPY_IS_REMOTE environment variable documentation - Added JobSpy section to .env.example with JOBSPY_IS_REMOTE variable - Documents remote-only job filtering option with default value of 0 (disabled) - Follows existing .env.example format with clear section headers and descriptions * test(remote-jobs): verify end-to-end functionality with comprehensive feedback loops
Job Ops Orchestrator
A unified orchestrator for the job application pipeline. Discovers jobs, scores them for suitability, generates tailored resumes, and provides a UI to manage applications.
Architecture
orchestrator/
├── src/
│ ├── server/ # Express backend
│ │ ├── api/ # REST API routes
│ │ ├── db/ # SQLite + Drizzle ORM
│ │ ├── pipeline/ # Orchestration logic
│ │ ├── repositories/ # Data access layer
│ │ └── services/ # Integrations (crawler, AI, PDF)
│ ├── client/ # React frontend
│ │ ├── api/ # API client
│ │ ├── components/ # UI components
│ │ └── styles/ # CSS design system
│ └── shared/ # Shared types
├── data/ # SQLite DB + generated PDFs (gitignored)
└── public/ # Static assets
Setup
-
Install dependencies:
cd orchestrator npm install -
Set up environment:
cp .env.example .env # The app is self-configuring. You can add keys via the UI Onboarding.After the server starts, use the onboarding modal to connect OpenRouter, link your v4.rxresu.me account, and select a template resume.
OpenRouter is the default LLM provider, but LM Studio, Ollama, OpenAI, and Gemini are also supported.
Deprecated:
OPENROUTER_API_KEY/openrouterApiKey. UseLLM_API_KEY/llmApiKeyinstead (legacy values are auto-migrated/copied for compatibility). -
Initialize database:
npm run db:migrate -
Start development server:
npm run devThis starts:
- Backend API at
http://localhost:3001 - Frontend at
http://localhost:5173
- Backend API at
API Endpoints
Jobs
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/jobs |
List all jobs (filter with ?status=ready,discovered) |
| GET | /api/jobs/:id |
Get single job |
| PATCH | /api/jobs/:id |
Update job |
| POST | /api/jobs/:id/process |
Generate resume for job |
| POST | /api/jobs/:id/apply |
Mark as applied + sync to Notion |
| POST | /api/jobs/:id/skip |
Mark as skipped |
Pipeline
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/pipeline/status |
Get pipeline status |
| GET | /api/pipeline/runs |
Get recent pipeline runs |
| POST | /api/pipeline/run |
Trigger pipeline manually |
| POST | /api/webhook/trigger |
Webhook for n8n (use WEBHOOK_SECRET) |
Daily Flow
-
17:00 - n8n triggers pipeline:
- Calls
POST /api/webhook/trigger - Pipeline crawls Gradcracker
- Scores jobs with AI
- Generates tailored resumes for top 10
- Calls
-
You review in the UI:
- See jobs at
http://localhost:5173 - "Ready" tab shows jobs with generated PDFs
- Click "View Job" to open application
- Download PDF and apply manually
- Click "Mark Applied" → syncs to Notion
- See jobs at
n8n Setup
Create a workflow with:
- Schedule Trigger - Every day at 17:00
- HTTP Request:
- Method: POST
- URL:
http://localhost:3001/api/webhook/trigger - Headers:
Authorization: Bearer YOUR_WEBHOOK_SECRET
Development
# Run just the server
npm run dev:server
# Run just the client
npm run dev:client
# Run the pipeline manually
npm run pipeline:run
# Build for production
npm run build
npm start
Tech Stack
- Backend: Express, TypeScript, Drizzle ORM, SQLite
- Frontend: React, Vite, CSS (custom design system)
- AI: Configurable LLM provider (OpenRouter default; also supports OpenAI/Gemini/LM Studio/Ollama)
- PDF Generation: RxResume v4 API export (configured via Settings)
- Job Crawling: Wraps existing TypeScript Crawlee crawler