oh-my-webbridge Docs
Skip to Content

Development

Repository layout, toolchain, build output, test commands, and the release path.

Developer preview

omw is in developer preview and iterating rapidly. THERE WILL BE COMPATIBILITY-BREAKING CHANGES.

The repository produces one Go binary, two extension bundles, one SDK file and the documentation site. Building needs Go 1.27.3 or newer, Node 24 and pnpm 11. Packaging a release also needs tar and zstd on PATH.

Repository layout

PathContents
cmd/omwCLI entry point: start, serve, stop, status, pair, connection, call, service, skill, install-skill, doctor.
internal/bridgeHTTP and WebSocket server, hub and routing, pairing, bindings, CDP proxy, uploads.
internal/configConfig directory, token, address, OMW_HOME.
internal/servicelaunchd and systemd user service written by omw service install.
extension/srcBackground worker for both browsers, actions, snapshots, presets, network capture, activity layer. extension/src/firefox holds the Firefox worker and the injected page.js.
extension/uiPopup and app pages (setup, settings). Vite, React 19, Base UI primitives from the coss registry, Tailwind CSS 4.
packages/brandIcon SVGs and theme.css, shared by the extension pages and the docs site.
sdk/index.ts, the JavaScript SDK that is bundled to dist/sdk.mjs.
docs/Fumadocs site on Next, a static export. Pages live in docs/content/docs; meta.json sets their order.
scripts/Build, filter compilation, release packaging, publishing, auditing.
tests/Node test suites, end-to-end suites and fixtures.

benchmarks/ holds the browser tool benchmark, examples/ holds SDK scripts, skills/ holds the agent skill that omw install-skill installs, and third_party/ holds the pinned filter lists and their license texts.

pnpm-workspace.yaml lists the root package, docs, extension/ui and packages/*, so Changesets versions the root package together with the workspace.

Toolchain

ToolVersion and role
Go1.27.3 or newer. The only third-party module is github.com/coder/websocket.
Node24 or newer, set in engines. Runs the build, tests, release scripts and the docs site.
pnpm11, pinned in packageManager. Install with pnpm install before any script runs.
TypeScript7, strict. The extension and SDK are checked as part of pnpm run check.
oxlint and oxfmtLint and format. oxlint runs type-aware through oxlint-tsgolint.
Vite8, builds the extension pages. esbuild bundles the workers and the SDK.

Build

pnpm install
pnpm run build
go build -trimpath -ldflags='-s -w' -o dist/omw ./cmd/omw

make build runs the same three steps, with pnpm install --frozen-lockfile.

pnpm run build runs scripts/build.mjs, which:

  1. Checks that package.json, both extension manifests and Version in internal/bridge/protocol.go carry the same version.
  2. Bundles extension/src/worker.ts with esbuild as an ES module, minified, for chrome156.
  3. Bundles extension/src/firefox/worker.ts for firefox153, and extension/src/firefox/page.ts as an IIFE.
  4. Runs the Vite build in extension/ui and copies popup.html, app.html and their assets into both extension directories.
  5. Renders the toolbar and app icons from the SVGs in packages/brand/icons with resvg.
  6. Compiles the filter lists and copies the license texts of the bundled npm packages and filter sources.
  7. Bundles sdk/index.ts to dist/sdk.mjs for Node 24.
  8. Writes dist/extension.sha256.json and dist/extension-firefox.sha256.json.

The outputs are dist/omw, dist/extension for Chrome, dist/extension-firefox for Firefox and dist/sdk.mjs. Chrome loads dist/extension as an unpacked extension. Firefox loads dist/extension-firefox as a temporary extension.

Extension pages

The popup, the setup page and the settings page are one Vite project. extension/ui/vite.config.ts lists popup.html and app.html as inputs, builds with a relative base so the pages work from a chrome-extension:// URL, and writes to dist/extension-ui. The build also emits bundled-modules.json, which scripts/build.mjs uses to collect the license of every npm module in the page bundle.

app/app.tsx picks the setup or settings page from the URL hash, so app.html#settings opens the settings page directly. Components taken from the coss registry live in extension/ui/src/components/ui as the registry provides them; compositions such as the side rail and the step list live in extension/ui/src/components. design.md in the repository root holds the presentation rules for these pages and the docs site. The pages read connection state from chrome.storage and call GET /ping and POST /pair themselves. The background worker owns the WebSocket.

Checks and tests

pnpm run check
pnpm run format:check
pnpm test
go vet ./...
go test -race ./...

make check runs go vet, the type-aware lint and the format check. make test runs go test -race and the Node suites.

CommandWhat it runs
pnpm run buildThe full build described above.
pnpm run checkoxlint --type-aware --type-check --deny-warnings --format=agent.
pnpm run formatoxfmt, writing changes. pnpm run format:check reports without writing.
pnpm testThe unit suites in tests/: SDK, worker, actions, presets, preset lifecycle, state, Firefox, release and benchmark.
pnpm run test:e2etests/e2e.test.mjs against Chrome. Needs CHROME_BIN.
pnpm run test:e2e:firefoxtests/e2e-firefox.test.mjs against Firefox. Needs FIREFOX_BIN.
pnpm run test:e2e:dualBoth browsers connected to one daemon. Needs both variables.
pnpm run test:e2e:firefox-pageRuns page.js inside Firefox. Needs FIREFOX_BIN.
pnpm run benchbenchmarks/run.mjs.
pnpm run release:planPreviews the next version without consuming fragments.
pnpm run release:checkChecks that the versions agree and the fragments parse; CI passes a base commit to also require a fragment for published changes.
pnpm run release:versionConsumes fragments and prepares versions locally.
pnpm run release:packBuilds the release archives. pnpm run release:audit validates them.

The end-to-end suites start dist/omw and load dist/extension or dist/extension-firefox, so run the build first. CHROME_BIN and FIREFOX_BIN are absolute paths to the browser binaries. A suite without its variable is skipped.

Documentation site

pnpm --filter @oh-my-webbridge/docs dev

The dev server listens on port 3884. Pages are MDX in docs/content/docs. docs/content/docs/meta.json lists them in sidebar order, and a new page has to be added there. Every path starts with a language segment, for example /en/sessions/; the site root sends a reader to the first browser language. pnpm --filter @oh-my-webbridge/docs build produces a static export for GitHub Pages. The build reads the Git history of each page for its contributors and page history, so it needs a full clone.

The demo videos and screenshots live on the orphan assets branch, which the Pages workflow checks out into docs/public/media. pnpm run media records them into dist/media with Chrome for Testing; pnpm run media --language zh-TW records the scenes that show extension text in another interface language.

Translations

English is the source language. The Traditional Chinese (Taiwan) text is translated from it by hand. pnpm run i18n converts the zh-TW text to Simplified Chinese (zh-CN) and to the Hong Kong and Macau variants (zh-HK, zh-MO) with OpenCC, then formats the output with oxfmt. Edit only the English and zh-TW files: the script overwrites the others. pnpm run i18n:check fails when a generated file differs from its source, and CI runs it.

TextEnglishzh-TW sourceGenerated
extension/locales/en.jsonzh-TW.jsonzh-CN.json, zh-HK.json, zh-MO.json, extension/_locales/
docs/content/docs/page.mdx, meta.jsonpage.zh-tw.mdx, meta.zh-tw.jsonpage.zh-cn.mdx, page.zh-hk.mdx, page.zh-mo.mdx, meta.*
docs/messages/site.en.jsonsite.zh-tw.json, history.zh-tw.jsonThe zh-cn, zh-hk and zh-mo files

The zh-CN conversion applies the OpenCC phrase tables (twp to cn), then the Mainland software terms listed in scripts/i18n.mjs, for example 分頁 to 标签页 and 工作階段 to 会话. The zh-HK and zh-MO conversions change character variants only (tw to hk); OpenCC has no separate Macau standard.

Each key in en.json must also be in zh-TW.json, with the same {placeholders}; otherwise the script stops. A heading in a translated page keeps the anchor of the English heading, for example ## 選擇器 [#selectors], so links to a section work in every language.

The extension pages, the popup, the toolbar title and the activity layer use the language chosen on the settings page, or the browser language. The extension name and description in the browser's extension list come from extension/_locales and always follow the browser language.

Changesets and releases

Version numbers and release notes come from fragments, not from commit messages, and nothing is published to npm.

  • Add one file per user-visible change under .changeset/, named after the change. The front matter holds the bump: patch for a fix, minor for a capability, major for breaking behavior.
  • The body is a short H1 title followed by the result the user sees, migration or scope notes where they apply, and the PR link once the PR exists. Fragments are rendered as GitHub Flavored Markdown, so close every block, keep raw HTML to comments, and write links inline.
  • A PR that changes published inputs, such as source, build scripts, dependencies, the release workflows or the extension manifests, fails CI without a new fragment. Tests and internal docs can omit one. Editing an existing fragment does not count.
  • A push to main maintains a draft release/next version PR. Its workflow runs scripts/release-pr.mjs, which consumes the fragments with scripts/release.mjs version, synchronizes the package, lockfile, daemon and manifest versions, and writes CHANGELOG.md and docs/releases/<version>.md.
  • After the version PR merges, the version workflow tags the chore(release): prepare <version> commit. The tag runs the release workflow, which rebuilds, audits every archive, uploads the assets, verifies their SHA-256 digests, and publishes the GitHub Release after that.
  • After the GitHub Release, the stores workflow (.github/workflows/stores.yml) submits omw-<version>-chrome-mv3.zip to the Chrome Web Store. It submits the unsigned add-on to addons.mozilla.org with a git archive of the tagged commit and build notes for the reviewers. Both packages are downloaded from the published GitHub Release and checked against its digests and SHA256SUMS. Each store publishes the version after its review. A version that a store already has is left unchanged, so a release can be rerun. Another Chrome version that is still in review or waiting to be published stops the Chrome submission.
  • The Chrome job runs in the chrome-web-store environment. Google Workload Identity Federation exchanges the job's GitHub OIDC token for a short-lived access token of the publisher service account. The identity provider admits only stores.yml of this repository in that environment, and no Google key is stored. The AMO job reads the API key and secret from the AMO_JWT_ISSUER and AMO_JWT_SECRET secrets of the addons-mozilla-org environment. The release workflow passes both secrets to the stores workflow by name, because a called workflow receives environment secrets only from its caller. Both environments deploy only from main and v* tags.
  • A manual run of the stores workflow from main prints the status of each store, which checks both sets of credentials. With a release tag in its tag input, the run checks out that tag and submits the tag's published GitHub Release, as the release workflow does: gh workflow run stores.yml --ref main -f tag=v0.2.1. Store listings, screenshots and privacy answers are edited in the dashboard of each store.
  • Archives use zstd level 3 and extract with zstd -dc archive.tar.zst | tar -xf - -C DIR.

License

The project is licensed under Apache-2.0. NOTICE holds the notices, and THIRD_PARTY.md lists the bundled filter lists and npm packages. Extension builds ship the full license texts under licenses/.

Contributors

Page history