Contributing
Source development requires the bun CLI on your PATH. The published npm package bundles its own
Bun runtime for users, but this checkout’s scripts run through your local Bun installation.
git clone https://github.com/lidge-jun/opencodex.gitcd opencodexbun installbun run dev:proxy # proxy API in dev modebun run dev:gui # dashboard dev server (another terminal)bun run typecheck # bun x tsc --noEmitbun run test # bun test ./tests/bun run dev remains an alias for bun run dev:proxy. The dashboard dev server is bun run dev:gui;
the packaged dashboard at GET / is produced by bun run build:gui (gui/dist).
Build and test commands
Section titled “Build and test commands”The root package is Bun-native TypeScript; there is no separate server compile step. Use the checked-in scripts so local commands match CI:
bun run typecheck # strict TypeScript checkbun run test # complete tests/ suitebun test tests/router.test.ts # focused test filebun run build:gui # Vite GUI build + package preparationbun run privacy:scan # credential/privacy scan used by CIbun run prepare:package # refresh package launchers/assetsMost tests are flat tests/*.test.ts Bun tests. tests/helpers/ contains shared fixtures and
tests/e2e-style/ contains broader native-parity scenarios. Keep a focused regression near the
existing tests for the subsystem you change; run the full suite for shared routing, adapters, config,
or server behavior.
The docs site you’re reading lives in docs-site/ (Astro + Starlight):
cd docs-site && bun install && bun devDocs publishing
Section titled “Docs publishing”The public docs publish to GitHub Pages at https://opencodex.me/. The
.github/workflows/deploy-docs.yml workflow runs on main pushes that touch docs-site/** or the
workflow itself, builds docs-site, and deploys the generated site. Before pushing docs changes,
run:
cd docs-sitebun install --frozen-lockfilebun run buildCI and releases
Section titled “CI and releases”GitHub Actions intentionally stay small:
- Windows CI (
.github/workflows/ci.yml) runs on pull requests andmainpushes that touch runtime, tests, package, script, TypeScript, or workflow files. Its Windows jobs cover install, typecheck, tests, privacy scan, a release-helper build smoke, GUI build,ocx help, and npm global install without a separately installed Bun by using the package’s bundled runtime. - Release (
.github/workflows/release.yml) is manual. It does not act as a second full CI pipeline; before dry-run or publish it requires the exact release commit (GITHUB_SHA) to already have a successful Windows CI run. - Super express release (
.github/workflows/super-express-release.yml) is the manual Windows packaging path. It resolves the selected ref to one immutable commit SHA and, like Release, refuses publication unless Windows CI succeeded for that exact SHA. Its desktop release contains an intentionally unsigned Squirrel.WindowsSetup.exe,RELEASES, and the full.nupkg. - Stale needs-info (
.github/workflows/stale-needs-info.yml) runs daily on the default branch. Open issues labeledneeds-infowith no activity for 14 days get a warning; after 7 more idle days they close as not planned. Any update clears the stale warning. To keep long-lived work open, removeneeds-info(for example when promoting an issue toroadmap).
Unsigned Windows installers may show an Unknown Publisher or Microsoft Defender SmartScreen warning. Relevant CI, packaging, and release jobs run defensive artifact collectors even after an earlier step fails. The collectors retain only explicitly safe installer/feed output and run metadata in GitHub Actions artifacts; collector failures are ignored so they cannot hide or replace the original job verdict. Retaining evidence never makes a failed build eligible for publication.
Use the helper for releases:
bun run release <version> # commits/pushes the bump; publish workflow is dry-run by defaultbun run release <version> --publish # publish after the CI-gated dry run is understoodbun run release:watch # watch the newest Release workflow runBranches
Section titled “Branches”dev— the default integration target. Open your pull request here unless it belongs to a scoped line below.dev2-go— parallel integration line for the Go native port (go/, the native runtime entrypoint, and the Go release-asset tooling). Open for pull requests alongsidedev. Send work here only when it belongs to the Go port; everything else goes todev. The automated target-branch check accepts both and cannot tell them apart, so scope is settled in review — a maintainer may ask you to retarget.main— releases only. It moves by maintainer-controlled promotion fromdev; do not open feature pull requests against it.preview— the prerelease train.
While the project moves its primary runtime to the Go native port, dev2-go
has to keep receiving everything that lands on dev. Nothing changes for you:
keep opening pull requests against dev. After a merge, a maintainer rebases
the work onto dev2-go and ports whatever needs a Go counterpart under go/,
and the item counts as finished only once both lines carry it.
Porting and rebase pull requests are welcome. Carrying a fix from one integration line to another, or rebasing a stale branch onto the current head, is normal contribution rather than noise — note the source commits in the description.
Pull requests
Section titled “Pull requests”- Target
dev(ordev2-goonly for scoped Go native-port work). Do not open ordinary feature or fix pull requests againstmain. - Branch from the current
devtip, not frommain. The requiredenforce-targetcheck rejects heads whose merge base sits on themaintip while the branch is far behind the pull request base (the failure mode seen in #644). - Write a real description: a Summary of what changed and why, plus a Test plan (or equivalent substance). Empty bodies, placeholder-only text, and descriptions that use escaped
\ninstead of real line breaks fail the check. - Workflow changes in this repository use
pull_request_target. Updated enforcement logic applies only after the workflow is promoted to the repository default branch — the same operational caveat documented in #631.
Project maintainers
Section titled “Project maintainers”The current maintainers, their responsibilities, and the review and merge policy are documented in
MAINTAINERS.md. GitHub review
ownership for the repository and security-sensitive paths is declared in .github/CODEOWNERS.
Conventions
Section titled “Conventions”- ES Modules only (
import/export), TypeScript,strictmode. Keepbun x tsc --noEmitclean. - ~500 lines per file max — split by responsibility (the
web-search/andvision/sidecars are good examples of small, focused modules behind a singleindex.ts). - Handle async errors at boundaries — sidecars never throw into the request path; they degrade to a graceful marker.
- Structure SOT — current maintainer invariants live in
structure/. Keep public user workflows indocs-site/and historical investigation notes indocs/. - Preserve exports — other modules may depend on them.
Adding a provider to the catalog
Section titled “Adding a provider to the catalog”All provider pickers and seeds derive from the canonical registry (src/providers/registry.ts):
{ id: "my-provider", label: "My Provider", baseUrl: "https://api.example.com/v1", adapter: "openai-chat", authKind: "key", dashboardUrl: "https://example.com/keys", models: ["model-a", "model-b"], defaultModel: "model-a", noVisionModels: ["model-a"], // text-only models → vision sidecar describes images},src/providers/derive.ts feeds that entry into ocx init, ocx provider, dashboard presets,
API-key login, and OAuth config seeds. enrichProviderFromCatalog() copies model metadata and
capability classifications onto the saved provider config. OAuth protocol implementations still
live in src/oauth/; registry metadata alone is not an OAuth flow.
Adding an adapter
Section titled “Adding an adapter”Implement ProviderAdapter (see Adapters) in src/adapters/,
register its name in src/server/adapter-resolve.ts, and bridge its output to internal
AdapterEvents. Reuse image.ts for image handling and follow openai-chat.ts for ordinary
streaming/tool calls; use fetchResponse only when the adapter owns transport retries, or runTurn
for a genuinely bidirectional transport such as Cursor. Add focused tests under tests/ and export
the factory from src/index.ts when it belongs to the public package API.
Verify before you claim done
Section titled “Verify before you claim done”Run the narrowest command that proves your change — bun run typecheck for types, a focused
bun test tests/<name>.test.ts or runtime probe for behavior, then the broader gates appropriate to
the affected surface. opencodex favors small, verifiable commits over large batches.

