diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index daa7cad..955c102 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,6 +34,33 @@ jobs: - run: npm ci - run: npm run format:check + # ────────────────────────────────────────────────────────── + # Oh My Pi host compatibility — real omp binary, mock API + # ────────────────────────────────────────────────────────── + omp-compat: + name: Oh My Pi compatibility + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + - uses: oven-sh/setup-bun@v2 + with: + bun-version: latest + - run: npm ci + - name: Install Oh My Pi + run: | + npm install -g @oh-my-pi/pi-coding-agent@latest + echo "OMP_BIN=$(npm prefix -g)/bin/omp" >> "$GITHUB_ENV" + - name: Verify omp starts + run: '"$OMP_BIN" --version' + - name: Run OMP compatibility suite + env: + OMP_COMPAT_REQUIRED: "1" + run: node tests/test-omp-compat.mjs + # ────────────────────────────────────────────────────────── # Code-level vulnerability scanning (SAST) # ────────────────────────────────────────────────────────── diff --git a/CHANGELOG.md b/CHANGELOG.md index f7ee868..ad2cb35 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased - Fix `omp plugin install` on Oh My Pi 18.x, which rejected 0.6.1 because its pi-ai lacks the `registerApiProvider` export; the compat registration now resolves at runtime and is skipped on hosts that register custom APIs themselves. +- Run the Oh My Pi compatibility suite against a real `omp` binary in CI as a required check, and assert there that the extension loads against OMP's bundled pi packages. ## 0.6.1 - 2026-09-01 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0a6b84f..32a9171 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -52,6 +52,17 @@ COMMANDCODE_E2E_PROVIDER_API_KEY_FILE=/path/to/provider-key npm run test:e2e:liv Use `npm run test:e2e:live:all` with the Go and GOAT file variables to run both subscription transports sequentially. Store keys in a secret manager and export each one to a new mode-`0600` temporary file for the test; never add key files to the repository. Direct `*_API_KEY` variables are intended primarily for protected CI secrets. +### Oh My Pi compatibility + +`tests/test-omp-compat.mjs` runs the extension inside a real `omp` binary against a mock Command Code API. It skips locally when `omp` is not on `PATH`; CI installs Oh My Pi and runs it as a required check with `OMP_COMPAT_REQUIRED=1`, so a change that only loads on pi fails CI instead of the next `omp plugin install`. + +To run it locally, point `OMP_BIN` at an omp executable (Oh My Pi needs Bun ≥ 1.3.14): + +```sh +npm install -g @oh-my-pi/pi-coding-agent +OMP_BIN="$(npm prefix -g)/bin/omp" node tests/test-omp-compat.mjs +``` + Before opening a PR, run: ```sh diff --git a/tests/test-omp-compat.mjs b/tests/test-omp-compat.mjs index ced7c03..4dc4cce 100644 --- a/tests/test-omp-compat.mjs +++ b/tests/test-omp-compat.mjs @@ -40,6 +40,10 @@ function findOmpBinary() { const OMP_BIN = findOmpBinary() if (!OMP_BIN) { + if (process.env.OMP_COMPAT_REQUIRED === "1") { + console.error("[omp-compat] FAIL - omp is required but not on PATH and OMP_BIN is unset") + process.exit(1) + } console.log("[omp-compat] SKIP - omp is not on PATH") process.exit(0) } @@ -211,6 +215,20 @@ function runOmp(args, timeoutMs = 30_000) { } try { + console.log("[omp-compat] extension loads against the host's pi packages") + // OMP remaps `@earendil-works/pi-ai/compat` onto its own pi-ai and rejects + // any named import that module does not export, both in `omp plugin + // install` validation and when loading the extension. 0.6.1 shipped such an + // import and could not be installed on omp 18 (#74). `omp models -e EXT` + // runs the same loader, so it reproduces that failure without a registry. + const load = await runOmp(["models", "-e", EXT_PATH]) + assert.equal(load.code, 0, load.stderr) + assert.doesNotMatch( + load.stdout + load.stderr, + /Failed to load extension|not found in module/, + "the extension must only import what OMP's bundled pi packages export", + ) + console.log("[omp-compat] list models through real extension") modelListRequestCount = 0 // Prefer the flag form `omp -e EXT --list-models`; Homebrew's `omp`