diff --git a/README.md b/README.md index 777cc86..3bd7b3c 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,12 @@ Terminal UI for managing git worktrees and launching your preferred coding tool npm i -g opencode-worktree ``` +Or run it without a global installation using Bun: + +```bash +bunx opencode-worktree +``` + ## Run ```bash diff --git a/bin/opencode-worktree b/bin/opencode-worktree index 9580059..9620b6c 100755 --- a/bin/opencode-worktree +++ b/bin/opencode-worktree @@ -2,9 +2,9 @@ import fs from 'node:fs'; import path from 'node:path'; -import os from 'node:os'; import { spawn } from 'node:child_process'; import { fileURLToPath } from 'node:url'; +import { getBinaryPath, installBinary } from '../script/runtime.mjs'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -12,56 +12,19 @@ const pkg = JSON.parse( fs.readFileSync(new URL('../package.json', import.meta.url), 'utf8'), ); -const detectPlatformAndArch = () => { - let platform; - switch (os.platform()) { - case 'darwin': - platform = 'darwin'; - break; - case 'linux': - platform = 'linux'; - break; - case 'win32': - platform = 'windows'; - break; - default: - platform = os.platform(); - break; - } - - let arch; - switch (os.arch()) { - case 'x64': - arch = 'x64'; - break; - case 'arm64': - arch = 'arm64'; - break; - default: - arch = os.arch(); - break; - } - - return { platform, arch }; -}; - -const resolveBinaryPath = () => { - const { platform } = detectPlatformAndArch(); - const binaryName = platform === 'windows' ? 'opencode-worktree.exe' : 'opencode-worktree-bin'; - const binaryPath = path.join(__dirname, '..', 'bin', binaryName); - +const resolveBinaryPath = async () => { + const packageRoot = path.join(__dirname, '..'); + const { binaryPath } = getBinaryPath(packageRoot); if (!fs.existsSync(binaryPath)) { - throw new Error( - `Binary not found at ${binaryPath}. Run npm install -g ${pkg.name} again to re-download.`, - ); + console.error('Downloading opencode-worktree binary...'); + await installBinary(packageRoot, pkg.version); } - return binaryPath; }; -const run = () => { +const run = async () => { try { - const binaryPath = resolveBinaryPath(); + const binaryPath = await resolveBinaryPath(); const args = process.argv.slice(2); const child = spawn(binaryPath, args, { stdio: 'inherit' }); diff --git a/script/postinstall.mjs b/script/postinstall.mjs index 8a006ad..fdcf335 100644 --- a/script/postinstall.mjs +++ b/script/postinstall.mjs @@ -2,78 +2,19 @@ import fs from "node:fs"; import path from "node:path"; -import os from "node:os"; import { fileURLToPath } from "node:url"; +import { getBinaryPath, installBinary as downloadBinary } from "./runtime.mjs"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const pkg = JSON.parse( fs.readFileSync(path.join(__dirname, "..", "package.json"), "utf8"), ); -const detectPlatformAndArch = () => { - let platform; - switch (os.platform()) { - case "darwin": - platform = "darwin"; - break; - case "linux": - platform = "linux"; - break; - case "win32": - platform = "windows"; - break; - default: - platform = os.platform(); - break; - } - - let arch; - switch (os.arch()) { - case "x64": - arch = "x64"; - break; - case "arm64": - arch = "arm64"; - break; - default: - arch = os.arch(); - break; - } - - return { platform, arch }; -}; - -const downloadFile = async (url, targetPath) => { - const response = await fetch(url); - if (!response.ok) { - throw new Error(`Failed to download ${url} (${response.status})`); - } - - const buffer = Buffer.from(await response.arrayBuffer()); - fs.writeFileSync(targetPath, buffer); -}; - const installBinary = async () => { - const { platform, arch } = detectPlatformAndArch(); - const isWindows = platform === "windows"; - const binaryName = isWindows - ? "opencode-worktree.exe" - : "opencode-worktree-bin"; - const downloadName = `opencode-worktree-${platform}-${arch}${isWindows ? ".exe" : ""}`; - const version = pkg.version; - const downloadUrl = `https://github.com/arturosdg/opencode-worktree/releases/download/v${version}/${downloadName}`; - - const binDir = path.join(__dirname, "..", "bin"); - const binaryPath = path.join(binDir, binaryName); - - fs.mkdirSync(binDir, { recursive: true }); - await downloadFile(downloadUrl, binaryPath); - - if (!isWindows) { - fs.chmodSync(binaryPath, 0o755); - } - - return binaryPath; + const packageRoot = path.join(__dirname, ".."); + const { binaryPath } = getBinaryPath(packageRoot); + if (fs.existsSync(binaryPath)) return binaryPath; + return downloadBinary(packageRoot, pkg.version); }; try { diff --git a/script/runtime.mjs b/script/runtime.mjs new file mode 100644 index 0000000..eeea55c --- /dev/null +++ b/script/runtime.mjs @@ -0,0 +1,28 @@ +import fs from "node:fs"; +import os from "node:os"; +import path from "node:path"; + +const detectPlatformAndArch = () => ({ + platform: { darwin: "darwin", linux: "linux", win32: "windows" }[os.platform()] ?? os.platform(), + arch: { x64: "x64", arm64: "arm64" }[os.arch()] ?? os.arch(), +}); + +export const getBinaryPath = (packageRoot) => { + const { platform } = detectPlatformAndArch(); + const binaryName = platform === "windows" ? "opencode-worktree.exe" : "opencode-worktree-bin"; + return { binaryPath: path.join(packageRoot, "bin", binaryName), binaryName }; +}; + +export const installBinary = async (packageRoot, version) => { + const { platform, arch } = detectPlatformAndArch(); + const isWindows = platform === "windows"; + const downloadName = `opencode-worktree-${platform}-${arch}${isWindows ? ".exe" : ""}`; + const downloadUrl = `https://github.com/arturosdg/opencode-worktree/releases/download/v${version}/${downloadName}`; + const { binaryPath } = getBinaryPath(packageRoot); + const response = await fetch(downloadUrl); + if (!response.ok) throw new Error(`Failed to download ${downloadUrl} (${response.status})`); + fs.mkdirSync(path.dirname(binaryPath), { recursive: true }); + fs.writeFileSync(binaryPath, Buffer.from(await response.arrayBuffer())); + if (!isWindows) fs.chmodSync(binaryPath, 0o755); + return binaryPath; +};