refactor: route raw Bun.file/write/hash/which/spawn through runtime shims
Eliminates 19 unguarded `Bun.*` runtime call sites in the plugin bundle that crashed with `ReferenceError: Bun is not defined` under Electron. Per-tool-call hot paths (executed on every Read/Edit): - src/tools/hashline-edit/hash-computation.ts: Bun.hash.xxHash32 → bunHashXxh32 - src/tools/hashline-edit/hashline-edit-executor.ts: 8 sites via bunFile/bunWrite - src/hooks/hashline-read-enhancer/hook.ts: Bun.file → bunFile - src/hooks/hashline-edit-diff-enhancer/hook.ts: 2 sites via bunFile Plugin-load paths: - src/hooks/claude-code-hooks/config.ts and config-loader.ts: Bun.file → bunFile - src/features/claude-code-mcp-loader/loader.ts: Bun.file → bunFile - src/features/claude-code-plugin-loader/mcp-server-loader.ts: Bun.file → bunFile - src/features/team-mode/deps.ts: Bun.spawn → spawn shim - src/hooks/session-notification-utils.ts: Bun.which → bunWhich, also drops the bare `declare const Bun` ambient declaration - src/shared/binary-downloader.ts: Bun.write → bunWrite Pure mechanical API swaps. No control-flow or signature changes.
This commit is contained in:
@@ -11,6 +11,7 @@ import type {
|
||||
import { transformMcpServer } from "./transformer"
|
||||
import { log } from "../../shared/logger"
|
||||
import { shouldLoadMcpServer } from "./scope-filter"
|
||||
import { bunFile } from "../../shared/bun-file-shim"
|
||||
|
||||
interface McpConfigPath {
|
||||
path: string
|
||||
@@ -37,7 +38,7 @@ async function loadMcpConfigFile(
|
||||
}
|
||||
|
||||
try {
|
||||
const content = await Bun.file(filePath).text()
|
||||
const content = await bunFile(filePath).text()
|
||||
return JSON.parse(content) as ClaudeCodeMcpConfig
|
||||
} catch (error) {
|
||||
log(`Failed to load MCP config from ${filePath}`, error)
|
||||
|
||||
@@ -7,6 +7,7 @@ import type { ClaudeCodeMcpConfig } from "../claude-code-mcp-loader/types"
|
||||
import { log } from "../../shared/logger"
|
||||
import type { LoadedPlugin } from "./types"
|
||||
import { resolvePluginPaths } from "./plugin-path-resolver"
|
||||
import { bunFile } from "../../shared/bun-file-shim"
|
||||
|
||||
export async function loadPluginMcpServers(
|
||||
plugins: LoadedPlugin[],
|
||||
@@ -18,7 +19,7 @@ export async function loadPluginMcpServers(
|
||||
if (!plugin.mcpPath || !existsSync(plugin.mcpPath)) continue
|
||||
|
||||
try {
|
||||
const content = await Bun.file(plugin.mcpPath).text()
|
||||
const content = await bunFile(plugin.mcpPath).text()
|
||||
let config = JSON.parse(content) as ClaudeCodeMcpConfig
|
||||
|
||||
config = resolvePluginPaths(config, plugin.installPath)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { TeamModeConfig } from "../../config/schema/team-mode"
|
||||
import { spawn } from "../../shared/bun-spawn-shim"
|
||||
|
||||
export interface TeamModeDependencyReport {
|
||||
tmuxAvailable: boolean
|
||||
@@ -20,7 +21,7 @@ export async function checkTeamModeDependencies(
|
||||
|
||||
async function probeBinary(cmd: string, args: string[]): Promise<boolean> {
|
||||
try {
|
||||
const proc = Bun.spawn({ cmd: [cmd, ...args], stdout: "pipe", stderr: "pipe" })
|
||||
const proc = spawn({ cmd: [cmd, ...args], stdout: "pipe", stderr: "pipe" })
|
||||
const code = await proc.exited
|
||||
return code === 0
|
||||
} catch {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { join } from "path"
|
||||
import type { ClaudeHookEvent } from "./types"
|
||||
import { log } from "../../shared/logger"
|
||||
import { getOpenCodeConfigDir } from "../../shared"
|
||||
import { bunFile } from "../../shared/bun-file-shim"
|
||||
|
||||
const CONFIG_CACHE_TTL_MS = 30_000
|
||||
|
||||
@@ -61,7 +62,7 @@ async function loadConfigFromPath(path: string): Promise<PluginExtendedConfig |
|
||||
}
|
||||
|
||||
try {
|
||||
const content = await Bun.file(path).text()
|
||||
const content = await bunFile(path).text()
|
||||
return JSON.parse(content) as PluginExtendedConfig
|
||||
} catch (error) {
|
||||
log("Failed to load config", { path, error })
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { join } from "path"
|
||||
import { existsSync } from "fs"
|
||||
import { getClaudeConfigDir } from "../../shared"
|
||||
import { bunFile } from "../../shared/bun-file-shim"
|
||||
import type { ClaudeHooksConfig, HookMatcher, HookAction } from "./types"
|
||||
|
||||
const CONFIG_CACHE_TTL_MS = 30_000
|
||||
@@ -126,7 +127,7 @@ export async function loadClaudeHooksConfig(
|
||||
for (const settingsPath of paths) {
|
||||
if (existsSync(settingsPath)) {
|
||||
try {
|
||||
const content = await Bun.file(settingsPath).text()
|
||||
const content = await bunFile(settingsPath).text()
|
||||
const settings = JSON.parse(content) as { hooks?: RawClaudeHooksConfig }
|
||||
if (settings.hooks) {
|
||||
const normalizedHooks = normalizeHooksConfig(settings.hooks)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { log } from "../../shared"
|
||||
import { bunFile } from "../../shared/bun-file-shim"
|
||||
import { generateUnifiedDiff, countLineDiffs } from "../../tools/hashline-edit/diff-utils"
|
||||
|
||||
interface HashlineEditDiffEnhancerConfig {
|
||||
@@ -38,7 +39,7 @@ function extractFilePath(args: Record<string, unknown>): string | undefined {
|
||||
|
||||
async function captureOldContent(filePath: string): Promise<string> {
|
||||
try {
|
||||
const file = Bun.file(filePath)
|
||||
const file = bunFile(filePath)
|
||||
if (await file.exists()) {
|
||||
return await file.text()
|
||||
}
|
||||
@@ -79,7 +80,7 @@ export function createHashlineEditDiffEnhancerHook(config: HashlineEditDiffEnhan
|
||||
|
||||
let newContent: string
|
||||
try {
|
||||
newContent = await Bun.file(filePath).text()
|
||||
newContent = await bunFile(filePath).text()
|
||||
} catch {
|
||||
log("[hashline-edit-diff-enhancer] failed to read new content", { filePath })
|
||||
return
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { PluginInput } from "@opencode-ai/plugin"
|
||||
import { bunFile } from "../../shared/bun-file-shim"
|
||||
import { computeLineHash } from "../../tools/hashline-edit/hash-computation"
|
||||
|
||||
const WRITE_SUCCESS_MARKER = "File written successfully."
|
||||
@@ -178,7 +179,7 @@ async function appendWriteHashlineOutput(output: { output: string; metadata: unk
|
||||
return
|
||||
}
|
||||
|
||||
const file = Bun.file(filePath)
|
||||
const file = bunFile(filePath)
|
||||
if (!(await file.exists())) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
import { log } from "../shared/logger"
|
||||
|
||||
declare const Bun: {
|
||||
which(commandName: string): string | null
|
||||
}
|
||||
import { bunWhich } from "../shared/bun-which-shim"
|
||||
|
||||
type Platform = "darwin" | "linux" | "win32" | "unsupported"
|
||||
|
||||
async function findCommand(commandName: string): Promise<string | null> {
|
||||
try {
|
||||
return Bun.which(commandName)
|
||||
return bunWhich(commandName)
|
||||
} catch (error) {
|
||||
log("[session-notification] failed to resolve command path", {
|
||||
commandName,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { chmodSync, existsSync, mkdirSync, unlinkSync } from "node:fs";
|
||||
import * as path from "node:path";
|
||||
import { spawn } from "./bun-spawn-shim";
|
||||
import { bunWrite } from "./bun-file-shim";
|
||||
import { validateArchiveEntries, type ArchiveEntry } from "./archive-entry-validator";
|
||||
import { extractZip } from "./zip-extractor";
|
||||
|
||||
@@ -26,7 +27,7 @@ export async function downloadArchive(downloadUrl: string, archivePath: string):
|
||||
}
|
||||
|
||||
const arrayBuffer = await response.arrayBuffer();
|
||||
await Bun.write(archivePath, arrayBuffer);
|
||||
await bunWrite(archivePath, arrayBuffer);
|
||||
}
|
||||
|
||||
export async function extractTarGz(
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { HASHLINE_DICT } from "./constants"
|
||||
import { createHashlineChunkFormatter } from "./hashline-chunk-formatter"
|
||||
import { bunHashXxh32 } from "../../shared/bun-hash-shim"
|
||||
|
||||
const RE_SIGNIFICANT = /[\p{L}\p{N}]/u
|
||||
|
||||
function computeNormalizedLineHash(lineNumber: number, normalizedContent: string): string {
|
||||
const stripped = normalizedContent
|
||||
const seed = RE_SIGNIFICANT.test(stripped) ? 0 : lineNumber
|
||||
const hash = Bun.hash.xxHash32(stripped, seed)
|
||||
const hash = bunHashXxh32(stripped, seed)
|
||||
const index = hash % 256
|
||||
return HASHLINE_DICT[index]
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { ToolContext } from "@opencode-ai/plugin/tool"
|
||||
import { publishToolMetadata } from "../../features/tool-metadata-store"
|
||||
import { bunFile, bunWrite } from "../../shared/bun-file-shim"
|
||||
import { applyHashlineEditsWithReport } from "./edit-operations"
|
||||
import { countLineDiffs, generateUnifiedDiff } from "./diff-utils"
|
||||
import { canonicalizeFileText, restoreFileText } from "./file-text-canonicalization"
|
||||
@@ -94,7 +95,7 @@ export async function executeHashlineEditTool(args: HashlineEditArgs, context: T
|
||||
|
||||
const edits = deleteMode ? [] : normalizeHashlineEdits(args.edits)
|
||||
|
||||
const file = Bun.file(filePath)
|
||||
const file = bunFile(filePath)
|
||||
const exists = await file.exists()
|
||||
if (!exists && !deleteMode && !canCreateFromMissingFile(edits)) {
|
||||
return `Error: File not found: ${filePath}`
|
||||
@@ -102,7 +103,7 @@ export async function executeHashlineEditTool(args: HashlineEditArgs, context: T
|
||||
|
||||
if (deleteMode) {
|
||||
if (!exists) return `Error: File not found: ${filePath}`
|
||||
await Bun.file(filePath).delete()
|
||||
await bunFile(filePath).delete()
|
||||
return `Successfully deleted ${filePath}`
|
||||
}
|
||||
|
||||
@@ -122,11 +123,11 @@ export async function executeHashlineEditTool(args: HashlineEditArgs, context: T
|
||||
|
||||
const writeContent = restoreFileText(canonicalNewContent, oldEnvelope)
|
||||
|
||||
await Bun.write(filePath, writeContent)
|
||||
await bunWrite(filePath, writeContent)
|
||||
|
||||
if (pluginCtx?.client) {
|
||||
await runFormattersForFile(pluginCtx.client as FormatterClient, context.directory, filePath)
|
||||
const formattedContent = Buffer.from(await Bun.file(filePath).arrayBuffer()).toString("utf8")
|
||||
const formattedContent = Buffer.from(await bunFile(filePath).arrayBuffer()).toString("utf8")
|
||||
if (formattedContent !== writeContent) {
|
||||
const formattedEnvelope = canonicalizeFileText(formattedContent)
|
||||
const formattedMeta = buildSuccessMeta(
|
||||
@@ -138,8 +139,8 @@ export async function executeHashlineEditTool(args: HashlineEditArgs, context: T
|
||||
)
|
||||
await publishToolMetadata(metadataContext, formattedMeta)
|
||||
if (rename && rename !== filePath) {
|
||||
await Bun.write(rename, formattedContent)
|
||||
await Bun.file(filePath).delete()
|
||||
await bunWrite(rename, formattedContent)
|
||||
await bunFile(filePath).delete()
|
||||
return `Moved ${filePath} to ${rename}`
|
||||
}
|
||||
return `Updated ${filePath}`
|
||||
@@ -147,8 +148,8 @@ export async function executeHashlineEditTool(args: HashlineEditArgs, context: T
|
||||
}
|
||||
|
||||
if (rename && rename !== filePath) {
|
||||
await Bun.write(rename, writeContent)
|
||||
await Bun.file(filePath).delete()
|
||||
await bunWrite(rename, writeContent)
|
||||
await bunFile(filePath).delete()
|
||||
}
|
||||
|
||||
const effectivePath = rename && rename !== filePath ? rename : filePath
|
||||
|
||||
Reference in New Issue
Block a user