Welcome to the internal source code repository for Orca. This project is structured as a Turborepo monorepo containing multiple applications and shared packages.
apps/desktop: The core, private local desktop application built with Electron, React, and Vite.apps/web: The Next.js 16 App Router landing page with Stripe and Supabase integration.apps/worker: A Node.js background service that fetches news via Serper.dev, scrapes articles, summarizes with GPT-4o-mini, and orchestrates jobs with BullMQ and node-cron.
packages/db: Shared database schemas, types, and Supabase client utilities.packages/ai: AI agent wrappers (topic refinement, news search, article summarization).packages/config: Shared environment variable resolvers, constants, and types.
- Bun (primary package manager) — or npm (v11+) as fallback
- Node.js 20+
- Redis — for the worker's BullMQ job queue (see Redis Setup below)
Run from the root directory:
bun installEach app/package has its own .env.local file. Copy from .env.example where available:
# Desktop app
cp apps/desktop/.env.example apps/desktop/.env.local
# Worker
cp apps/worker/.env.example apps/worker/.env.localFill out the required API keys. See the Environment Variables section below for details.
┌─────────────────────────────────────────────────────────┐
│ Desktop App │
│ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │
│ │ Vite │◄───│ Electron │ │ OnboardingFlow │ │
│ │(:5173) │ │ Window │ │ (creates topics)│ │
│ └──────────┘ └────┬─────┘ └────────┬─────────┘ │
│ │ │ │
└───────────────────────┼───────────────────┼────────────┘
│ │
▼ ▼
┌─────────────────┐ ┌──────────────┐
│ Supabase │ │ Worker │
│ (PostgreSQL) │◄──│ (:3001) │
│ │ │ │
│ • users │ │ • cron sched │
│ • topics │ │ • fetch jobs │
│ • articles │ │ • AI summary │
│ • subscriptions │ │ • upsert art │
└─────────────────┘ └──────────────┘
You need 3 terminals running simultaneously:
cd apps/desktop
bun run dev:uiServes the React UI at http://localhost:5173 with hot module replacement.
cd apps/desktop
bun run devOpens the desktop app window. Loads content from the Vite dev server in development mode. DevTools open automatically.
cd apps/worker
bun run devStarts the cron scheduler, BullMQ workers, and HTTP server on port 3001. Handles:
- Periodic news fetching (every 15 min by default)
- Article scraping and AI summarization
- Immediate fetch triggers via
POST /trigger-fetch
| What you want to test | What to run | What won't work |
|---|---|---|
| UI only (styling, components) | Terminal 1 + 2 | No articles will appear (worker not running) |
| Full article flow | All 3 terminals | — |
| Worker pipeline only | Terminal 3 | No UI, but you can trigger via curl |
- Start all 3 terminals (see above)
- Sign in to the desktop app
- Click the + button in the sidebar to create a new topic
- Complete the 5-step onboarding flow
- Immediately after topic creation, the desktop app calls the worker's
/trigger-fetchendpoint - Watch the worker terminal for logs:
[worker] Manual fetch trigger received { topicId: "...", initiatedBy: "manual" } [worker] Fetch pipeline completed { topicId: "...", articleCount: 5 } - Articles appear in the desktop app within seconds (after scraping + AI summarization completes)
Note: Without the worker running, topics are created but no articles are fetched — the UI will show "No articles yet for this topic".
The worker uses BullMQ which requires Redis. You have two options:
- Create a free account at upstash.com
- Create a new Redis database
- Copy the connection strings into
apps/worker/.env.local:UPSTASH_REDIS_URL=redis://... UPSTASH_REDIS_REST_URL=https://... UPSTASH_REDIS_REST_TOKEN=...
# Via Homebrew
brew install redis
brew services start redis
# Via Docker
docker run -d -p 6379:6379 redis:7-alpineThen set in apps/worker/.env.local:
REDIS_URL=redis://localhost:6379
| Variable | Required | Description |
|---|---|---|
VITE_SUPABASE_URL |
Yes | Your Supabase project URL |
VITE_SUPABASE_ANON_KEY |
Yes | Supabase anon/publishable key |
VITE_APP_URL |
No | Web app URL (for pricing links) |
VITE_WORKER_URL |
No | Worker URL for immediate fetch triggers. Local: http://localhost:3001, Production: Railway URL |
| Variable | Required | Description |
|---|---|---|
SUPABASE_URL |
Yes | Supabase project URL |
SUPABASE_SERVICE_ROLE_KEY |
Yes | Service role key (for writes) |
OPENAI_API_KEY |
Yes | For article summarization |
SERPER_API_KEY |
Yes | For Google news search |
UPSTASH_REDIS_URL |
Yes | Redis connection string |
UPSTASH_REDIS_REST_URL |
Yes | Upstash REST API URL |
UPSTASH_REDIS_REST_TOKEN |
Yes | Upstash REST API token |
WORKER_POLL_CRON |
No | Cron expression for scheduler (default: */15 * * * *) |
WORKER_AUTH_TOKEN |
No | Bearer token for /trigger-fetch endpoint (optional, recommended for production) |
PORT |
No | HTTP server port (default: 3001) |
Start all apps in parallel from the root:
bun run devBuild all applications and packages in dependency order:
bun run buildbun run lint# Web only
bun --filter=@newsflow/web dev
# Desktop only
bun --filter=@newsflow/desktop dev
# Worker only
bun --filter=@newsflow/worker devsudo lsof -ti :5173 | xargs kill -9- Check that Redis is running:
redis-cli ping(should returnPONG) - Verify
UPSTASH_REDIS_URLorREDIS_URLin.env.local - For Upstash, ensure the REST URL and token are also set
- Check DevTools console (opens automatically in dev mode)
- Common causes:
- Module format error:
@newsflow/dbor@newsflow/confignot built as ESM → rebuild:cd packages/db && npx tsc - Missing env vars: Supabase URL or key not set → check
.env.local - Vite not running: Electron can't load content → start
bun run dev:uifirst
- Module format error:
- See
.agents/blank-screen-debug-report.mdfor detailed debugging guide
- Ensure the worker is running (Terminal 3)
- Check worker terminal for fetch logs
- Verify
VITE_WORKER_URLis set in desktop.env.local - Check that
SERPER_API_KEYandOPENAI_API_KEYare set in worker.env.local - Test worker manually:
curl -X POST http://localhost:3001/trigger-fetch \ -H "Content-Type: application/json" \ -d '{"topicId": "your-topic-uuid"}'
Status: Implemented
When a new topic is created, the desktop app immediately triggers a fetch via the worker's HTTP endpoint (POST /trigger-fetch). This bypasses the 15-minute cron wait and starts fetching articles right away.
- Local dev: Set
VITE_WORKER_URL=http://localhost:3001in desktop.env.local - Production: Set
VITE_WORKER_URLto your Railway worker URL - Auth: Optional
WORKER_AUTH_TOKENfor production security