fix(opencode): ship @opencode-ai/plugin as a runtime dependency

The compiled OpenCode tools import `tool` from `@opencode-ai/plugin/tool`
at module top level, and the hooks plugin imports those tools statically.
The package was only a devDependency, so production installs (OpenCode's
npm plugin cache, `npm install --omit=dev`) could not resolve it and the
whole plugin failed to load.

Move it to `dependencies` and add a build-opencode regression test that
fails when the built bundle imports a package not declared as a runtime
dependency.

Fixes #3543
This commit is contained in:
Gaurav Dubey committed 2026-10-13 20:37:11 +00:00
1 parent b92f181b3b
commit 106024070e
3 files changed
+33 -34

No files matched your search

+1 -33
View File
@@ -10,6 +10,7 @@
"license": "MIT",
"dependencies": {
"@iarna/toml": "2.2.5",
"@opencode-ai/plugin": "1.18.34",
"ajv": "8.20.0",
"js-yaml": "4.3.2",
"sql.js": "1.14.2"
@@ -24,7 +25,6 @@
},
"devDependencies": {
"@eslint/js": "9.39.2",
"@opencode-ai/plugin": "1.18.34",
"@types/node": "26.6.4",
"c8": "11.0.0",
"eslint": "10.11.0",
@@ -40,7 +40,6 @@
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-3.0.8.tgz",
"integrity": "sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"json-schema": "^0.4.0"
@@ -344,7 +343,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -358,7 +356,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -372,7 +369,6 @@
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -386,7 +382,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -400,7 +395,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -414,7 +408,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -425,7 +418,6 @@
"version": "1.18.34",
"resolved": "https://registry.npmjs.org/@opencode-ai/plugin/-/plugin-1.18.34.tgz",
"integrity": "sha512-bC1EBL/QJ6LpC7liKZXr5JA+oa9qDAwTzpEgPxPsx1pbIUdhKOqg3/yoPEQgoa4YdO3hsgCWNFmbDE/DeOl/Ug==",
"dev": true,
"license": "MIT",
"dependencies": {
"@ai-sdk/provider": "3.0.8",
@@ -454,7 +446,6 @@
"version": "1.18.34",
"resolved": "https://registry.npmjs.org/@opencode-ai/sdk/-/sdk-1.18.34.tgz",
"integrity": "sha512-R9QWUpcngtdQ6FCixz7NC6n1WsSho6CEuRUzYHz/CzZcp3miSklDzXkm0oDwXVCnuEu+ANoeM0f6RDp51kOMzQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"cross-spawn": "7.0.6"
@@ -464,7 +455,6 @@
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
"integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/debug": {
@@ -826,7 +816,6 @@
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
"dev": true,
"license": "MIT",
"dependencies": {
"path-key": "^3.1.0",
@@ -900,7 +889,6 @@
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
"dev": true,
"license": "Apache-2.0",
"optional": true,
"engines": {
@@ -925,7 +913,6 @@
"version": "4.0.0-beta.83",
"resolved": "https://registry.npmjs.org/effect/-/effect-4.0.0-beta.83.tgz",
"integrity": "sha512-0wsak8RtgGAr9UWSbVDgJHZcUqMSvicHcvaZv1MbMM7MCGgW4Rn/137J1MHQbwYPcwYGxT/IqehFd+UbYuj78w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@standard-schema/spec": "^1.1.0",
@@ -1165,7 +1152,6 @@
"version": "4.9.0",
"resolved": "https://registry.npmjs.org/fast-check/-/fast-check-4.9.0.tgz",
"integrity": "sha512-7ms6T7SybUev/PQITciI0yLM2pOSFy5zpG8Ty7tQofcVaQUvrMXp6CBwqF6fThLCLOrfBtuHAtwq6Yu4XPCllg==",
"dev": true,
"funding": [
{
"type": "individual",
@@ -1252,7 +1238,6 @@
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/find-my-way-ts/-/find-my-way-ts-0.1.6.tgz",
"integrity": "sha512-a85L9ZoXtNAey3Y6Z+eBWW658kO/MwR7zIafkIUPUMf3isZG0NCs2pjW2wtjxAKuJPxMAsHUIP4ZPGv0o5gyTA==",
"dev": true,
"license": "MIT"
},
"node_modules/find-up": {
@@ -1436,7 +1421,6 @@
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/ini/-/ini-7.0.0.tgz",
"integrity": "sha512-ifK0CgjALofS5bkrcTy4RaQ9Vx2Knf/eLeIO+NaswQEpH1UblrtTSCIvN71qQDMq0PeQ/SSPojvEJp9vvvfr+w==",
"dev": true,
"license": "ISC",
"engines": {
"node": "^22.22.2 || ^24.15.0 || >=26.0.0"
@@ -1527,7 +1511,6 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
"dev": true,
"license": "ISC"
},
"node_modules/istanbul-lib-coverage": {
@@ -1595,7 +1578,6 @@
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
"integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==",
"dev": true,
"license": "(AFL-2.1 OR BSD-3-Clause)"
},
"node_modules/json-schema-traverse": {
@@ -1669,7 +1651,6 @@
"version": "1.30.0",
"resolved": "https://registry.npmjs.org/kubernetes-types/-/kubernetes-types-1.30.0.tgz",
"integrity": "sha512-Dew1okvhM/SQcIa2rcgujNndZwU8VnSapDgdxlYoB84ZlpAD43U6KLAFqYo17ykSFGHNPrg0qry0bP+GJd9v7Q==",
"dev": true,
"license": "Apache-2.0"
},
"node_modules/levn": {
@@ -2426,7 +2407,6 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-2.1.0.tgz",
"integrity": "sha512-p/pBCVO63CsvvpkomUnNNag6+n38rULuDA6HHe70o2gtC8ODI52foF/4ko2qQcp6OiErJXTmrZeXmsGGHsIQNQ==",
"dev": true,
"license": "MIT",
"optionalDependencies": {
"msgpackr-extract": "^3.0.4"
@@ -2436,7 +2416,6 @@
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.4.tgz",
"integrity": "sha512-4kmO/MdyUIkLIvTPr8VHLil4AtoKIoniWPIEk5+CDy0xnWC84azhSFmuJ7PxZdsYtiP5kEeQsORAVIeMgxT+Hw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
@@ -2459,7 +2438,6 @@
"version": "0.2.8",
"resolved": "https://registry.npmjs.org/multipasta/-/multipasta-0.2.8.tgz",
"integrity": "sha512-ZPWuMKyv0cSO29f7hozp+k6+crZbQijV8ipMvxNxRf2SwtYGTX1ZX89Kd20VV4H9Znonx+EQn+iy1wGQsJ+b+Q==",
"dev": true,
"license": "MIT"
},
"node_modules/natural-compare": {
@@ -2473,7 +2451,6 @@
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz",
"integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
@@ -2569,7 +2546,6 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -2638,7 +2614,6 @@
"version": "8.4.2",
"resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-8.4.2.tgz",
"integrity": "sha512-vvuOGgcuPJAirlHvuQw1TrOiw7ptaIXXmIbNuiNOY6lNGJJH49PQ1Kj4nd783nPdQhQdicgOjVI2yI/9BD6/Ng==",
"dev": true,
"funding": [
{
"type": "individual",
@@ -2723,7 +2698,6 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
"dev": true,
"license": "MIT",
"dependencies": {
"shebang-regex": "^3.0.0"
@@ -2736,7 +2710,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -2869,7 +2842,6 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/toml/-/toml-4.3.0.tgz",
"integrity": "sha512-lVb8X9BsPVuH0M4BKeS91tXAmJvCjQ5UIyAbQFaxkKGyUFK2RPkhwaFSQH8vbpl1d23eu/IBH+dwVMHWaq9A5A==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=20"
@@ -2930,7 +2902,6 @@
"version": "14.0.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.2.tgz",
"integrity": "sha512-xZe/16rV4aa+HGSOCiY2YeLT1OybRLrrkL/Rqaq7p7GMVXjFh+6wN4oMYgjFmnSnhY8t6Xpdl2l9qmnHYuMHwQ==",
"dev": true,
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
@@ -2959,7 +2930,6 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"dev": true,
"license": "ISC",
"dependencies": {
"isexe": "^2.0.0"
@@ -2995,7 +2965,6 @@
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz",
"integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==",
"dev": true,
"license": "ISC",
"bin": {
"yaml": "bin.mjs"
@@ -3091,7 +3060,6 @@
"version": "4.1.8",
"resolved": "https://registry.npmjs.org/zod/-/zod-4.1.8.tgz",
"integrity": "sha512-5R1P+WwQqmmMIEACyzSvo4JXHY5WiAFHRMg+zBZKgKS+Q1viRa0C1hmUKtHltoIFKtIdki3pRxkmpP74jnNYHQ==",
"dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/colinhacks"
+1 -1
View File
@@ -543,6 +543,7 @@
},
"dependencies": {
"@iarna/toml": "2.2.5",
"@opencode-ai/plugin": "1.18.34",
"ajv": "8.20.0",
"js-yaml": "4.3.2",
"sql.js": "1.14.2"
@@ -560,7 +561,6 @@
},
"devDependencies": {
"@eslint/js": "9.39.2",
"@opencode-ai/plugin": "1.18.34",
"@types/node": "26.6.4",
"c8": "11.0.0",
"eslint": "10.11.0",
+31
View File
@@ -4,6 +4,7 @@
const assert = require("assert")
const fs = require("fs")
const { builtinModules } = require("module")
const os = require("os")
const path = require("path")
const { spawnSync } = require("child_process")
@@ -116,6 +117,36 @@ function main() {
assert.strictEqual(result.status, 0, result.stderr)
assert.ok(fs.existsSync(distEntry), ".opencode/dist/index.js should exist after build")
}],
["built OpenCode bundle only imports packages declared as runtime dependencies", () => {
// OpenCode installs npm plugins without devDependencies, so any package
// the compiled bundle imports at runtime must ship as a dependency.
const runtimeDeps = new Set([
...Object.keys(packageJson.dependencies || {}),
...Object.keys(packageJson.peerDependencies || {}),
])
const distDir = path.dirname(distEntry)
const imported = new Set()
const walk = (dir) => {
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
const entryPath = path.join(dir, entry.name)
if (entry.isDirectory()) {
walk(entryPath)
} else if (entry.name.endsWith(".js")) {
const source = fs.readFileSync(entryPath, "utf8")
for (const match of source.matchAll(/(?:\bfrom|\bimport)\s*\(?\s*["']([^"'.][^"']*)["']/g)) {
const specifier = match[1]
if (specifier.startsWith("node:") || builtinModules.includes(specifier)) continue
const parts = specifier.split("/")
imported.add(specifier.startsWith("@") ? parts.slice(0, 2).join("/") : parts[0])
}
}
}
}
walk(distDir)
assert.ok(imported.size > 0, "expected the OpenCode bundle to import at least one package")
const undeclared = [...imported].filter((name) => !runtimeDeps.has(name)).sort()
assert.deepStrictEqual(undeclared, [], `undeclared runtime imports: ${undeclared.join(", ")}`)
}],
["package.json declares a resolvable OpenCode plugin entry", () => {
assert.strictEqual(packageJson.main, ".opencode/dist/index.js")
assert.ok(packageJson.exports, "package.json must declare an exports map")