Merge pull request #3227 from code-yeongyu/fix/installer-and-auto-update
fix(installer): enforce min version + upgrade pinned entries
This commit is contained in:
@@ -21,11 +21,12 @@ describe("runCliInstaller", () => {
|
|||||||
console.error = originalConsoleError
|
console.error = originalConsoleError
|
||||||
})
|
})
|
||||||
|
|
||||||
it("completes installation without auth plugin or provider config steps", async () => {
|
it("blocks installation when OpenCode is below the minimum version", async () => {
|
||||||
//#given
|
// given
|
||||||
const restoreSpies = [
|
const restoreSpies = [
|
||||||
spyOn(configManager, "detectCurrentConfig").mockReturnValue({
|
spyOn(configManager, "detectCurrentConfig").mockReturnValue({
|
||||||
isInstalled: false,
|
isInstalled: false,
|
||||||
|
installedVersion: null,
|
||||||
hasClaude: false,
|
hasClaude: false,
|
||||||
isMax20: false,
|
isMax20: false,
|
||||||
hasOpenAI: false,
|
hasOpenAI: false,
|
||||||
@@ -34,9 +35,56 @@ describe("runCliInstaller", () => {
|
|||||||
hasOpencodeZen: false,
|
hasOpencodeZen: false,
|
||||||
hasZaiCodingPlan: false,
|
hasZaiCodingPlan: false,
|
||||||
hasKimiForCoding: false,
|
hasKimiForCoding: false,
|
||||||
|
hasOpencodeGo: false,
|
||||||
}),
|
}),
|
||||||
spyOn(configManager, "isOpenCodeInstalled").mockResolvedValue(true),
|
spyOn(configManager, "isOpenCodeInstalled").mockResolvedValue(true),
|
||||||
spyOn(configManager, "getOpenCodeVersion").mockResolvedValue("1.0.200"),
|
spyOn(configManager, "getOpenCodeVersion").mockResolvedValue("1.3.9"),
|
||||||
|
]
|
||||||
|
const addPluginSpy = spyOn(configManager, "addPluginToOpenCodeConfig")
|
||||||
|
|
||||||
|
const args: InstallArgs = {
|
||||||
|
tui: false,
|
||||||
|
claude: "no",
|
||||||
|
openai: "no",
|
||||||
|
gemini: "no",
|
||||||
|
copilot: "no",
|
||||||
|
opencodeZen: "no",
|
||||||
|
zaiCodingPlan: "no",
|
||||||
|
kimiForCoding: "no",
|
||||||
|
opencodeGo: "no",
|
||||||
|
}
|
||||||
|
|
||||||
|
// when
|
||||||
|
const result = await runCliInstaller(args, "3.16.0")
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(result).toBe(1)
|
||||||
|
expect(addPluginSpy).not.toHaveBeenCalled()
|
||||||
|
|
||||||
|
for (const spy of restoreSpies) {
|
||||||
|
spy.mockRestore()
|
||||||
|
}
|
||||||
|
addPluginSpy.mockRestore()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("completes installation without auth plugin or provider config steps", async () => {
|
||||||
|
// given
|
||||||
|
const restoreSpies = [
|
||||||
|
spyOn(configManager, "detectCurrentConfig").mockReturnValue({
|
||||||
|
isInstalled: false,
|
||||||
|
installedVersion: null,
|
||||||
|
hasClaude: false,
|
||||||
|
isMax20: false,
|
||||||
|
hasOpenAI: false,
|
||||||
|
hasGemini: false,
|
||||||
|
hasCopilot: false,
|
||||||
|
hasOpencodeZen: false,
|
||||||
|
hasZaiCodingPlan: false,
|
||||||
|
hasKimiForCoding: false,
|
||||||
|
hasOpencodeGo: false,
|
||||||
|
}),
|
||||||
|
spyOn(configManager, "isOpenCodeInstalled").mockResolvedValue(true),
|
||||||
|
spyOn(configManager, "getOpenCodeVersion").mockResolvedValue("1.4.0"),
|
||||||
spyOn(configManager, "addPluginToOpenCodeConfig").mockResolvedValue({
|
spyOn(configManager, "addPluginToOpenCodeConfig").mockResolvedValue({
|
||||||
success: true,
|
success: true,
|
||||||
configPath: "/tmp/opencode.jsonc",
|
configPath: "/tmp/opencode.jsonc",
|
||||||
@@ -56,12 +104,13 @@ describe("runCliInstaller", () => {
|
|||||||
opencodeZen: "no",
|
opencodeZen: "no",
|
||||||
zaiCodingPlan: "no",
|
zaiCodingPlan: "no",
|
||||||
kimiForCoding: "no",
|
kimiForCoding: "no",
|
||||||
|
opencodeGo: "no",
|
||||||
}
|
}
|
||||||
|
|
||||||
//#when
|
// when
|
||||||
const result = await runCliInstaller(args, "3.4.0")
|
const result = await runCliInstaller(args, "3.4.0")
|
||||||
|
|
||||||
//#then
|
// then
|
||||||
expect(result).toBe(0)
|
expect(result).toBe(0)
|
||||||
|
|
||||||
for (const spy of restoreSpies) {
|
for (const spy of restoreSpies) {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import {
|
|||||||
printWarning,
|
printWarning,
|
||||||
validateNonTuiArgs,
|
validateNonTuiArgs,
|
||||||
} from "./install-validators"
|
} from "./install-validators"
|
||||||
|
import { getUnsupportedOpenCodeVersionMessage } from "./minimum-opencode-version"
|
||||||
|
|
||||||
export async function runCliInstaller(args: InstallArgs, version: string): Promise<number> {
|
export async function runCliInstaller(args: InstallArgs, version: string): Promise<number> {
|
||||||
const validation = validateNonTuiArgs(args)
|
const validation = validateNonTuiArgs(args)
|
||||||
@@ -57,6 +58,12 @@ export async function runCliInstaller(args: InstallArgs, version: string): Promi
|
|||||||
printInfo("Visit https://opencode.ai/docs for installation instructions")
|
printInfo("Visit https://opencode.ai/docs for installation instructions")
|
||||||
} else {
|
} else {
|
||||||
printSuccess(`OpenCode ${openCodeVersion ?? ""} detected`)
|
printSuccess(`OpenCode ${openCodeVersion ?? ""} detected`)
|
||||||
|
|
||||||
|
const unsupportedVersionMessage = getUnsupportedOpenCodeVersionMessage(openCodeVersion)
|
||||||
|
if (unsupportedVersionMessage) {
|
||||||
|
printWarning(unsupportedVersionMessage)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isUpdate) {
|
if (isUpdate) {
|
||||||
|
|||||||
@@ -79,12 +79,8 @@ export async function addPluginToOpenCodeConfig(currentVersion: string): Promise
|
|||||||
|
|
||||||
const normalizedPlugins = [...otherPlugins]
|
const normalizedPlugins = [...otherPlugins]
|
||||||
|
|
||||||
if (canonicalEntries.length > 0) {
|
if (canonicalEntries.length > 0 || legacyEntries.length > 0) {
|
||||||
normalizedPlugins.push(canonicalEntries[0])
|
normalizedPlugins.push(pluginEntry)
|
||||||
} else if (legacyEntries.length > 0) {
|
|
||||||
const versionMatch = legacyEntries[0].match(/@(.+)$/)
|
|
||||||
const preservedVersion = versionMatch ? versionMatch[1] : null
|
|
||||||
normalizedPlugins.push(preservedVersion ? `${PLUGIN_NAME}@${preservedVersion}` : pluginEntry)
|
|
||||||
} else {
|
} else {
|
||||||
normalizedPlugins.push(pluginEntry)
|
normalizedPlugins.push(pluginEntry)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { afterEach, beforeEach, describe, expect, it } from "bun:test"
|
import { afterEach, beforeEach, describe, expect, it, spyOn } from "bun:test"
|
||||||
import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"
|
import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"
|
||||||
import { tmpdir } from "node:os"
|
import { tmpdir } from "node:os"
|
||||||
import { join } from "node:path"
|
import { join } from "node:path"
|
||||||
@@ -6,6 +6,7 @@ import { join } from "node:path"
|
|||||||
import { resetConfigContext } from "./config-context"
|
import { resetConfigContext } from "./config-context"
|
||||||
import { detectCurrentConfig } from "./detect-current-config"
|
import { detectCurrentConfig } from "./detect-current-config"
|
||||||
import { addPluginToOpenCodeConfig } from "./add-plugin-to-opencode-config"
|
import { addPluginToOpenCodeConfig } from "./add-plugin-to-opencode-config"
|
||||||
|
import * as pluginNameWithVersion from "./plugin-name-with-version"
|
||||||
|
|
||||||
describe("detectCurrentConfig - single package detection", () => {
|
describe("detectCurrentConfig - single package detection", () => {
|
||||||
let testConfigDir = ""
|
let testConfigDir = ""
|
||||||
@@ -109,17 +110,19 @@ describe("addPluginToOpenCodeConfig - single package writes", () => {
|
|||||||
expect(savedConfig.plugin).toEqual(["oh-my-openagent"])
|
expect(savedConfig.plugin).toEqual(["oh-my-openagent"])
|
||||||
})
|
})
|
||||||
|
|
||||||
it("upgrades a version-pinned legacy entry to canonical", async () => {
|
it("updates a version-pinned legacy entry to the requested version", async () => {
|
||||||
// given
|
// given
|
||||||
writeFileSync(testConfigPath, JSON.stringify({ plugin: ["oh-my-opencode@3.10.0"] }, null, 2) + "\n", "utf-8")
|
const getPluginNameWithVersionSpy = spyOn(pluginNameWithVersion, "getPluginNameWithVersion").mockResolvedValue("oh-my-openagent@3.16.0")
|
||||||
|
writeFileSync(testConfigPath, JSON.stringify({ plugin: ["oh-my-opencode@3.15.0"] }, null, 2) + "\n", "utf-8")
|
||||||
|
|
||||||
// when
|
// when
|
||||||
const result = await addPluginToOpenCodeConfig("3.11.0")
|
const result = await addPluginToOpenCodeConfig("3.16.0")
|
||||||
|
|
||||||
// then
|
// then
|
||||||
expect(result.success).toBe(true)
|
expect(result.success).toBe(true)
|
||||||
const savedConfig = JSON.parse(readFileSync(testConfigPath, "utf-8"))
|
const savedConfig = JSON.parse(readFileSync(testConfigPath, "utf-8"))
|
||||||
expect(savedConfig.plugin).toEqual(["oh-my-openagent@3.10.0"])
|
expect(savedConfig.plugin).toEqual(["oh-my-openagent@3.16.0"])
|
||||||
|
getPluginNameWithVersionSpy.mockRestore()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("removes stale legacy entry when canonical and legacy entries both exist", async () => {
|
it("removes stale legacy entry when canonical and legacy entries both exist", async () => {
|
||||||
@@ -135,17 +138,36 @@ describe("addPluginToOpenCodeConfig - single package writes", () => {
|
|||||||
expect(savedConfig.plugin).toEqual(["oh-my-openagent"])
|
expect(savedConfig.plugin).toEqual(["oh-my-openagent"])
|
||||||
})
|
})
|
||||||
|
|
||||||
it("preserves a canonical entry when it already exists", async () => {
|
it("preserves a canonical entry when the same version is re-installed", async () => {
|
||||||
// given
|
// given
|
||||||
|
const getPluginNameWithVersionSpy = spyOn(pluginNameWithVersion, "getPluginNameWithVersion").mockResolvedValue("oh-my-openagent@3.10.0")
|
||||||
writeFileSync(testConfigPath, JSON.stringify({ plugin: ["oh-my-openagent@3.10.0"] }, null, 2) + "\n", "utf-8")
|
writeFileSync(testConfigPath, JSON.stringify({ plugin: ["oh-my-openagent@3.10.0"] }, null, 2) + "\n", "utf-8")
|
||||||
|
|
||||||
// when
|
// when
|
||||||
const result = await addPluginToOpenCodeConfig("3.11.0")
|
const result = await addPluginToOpenCodeConfig("3.10.0")
|
||||||
|
|
||||||
// then
|
// then
|
||||||
expect(result.success).toBe(true)
|
expect(result.success).toBe(true)
|
||||||
const savedConfig = JSON.parse(readFileSync(testConfigPath, "utf-8"))
|
const savedConfig = JSON.parse(readFileSync(testConfigPath, "utf-8"))
|
||||||
expect(savedConfig.plugin).toEqual(["oh-my-openagent@3.10.0"])
|
expect(savedConfig.plugin).toEqual(["oh-my-openagent@3.10.0"])
|
||||||
|
getPluginNameWithVersionSpy.mockRestore()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("blocks a downgrade for a version-pinned canonical entry", async () => {
|
||||||
|
// given
|
||||||
|
const getPluginNameWithVersionSpy = spyOn(pluginNameWithVersion, "getPluginNameWithVersion").mockResolvedValue("oh-my-openagent@3.15.0")
|
||||||
|
writeFileSync(testConfigPath, JSON.stringify({ plugin: ["oh-my-openagent@3.16.0"] }, null, 2) + "\n", "utf-8")
|
||||||
|
|
||||||
|
// when
|
||||||
|
const result = await addPluginToOpenCodeConfig("3.15.0")
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(result.success).toBe(false)
|
||||||
|
expect(result.error).toContain("Downgrade")
|
||||||
|
|
||||||
|
const savedConfig = JSON.parse(readFileSync(testConfigPath, "utf-8"))
|
||||||
|
expect(savedConfig.plugin).toEqual(["oh-my-openagent@3.16.0"])
|
||||||
|
getPluginNameWithVersionSpy.mockRestore()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("rewrites quoted jsonc plugin field in place", async () => {
|
it("rewrites quoted jsonc plugin field in place", async () => {
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ describe("install CLI - binary check behavior", () => {
|
|||||||
test("non-TUI mode: should still succeed and complete all steps when binary exists", async () => {
|
test("non-TUI mode: should still succeed and complete all steps when binary exists", async () => {
|
||||||
// given OpenCode binary IS installed
|
// given OpenCode binary IS installed
|
||||||
isOpenCodeInstalledSpy = spyOn(configManager, "isOpenCodeInstalled").mockResolvedValue(true)
|
isOpenCodeInstalledSpy = spyOn(configManager, "isOpenCodeInstalled").mockResolvedValue(true)
|
||||||
getOpenCodeVersionSpy = spyOn(configManager, "getOpenCodeVersion").mockResolvedValue("1.0.200")
|
getOpenCodeVersionSpy = spyOn(configManager, "getOpenCodeVersion").mockResolvedValue("1.4.0")
|
||||||
|
|
||||||
// given mock npm fetch
|
// given mock npm fetch
|
||||||
globalThis.fetch = mock(() =>
|
globalThis.fetch = mock(() =>
|
||||||
@@ -157,6 +157,6 @@ describe("install CLI - binary check behavior", () => {
|
|||||||
// then should have printed success (OK symbol)
|
// then should have printed success (OK symbol)
|
||||||
const allCalls = mockConsoleLog.mock.calls.flat().join("\n")
|
const allCalls = mockConsoleLog.mock.calls.flat().join("\n")
|
||||||
expect(allCalls).toContain("[OK]")
|
expect(allCalls).toContain("[OK]")
|
||||||
expect(allCalls).toContain("OpenCode 1.0.200")
|
expect(allCalls).toContain("OpenCode 1.4.0")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { MIN_OPENCODE_VERSION } from "./doctor/constants"
|
||||||
|
import { compareVersions } from "../shared/opencode-version"
|
||||||
|
|
||||||
|
export function getUnsupportedOpenCodeVersionMessage(openCodeVersion: string | null): string | null {
|
||||||
|
if (!openCodeVersion) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compareVersions(openCodeVersion, MIN_OPENCODE_VERSION) >= 0) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return `Detected OpenCode ${openCodeVersion}, but ${MIN_OPENCODE_VERSION}+ is required. Update OpenCode, then rerun the installer.`
|
||||||
|
}
|
||||||
@@ -0,0 +1,129 @@
|
|||||||
|
import { afterEach, beforeEach, describe, expect, it, spyOn } from "bun:test"
|
||||||
|
import * as p from "@clack/prompts"
|
||||||
|
import * as configManager from "./config-manager"
|
||||||
|
import * as tuiInstallPrompts from "./tui-install-prompts"
|
||||||
|
import { runTuiInstaller } from "./tui-installer"
|
||||||
|
|
||||||
|
function createMockSpinner(): ReturnType<typeof p.spinner> {
|
||||||
|
return {
|
||||||
|
start: () => undefined,
|
||||||
|
stop: () => undefined,
|
||||||
|
message: () => undefined,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("runTuiInstaller", () => {
|
||||||
|
const originalIsStdinTty = process.stdin.isTTY
|
||||||
|
const originalIsStdoutTty = process.stdout.isTTY
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
Object.defineProperty(process.stdin, "isTTY", { configurable: true, value: true })
|
||||||
|
Object.defineProperty(process.stdout, "isTTY", { configurable: true, value: true })
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
Object.defineProperty(process.stdin, "isTTY", { configurable: true, value: originalIsStdinTty })
|
||||||
|
Object.defineProperty(process.stdout, "isTTY", { configurable: true, value: originalIsStdoutTty })
|
||||||
|
})
|
||||||
|
|
||||||
|
it("blocks installation when OpenCode is below the minimum version", async () => {
|
||||||
|
// given
|
||||||
|
const restoreSpies = [
|
||||||
|
spyOn(p, "spinner").mockReturnValue(createMockSpinner()),
|
||||||
|
spyOn(p, "intro").mockImplementation(() => undefined),
|
||||||
|
spyOn(p.log, "warn").mockImplementation(() => undefined),
|
||||||
|
spyOn(configManager, "detectCurrentConfig").mockReturnValue({
|
||||||
|
isInstalled: false,
|
||||||
|
installedVersion: null,
|
||||||
|
hasClaude: false,
|
||||||
|
isMax20: false,
|
||||||
|
hasOpenAI: false,
|
||||||
|
hasGemini: false,
|
||||||
|
hasCopilot: false,
|
||||||
|
hasOpencodeZen: false,
|
||||||
|
hasZaiCodingPlan: false,
|
||||||
|
hasKimiForCoding: false,
|
||||||
|
hasOpencodeGo: false,
|
||||||
|
}),
|
||||||
|
spyOn(configManager, "isOpenCodeInstalled").mockResolvedValue(true),
|
||||||
|
spyOn(configManager, "getOpenCodeVersion").mockResolvedValue("1.3.9"),
|
||||||
|
]
|
||||||
|
const promptSpy = spyOn(tuiInstallPrompts, "promptInstallConfig")
|
||||||
|
const addPluginSpy = spyOn(configManager, "addPluginToOpenCodeConfig")
|
||||||
|
const outroSpy = spyOn(p, "outro").mockImplementation(() => undefined)
|
||||||
|
|
||||||
|
// when
|
||||||
|
const result = await runTuiInstaller({ tui: true }, "3.16.0")
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(result).toBe(1)
|
||||||
|
expect(promptSpy).not.toHaveBeenCalled()
|
||||||
|
expect(addPluginSpy).not.toHaveBeenCalled()
|
||||||
|
expect(outroSpy).toHaveBeenCalled()
|
||||||
|
|
||||||
|
for (const spy of restoreSpies) {
|
||||||
|
spy.mockRestore()
|
||||||
|
}
|
||||||
|
promptSpy.mockRestore()
|
||||||
|
addPluginSpy.mockRestore()
|
||||||
|
outroSpy.mockRestore()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("proceeds when OpenCode meets the minimum version", async () => {
|
||||||
|
// given
|
||||||
|
const restoreSpies = [
|
||||||
|
spyOn(p, "spinner").mockReturnValue(createMockSpinner()),
|
||||||
|
spyOn(p, "intro").mockImplementation(() => undefined),
|
||||||
|
spyOn(p.log, "info").mockImplementation(() => undefined),
|
||||||
|
spyOn(p.log, "warn").mockImplementation(() => undefined),
|
||||||
|
spyOn(p.log, "success").mockImplementation(() => undefined),
|
||||||
|
spyOn(p.log, "message").mockImplementation(() => undefined),
|
||||||
|
spyOn(p, "note").mockImplementation(() => undefined),
|
||||||
|
spyOn(p, "outro").mockImplementation(() => undefined),
|
||||||
|
spyOn(configManager, "detectCurrentConfig").mockReturnValue({
|
||||||
|
isInstalled: false,
|
||||||
|
installedVersion: null,
|
||||||
|
hasClaude: false,
|
||||||
|
isMax20: false,
|
||||||
|
hasOpenAI: false,
|
||||||
|
hasGemini: false,
|
||||||
|
hasCopilot: false,
|
||||||
|
hasOpencodeZen: false,
|
||||||
|
hasZaiCodingPlan: false,
|
||||||
|
hasKimiForCoding: false,
|
||||||
|
hasOpencodeGo: false,
|
||||||
|
}),
|
||||||
|
spyOn(configManager, "isOpenCodeInstalled").mockResolvedValue(true),
|
||||||
|
spyOn(configManager, "getOpenCodeVersion").mockResolvedValue("1.4.0"),
|
||||||
|
spyOn(tuiInstallPrompts, "promptInstallConfig").mockResolvedValue({
|
||||||
|
hasClaude: false,
|
||||||
|
isMax20: false,
|
||||||
|
hasOpenAI: false,
|
||||||
|
hasGemini: false,
|
||||||
|
hasCopilot: false,
|
||||||
|
hasOpencodeZen: false,
|
||||||
|
hasZaiCodingPlan: false,
|
||||||
|
hasKimiForCoding: false,
|
||||||
|
hasOpencodeGo: false,
|
||||||
|
}),
|
||||||
|
spyOn(configManager, "addPluginToOpenCodeConfig").mockResolvedValue({
|
||||||
|
success: true,
|
||||||
|
configPath: "/tmp/opencode.jsonc",
|
||||||
|
}),
|
||||||
|
spyOn(configManager, "writeOmoConfig").mockReturnValue({
|
||||||
|
success: true,
|
||||||
|
configPath: "/tmp/oh-my-opencode.jsonc",
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
|
||||||
|
// when
|
||||||
|
const result = await runTuiInstaller({ tui: true }, "3.16.0")
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(result).toBe(0)
|
||||||
|
|
||||||
|
for (const spy of restoreSpies) {
|
||||||
|
spy.mockRestore()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
writeOmoConfig,
|
writeOmoConfig,
|
||||||
} from "./config-manager"
|
} from "./config-manager"
|
||||||
import { detectedToInitialValues, formatConfigSummary, SYMBOLS } from "./install-validators"
|
import { detectedToInitialValues, formatConfigSummary, SYMBOLS } from "./install-validators"
|
||||||
|
import { getUnsupportedOpenCodeVersionMessage } from "./minimum-opencode-version"
|
||||||
import { promptInstallConfig } from "./tui-install-prompts"
|
import { promptInstallConfig } from "./tui-install-prompts"
|
||||||
|
|
||||||
export async function runTuiInstaller(args: InstallArgs, version: string): Promise<number> {
|
export async function runTuiInstaller(args: InstallArgs, version: string): Promise<number> {
|
||||||
@@ -39,6 +40,13 @@ export async function runTuiInstaller(args: InstallArgs, version: string): Promi
|
|||||||
p.note("Visit https://opencode.ai/docs for installation instructions", "Installation Guide")
|
p.note("Visit https://opencode.ai/docs for installation instructions", "Installation Guide")
|
||||||
} else {
|
} else {
|
||||||
spinner.stop(`OpenCode ${openCodeVersion ?? "installed"} ${color.green("[OK]")}`)
|
spinner.stop(`OpenCode ${openCodeVersion ?? "installed"} ${color.green("[OK]")}`)
|
||||||
|
|
||||||
|
const unsupportedVersionMessage = getUnsupportedOpenCodeVersionMessage(openCodeVersion)
|
||||||
|
if (unsupportedVersionMessage) {
|
||||||
|
p.log.warn(unsupportedVersionMessage)
|
||||||
|
p.outro(color.red("Installation blocked."))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const config = await promptInstallConfig(detected)
|
const config = await promptInstallConfig(detected)
|
||||||
|
|||||||
Reference in New Issue
Block a user