1 根因分析
根本原因 : oh-my-openagent 插件使用 bun build --target bun 构建,产物包含大量 Bun 专有 API 调用(Bun.file()、Bun.spawn()、Bun.which() 等)。OpenCode Desktop 使用 Node.js/Electron 运行时,不认识 Bun 全局变量,导致插件加载失败。
问题链:
scss
插件源码使用 Bun API
↓
bun build --target bun 内联 Bun 调用
↓
OpenCode Desktop (Node.js) 加载插件
↓
ReferenceError: Bun is not defined
↓
插件加载失败 → Agents 不注册
2 解决方案
核心思路: 在插件加载前注入 Bun polyfill,提供 Node.js 等价实现。
完整修复脚本 (script/patch-bun-polyfill.ts):
dart
#!/usr/bin/env bun
import { readFileSync, writeFileSync } from "node:fs"
import { dirname, join } from "node:path"
import { fileURLToPath } from "node:url"
const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url))
const DIST_PATH = join(SCRIPT_DIR, "..", "dist", "index.js")
const POLYFILL_MARKER = "// __BUN_POLYFILL_INJECTED__"
const POLYFILL = `
${POLYFILL_MARKER}
if (typeof Bun === 'undefined') {
const fs = __require('fs');
const path = __require('path');
const { spawn, spawnSync, execSync } = __require('child_process');
const { createHash } = __require('crypto');
const { Readable } = __require('stream');
globalThis.Bun = {
file: (filePath) => ({
text: () => fs.promises.readFile(filePath, 'utf-8'),
arrayBuffer: () => fs.promises.readFile(filePath).then(b => b.buffer),
exists: () => fs.promises.access(filePath).then(() => true, () => false),
delete: () => fs.promises.unlink(filePath),
size: fs.statSync(filePath).size,
}),
write: (filePath, data) => fs.promises.writeFile(filePath, data),
spawn: (cmd, opts) => {
const args = Array.isArray(cmd) ? cmd : [cmd];
const proc = spawn(args[0], args.slice(1), {
...opts,
stdio: opts?.stdio || ['ignore', 'pipe', 'pipe']
});
return {
exited: new Promise((resolve, reject) => {
proc.on('close', (code) => resolve(code));
proc.on('error', reject);
}),
stdout: proc.stdout,
stderr: proc.stderr,
pid: proc.pid,
kill: (signal) => proc.kill(signal),
};
},
spawnSync: (cmd, opts) => {
const args = Array.isArray(cmd) ? cmd : [cmd];
const result = spawnSync(args[0], args.slice(1), {
...opts,
stdio: opts?.stdio || ['ignore', 'pipe', 'pipe']
});
return {
stdout: result.stdout,
stderr: result.stderr,
exitCode: result.status ?? 1,
pid: result.pid ?? -1,
};
},
which: (cmd) => {
try {
const result = process.platform === 'win32'
? execSync('where ' + cmd, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] })
.trim().split('\\n')[0]
: execSync('which ' + cmd, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] })
.trim();
return result || null;
} catch { return null; }
},
hash: {
xxHash32: (data, seed) => {
const h = createHash('md5')
.update(typeof data === 'string' ? data : Buffer.from(data))
.digest();
return h.readUInt32LE(0) ^ (seed || 0);
},
},
serve: () => { throw new Error('Bun.serve not available in Node.js'); },
readableStreamToText: async (stream) => {
if (stream instanceof Readable) {
const chunks = [];
for await (const chunk of stream) chunks.push(chunk);
return Buffer.concat(chunks).toString('utf-8');
}
return new Response(stream).text();
},
sleep: (ms) => new Promise(r => setTimeout(r, ms)),
};
}
`
const original = readFileSync(DIST_PATH, "utf-8")
if (original.includes(POLYFILL_MARKER)) {
console.log("Bun polyfill already present, skipping.")
process.exit(0)
}
// Find the __require definition line and insert polyfill after it
const lines = original.split("\n")
const requireLineIndex = lines.findIndex(
l => l.includes('var __require = typeof import.meta.require')
)
if (requireLineIndex === -1) {
throw new Error("Could not find __require definition line")
}
lines.splice(requireLineIndex + 1, 0, POLYFILL)
const patched = lines.join("\n")
writeFileSync(DIST_PATH, patched, "utf-8")
console.log("Injected Bun polyfill for Node.js/Electron compatibility")
3 一键修复命令
bash
# 1. 进入源码目录
cd ~\oh-my-openagent
# 2. 安装依赖
bun install
# 3. 构建(自动应用 node-require-shim)
bun run build
# 4. 注入 Bun polyfill
bun run script/patch-bun-polyfill.ts
# 5. 部署到缓存目录
copy /Y dist\index.js "%USERPROFILE%\.cache\opencode\packages\oh-my-openagent@3.17.15\node_modules\oh-my-openagent\dist\index.js"
# 6. 部署到配置目录
copy /Y dist\index.js "%USERPROFILE%\.config\opencode\node_modules\oh-my-openagent\dist\index.js"