Add OpenCode binary selection with version detection

- Add binary selector component with dropdown and validation
- Support custom OpenCode binary paths and system PATH binary
- Async version checking for all binaries when selector opens
- Store recent binaries with timestamps and version info
- Enhanced instance creation to use selected binary
- Improved storage system for binary preferences
- Fixed validation to handle system PATH binaries properly
This commit is contained in:
Shantur Rathore
2025-10-26 10:26:32 +00:00
parent f4a664bfe7
commit f63a4b3754
11 changed files with 684 additions and 55 deletions

View File

@@ -50,14 +50,14 @@ class ProcessManager {
}
}
async spawn(folder: string, instanceId: string): Promise<ProcessInfo> {
async spawn(folder: string, instanceId: string, binaryPath?: string): Promise<ProcessInfo> {
this.validateFolder(folder)
const binaryPath = this.validateOpenCodeBinary()
const actualBinaryPath = binaryPath ? this.validateCustomBinary(binaryPath) : this.validateOpenCodeBinary()
this.sendLog(instanceId, "info", `Starting OpenCode server for ${folder}...`)
this.sendLog(instanceId, "info", `Starting OpenCode server for ${folder} using ${actualBinaryPath}...`)
return new Promise((resolve, reject) => {
const child = spawn("opencode", ["serve", "--port", "0", "--print-logs", "--log-level", "DEBUG"], {
const child = spawn(actualBinaryPath, ["serve", "--port", "0", "--print-logs", "--log-level", "DEBUG"], {
cwd: folder,
stdio: ["ignore", "pipe", "pipe"],
env: process.env,
@@ -103,7 +103,7 @@ class ProcessManager {
}
this.processes.set(child.pid!, meta)
resolve({ pid: child.pid!, port, binaryPath })
resolve({ pid: child.pid!, port, binaryPath: actualBinaryPath })
}
const meta = this.processes.get(child.pid!)
@@ -221,6 +221,28 @@ class ProcessManager {
)
}
}
private validateCustomBinary(binaryPath: string): string {
if (!existsSync(binaryPath)) {
throw new Error(`OpenCode binary not found: ${binaryPath}`)
}
const stats = statSync(binaryPath)
if (!stats.isFile()) {
throw new Error(`Path is not a file: ${binaryPath}`)
}
// Check if executable (on Unix systems)
if (process.platform !== "win32") {
try {
execSync(`test -x "${binaryPath}"`, { stdio: "pipe" })
} catch {
throw new Error(`Binary is not executable: ${binaryPath}`)
}
}
return binaryPath
}
}
export const processManager = new ProcessManager()