Development environment
Run OpenQuok's agent, backend, workers and web apps locally, execute tests, database scripts, and deployment commands.
Connect your agent today
Draft from chat, review in your calendar, and publish only what you approve.
Warning
Note
infra/docker-compose.yml is for contributors working on the hosted openquok.com.
Those who want own self-host should use Docker Compose (self-host).
Backend local development
You can work from the monorepo root (as defined in the root package.json) or backend/. Pick one.
Note
pg_cron on Supabase.
For example, the `user-auth` module periodically removes expired rows from `public.refresh_tokens`.
If you deploy to Supabase and cron is not enabled, those jobs won’t run even if migrations are pushed.
See Database & migrations for pg_cron setup.Frontend local development
Warning
Note
The web dev server serves HTTPS at https://localhost:5173. Keep VITE_FRONTEND_DOMAIN_URL and the backend’s FRONTEND_DOMAIN_URL on that exact origin, and follow Vite (SvelteKit) → HTTPS local development and the API base URL so API calls and auth cookies stay same-origin.
You can work from the monorepo root or change into web/ and run package scripts there.
Redis (cache)
If you use CACHE_PROVIDER=redis, run Redis locally or use a managed instance and set REDIS_HOST, REDIS_PORT, and password fields to match.
- Local Docker: Docker (local services)
- Full Redis config: Redis cache
Workflow-style jobs can run in the API process (Flowcraft), but the repo also ships long-running workers under orchestrator/.
Worker Processes (BullMQ)
If you configure orchestrator flows to use BullMQ, the API enqueues jobs to Redis and separate worker processes execute them.
Note
Run local Redis (BullMQ) — quickest path is Docker Compose:
# From repo root
docker compose -f infra/docker-compose.yml up -d redis Start a worker locally — each worker is its own process:
# From repo root
pnpm orchestrator:dev:worker:integration-refresh-bullmq
pnpm orchestrator:dev:worker:notification-email-bullmq
pnpm orchestrator:dev:worker:scheduled-social-post-bullmq Worker env and Redis queue details live in Configuration → Worker → Docker (local Redis).
CLI auth server (device flow)
The CLI auth server (agent/server) implements the OAuth2 device flow.
Most developers can use the hosted server by default, but you can run it locally for end-to-end testing.
Note
Use CLI and CLI authentication to install the CLI, run openquok auth:login, and set OPENQUOK_AUTH_SERVER. Server-side env is covered under Configuration - Agent.
- Creating / rotating client ID + secret (operator/admin): Admin — OAuth Server
Run local Postgres (auth server state) — the auth server stores device-flow state in Postgres:
# From repo root
docker compose -f infra/docker-compose.yml up -d postgres Then set DATABASE_URL in agent/server/.env.development.local to match (defaults to postgresql://openquok:openquok@localhost:5432/openquok_cli_auth).
Run the CLI auth server locally:
# From repo root
pnpm agent-server:dev Once running, point the CLI at it via OPENQUOK_AUTH_SERVER or —authServer — see CLI authentication.
Running the CLI from the monorepo (unpublished)
Run from source (recommended for daily dev) — uses the cli script (tsx src/index.ts); no build needed, picks up source changes on every invocation:
pnpm --filter ./agent cli -- --help
pnpm --filter ./agent cli -- integrations:trigger --help
pnpm --filter ./agent cli -- auth:login --authServer http://localhost:3111 Per-command —help shows the Examples: section with copy-pasteable invocations for non-obvious payloads (JSON -d, ISO timestamps, etc.).
Run the compiled binary — useful when you want to confirm the published bundle behaves the same:
pnpm --filter ./agent build
node agent/dist/index.js auth:login --authServer http://localhost:3111 Run the compiled binary via the start script — same end result, slightly tidier:
pnpm --filter ./agent build
pnpm --filter ./agent start -- auth:login --authServer http://localhost:3111 Smoke-test the CLI surface — after adding or renaming commands under agent/src/commands/, verify every group is wired into registerAllCommands and responds to —help:
# 1. Top-level --help: should list auth, integrations, posts, analytics, upload, upload-from-url
pnpm --filter ./agent cli -- --help
# 2. Each command must respond to --help with its yargs Examples: block
pnpm --filter ./agent cli -- analytics:platform --help
pnpm --filter ./agent cli -- analytics:post --help
pnpm --filter ./agent cli -- posts:status --help
pnpm --filter ./agent cli -- posts:delete --help
pnpm --filter ./agent cli -- posts:missing --help
pnpm --filter ./agent cli -- posts:connect --help
pnpm --filter ./agent cli -- upload-from-url --help Connectivity smoke — requires a valid opo_ programmatic token or stored credentials. Confirms auth + workspace plumbing end-to-end against a running backend:
pnpm -s --filter ./agent cli -- auth:status
pnpm -s --filter ./agent cli -- integrations:list | jq '.[] | {id, identifier}'
pnpm -s --filter ./agent cli -- posts:list | jq '.success, (.data.posts | type)' Every command emits machine-readable JSON on stdout, so piping into jq is the recommended way to test the CI.
Note
When you run pnpm --filter ./agent cli -- … | jq …, pnpm may print script lifecycle lines (lines starting with >) to stdout before the CLI JSON. That breaks jq and can surface a Node EPIPE after jq exits. Use pnpm -s (silent) for piped examples, or run without a pipe first to inspect raw output:
pnpm -s --filter ./agent cli -- integrations:list Smoke-test post kanban review (CLI + web)
Use this flow to seed agent-edited drafts with the CLI and exercise the kanban review board on the account page . The CLI always sends isAgent: true on posts:create; human actions in the dashboard clear is_agent_edited when you mark a post reviewed or schedule it from the UI.
Database columns
The posts table needs note, is_agent_edited, and is_reviewed (see post migrations under backend/supabase/db/post/).
Start the API and web app
In separate terminals from the repository root:
pnpm backend:dev
pnpm web:dev Sign in at https://localhost:5173 with the same workspace you will use for CLI credentials.
Point the CLI at your local API
The CLI defaults to the hosted API unless overridden. For local smoke runs:
export OPENQUOK_API_URL=http://localhost:3000 Use the same origin your web app uses for API calls if you proxy through another host (see HTTPS local development and the API base URL). Set this before auth:login --apiKey so the CLI does not call the hosted API with a local-only key.
Authenticate the CLI
Programmatic token (fastest) — rotate a token from Account → Settings → Developers → Access, then store it:
pnpm -s --filter ./agent cli -- auth:login --apiKey "opo_your_programmatic_token" Or export OPENQUOK_API_KEY for the session (run auth:logout first if ~/.openquok/credentials.json already exists — stored credentials take priority over env).
OAuth device flow — requires the local auth server (pnpm agent-server:dev) and the same --authServer pattern as above:
pnpm --filter ./agent cli -- auth:login --authServer http://localhost:3111 Confirm connectivity:
pnpm --filter ./agent cli -- auth:status List channels and create an agent draft
Resolve an integration UUID, then create a draft (lands in the kanban Drafted column with the agent flag set):
pnpm -s --filter ./agent cli -- integrations:list | jq '.[] | {id, identifier}' Alternatively, print it via terminal:
pnpm -s --filter ./agent cli -- integrations:list If you see “success”: false and 401, re-run auth:login --apiKey after OPENQUOK_API_URL is set, or run auth:logout when stale ~/.openquok/credentials.json points at the wrong API.
Set variables from the list output (replace the placeholder, or capture the first channel id):
export INTEGRATION_ID="<integration-id>"
export SCHEDULED_AT="$(node -e "const d=new Date(Date.now()+864e5); d.setUTCHours(12,0,0,0); process.stdout.write(d.toISOString())")"
echo "INTEGRATION_ID=$INTEGRATION_ID"
echo "SCHEDULED_AT=$SCHEDULED_AT" Warning
Every continued line must end with \ (no spaces after it). If you paste the posts:create block without backslashes, only the first line runs and yargs reports scheduledAt is required.
Create the draft (one line — safe to copy; use --scheduledAt so the schedule time is unambiguous):
pnpm -s --filter ./agent cli -- posts:create -c "Kanban review smoke test" --scheduledAt "$SCHEDULED_AT" -i "$INTEGRATION_ID" -t draft --note "Confirm CTA link before scheduling" Inspect the response:
pnpm -s --filter ./agent cli -- posts:create -c "Kanban review smoke test" --scheduledAt "$SCHEDULED_AT" -i "$INTEGRATION_ID" -t draft --note "Confirm CTA link before scheduling" | jq '{postGroup: .data.postGroup, postId: .data.posts[0].id, isAgentEdited: .data.posts[0].isAgentEdited, note: .data.posts[0].note, state: .data.posts[0].state}' Expect isAgentEdited: true and state: DRAFT in the response.
See CLI — Managing posts and Update Review Todo for flag and API details.
Exercise the kanban in the browser
- Open Account (kanban below your profile).
- Set the filter to Agent (not Human).
- Find the card under Drafted.
- As a human: edit the review note, check reviewed, then schedule or publish from the dashboard — the card should move columns and no longer show as agent-edited after review.
Tip
posts:review-todo via the CLI is for the programmatic path (keeps isAgentEdited true). The dashboard uses session PUT /api/v1/posts/:postId/review-todo and clears the agent flag when a human marks review complete — that is what this UI smoke test validates.
Backend e2e
The repository includes an automated flow that creates via the real CLI bundle and asserts review + schedule over HTTP:
pnpm --filter ./backend test:e2e:post-review Requires Supabase E2E env (same as other backend e2e suites). See backend/tests/e2e/post.review.e2e.test.ts.
Note
If you're changing positional descriptions or .example() calls in agent/src/commands/, run pnpm --filter ./agent dev — it watches the source and re-prints --help on every save, which is the fastest feedback loop for tweaking help text.
Note
Run the CLI auth server (agent/server) so http://localhost:3111 is reachable — from the repo root, pnpm agent-server:dev is typical. For the simplest local loop, omit BROWSER_ORIGIN, register OAuth callback http://localhost:3111/device/callback, and keep SERVER_URL=http://localhost:3111 — see Configuration → Agent → Environment variables. To mimic production (browser on the web dev server), set BROWSER_ORIGIN to your web origin and CLI_AUTH_SERVER_URL=http://localhost:3111 in web/.env.development.local.
Deployment
For a full production sequence, use Production deployment. The commands below are quick references from the repository root .
Backend JS bundle — runs tsup in backend/ to produce the bundled API output used for deploys.
pnpm --filter ./backend build:js Deploy backend to Vercel — invokes the Vercel CLI with backend/ as the working directory.
pnpm vercel:deploy:backend Deploy web to Vercel — invokes the Vercel CLI with web/ as the working directory.
pnpm vercel:deploy:web